This is an automated email from the ASF dual-hosted git repository. mbien pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/roller.git
commit 09489e6c3630fac21ff6b40c8195eecca2de1b78 Author: Michael Bien <mbie...@gmail.com> AuthorDate: Sun Jul 11 09:04:29 2021 +0200 Added EscapeSourceCodePlugin, a WeblogEntryPlugin to escape angle brackes from code within pre tags. --- .../plugins/entry/EscapeSourceCodePlugin.java | 88 ++++++++++++ .../roller/weblogger/config/roller.properties | 3 +- .../plugins/entry/EscapeSourceCodePluginTest.java | 157 +++++++++++++++++++++ 3 files changed, 247 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/apache/roller/weblogger/business/plugins/entry/EscapeSourceCodePlugin.java b/app/src/main/java/org/apache/roller/weblogger/business/plugins/entry/EscapeSourceCodePlugin.java new file mode 100644 index 0000000..845b08e --- /dev/null +++ b/app/src/main/java/org/apache/roller/weblogger/business/plugins/entry/EscapeSourceCodePlugin.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * 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. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ + +package org.apache.roller.weblogger.business.plugins.entry; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.roller.weblogger.WebloggerException; +import org.apache.roller.weblogger.pojos.Weblog; +import org.apache.roller.weblogger.pojos.WeblogEntry; + + +import static java.util.regex.Pattern.*; + +/** + * Escapes angle brackets inside pre tags (code tags which follow right after pre are not escaped). + */ +public class EscapeSourceCodePlugin implements WeblogEntryPlugin { + + private static final String LT = "<"; // '<' + + private static final Pattern PRE_PATTERN = Pattern.compile( + "<pre\\s*[^>]*>" + "(.*?)" + "</pre\\s*>", MULTILINE | DOTALL | CASE_INSENSITIVE); + + private static final Pattern CODE_PATTERN = Pattern.compile( + "<code\\s*.[^>]*>" + "(.*?)" + "</code\\s*>", MULTILINE | DOTALL | CASE_INSENSITIVE); + + @Override + public String getName() { + return "Sourcecode Escaper"; + } + + @Override + public String getDescription() { + return "Escapes angle brackets inside pre tags, code tags are retained."; + } + + @Override + public void init(Weblog weblog) throws WebloggerException {} + + @Override + public String render(WeblogEntry entry, String str) { + + StringBuilder result = new StringBuilder(str.length()+32); + + Matcher pre_matcher = PRE_PATTERN.matcher(str); + + while(pre_matcher.find()) { + + String pre_full = pre_matcher.group(0); + String pre_inner = pre_matcher.group(1); + + Matcher code_matcher = CODE_PATTERN.matcher(pre_inner); + + if (code_matcher.find()) { + String code_inner = code_matcher.group(1); + pre_matcher.appendReplacement(result, pre_full.replace(code_inner, escape(code_inner))); + } else { + pre_matcher.appendReplacement(result, pre_full.replace(pre_inner, escape(pre_inner))); + } + + } + pre_matcher.appendTail(result); + + return result.toString(); + } + + // we only have to escape the opening angle bracket for valid html/xhtml + private static String escape(String code_inner) { + return code_inner.replace("<", LT); + } + +} diff --git a/app/src/main/resources/org/apache/roller/weblogger/config/roller.properties b/app/src/main/resources/org/apache/roller/weblogger/config/roller.properties index c82086b..af29d08 100644 --- a/app/src/main/resources/org/apache/roller/weblogger/config/roller.properties +++ b/app/src/main/resources/org/apache/roller/weblogger/config/roller.properties @@ -548,7 +548,8 @@ guice.backend.module=org.apache.roller.weblogger.business.jpa.JPAWebloggerModule plugins.page=\ org.apache.roller.weblogger.business.plugins.entry.ConvertLineBreaksPlugin \ ,org.apache.roller.weblogger.business.plugins.entry.ObfuscateEmailPlugin \ -,org.apache.roller.weblogger.business.plugins.entry.SmileysPlugin +,org.apache.roller.weblogger.business.plugins.entry.SmileysPlugin\ +,org.apache.roller.weblogger.business.plugins.entry.EscapeSourceCodePlugin # The list of configured WeblogEntryEditors available to users diff --git a/app/src/test/java/org/apache/roller/weblogger/business/plugins/entry/EscapeSourceCodePluginTest.java b/app/src/test/java/org/apache/roller/weblogger/business/plugins/entry/EscapeSourceCodePluginTest.java new file mode 100644 index 0000000..ff7b585 --- /dev/null +++ b/app/src/test/java/org/apache/roller/weblogger/business/plugins/entry/EscapeSourceCodePluginTest.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * 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. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ + +package org.apache.roller.weblogger.business.plugins.entry; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author mbien + */ +public class EscapeSourceCodePluginTest { + + @Test + public void passthrough() { + + EscapeSourceCodePlugin instance = new EscapeSourceCodePlugin(); + + String input = "Stay a while and listen."; + assertEquals(input, instance.render(null, input)); + + input = "<!DOCTYPE html>\n" + + "<html>\n" + + " <head>\n" + + " <title>Hi!</title>\n" + + " </head>\n" + + " <body>\n" + + " <p>Hello There</p>\n" + + " </body>\n" + + "</html>"; + assertEquals(input, instance.render(null, input)); + + input = "<!DOCTYPE html>\n" + + "<html>\n" + + " <head>\n" + + " <title>Hi!</title>\n" + + " </head>\n" + + " <body>\n" + + " <pre>Hello There</pre>\n" + // pre + " <code>Hello There</code>\n" + // code + " </body>\n" + + "</html>"; + assertEquals(input, instance.render(null, input)); + + } + + @Test + public void substitution1() { + + EscapeSourceCodePlugin instance = new EscapeSourceCodePlugin(); + + String input = "<pre><></pre>"; + String expected = "<pre><></pre>"; + + assertEquals(expected, instance.render(null, input)); + + } + + @Test + public void substitution2() { + + EscapeSourceCodePlugin instance = new EscapeSourceCodePlugin(); + + String input = "\n<!DOCTYPE html>\n" + + "<html>\n" + + " <head>\n" + + " <title>Hi!</title>\n" + + " </head>\n" + + " <body>\n" + + " <pre><code class='language-java'>private final Map<String, List<?>> map = new HashMap<>();</code></pre>\n"+ + " </body>\n" + + "</html>"; + + String expected = "\n<!DOCTYPE html>\n" + + "<html>\n" + + " <head>\n" + + " <title>Hi!</title>\n" + + " </head>\n" + + " <body>\n" + + " <pre><code class='language-java'>private final Map<String, List<?>> map = new HashMap<>();</code></pre>\n"+ + " </body>\n" + + "</html>"; + assertEquals(expected, instance.render(null, input)); + + } + + @Test + public void substitution3() { + + EscapeSourceCodePlugin instance = new EscapeSourceCodePlugin(); + + String input = "<!DOCTYPE html>\n" + + "<html>\n" + + " <head>\n" + + " <title>Hi!</title>\n" + + " </head>\n" + + " <body>\n" + + " <h3>some java</h3>\n" + + " <pre>\n" + + " <code class='language-java'>\n"+ + " private final Map<String, List<?>> map = new HashMap<>();\n"+ + " </code>\n" + + " </pre>\n" + + " <h3>some xml</h3>\n" + + " <pre>\n" + + " <code class='language-xml'>\n"+ + " <foo id = '5'>\n"+ + " <bar>asdf</bar>\n"+ + " </foo>\n"+ + " </code>\n" + + " </pre>\n" + + " </body>\n" + + "</html>"; + + String expected = "<!DOCTYPE html>\n" + + "<html>\n" + + " <head>\n" + + " <title>Hi!</title>\n" + + " </head>\n" + + " <body>\n" + + " <h3>some java</h3>\n" + + " <pre>\n" + + " <code class='language-java'>\n"+ + " private final Map<String, List<?>> map = new HashMap<>();\n"+ + " </code>\n" + + " </pre>\n" + + " <h3>some xml</h3>\n" + + " <pre>\n" + + " <code class='language-xml'>\n"+ + " <foo id = '5'>\n"+ + " <bar>asdf</bar>\n"+ + " </foo>\n"+ + " </code>\n" + + " </pre>\n" + + " </body>\n" + + "</html>"; + assertEquals(expected, instance.render(null, input)); + + } + +}