All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.databasesandlife.util.wicket.MultilineLabelWithClickableLinks Maven / Gradle / Ivy

There is a newer version: 21.0.1
Show newest version
package com.databasesandlife.util.wicket;

import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.string.Strings;

import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Matcher.quoteReplacement;

/** 
 * Displays the text in the model over multiple HTML lines, and makes links clickable. 
 *
 * @author This source is copyright Adrian Smith and licensed under the LGPL 3.
 * @see Project on GitHub
 */
public class MultilineLabelWithClickableLinks extends Label {

    public MultilineLabelWithClickableLinks(final String id, Serializable label) {
        super(id, label);
        setEscapeModelStrings(false);
    }

    public MultilineLabelWithClickableLinks(final String id, IModel model) {
        super(id, model);
        setEscapeModelStrings(false);
    }

    /**
     * Matches "foo://foo.foo/foo".
     * 
    *
  • Protocol is optional *
  • Domain must contain at least one dot (TLD list is too long for whitelist) *
  • Last part is optional and can contain anything apart from space and trailing punctuation * (= part of the sentence in which the link is embedded) *
*

Quotes are not allowed because we don't want <a href="foo"> to have foo containing quotes (XSS). */ protected static final Pattern linkPattern = Pattern.compile( "(\\w{2,7}:/+)?([\\w-]+\\.)+[\\w-]+(/([^\\s'\"]*[\\w])?)?", Pattern.CASE_INSENSITIVE); /** * Takes some plain text and returns HTML with URLs marked up as clickable links. * *

Making links clickable is not as easy as it seems. *

    *
  • Conversion from plain text to HTML requires that entities * such as "&" get replaced by "&amp;". *
  • Links such as "foo.com/a&b" need to get replaced by * "<a href='foo.com/a&b'>foo.com/a&amp;b</a>". *
* *

Therefore, *

    *
  • One cannot firstly replace entities and then markup links, as the links should contain * unescaped "&" as opposed to "&amp;". *
  • One cannot firstly encode links and then replace entities as the angle brackets in the link's * "<a href.." would get replaced by "&lt;a href..." which the * browser would not understand. *
* *

Therefore, the replacement of HTML entities, and the replacement of links, must be done * in a single pass, not one after another. * * @param plainText Plain text, no HTML. * @return Safe HTML with entities encoded and links marked up. */ public static CharSequence encodeLinksToHtml(CharSequence plainText) { var m = linkPattern.matcher(plainText); var result = new StringBuffer(); while (m.find()) { var replacement = "" + Strings.escapeMarkup(m.group()) + ""; var backgroundStartInclIdx = result.length(); m.appendReplacement(result, quoteReplacement(replacement)); var backgroundEndExclIdx = result.length() - replacement.length(); var background = result.substring(backgroundStartInclIdx, backgroundEndExclIdx); result.replace(backgroundStartInclIdx, backgroundEndExclIdx, Strings.escapeMarkup(background).toString()); } var backgroundStartInclIdx = result.length(); m.appendTail(result); var backgroundEndExclIdx = result.length(); var background = result.substring(backgroundStartInclIdx, backgroundEndExclIdx); result.replace(backgroundStartInclIdx, backgroundEndExclIdx, Strings.escapeMarkup(background).toString()); return result; } @Override public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) { var plainText = getDefaultModelObjectAsString(); var htmlWithLinksMarkedup = encodeLinksToHtml(plainText); var htmlWIthLinksAndNewlines = htmlWithLinksMarkedup.toString().replace("\n", "
"); replaceComponentTagBody(markupStream, openTag, htmlWIthLinksAndNewlines); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy