+++ /dev/null
-/*
- * 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);
- }
-
-}
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;
/** The parser filter. */
private final ParserFilter parserFilter;
+ private final RenderFilter renderFilter;
private final ListNotificationFilter listNotificationFilter;
private final PostVisibilityFilter postVisibilityFilter;
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());
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)));
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;
*/
public class EditImageAjaxPage extends JsonPage {
- /** Parser for image descriptions. */
private final ParserFilter parserFilter;
+ private final RenderFilter renderFilter;
/**
* Creates a new edit image AJAX page.
* @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;
}
//
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);
}
}
--- /dev/null
+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)
+ }
+
+}
<%/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>
</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">
<%/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>
<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>
<%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>
-<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>
<%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>
<%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>
+++ /dev/null
-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 …"));
- }
-
- @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 …"));
- }
-
- @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 …"));
- }
-
- @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 …"));
- }
-
- @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 …"));
- }
-
- 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 …"));
- }
-
- @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");
- }
-
-}
--- /dev/null
+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))
+ }
+
+}