Replace parser filter with separate filters
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 10 Nov 2016 20:10:11 +0000 (21:10 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 11 Nov 2016 07:10:00 +0000 (08:10 +0100)
14 files changed:
src/main/java/net/pterodactylus/sone/template/ParserFilter.java [deleted file]
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/java/net/pterodactylus/sone/web/ajax/EditImageAjaxPage.java
src/main/kotlin/net/pterodactylus/sone/template/ParserFilter.kt [new file with mode: 0644]
src/main/resources/templates/imageBrowser.html
src/main/resources/templates/include/browseAlbums.html
src/main/resources/templates/include/viewPost.html
src/main/resources/templates/include/viewReply.html
src/main/resources/templates/invalid.html
src/main/resources/templates/notify/newVersionNotification.html
src/main/resources/templates/notify/soneInsertNotification.html
src/main/resources/templates/viewSone.html
src/test/java/net/pterodactylus/sone/template/ParserFilterTest.java [deleted file]
src/test/kotlin/net/pterodactylus/sone/template/ParserFilterTest.kt [new file with mode: 0644]

diff --git a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java
deleted file mode 100644 (file)
index e2afb9c..0000000
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Sone - ParserFilter.java - Copyright © 2011–2016 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.template;
-
-import static java.lang.String.valueOf;
-import static net.pterodactylus.sone.utils.NumberParsers.parseInt;
-
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-import net.pterodactylus.sone.core.Core;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.text.FreemailPart;
-import net.pterodactylus.sone.text.FreenetLinkPart;
-import net.pterodactylus.sone.text.LinkPart;
-import net.pterodactylus.sone.text.Part;
-import net.pterodactylus.sone.text.PlainTextPart;
-import net.pterodactylus.sone.text.PostPart;
-import net.pterodactylus.sone.text.SonePart;
-import net.pterodactylus.sone.text.SoneTextParser;
-import net.pterodactylus.sone.text.SoneTextParserContext;
-import net.pterodactylus.util.template.Filter;
-import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
-import net.pterodactylus.util.template.TemplateContextFactory;
-import net.pterodactylus.util.template.TemplateParser;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-
-/**
- * Filter that filters a given text through a {@link SoneTextParser}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class ParserFilter implements Filter {
-
-       /** The core. */
-       private final Core core;
-
-       /** The link parser. */
-       private final SoneTextParser soneTextParser;
-
-       /** The template context factory. */
-       private final TemplateContextFactory templateContextFactory;
-
-       /** The template for {@link PlainTextPart}s. */
-       private static final Template plainTextTemplate = TemplateParser.parse(new StringReader("<%text|html>"));
-
-       /** The template for {@link FreenetLinkPart}s. */
-       private static final Template linkTemplate = TemplateParser.parse(new StringReader("<a class=\"<%cssClass|html>\" href=\"<%link|html>\" title=\"<%title|html>\"><%text|html></a>"));
-
-       /**
-        * Creates a new filter that runs its input through a {@link SoneTextParser}
-        * .
-        *
-        * @param core
-        *            The core
-        * @param templateContextFactory
-        *            The context factory for rendering the parts
-        * @param soneTextParser
-        *            The Sone text parser
-        */
-       public ParserFilter(Core core, TemplateContextFactory templateContextFactory, SoneTextParser soneTextParser) {
-               this.core = core;
-               this.templateContextFactory = templateContextFactory;
-               this.soneTextParser = soneTextParser;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
-               String text = valueOf(data);
-               int length = parseInt(valueOf(parameters.get("length")), -1);
-               int cutOffLength = parseInt(valueOf(parameters.get("cut-off-length")), length);
-               Object sone = parameters.get("sone");
-               if (sone instanceof String) {
-                       sone = core.getSone((String) sone).orNull();
-               }
-               SoneTextParserContext context = new SoneTextParserContext((Sone) sone);
-               StringWriter parsedTextWriter = new StringWriter();
-               Iterable<Part> parts = soneTextParser.parse(text, context);
-               if (length > -1) {
-                       int allPartsLength = 0;
-                       List<Part> shortenedParts = new ArrayList<Part>();
-                       for (Part part : parts) {
-                               if (part instanceof PlainTextPart) {
-                                       String longText = part.getText();
-                                       if (allPartsLength < cutOffLength) {
-                                               if ((allPartsLength + longText.length()) > cutOffLength) {
-                                                       shortenedParts.add(new PlainTextPart(longText.substring(0, cutOffLength - allPartsLength) + "…"));
-                                               } else {
-                                                       shortenedParts.add(part);
-                                               }
-                                       }
-                                       allPartsLength += longText.length();
-                               } else if (part instanceof LinkPart) {
-                                       if (allPartsLength < cutOffLength) {
-                                               shortenedParts.add(part);
-                                       }
-                                       allPartsLength += part.getText().length();
-                               } else {
-                                       if (allPartsLength < cutOffLength) {
-                                               shortenedParts.add(part);
-                                       }
-                               }
-                       }
-                       if (allPartsLength >= length) {
-                               parts = shortenedParts;
-                       }
-               }
-               render(parsedTextWriter, parts);
-               return parsedTextWriter.toString();
-       }
-
-       //
-       // PRIVATE METHODS
-       //
-
-       /**
-        * Renders the given parts.
-        *
-        * @param writer
-        *            The writer to render the parts to
-        * @param parts
-        *            The parts to render
-        */
-       private void render(Writer writer, Iterable<Part> parts) {
-               for (Part part : parts) {
-                       render(writer, part);
-               }
-       }
-
-       /**
-        * Renders the given part.
-        *
-        * @param writer
-        *            The writer to render the part to
-        * @param part
-        *            The part to render
-        */
-       @SuppressWarnings("unchecked")
-       private void render(Writer writer, Part part) {
-               if (part instanceof PlainTextPart) {
-                       render(writer, (PlainTextPart) part);
-               } else if (part instanceof FreenetLinkPart) {
-                       render(writer, (FreenetLinkPart) part);
-               } else if (part instanceof LinkPart) {
-                       render(writer, (LinkPart) part);
-               } else if (part instanceof SonePart) {
-                       render(writer, (SonePart) part);
-               } else if (part instanceof PostPart) {
-                       render(writer, (PostPart) part);
-               } else if (part instanceof FreemailPart) {
-                       render(writer, (FreemailPart) part);
-               } else if (part instanceof Iterable<?>) {
-                       render(writer, (Iterable<Part>) part);
-               }
-       }
-
-       /**
-        * Renders the given plain-text part.
-        *
-        * @param writer
-        *            The writer to render the part to
-        * @param plainTextPart
-        *            The part to render
-        */
-       private void render(Writer writer, PlainTextPart plainTextPart) {
-               TemplateContext templateContext = templateContextFactory.createTemplateContext();
-               templateContext.set("text", plainTextPart.getText());
-               plainTextTemplate.render(templateContext, writer);
-       }
-
-       /**
-        * Renders the given freenet link part.
-        *
-        * @param writer
-        *            The writer to render the part to
-        * @param freenetLinkPart
-        *            The part to render
-        */
-       private void render(Writer writer, FreenetLinkPart freenetLinkPart) {
-               renderLink(writer, "/" + freenetLinkPart.getLink(), freenetLinkPart.getText(), freenetLinkPart.getTitle(), freenetLinkPart.isTrusted() ? "freenet-trusted" : "freenet");
-       }
-
-       /**
-        * Renders the given link part.
-        *
-        * @param writer
-        *            The writer to render the part to
-        * @param linkPart
-        *            The part to render
-        */
-       private void render(Writer writer, LinkPart linkPart) {
-               try {
-                       renderLink(writer, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode(linkPart.getLink(), "UTF-8"), linkPart.getText(), linkPart.getTitle(), "internet");
-               } catch (UnsupportedEncodingException uee1) {
-                       /* not possible for UTF-8. */
-                       throw new RuntimeException("The JVM does not support UTF-8 encoding!", uee1);
-               }
-       }
-
-       /**
-        * Renders the given Sone part.
-        *
-        * @param writer
-        *            The writer to render the part to
-        * @param sonePart
-        *            The part to render
-        */
-       private void render(Writer writer, SonePart sonePart) {
-               if ((sonePart.getSone() != null) && (sonePart.getSone().getName() != null)) {
-                       renderLink(writer, "viewSone.html?sone=" + sonePart.getSone().getId(), SoneAccessor.getNiceName(sonePart.getSone()), SoneAccessor.getNiceName(sonePart.getSone()), "in-sone");
-               } else {
-                       renderLink(writer, "/WebOfTrust/ShowIdentity?id=" + sonePart.getSone().getId(), sonePart.getSone().getId(), sonePart.getSone().getId(), "in-sone");
-               }
-       }
-
-       /**
-        * Renders the given post part.
-        *
-        * @param writer
-        *            The writer to render the part to
-        * @param postPart
-        *            The part to render
-        */
-       private void render(Writer writer, PostPart postPart) {
-               SoneTextParser parser = new SoneTextParser(core, core);
-               SoneTextParserContext parserContext = new SoneTextParserContext(postPart.getPost().getSone());
-               Iterable<Part> parts = parser.parse(postPart.getPost().getText(), parserContext);
-               StringBuilder excerpt = new StringBuilder();
-               for (Part part : parts) {
-                       excerpt.append(part.getText());
-                       if (excerpt.length() > 20) {
-                               int lastSpace = excerpt.lastIndexOf(" ", 20);
-                               if (lastSpace > -1) {
-                                       excerpt.setLength(lastSpace);
-                               } else {
-                                       excerpt.setLength(20);
-                               }
-                               excerpt.append("…");
-                               break;
-                       }
-               }
-               renderLink(writer, "viewPost.html?post=" + postPart.getPost().getId(), excerpt.toString(), SoneAccessor.getNiceName(postPart.getPost().getSone()), "in-sone");
-       }
-
-       private void render(@Nonnull Writer writer, @Nonnull FreemailPart freemailPart) {
-               Optional<Sone> sone = core.getSone(freemailPart.getIdentityId());
-               String soneName = sone.transform(new Function<Sone, String>() {
-                       @Nonnull
-                       @Override
-                       public String apply(Sone input) {
-                               return SoneAccessor.getNiceName(input);
-                       }
-               }).or(freemailPart.getIdentityId());
-               renderLink(writer,
-                               "/Freemail/NewMessage?to=" + freemailPart.getIdentityId(),
-                               String.format("%s@%s.freemail", freemailPart.getEmailLocalPart(), soneName),
-                               String.format("%s\n%s@%s.freemail", soneName, freemailPart.getEmailLocalPart(), freemailPart.getFreemailId()),
-                               "in-sone");
-       }
-
-       /**
-        * Renders the given link.
-        *
-        * @param writer
-        *            The writer to render the link to
-        * @param link
-        *            The link to render
-        * @param text
-        *            The text of the link
-        * @param title
-        *            The title of the link
-        * @param cssClass
-        *            The CSS class of the link
-        */
-       private void renderLink(Writer writer, String link, String text, String title, String cssClass) {
-               TemplateContext templateContext = templateContextFactory.createTemplateContext();
-               templateContext.set("cssClass", cssClass);
-               templateContext.set("link", link);
-               templateContext.set("text", text);
-               templateContext.set("title", title);
-               linkTemplate.render(templateContext, writer);
-       }
-
-}
index b7e3287..bbb3396 100644 (file)
@@ -87,6 +87,7 @@ import net.pterodactylus.sone.template.JavascriptFilter;
 import net.pterodactylus.sone.template.ParserFilter;
 import net.pterodactylus.sone.template.PostAccessor;
 import net.pterodactylus.sone.template.ProfileAccessor;
+import net.pterodactylus.sone.template.RenderFilter;
 import net.pterodactylus.sone.template.ReplyAccessor;
 import net.pterodactylus.sone.template.ReplyGroupFilter;
 import net.pterodactylus.sone.template.RequestChangeFilter;
@@ -198,6 +199,7 @@ public class WebInterface {
 
        /** The parser filter. */
        private final ParserFilter parserFilter;
+       private final RenderFilter renderFilter;
 
        private final ListNotificationFilter listNotificationFilter;
        private final PostVisibilityFilter postVisibilityFilter;
@@ -284,7 +286,8 @@ public class WebInterface {
                templateContextFactory.addFilter("match", new MatchFilter());
                templateContextFactory.addFilter("css", new CssClassNameFilter());
                templateContextFactory.addFilter("js", new JavascriptFilter());
-               templateContextFactory.addFilter("parse", parserFilter = new ParserFilter(getCore(), templateContextFactory, soneTextParser));
+               templateContextFactory.addFilter("parse", parserFilter = new ParserFilter(getCore(), soneTextParser));
+               templateContextFactory.addFilter("render", renderFilter = new RenderFilter(getCore(), templateContextFactory));
                templateContextFactory.addFilter("reparse", new ReparseFilter());
                templateContextFactory.addFilter("unknown", new UnknownDateFilter(getL10n(), "View.Sone.Text.UnknownDate"));
                templateContextFactory.addFilter("format", new FormatFilter());
@@ -745,7 +748,7 @@ public class WebInterface {
                pageToadlets.add(pageToadletFactory.createPageToadlet(new FollowSoneAjaxPage(this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new UnfollowSoneAjaxPage(this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new EditAlbumAjaxPage(this)));
-               pageToadlets.add(pageToadletFactory.createPageToadlet(new EditImageAjaxPage(this, parserFilter)));
+               pageToadlets.add(pageToadletFactory.createPageToadlet(new EditImageAjaxPage(this, parserFilter, renderFilter)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new TrustAjaxPage(this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new DistrustAjaxPage(this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new UntrustAjaxPage(this)));
index 6f67ccb..f606e50 100644 (file)
@@ -19,6 +19,8 @@ package net.pterodactylus.sone.web.ajax;
 
 import net.pterodactylus.sone.data.Image;
 import net.pterodactylus.sone.template.ParserFilter;
+import net.pterodactylus.sone.template.RenderFilter;
+import net.pterodactylus.sone.text.Part;
 import net.pterodactylus.sone.text.TextFilter;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.sone.web.page.FreenetRequest;
@@ -33,8 +35,8 @@ import com.google.common.collect.ImmutableMap;
  */
 public class EditImageAjaxPage extends JsonPage {
 
-       /** Parser for image descriptions. */
        private final ParserFilter parserFilter;
+       private final RenderFilter renderFilter;
 
        /**
         * Creates a new edit image AJAX page.
@@ -44,9 +46,10 @@ public class EditImageAjaxPage extends JsonPage {
         * @param parserFilter
         *            The parser filter for image descriptions
         */
-       public EditImageAjaxPage(WebInterface webInterface, ParserFilter parserFilter) {
+       public EditImageAjaxPage(WebInterface webInterface, ParserFilter parserFilter, RenderFilter renderFilter) {
                super("editImage.ajax", webInterface);
                this.parserFilter = parserFilter;
+               this.renderFilter = renderFilter;
        }
 
        //
@@ -83,7 +86,14 @@ public class EditImageAjaxPage extends JsonPage {
                String description = request.getHttpRequest().getParam("description").trim();
                image.modify().setTitle(title).setDescription(TextFilter.filter(request.getHttpRequest().getHeader("host"), description)).update();
                webInterface.getCore().touchConfiguration();
-               return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", (String) parserFilter.format(new TemplateContext(), image.getDescription(), ImmutableMap.<String, Object>builder().put("sone", image.getSone()).build()));
+               return createSuccessJsonObject().put("imageId", image.getId()).put("title", image.getTitle()).put("description", image.getDescription()).put("parsedDescription", renderImageDescription(image));
+       }
+
+       private String renderImageDescription(Image image) {
+               TemplateContext templateContext = new TemplateContext();
+               ImmutableMap<String, Object> parameters = ImmutableMap.<String, Object>builder().put("sone", image.getSone()).build();
+               Object parts = parserFilter.format(templateContext, image.getDescription(), parameters);
+               return (String) renderFilter.format(templateContext, parts, parameters);
        }
 
 }
diff --git a/src/main/kotlin/net/pterodactylus/sone/template/ParserFilter.kt b/src/main/kotlin/net/pterodactylus/sone/template/ParserFilter.kt
new file mode 100644 (file)
index 0000000..4d2f9c1
--- /dev/null
@@ -0,0 +1,28 @@
+package net.pterodactylus.sone.template
+
+import net.pterodactylus.sone.core.Core
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.text.Part
+import net.pterodactylus.sone.text.SoneTextParser
+import net.pterodactylus.sone.text.SoneTextParserContext
+import net.pterodactylus.util.template.Filter
+import net.pterodactylus.util.template.TemplateContext
+
+/**
+ * Parses a [String] into a number of [Part]s.
+ */
+class ParserFilter(private val core: Core, private val soneTextParser: SoneTextParser) : Filter {
+
+       override fun format(templateContext: TemplateContext?, data: Any?, parameters: MutableMap<String, Any?>?): Any? {
+               val text = data?.toString() ?: return listOf<Part>()
+               val soneParameter = parameters?.get("sone")
+               val sone = when (soneParameter) {
+                       is String -> core.getSone(soneParameter).orNull()
+                       is Sone -> soneParameter
+                       else -> null
+               }
+               val context = SoneTextParserContext(sone as? Sone)
+               return soneTextParser.parse(text, context)
+       }
+
+}
index 644848a..7e4c824 100644 (file)
                                <%/foreach>
                        </div>
 
-                       <p id="description"><% album.description|parse sone=album.sone></p>
+                       <p id="description"><% album.description|parse sone=album.sone|render></p>
 
                        <%if album.sone.local>
                                <div class="show-edit-album hidden toggle-link"><a class="small-link">» <%= Page.ImageBrowser.Album.Edit.Title|l10n|html></a></div>
                                        </div>
                                        <div class="show-data">
                                                <div class="image-title"><% image.title|html></div>
-                                               <div class="image-description"><% image.description|parse sone=image.sone></div>
+                                               <div class="image-description"><% image.description|parse sone=image.sone|render></div>
                                        </div>
                                        <%if album.sone.local>
                                                <form class="edit-image" action="editImage.html" method="post">
                                <%/if>
                        </div>
 
-                       <p class="parsed"><%image.description|parse sone=image.sone></p>
+                       <p class="parsed"><%image.description|parse sone=image.sone|render></p>
 
                        <%if image.sone.local>
 
                                <div class="show-data">
                                        <div class="album-sone"><a href="imageBrowser.html?sone=<%album.sone.id|html>"><%album.sone.niceName|html></a></div>
                                        <div class="album-title"><% album.title|html> (<%= View.Sone.Stats.Images|l10n 0=album.images.size>)</div>
-                                       <div class="album-description"><% album.description|parse sone=album.sone></div>
+                                       <div class="album-description"><% album.description|parse sone=album.sone|render></div>
                                </div>
                        </div>
                        <%= false|store key==endRow>
index 9aacad3..7c8c490 100644 (file)
@@ -14,7 +14,7 @@
                </div>
                <div class="show-data">
                        <div class="album-title"><% album.title|html> (<%= View.Sone.Stats.Images|l10n 0=album.images.size>)</div>
-                       <div class="album-description"><% album.description|parse sone=album.sone></div>
+                       <div class="album-description"><% album.description|parse sone=album.sone|render></div>
                </div>
                <%if album.sone.local>
                        <form class="edit-album" action="editAlbum.html" method="post">
index 1c53d42..24ce420 100644 (file)
@@ -27,8 +27,8 @@
                                <%/if>
                        <%/if>
                        <% post.text|html|store key==originalText text==true>
-                       <% post.text|parse sone=post.sone|store key==parsedText text==true>
-                       <% post.text|parse sone=post.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|store key==shortText text==true>
+                       <% post.text|parse sone=post.sone|render|store key==parsedText text==true>
+                       <% post.text|parse sone=post.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|render|store key==shortText text==true>
                        <div class="post-text raw-text<%if !raw> hidden<%/if>"><% originalText></div>
                        <div class="post-text text<%if raw> hidden<%/if><%if !shortText|match key=parsedText> hidden<%/if>"><% parsedText></div>
                        <div class="post-text short-text<%if raw> hidden<%/if><%if shortText|match key=parsedText> hidden<%/if>"><% shortText></div>
index 5e2ed7d..1ef4d99 100644 (file)
@@ -15,8 +15,8 @@
                <div>
                        <div class="author profile-link"><a href="viewSone.html?sone=<% reply.sone.id|html>"><% reply.sone.niceName|html></a></div>
                        <% reply.text|html|store key==originalText text==true>
-                       <% reply.text|parse sone=reply.sone|store key==parsedText text==true>
-                       <% reply.text|parse sone=reply.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|store key==shortText text==true>
+                       <% reply.text|parse sone=reply.sone|render|store key==parsedText text==true>
+                       <% reply.text|parse sone=reply.sone length=core.preferences.charactersPerPost cut-off-length=core.preferences.postCutOffLength|render|store key==shortText text==true>
                        <div class="reply-text raw-text<%if !raw> hidden<%/if>"><% originalText></div>
                        <div class="reply-text text<%if raw> hidden<%/if><%if !shortText|match key=parsedText> hidden<%/if>"><% parsedText></div>
                        <div class="reply-text short-text<%if raw> hidden<%/if><%if shortText|match key=parsedText> hidden<%/if>"><% shortText></div>
index 3ac37cf..922829c 100644 (file)
@@ -4,9 +4,9 @@
 
        <%foreach messages message>
                <%if message|substring start==0 length==1|match value=='!'>
-                       <p class="error"><% message|substring start==1|parse></p>
+                       <p class="error"><% message|substring start==1|parse|render></p>
                <%else>
-                       <p><% message|parse></p>
+                       <p><% message|parse|render></p>
                <%/if>
        <%foreachelse>
                <p><%= Page.Invalid.Text|l10n|html|replace needle=="{link}" replacement=='<a href="index.html">'|replace needle=="{/link}" replacement=='</a>'></p>
index f35ecaf..fa9c53e 100644 (file)
@@ -1,4 +1,4 @@
-<div class="text"><%= Notification.NewVersion.Text|l10n|replace needle=="{version}" replacement=latestVersion|replace needle=="{edition}" replacement=latestEdition|parse sone=="nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"></div>
+<div class="text"><%= Notification.NewVersion.Text|l10n|replace needle=="{version}" replacement=latestVersion|replace needle=="{edition}" replacement=latestEdition|parse sone=="nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"|render></div>
 <%if disruptive>
        <div class="text"><%= Notification.NewVersion.Disruptive.Text|l10n|html|replace needle=="{em}" replacement=="<em>"|replace needle=="{/em}" replacement=="</em>"></div>
 <%/if>
index c15864f..8965e36 100644 (file)
@@ -1,7 +1,7 @@
 <%if soneStatus|match value=="inserting">
-       <%= Notification.SoneIsInserting.Text|l10n 0=insertSone.id|parse>
+       <%= Notification.SoneIsInserting.Text|l10n 0=insertSone.id|parse|render>
 <%elseif soneStatus|match value=="inserted">
-       <%= Notification.SoneIsInserted.Text|l10n 0=insertSone.id 1=insertDuration|parse>
+       <%= Notification.SoneIsInserted.Text|l10n 0=insertSone.id 1=insertDuration|parse|render>
 <%elseif soneStatus|match value=="insert-aborted">
-       <%= Notification.SoneInsertAborted.Text|l10n 0=insertSone.id|parse>
+       <%= Notification.SoneInsertAborted.Text|l10n 0=insertSone.id|parse|render>
 <%/if>
index 4b88474..dd994ea 100644 (file)
@@ -52,7 +52,7 @@
                        <%foreach sone.profile.fields field>
                                <div class="profile-field">
                                        <div class="name"><% field.name|html></div>
-                                       <div class="value"><% field.value|parse sone=sone></div>
+                                       <div class="value"><% field.value|parse sone=sone|render></div>
                                </div>
                        <%/foreach>
 
diff --git a/src/test/java/net/pterodactylus/sone/template/ParserFilterTest.java b/src/test/java/net/pterodactylus/sone/template/ParserFilterTest.java
deleted file mode 100644 (file)
index 5820652..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-package net.pterodactylus.sone.template;
-
-import static java.util.Arrays.asList;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.ArgumentCaptor.forClass;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.net.URLEncoder;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import net.pterodactylus.sone.core.Core;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.Profile;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.text.FreemailPart;
-import net.pterodactylus.sone.text.FreenetLinkPart;
-import net.pterodactylus.sone.text.LinkPart;
-import net.pterodactylus.sone.text.Part;
-import net.pterodactylus.sone.text.PartContainer;
-import net.pterodactylus.sone.text.PlainTextPart;
-import net.pterodactylus.sone.text.PostPart;
-import net.pterodactylus.sone.text.SonePart;
-import net.pterodactylus.sone.text.SoneTextParser;
-import net.pterodactylus.sone.text.SoneTextParserContext;
-import net.pterodactylus.util.template.HtmlFilter;
-import net.pterodactylus.util.template.TemplateContext;
-import net.pterodactylus.util.template.TemplateContextFactory;
-
-import com.google.common.base.Optional;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Attribute;
-import org.jsoup.nodes.Element;
-import org.jsoup.nodes.TextNode;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-/**
- * Unit test for {@link ParserFilter}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class ParserFilterTest {
-
-       private static final String FREEMAIL_ID = "t4dlzfdww3xvsnsc6j6gtliox6zaoak7ymkobbmcmdw527ubuqra";
-       private static final String SONE_FREEMAIL = "sone@" + FREEMAIL_ID + ".freemail";
-       private static final String SONE_IDENTITY = "nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI";
-       private static final String POST_ID = "37a06250-6775-4b94-86ff-257ba690953c";
-
-       private final Core core = mock(Core.class);
-       private final TemplateContextFactory templateContextFactory = new TemplateContextFactory();
-       private final TemplateContext templateContext;
-       private final SoneTextParser soneTextParser = mock(SoneTextParser.class);
-       private final ParserFilter filter = new ParserFilter(core, templateContextFactory, soneTextParser);
-       private final Sone sone = setupSone(SONE_IDENTITY, "Sone", "First");
-       private final Map<String, Object> parameters = new HashMap<>();
-
-       public ParserFilterTest() {
-               templateContextFactory.addFilter("html", new HtmlFilter());
-               templateContext = templateContextFactory.createTemplateContext();
-       }
-
-       @Test
-       public void givenSoneIsUsedInParseContext() {
-               setupSoneAndVerifyItIsUsedInContext(sone, sone);
-       }
-
-       private void setupSoneAndVerifyItIsUsedInContext(Object soneOrSoneId, Sone sone) {
-               setupParser("text", new PlainTextPart("text"));
-               parameters.put("sone", sone);
-               filter.format(templateContext, "text", parameters);
-               ArgumentCaptor<SoneTextParserContext> context = forClass(SoneTextParserContext.class);
-               verify(soneTextParser).parse(eq("text"), context.capture());
-               assertThat(context.getValue().getPostingSone(), is(sone));
-       }
-
-       @Test
-       public void soneWithGivenSoneIdIsUsedInParseContext() {
-               setupSoneAndVerifyItIsUsedInContext(SONE_IDENTITY, sone);
-       }
-
-       @Test
-       public void plainTextIsRenderedCorrectly() {
-               setupParser("plain text", new PlainTextPart("plain text"));
-               String result = (String) filter.format(templateContext, "plain text", Collections.<String, Object>emptyMap());
-               assertThat(result, is("plain text"));
-       }
-
-       private void setupParser(String text, Part... parsedParts) {
-               when(soneTextParser.parse(eq(text), any(SoneTextParserContext.class))).thenReturn(asList(parsedParts));
-       }
-
-       @Test
-       public void plainTextPartIsShortenedIfLengthExceedsMaxLength() {
-               setupParser("text", new PlainTextPart("This is a long text."));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               assertThat(output, is("This is a &hellip;"));
-       }
-
-       @Test
-       public void plainTextPartIsNotShortenedIfLengthDoesNotExceedMaxLength() {
-               setupParser("text", new PlainTextPart("This is a long text."));
-               setLengthAndCutOffLength(20, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               assertThat(output, is("This is a &hellip;"));
-       }
-
-       @Test
-       public void shortPartsAreNotShortened() {
-               setupParser("text", new PlainTextPart("This."));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               assertThat(output, is("This."));
-       }
-
-       @Test
-       public void multiplePlainTextPartsAreShortened() {
-               setupParser("text", new PlainTextPart("This "), new PlainTextPart("is a long text."));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               assertThat(output, is("This is a &hellip;"));
-       }
-
-       @Test
-       public void partsAfterLengthHasBeenReachedAreIgnored() {
-               setupParser("text", new PlainTextPart("This is a long text."), new PlainTextPart(" And even more."));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               assertThat(output, is("This is a &hellip;"));
-       }
-
-       @Test
-       public void linkPartsAreNotShortened() {
-               setupParser("text", new FreenetLinkPart("KSK@gpl.txt", "This is a long text.", false));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               Element linkNode = Jsoup.parseBodyFragment(output).body().child(0);
-               verifyLink(linkNode, "/KSK@gpl.txt", "freenet", "KSK@gpl.txt", "This is a long text.");
-       }
-
-       @Test
-       public void additionalLinkPartsAreIgnored() {
-               setupParser("text", new PlainTextPart("This is a long text."), new FreenetLinkPart("KSK@gpl.txt", "This is a long text.", false));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               assertThat(output, is("This is a &hellip;"));
-       }
-
-       private void setLengthAndCutOffLength(int length, int cutOffLength) {
-               parameters.put("length", length);
-               parameters.put("cut-off-length", cutOffLength);
-       }
-
-       @Test
-       public void sonePartsAreAddedButTheirLengthIsIgnored() {
-               setupParser("text", new SonePart(sone), new PlainTextPart("This is a long text."));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               Element body = Jsoup.parseBodyFragment(output).body();
-               Element linkNode = (Element) body.childNode(0);
-               System.out.println(linkNode);
-               verifyLink(linkNode, "viewSone.html?sone=" + SONE_IDENTITY, "in-sone", "First", "First");
-               assertThat(((TextNode) body.childNode(1)).text(), is("This is a …"));
-       }
-
-       @Test
-       public void additionalSonePartsAreIgnored() {
-               setupParser("text", new PlainTextPart("This is a long text."), new SonePart(sone));
-               setLengthAndCutOffLength(15, 10);
-               String output = (String) filter.format(templateContext, "text", parameters);
-               assertThat(output, is("This is a &hellip;"));
-       }
-
-       @Test
-       public void freenetLinkIsRenderedCorrectly() {
-               setupParser("KSK@gpl.txt", new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false));
-               Element linkNode = filterText("KSK@gpl.txt");
-               verifyLink(linkNode, "/KSK@gpl.txt", "freenet", "KSK@gpl.txt", "gpl.txt");
-       }
-
-       private void verifyLink(Element linkNode, String url, String cssClass, String tooltip, String text) {
-               assertThat(linkNode.nodeName(), is("a"));
-               assertThat(linkNode.attributes().asList(), containsInAnyOrder(
-                               new Attribute("href", url),
-                               new Attribute("class", cssClass),
-                               new Attribute("title", tooltip)
-               ));
-               assertThat(linkNode.text(), is(text));
-       }
-
-       @Test
-       public void trustedFreenetLinkIsRenderedWithCorrectCssClass() {
-               setupParser("KSK@gpl.txt", new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", true));
-               Element linkNode = filterText("KSK@gpl.txt");
-               verifyLink(linkNode, "/KSK@gpl.txt", "freenet-trusted", "KSK@gpl.txt", "gpl.txt");
-       }
-
-       private Element filterText(String text) {
-               String output = (String) filter.format(templateContext, text, Collections.<String, Object>emptyMap());
-               return Jsoup.parseBodyFragment(output).body().child(0);
-       }
-
-       @Test
-       public void internetLinkIsRenderedCorrectly() throws Exception {
-               setupParser("http://test.com/test.html", new LinkPart("http://test.com/test.html", "test.com/test.html"));
-               Element linkNode = filterText("http://test.com/test.html");
-               verifyLink(linkNode, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode("http://test.com/test.html", "UTF-8"), "internet",
-                               "http://test.com/test.html", "test.com/test.html");
-       }
-
-       @Test
-       public void sonePartsAreRenderedCorrectly() {
-               setupParser("sone://" + SONE_IDENTITY, new SonePart(sone));
-               Element linkNode = filterText("sone://" + SONE_IDENTITY);
-               verifyLink(linkNode, "viewSone.html?sone=" + SONE_IDENTITY, "in-sone", "First", "First");
-       }
-
-       private Sone setupSone(String identity, String name, String firstName) {
-               Sone sone = mock(Sone.class);
-               when(sone.getId()).thenReturn(identity);
-               when(sone.getProfile()).thenReturn(new Profile(sone));
-               when(sone.getName()).thenReturn(name);
-               sone.getProfile().setFirstName(firstName);
-               when(core.getSone(identity)).thenReturn(Optional.of(sone));
-               return sone;
-       }
-
-       @Test
-       public void sonePartsWithUnknownSoneIsRenderedAsLinkToWebOfTrust() {
-               Sone sone = setupSone(SONE_IDENTITY, null, "First");
-               setupParser("sone://" + SONE_IDENTITY, new SonePart(sone));
-               Element linkNode = filterText("sone://" + SONE_IDENTITY);
-               verifyLink(linkNode, "/WebOfTrust/ShowIdentity?id=" + SONE_IDENTITY, "in-sone", SONE_IDENTITY, SONE_IDENTITY);
-       }
-
-       @Test
-       public void postPartIsCutOffCorrectlyWhenThereAreSpaces() {
-               Post post = setupPost(sone, "1234 678901 345 789012 45678 01.");
-               setupParser("post://" + POST_ID, new PostPart(post));
-               Element linkNode = filterText("post://" + POST_ID);
-               verifyLink(linkNode, "viewPost.html?post=" + POST_ID, "in-sone", "First", "1234 678901 345…");
-       }
-
-       private Post setupPost(Sone sone, String value) {
-               Post post = mock(Post.class);
-               when(post.getId()).thenReturn(POST_ID);
-               when(post.getSone()).thenReturn(sone);
-               when(post.getText()).thenReturn(value);
-               return post;
-       }
-
-       @Test
-       public void postPartIsCutOffCorrectlyWhenThereAreNoSpaces() {
-               Post post = setupPost(sone, "1234567890123456789012345678901.");
-               setupParser("post://" + POST_ID, new PostPart(post));
-               Element linkNode = filterText("post://" + POST_ID);
-               verifyLink(linkNode, "viewPost.html?post=" + POST_ID, "in-sone", "First", "12345678901234567890…");
-       }
-
-       @Test
-       public void postPartShorterThan21CharsIsNotCutOff() {
-               Post post = setupPost(sone, "12345678901234567890");
-               setupParser("post://" + POST_ID, new PostPart(post));
-               Element linkNode = filterText("post://" + POST_ID);
-               verifyLink(linkNode, "viewPost.html?post=" + POST_ID, "in-sone", "First", "12345678901234567890");
-       }
-
-       @Test
-       public void multiplePartsAreRenderedCorrectly() {
-               PartContainer parts = new PartContainer();
-               parts.add(new PlainTextPart("te"));
-               parts.add(new PlainTextPart("xt"));
-               setupParser("text", parts);
-               String result = (String) filter.format(templateContext, "text", Collections.<String, Object>emptyMap());
-               assertThat(result, is("text"));
-       }
-
-       @Test
-       public void freemailAddressIsDisplayedCorrectly() {
-               setupParser(SONE_FREEMAIL, new FreemailPart("sone", FREEMAIL_ID, SONE_IDENTITY));
-               Element linkNode = filterText(SONE_FREEMAIL);
-               verifyLink(linkNode, "/Freemail/NewMessage?to=" + SONE_IDENTITY, "in-sone", "First\n" + SONE_FREEMAIL, "sone@First.freemail");
-       }
-
-}
diff --git a/src/test/kotlin/net/pterodactylus/sone/template/ParserFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/ParserFilterTest.kt
new file mode 100644 (file)
index 0000000..3c735c2
--- /dev/null
@@ -0,0 +1,65 @@
+package net.pterodactylus.sone.template
+
+import com.google.common.base.Optional.of
+import net.pterodactylus.sone.core.Core
+import net.pterodactylus.sone.data.Sone
+import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.text.SoneTextParser
+import net.pterodactylus.sone.text.SoneTextParserContext
+import net.pterodactylus.util.template.TemplateContext
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.emptyIterable
+import org.junit.Test
+import org.mockito.ArgumentCaptor.forClass
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+
+/**
+ * Unit test for [ParserFilter].
+ */
+class ParserFilterTest {
+
+       companion object {
+               private const val SONE_IDENTITY = "nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"
+       }
+
+       private val core = mock<Core>()
+       private val sone = setupSone(SONE_IDENTITY)
+       private val soneTextParser = mock<SoneTextParser>()
+       private val templateContext = TemplateContext()
+       private val parameters = mutableMapOf<String, Any?>()
+       private val filter = ParserFilter(core, soneTextParser)
+
+       private fun setupSone(identity: String): Sone {
+               val sone = mock<Sone>()
+               `when`(sone.id).thenReturn(identity)
+               `when`(core.getSone(identity)).thenReturn(of(sone))
+               return sone
+       }
+
+       @Test
+       fun `parsing null returns an empty iterable`() {
+               assertThat(filter.format(templateContext, null, mutableMapOf()) as Iterable<*>, emptyIterable())
+       }
+
+       @Test
+       fun `given sone is used to create parser context`() {
+               setupSoneAndVerifyItIsUsedInContext(sone, sone)
+       }
+
+       @Test
+       fun `sone with given sone ID is used to create parser context`() {
+               setupSoneAndVerifyItIsUsedInContext(SONE_IDENTITY, sone)
+       }
+
+       private fun setupSoneAndVerifyItIsUsedInContext(soneOrSoneId: Any, sone: Sone) {
+               parameters.put("sone", soneOrSoneId)
+               filter.format(templateContext, "text", parameters)
+               val context = forClass(SoneTextParserContext::class.java)
+               verify(soneTextParser).parse(eq<String>("text"), context.capture())
+               assertThat(context.value.postingSone, `is`(sone))
+       }
+
+}