- #if($model.getRequestParameter("weblog"))
- #set($handle = $model.getRequestParameter("weblog"))
- Back to blog directory
- #set($profileWeblog = $site.getWeblog($handle))
+ ## Render the profile only for a weblog that exists, and build
+ ## the back-link from the resolved weblog's own handle.
+ #set($profileWeblog = false)
+ #set($requestedHandle = $model.getRequestParameter("weblog"))
+ #if($requestedHandle)
+ #set($profileWeblog = $site.getWeblog($requestedHandle))
+ #end
+ #if($profileWeblog)
+ Back to blog directory
#includeTemplate($model.weblog "_blogprofile")
#else
#set($pageLength = $maxResults)
diff --git a/app/src/test/java/org/apache/roller/weblogger/business/WeblogStatsTest.java b/app/src/test/java/org/apache/roller/weblogger/business/WeblogStatsTest.java
index b18ac1d38..e205f3df5 100644
--- a/app/src/test/java/org/apache/roller/weblogger/business/WeblogStatsTest.java
+++ b/app/src/test/java/org/apache/roller/weblogger/business/WeblogStatsTest.java
@@ -122,10 +122,14 @@ public void testGetUserNameLetterMap() throws Exception {
@Test
public void testGetWeblogLetterMap() throws Exception {
WeblogManager mgr = WebloggerFactory.getWeblogger().getWeblogManager();
- Map map = mgr.getWeblogHandleLetterMap();
- assertNotNull(map.get("A"));
- assertNotNull(map.get("B"));
- assertNotNull(map.get("C"));
+ Map map = mgr.getWeblogHandleLetterMap();
+ // The frontpage blog directory validates its letter parameter against
+ // these keys, so the contract is the exact A-Z set rather than a
+ // sample: a missing key would silently reject a legitimate letter.
+ assertEquals(26, map.size(), "expected the complete A-Z key set");
+ for (char c = 'A'; c <= 'Z'; c++) {
+ assertNotNull(map.get(String.valueOf(c)), "missing key " + c);
+ }
}
@AfterEach
diff --git a/app/src/test/java/org/apache/roller/weblogger/ui/rendering/velocity/FrontpageDirectoryRenderingTest.java b/app/src/test/java/org/apache/roller/weblogger/ui/rendering/velocity/FrontpageDirectoryRenderingTest.java
new file mode 100644
index 000000000..2c3103c3d
--- /dev/null
+++ b/app/src/test/java/org/apache/roller/weblogger/ui/rendering/velocity/FrontpageDirectoryRenderingTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.ui.rendering.velocity;
+
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Renders the bundled frontpage blog-directory template against the real
+ * Velocity engine and asserts how it treats the caller-supplied
+ * letter parameter.
+ *
+ *
The template is reached anonymously, so the parameter is untrusted. The
+ * contract is that only a value which normalizes to one of the directory's own
+ * A-Z keys is used, and that anything else falls back to the complete directory
+ * without the rejected value appearing in the response in any form — raw,
+ * HTML-encoded, or URL-encoded.
+ */
+public class FrontpageDirectoryRenderingTest {
+
+ private static final String THEME_DIR = "src/main/webapp/themes/frontpage";
+ private static final String TEMPLATE = "_blogdirectory.vm";
+
+ private static VelocityEngine engine;
+
+ @BeforeAll
+ public static void setUpEngine() {
+ Properties props = new Properties();
+ props.setProperty("resource.loaders", "file");
+ props.setProperty("resource.loader.file.class",
+ "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
+ props.setProperty("resource.loader.file.path", THEME_DIR);
+ engine = new VelocityEngine();
+ engine.init(props);
+ }
+
+ /** Minimal stand-ins for the model objects the template reads. */
+ public static class StubModel {
+ private final String letter;
+ StubModel(String letter) { this.letter = letter; }
+ public String getRequestParameter(String name) {
+ return "letter".equals(name) ? letter : null;
+ }
+ }
+
+ public static class StubPager {
+ public List