Skip to content

Phase 4: Migrate all views from JSP/JSF to Thymeleaf#14

Open
devin-ai-integration[bot] wants to merge 2 commits intodevin/1771291744-spring-boot-migration-phase1-2from
devin/1771295177-phase4-thymeleaf-views
Open

Phase 4: Migrate all views from JSP/JSF to Thymeleaf#14
devin-ai-integration[bot] wants to merge 2 commits intodevin/1771291744-spring-boot-migration-phase1-2from
devin/1771295177-phase4-thymeleaf-views

Conversation

@devin-ai-integration
Copy link

@devin-ai-integration devin-ai-integration bot commented Feb 17, 2026

Phase 4: Migrate all views from JSP/JSF to Thymeleaf with layout dialect

Summary

Replaces the Phase 3 placeholder Thymeleaf templates with full implementations that use the Thymeleaf Layout Dialect (layout:decorate) to mirror the original Tiles-based layout system. Adds a base layout template, four fragments (header, footer, logged-off menu, logged-on menu), and rewrites all six page templates to match the original JSP/JSF views.

New files:

  • templates/layout/base.html — Table-based layout matching layout.jsp (header, menu sidebar, content, footer)
  • templates/fragments/header.html, footer.html — Direct message-key conversions of header.jsp/footer.jsp
  • templates/fragments/loggedoff-menu.html — Register + Log On links (replaces LoggedOff JSF bean + loggedoff.jsp)
  • templates/fragments/loggedon-menu.html — Edit Registration + Log Off links (replaces LoggedOn JSF bean + loggedon.jsp)

Modified files (placeholder → full implementation):

  • welcome.html, logon.html, mainMenu.html, registration.html, subscription.html, changePassword.html

All 160 existing unit tests pass. No new rendering/integration tests were added.

Updates since last revision

  • Fixed dual-menu bug on registration.html: Combining th:if and th:replace on the same <div> element caused both the logged-on and logged-off menus to render simultaneously. Fixed by wrapping conditionals in <th:block> elements to separate the th:if evaluation from the th:replace processing.
  • Manual browser testing completed: Started the Spring Boot app and verified the full flow: /welcome → Log On (user/pass) → /mainMenu → Edit Registration (with subscription table) → Edit Subscription → Log Off. All pages render correctly with proper layout, menus, and form data.

Welcome page
Logon page
Main menu
Registration with subscriptions
Subscription edit

Review & Testing Checklist for Human

  • session.user.username null safetymainMenu.html:15 and subscription.html:27 access ${session.user.username} without a null guard. If a user navigates directly to these URLs without a session, Thymeleaf will throw a TemplateProcessingException before the controller's redirect-to-logon can fire. Verify the controllers redirect first, or add ?. safe-navigation (${session.user?.username}).
  • Password fields in registration.html use type="text" instead of type="password" — Lines 40 and 47 use <input type="text" for password and password2 fields, exposing passwords on screen. The original JSP also used h:inputText (not h:inputSecret), so this may be faithful to the original, but confirm this is intentional.
  • Dual <title> elements in registration.html and subscription.html — Both files have two <title> elements with th:if conditions (lines 6-7). Verify the Thymeleaf Layout Dialect's title-pattern merging works correctly and only one title is used. Check the browser tab title for "Register" vs "Edit Registration" and "Create Subscription" vs "Edit Subscription" vs "Delete Subscription".
  • changePassword.html no longer displays the username — The original placeholder showed <p th:text="${username}">username</p> (from the ExpiredPasswordException handler's model attribute). The new template removes this. Test the expired password flow (username "expired") and verify the user experience is acceptable without seeing which account has the expired password.
  • Full click-through test: Run JAVA_HOME=/path/to/jdk17 mvn spring-boot:run -f apps/example2-spring-boot/pom.xml and verify: /welcome → Register (create new user) → Log On → /mainMenu → Edit Registration → Add/Edit/Delete subscription → Log Off. Check that the layout (header/menu/content/footer in table) renders correctly on every page and forms submit properly.

Notes

  • The Cancel button on registration.html is now a plain link (<a th:href="@{/mainMenu}">) instead of a form submit button with onclick="bCancel=true;". The Spring controller doesn't have cancel handling, so this is probably fine, but it's a behavior change from the original Struts action.
  • CSS classes (form-background, list-background, form-prompt, etc.) match the original JSP styling and are already present in static/css/stylesheet.css.
  • All message keys (#{logon.header}, #{prompt.username}, etc.) are already defined in messages.properties.
  • The th:block wrapper fix for conditional menu rendering is a workaround for a Thymeleaf Layout Dialect processing order issue when combining th:if and th:replace on the same element.

Link to Devin run: https://jack-meigel.devinenterprise.com/sessions/d762744597864202aa3edeeb783e4a6c
Requested by: @cogjack


Open with Devin

- Add base layout template using Thymeleaf Layout Dialect (layout:decorate)
- Add fragment templates: header, footer, loggedoff-menu, loggedon-menu
- Replace placeholder page templates with full implementations:
  - welcome.html: Landing page with loggedoff menu
  - logon.html: Login form with validation errors, table layout
  - mainMenu.html: Post-login menu with user greeting
  - registration.html: Full registration/edit form with subscription list
  - subscription.html: Create/edit/delete subscription form
  - changePassword.html: Expired password page
- All templates use layout:decorate to extend base layout
- Conditional menu display based on session user state
- Form validation errors displayed with th:errors
- Internationalized text via #{} message expressions
- CSS classes match original JSP styling (form-background, list-background, etc.)

All 160 existing tests pass.

Co-Authored-By: Jack  Meigel <jack.meigel@cognition.ai>
@devin-ai-integration
Copy link
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Use th:block wrapper around conditional th:replace to prevent both
loggedon and loggedoff menus from rendering simultaneously. The
th:if on a div with th:replace had a processing order issue in
Thymeleaf Layout Dialect.

Co-Authored-By: Jack  Meigel <jack.meigel@cognition.ai>
Copy link
Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

<tr>
<td class="form-prompt"><label for="password" th:text="#{prompt.password}">Password</label></td>
<td class="form-field">
<input type="text" th:field="*{password}" id="password" size="16" />
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Password fields changed from type="password" to type="text" in registration form

The password and password confirmation fields in registration.html were changed from type="password" to type="text", causing passwords to be displayed in plain text on screen.

Security Impact

The old code used type="password" for both password fields (visible in the diff's left side at lines 32 and 37), which masks the input. The new code at lines 44 and 51 uses type="text", which renders the password as visible plain text. This is a security issue as passwords will be visible to anyone who can see the user's screen (shoulder surfing), and may also be captured by browser auto-fill or screen recording software.

Actual: Password input is displayed as plain text (type="text").
Expected: Password input should be masked (type="password").

Suggested change
<input type="text" th:field="*{password}" id="password" size="16" />
<input type="password" th:field="*{password}" id="password" size="16" />
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<tr>
<td class="form-prompt"><label for="password2" th:text="#{prompt.password2}">(Repeat) Password</label></td>
<td class="form-field">
<input type="text" th:field="*{password2}" id="password2" size="16" />
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Password confirmation field changed from type="password" to type="text" in registration form

The password2 (confirmation) field in registration.html was changed from type="password" to type="text", causing the repeated password to be displayed in plain text.

Security Impact

Same issue as the password field — the old code at diff left side line 37 used type="password", but the new code at line 51 uses type="text". This exposes the confirmation password entry to shoulder surfing.

Suggested change
<input type="text" th:field="*{password2}" id="password2" size="16" />
<input type="password" th:field="*{password2}" id="password2" size="16" />
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<tr>
<td class="form-prompt"><label for="password" th:text="#{prompt.mailPassword}">Mail Password</label></td>
<td class="form-field">
<input type="text" th:field="*{password}" id="password" size="50" />
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Subscription mail password field changed from type="password" to type="text"

The mail password field in subscription.html was changed from type="password" to type="text", causing the mail server password to be displayed in plain text.

Security Impact

The old code at diff left side line 22 used type="password" for the mail password field, but the new code at line 52 uses type="text". This exposes the user's mail server password in plain text on screen.

Actual: <input type="text" th:field="*{password}" id="password" size="50" />
Expected: <input type="password" th:field="*{password}" id="password" size="50" />

Suggested change
<input type="text" th:field="*{password}" id="password" size="50" />
<input type="password" th:field="*{password}" id="password" size="50" />
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<div layout:fragment="content">
<h3>
<span th:text="#{mainMenu.heading}">Main Menu Options for </span>
<span th:text="${session.user.username}">username</span>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 mainMenu.html accesses session.user.username without null guard, but controller has no login check

The mainMenu.html template at line 15 accesses ${session.user.username}, but the WelcomeController.mainMenu() method does not verify the user is logged in before rendering the template.

Root Cause

The WelcomeController.mainMenu() at controller/WelcomeController.java:37-40 simply returns the "mainMenu" view name without checking if a user exists in the session:

@GetMapping("/mainMenu")
public String mainMenu() {
    return "mainMenu";
}

The template at mainMenu.html:15 then accesses ${session.user.username}. If a user navigates directly to /mainMenu without being logged in, session.user will be null, causing a Thymeleaf template evaluation error (equivalent to a NullPointerException) when trying to access .username on a null object.

Impact: Unauthenticated users visiting /mainMenu will see a 500 error page instead of being redirected to the login page.

Prompt for agents
The mainMenu.html template at line 15 accesses ${session.user.username} but the WelcomeController.mainMenu() method at apps/example2-spring-boot/src/main/java/org/apache/struts/webapp/example2/controller/WelcomeController.java:37-40 does not check if the user is logged in. Add a session check to the mainMenu() controller method similar to what RegistrationController does: retrieve the user from session using Constants.USER_KEY, and if null, redirect to the logon page. The method signature needs to accept HttpSession as a parameter.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant