Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing error template #43

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/main/kotlin/net/grandcentrix/backend/plugins/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import net.grandcentrix.backend.repository.HousesRepository.Companion.HousesRepo
import net.grandcentrix.backend.repository.MoviesRepository.Companion.MoviesRepositoryInstance
import net.grandcentrix.backend.repository.PotionsRepository.Companion.PotionsRepositoryInstance
import net.grandcentrix.backend.repository.SpellsRepository.Companion.SpellsRepositoryInstance
import respondTemplates

Choose a reason for hiding this comment

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

That's a weird import. Usually imports are a reverse FQDN of some kind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

intelij just told me to do this


fun Application.configureRouting() {

Expand Down Expand Up @@ -114,7 +115,7 @@ fun Application.configureRouting() {
get("/books") {
val userSession: UserSession? = call.sessions.get<UserSession>()
val username = call.sessions.get<UserSession>()?.username
call.respondTemplate(
call.respondTemplates(
"books.ftl",
mapOf(
"books" to BooksRepositoryInstance.getAll(),
Expand All @@ -129,7 +130,7 @@ fun Application.configureRouting() {
get("/houses") {
val userSession: UserSession? = call.sessions.get<UserSession>()
val username = call.sessions.get<UserSession>()?.username
call.respondTemplate(
call.respondTemplates(
"houses.ftl",
mapOf(
"houses" to HousesRepositoryInstance.getAll(),
Expand All @@ -144,7 +145,7 @@ fun Application.configureRouting() {
get("/characters") {
val userSession: UserSession? = call.sessions.get<UserSession>()
val username = call.sessions.get<UserSession>()?.username
call.respondTemplate(
call.respondTemplates(
"characters.ftl",
mapOf(
"characters" to CharactersRepositoryInstance.getAll(),
Expand All @@ -160,7 +161,7 @@ fun Application.configureRouting() {
val userSession: UserSession? = call.sessions.get<UserSession>()
val username = call.sessions.get<UserSession>()?.username

call.respondTemplate(
call.respondTemplates(
"movies.ftl",
mapOf(
"movies" to MoviesRepositoryInstance.getAll(),
Expand All @@ -176,7 +177,7 @@ fun Application.configureRouting() {
val userSession: UserSession? = call.sessions.get<UserSession>()
val username = call.sessions.get<UserSession>()?.username

call.respondTemplate(
call.respondTemplates(
"potions.ftl",
mapOf(
"potions" to PotionsRepositoryInstance.getAll(),
Expand All @@ -192,7 +193,7 @@ fun Application.configureRouting() {
val userSession: UserSession? = call.sessions.get<UserSession>()
val username = call.sessions.get<UserSession>()?.username

call.respondTemplate(
call.respondTemplates(
"spells.ftl",
mapOf(
"spells" to SpellsRepositoryInstance.getAll(),
Expand Down
13 changes: 7 additions & 6 deletions src/main/kotlin/net/grandcentrix/backend/plugins/StatusPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.ktor.server.routing.*
import io.ktor.server.sessions.*
import net.grandcentrix.backend.controllers.UserSession
import net.grandcentrix.backend.controllers.getProfilePicture
import respondTemplates

fun Application.configureStatusPage() {
routing {
Expand All @@ -21,7 +22,7 @@ fun Application.configureStatusPage() {
when (cause) {
is RequestException -> {
val userSession: UserSession? = call.sessions.get<UserSession>()
call.respondTemplate(
call.respondTemplates(
"error.ftl",
mapOf(
"errorMessage" to cause.message,
Expand All @@ -36,7 +37,7 @@ fun Application.configureStatusPage() {

is DAOException -> {
val userSession = call.sessions.get<UserSession>()
call.respondTemplate(
call.respondTemplates(
"error.ftl",
mapOf(
"errorMessage" to cause.message,
Expand All @@ -62,7 +63,7 @@ fun Application.configureStatusPage() {

is UserAlreadyExistsException -> {
val userSession: UserSession? = call.sessions.get<UserSession>()
call.respondTemplate(
call.respondTemplates(
"error.ftl",
mapOf(
"errorMessage" to cause.message,
Expand All @@ -79,7 +80,7 @@ fun Application.configureStatusPage() {

else -> {
val userSession = call.sessions.get<UserSession>()
call.respondTemplate(
call.respondTemplates(
"error.ftl",
mapOf(
"errorMessage" to cause.message,
Expand All @@ -94,7 +95,7 @@ fun Application.configureStatusPage() {

status(HttpStatusCode.NotFound) { call, _ ->
val userSession: UserSession? = call.sessions.get<UserSession>()
call.respondTemplate(
call.respondTemplates(
"error.ftl",
mapOf(
"errorMessage" to "Oops! It wasn't possible to find the page, or it doesn't exist.",
Expand All @@ -107,7 +108,7 @@ fun Application.configureStatusPage() {

status(HttpStatusCode.InternalServerError) { call, _ ->
val userSession: UserSession? = call.sessions.get<UserSession>()
call.respondTemplate(
call.respondTemplates(
"error.ftl",
mapOf(
"errorMessage" to "Status 500 - Internal Server Error",
Expand Down
28 changes: 27 additions & 1 deletion src/main/kotlin/net/grandcentrix/backend/plugins/Templating.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
import freemarker.cache.ClassTemplateLoader
import freemarker.core.HTMLOutputFormat
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.freemarker.*
import io.ktor.server.request.*
import io.ktor.server.response.*

fun Application.configureTemplating() {
install(FreeMarker) {
templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates")
outputFormat = HTMLOutputFormat.INSTANCE
}
}
}

suspend fun ApplicationCall.respondTemplates(

Choose a reason for hiding this comment

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

Why the plural in the name here? We must only respond with a single response and this response is rendered based on one template. This template might reference other templates as a form of composition, but it is still a single template.
As we already discussed we unfortunately can't overwrite already existing extension functions easily, but better naming is always a good idea :)

template: String, // The name of the template to be rendered
model: Any? = null, // The data model to be passed to the template, default is null

Choose a reason for hiding this comment

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

Do we have cases where the model is null?

etag: String? = null, // The ETag for caching purposes, default is null

Choose a reason for hiding this comment

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

Do we ever set the etag when calling respondTemplates?

contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8) // The content type of the response, default is HTML with UTF-8 charset

Choose a reason for hiding this comment

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

Nice to set the correct content type, but do we need this as a parameter? The templates will always be HTML. If we want a different content format we most likely need to create another mechanism to build our responses.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

okay as we talked about i just copy/pasted the implementation because it was marked as "Read only" so i didnt thought about it much more. But i will have a look at this and the two things above and see if it breaks something when i delete it or change it

): Unit {
// Retrieve the "HX-Request" header from the request and convert it to a Boolean. Default to false if the header is not present.
val hxRequest = request.header("HX-Request")?.toBoolean() ?: false

// Prepare the model to include the hxRequest variable. If the original model is a Map, add hxRequest to it.
// Otherwise, create a new map with hxRequest and the original model.
val modelWithHxRequest = if (model is Map<*, *>) {
model + ("hxRequest" to hxRequest) // Add hxRequest to the existing model map
} else {

Choose a reason for hiding this comment

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

That could cause problems. In your parameters you accept Any? as a type for the model. And here you check if model is not just Any but specifically some kind of Map. But what happens here if we have a model which does not implement the Map interface? Would a template depending on such a model render correctly?

mapOf("hxRequest" to hxRequest, "model" to model) // Create a new map containing hxRequest and the original model
}

// Respond with the rendered FreeMarker template, passing the template name, modified model, etag, and content type.
respond(FreeMarkerContent(template, modelWithHxRequest, etag, contentType))
}


105 changes: 56 additions & 49 deletions src/main/resources/templates/_layout.ftl
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
<#global userSession = "null">
<#global profilePicture = "/static/img/no_profile_picture.png">

<#macro base>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Wizard</title>
<link href="/static/style.css" rel="stylesheet" />
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200"
/>
<script src="https://unpkg.com/[email protected]"
integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw"
crossorigin="anonymous">
</script>
</head>
<body>
<#macro base hxRequest=false>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Wizard</title>
<link href="/static/style.css" rel="stylesheet" />
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200"
/>
<script src="https://unpkg.com/[email protected]"
integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw"
crossorigin="anonymous">
</script>
</head>
<body>
<#if !hxRequest>
<header>
<nav class="user-menu">
<!-- Profile Picture Dropdown Section -->
<div class="dropdown tooltip" id="profile-dropdown">
<a href="#">
<img class="profile-picture" id="profile-pic" src="${profilePicture}" alt="Profile Picture">
</a>
<#if userSession == "null">
<div class="dropdown-content">
<a href="/signup">Signup</a>
<a href="/login">Login</a>
<nav class="user-menu">
<!-- Profile Picture Dropdown Section -->
<div class="dropdown tooltip" id="profile-dropdown">
<a href="#">
<img class="profile-picture" id="profile-pic" src="${profilePicture}" alt="Profile Picture">
</a>
<#if userSession == "null">
<div class="dropdown-content">
<a href="/signup">Signup</a>
<a href="/login">Login</a>
</div>
<#elseif userSession != "null">
<div class="tooltip">
<div class="tooltiptext">
<#if username??>
<p>Username: ${username}
<#if house??>
<img src="/static/img/${house}_symbol.png" alt="${house} Symbol" style="width: 20px; height: 20px; vertical-align: middle;">
</#if>
</p>
</#if>
</div>
<!-- End of Profile Picture Dropdown Section -->
<#elseif userSession != "null">
<div class="tooltip">
<div class="tooltiptext">
<#if username??>
<p>Username: ${username}
<#if house??>
<img src="/static/img/${house}_symbol.png" alt="${house} Symbol" style="width: 20px; height: 20px; vertical-align: middle;">
</#if>
</p>
</#if>
</div>
</div>
<div class="dropdown-content">
<a href="/profile#favourites">Favourites</a>
<a href="/logout">Logout</a>
</div>
</#if>
</div>
</nav>
</div>
<div class="dropdown-content">
<a href="/profile#favourites">Favourites</a>
<a href="/logout">Logout</a>
</div>
</#if>
</div>
</nav>

<h1 class="logo">
<a href="/">Wizard</a>
Expand All @@ -63,8 +63,15 @@
</label>
<div class="bg-effect"></div>
</header>
</#if>

<#if hxRequest>
<section class="container">
</#if>

<#nested />

<section class="container">
<#nested>
</section>
</#macro>
<#if hxRequest>
</section>
</#if>
</#macro>
7 changes: 2 additions & 5 deletions src/main/resources/templates/error.ftl
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
<#import "_layout.ftl" as layout />
<#assign userSession = session in layout>
<#assign profilePicture = profilePictureData in layout>


<@layout.base>
<@layout.base hxRequest=hxRequest>
<h2>${errorMessage}</h2>

<section class="content">
<a class="button" href="${redirectLink}">Back</a>
</section>
</@layout.base>
</@layout.base>