diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 1fa51146..7fa3023d 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1 +1,23 @@ -* xref:index.adoc[Quarkus Renarde] +include::./includes/attributes.adoc[] +:config-file: application.properties + +* xref:index.adoc[Getting started] +* xref:concepts.adoc[Main Concepts] +** xref:concepts.adoc#models[Models] +** xref:concepts.adoc#controllers[Controllers] +** xref:concepts.adoc#views[Views] +* xref:advanced.adoc[Advanced Guides] +** xref:advanced.adoc#forms[Forms] +** xref:advanced.adoc#routing[Routing] +** xref:advanced.adoc#emails[Emails] +** xref:advanced.adoc#localisation[Localisation / Internationalisation] +** xref:advanced.adoc#flash_scope[Flash Scope] +** xref:advanced.adoc#htmx[htmx] +** xref:advanced.adoc#generating_barcodes[Generating barcodes] +** xref:advanced.adoc#generating_pdf_documents[Generating PDF] +* xref:security.adoc[Security] +** xref:security.adoc#_csrf[CSRF] +** xref:security.adoc#_custom_authentication_with_jwt[Custom Auth (JWT)] +** xref:security.adoc#oidc[OIDC] +* xref:config-reference.adoc[Config Reference] + diff --git a/docs/modules/ROOT/pages/advanced.adoc b/docs/modules/ROOT/pages/advanced.adoc new file mode 100644 index 00000000..122927df --- /dev/null +++ b/docs/modules/ROOT/pages/advanced.adoc @@ -0,0 +1,755 @@ += Renarde image:renarde-head.svg[width=25em] Web Framework - Advanced +:favicon: _images/renarde-head.svg + +include::./includes/attributes.adoc[] +:config-file: application.properties + + +[#forms] +== Forms + +A lot of the time, you need to send data from the browser to your endpoints, which is often done with forms. + +=== The HTML form + +Creating forms in Renarde is easy: let's see an example of how to do it in Qute: + +[source,html] +---- +{#form uri:Login.complete(newUser.confirmationCode)} + +
+ +{/form} +---- + +Here we're defining a form whose action will go to `Register.complete(newUser.confirmationCode)` and +which contains several form elements, which are just tags to make composition easier. For example `formElement` is +a custom Qute tag for Bootstrap which defines layout for the form element and displays any associated error: + +[source,html] +---- ++ This is an automated email, you should not reply to it: your mail will be ignored. +
+ + +---- + +And for text in `src/main/resources/templates/email.txt`: + +[source,txt] +---- +{#insert /} + +This is an automated email, you should not reply to it: your mail will be ignored. +---- + +You can then use those templates in your emails in `src/main/resources/templates/Emails/confirm.html`: + +[source,html] +---- +{#include email.html } + ++ Welcome to Todos. +
+ ++ You received this email because someone (hopefully you) wants to register on Todos. +
+ ++ If you don't want to register, you can safely ignore this email. +
+ ++ If you want to register, complete your registration. +
+{/include} +---- + +And for text in `src/main/resources/templates/Emails/confirm.txt`: + +[source,txt] +---- +{#include email.txt} + +Welcome to Todos. + +You received this email because someone (hopefully you) wants to register on Todos. + +If you don't want to register, you can safely ignore this email. + +If you want to register, complete your registration by going to the following address: + +{uriabs:Login.confirm(user.confirmationCode)} +{/include} +---- + +Note that in emails you will want to use the `uriabs` namespace for absolute URIs and not relative ones, +otherwise the links won't work for your email recipients. + +You can find more information in the {quarkus-guides-url}/mailer-reference[Quarkus mailer documentation]. + +[#localisation] +== Localisation / Internationalisation + +You can declare your default language and supported languages in `src/main/resources/application.properties`: + +[source,properties] +---- +# This is the default locale for your application +quarkus.default-locale=en +# These are the supported locales (should include the default locale, but order is not important) +quarkus.locales=en,fr +---- + +Next, you can declare your default language messages in the `src/main/resources/message.properties` file: + +[source,properties] +---- +# A simple message +hello=Hello World +# A parameterised message for your view +views_Application_index_greet=Hello %s +---- + +Declare your other language translations in the `src/main/resources/message_fr.properties` file: + +[source,properties] +---- +hello=Bonjour Monde +views_Application_index_greet=Salut %s +---- + +Now you can use these translated messages in your controller: + +[source,java] +---- +public static class Application extends Controller { + + @CheckedTemplate + public static class Templates { + public static native TemplateInstance index(String name); + } + + public TemplateInstance index() { + return Templates.index("Stef"); + } + + public String hello() { + return i18n.formatMessage("hello"); + } +} +---- + +Or in your template: + +[source,txt] +---- +With no parameter: +{m:hello} +With parameters: +{m:views_Application_index_greet(name)} +---- + +=== Selecting the language + +The current language for a request will depend on the following (in order): + +. The `_renarde_language` cookie, if set +. The https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language[`Accept-Language`] HTTP header, if set, +which defines an ordered list of languages by user preference. We will select a best matching language from the set +of `quarkus.locales`. +. If nothing else, we default to the default language as set by `quarkus.default-locale`, which defaults to `en_US`. + +You can override the user's language with a cookie by calling `i18n.set(language)`: + +[source,java] +---- +public static class Application extends Controller { + + @CheckedTemplate + public static class Templates { + public static native TemplateInstance index(); + } + + public void index() { + return Templates.index(); + } + + @POST + public void french() { + i18n.set("fr"); + index(); + } + + @POST + public void english() { + i18n.set("en"); + index(); + } +} +---- + +[#flash_scope] +== Flash scope + +If you need to pass values from one endpoint to another request after a redirect, you can use the Flash scope. +Usually this is done in a `@POST` endpoint, by filling the Flash scope with either errors or messages, +before trigerring a redirect to the right `@GET` endpoint. + +You can push values in the Flash scope in an endpoint using the `flash(name, value)` method, or using the +`Flash` injectable component. + +You can read values from the Flash scope in your Qute views using the `{flash:name}` namespace. + +The Flash scope only lasts from one request to the next and is cleared at each request. + +[#htmx] +== htmx + +If you want to use https://htmx.org/[htmx] with Renarde, we added an extra layer to make things more convenient: +[source,java] +---- +public class Application extends HxController { + + @CheckedTemplate + public static class Templates { + public static native TemplateInstance foo(int a, int b); + public static native TemplateInstance foo$sectionA(int a); // <1> + public static native TemplateInstance foo$sectionB(int b); // <1> + + public static native TemplateInstance bar(); + } + + public TemplateInstance foo() { + if (isHxRequest()) { // <2> + return concatTemplates( // <3> + Templates.foo$sectionA(1), + Templates.foo$sectionB(2) + ); + } + return Templates.foo(1, 2); + } + + public TemplateInstance bar() { + onlyHxRequest(); // <4> + this.hx(HxResponseHeader.TRIGGER, "refresh"); // <5> + return Templates.bar(); + } +} +---- +<1> {quarkus-guides-url}/qute-reference#fragments[Qute fragments] declarations +<2> Check if this is a htmx request by looking for the `HX-Request` header or using flash data for redirects +<3> https://htmx.org/attributes/hx-swap-oob/[Out of band swap] with different templates or {quarkus-guides-url}/qute-reference#fragments[fragments] +<4> Only Hx requests are allowed, else it will fail with a BAD_REQUEST error +<5> Flag the response with an https://htmx.org/reference/#response_headers[htmx response header] + +For CSRF Security, you need a form parameter with the CSRF Token. By adding this 👇 when doing a hx-post/put/delete, The Hx requests will be sent with the CSRF parameter: +[source,html] +---- +# | +Task | +
---|---|
{todo.id} | +{todo.task} | +
# | -Task | -
---|---|
{todo.id} | -{todo.task} | -
- This is an automated email, you should not reply to it: your mail will be ignored. -
- - ----- - -And for text in `src/main/resources/templates/email.txt`: - -[source,txt] ----- -{#insert /} - -This is an automated email, you should not reply to it: your mail will be ignored. ----- - -You can then use those templates in your emails in `src/main/resources/templates/Emails/confirm.html`: - -[source,html] ----- -{#include email.html } - -- Welcome to Todos. -
- -- You received this email because someone (hopefully you) wants to register on Todos. -
- -- If you don't want to register, you can safely ignore this email. -
- -- If you want to register, complete your registration. -
-{/include} ----- - -And for text in `src/main/resources/templates/Emails/confirm.txt`: - -[source,txt] ----- -{#include email.txt} - -Welcome to Todos. - -You received this email because someone (hopefully you) wants to register on Todos. - -If you don't want to register, you can safely ignore this email. - -If you want to register, complete your registration by going to the following address: - -{uriabs:Login.confirm(user.confirmationCode)} -{/include} ----- - -Note that in emails you will want to use the `uriabs` namespace for absolute URIs and not relative ones, -otherwise the links won't work for your email recipients. - -You can find more information in the {quarkus-guides-url}/mailer-reference[Quarkus mailer documentation]. - -== Localisation / Internationalisation - -You can declare your default language and supported languages in `src/main/resources/application.properties`: - -[source,properties] ----- -# This is the default locale for your application -quarkus.default-locale=en -# These are the supported locales (should include the default locale, but order is not important) -quarkus.locales=en,fr ----- - -Next, you can declare your default language messages in the `src/main/resources/message.properties` file: - -[source,properties] ----- -# A simple message -hello=Hello World -# A parameterised message for your view -views_Application_index_greet=Hello %s ----- - -Declare your other language translations in the `src/main/resources/message_fr.properties` file: - -[source,properties] ----- -hello=Bonjour Monde -views_Application_index_greet=Salut %s ----- - -Now you can use these translated messages in your controller: - -[source,java] ----- -public static class Application extends Controller { - - @CheckedTemplate - public static class Templates { - public static native TemplateInstance index(String name); - } - - public TemplateInstance index() { - return Templates.index("Stef"); - } - - public String hello() { - return i18n.formatMessage("hello"); - } -} ----- - -Or in your template: - -[source,txt] ----- -With no parameter: -{m:hello} -With parameters: -{m:views_Application_index_greet(name)} ----- - -=== Selecting the language - -The current language for a request will depend on the following (in order): - -. The `_renarde_language` cookie, if set -. The https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language[`Accept-Language`] HTTP header, if set, - which defines an ordered list of languages by user preference. We will select a best matching language from the set - of `quarkus.locales`. -. If nothing else, we default to the default language as set by `quarkus.default-locale`, which defaults to `en_US`. - -You can override the user's language with a cookie by calling `i18n.set(language)`: - -[source,java] ----- -public static class Application extends Controller { - - @CheckedTemplate - public static class Templates { - public static native TemplateInstance index(); - } - - public void index() { - return Templates.index(); - } - - @POST - public void french() { - i18n.set("fr"); - index(); - } - - @POST - public void english() { - i18n.set("en"); - index(); - } -} ----- - -== Flash scope - -If you need to pass values from one endpoint to another request after a redirect, you can use the Flash scope. -Usually this is done in a `@POST` endpoint, by filling the Flash scope with either errors or messages, -before trigerring a redirect to the right `@GET` endpoint. - -You can push values in the Flash scope in an endpoint using the `flash(name, value)` method, or using the -`Flash` injectable component. - -You can read values from the Flash scope in your Qute views using the `{flash:name}` namespace. - -The Flash scope only lasts from one request to the next and is cleared at each request. - -== htmx - -If you want to use https://htmx.org/[htmx] with Renarde, we added an extra layer to make things more convenient: -[source,java] ----- -public class Application extends HxController { - - @CheckedTemplate - public static class Templates { - public static native TemplateInstance foo(int a, int b); - public static native TemplateInstance foo$sectionA(int a); // <1> - public static native TemplateInstance foo$sectionB(int b); // <1> - - public static native TemplateInstance bar(); - } - - public TemplateInstance foo() { - if (isHxRequest()) { // <2> - return concatTemplates( // <3> - Templates.foo$sectionA(1), - Templates.foo$sectionB(2) - ); - } - return Templates.foo(1, 2); - } - - public TemplateInstance bar() { - onlyHxRequest(); // <4> - this.hx(HxResponseHeader.TRIGGER, "refresh"); // <5> - return Templates.bar(); - } -} ----- -<1> {quarkus-guides-url}/qute-reference#fragments[Qute fragments] declarations -<2> Check if this is a htmx request by looking for the `HX-Request` header or using flash data for redirects -<3> https://htmx.org/attributes/hx-swap-oob/[Out of band swap] with different templates or {quarkus-guides-url}/qute-reference#fragments[fragments] -<4> Only Hx requests are allowed, else it will fail with a BAD_REQUEST error -<5> Flag the response with an https://htmx.org/reference/#response_headers[htmx response header] - -For CSRF Security, you need a form parameter with the CSRF Token. By adding this 👇 when doing a hx-post/put/delete, The Hx requests will be sent with the CSRF parameter: -[source,html] ----- -