How do i "just use" Thymeleaf? - thymeleaf

i tried to find tutorials online, but i only found them in the context of web applications. in my case what i have is an html template that i want to fill "manually", meaning it should run from a static main method without spring or any other framework. just "plain thymeleaf"
i have for example a test html file containing:
<p th:text="#{test.message}">This is a report</p>
how does the java code look like that simply fills this variable? i couldn't find a minimal running code example.

The following is a standalone Thymeleaf demo (no web application needed; no Spring used):
Assumptions:
I have a messages file called test.properties containing the following:
test.message=This is a message to you.
I have a related HTML template called test.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<p th:text="#{test.message}">This is a report</p>
<p th:text="${note}">This is a note</p>
</html>
In the above template, I added Thymeleaf variable expression (${note}), as well as the message expression - just to make the demo more complete. You may not even need that.
You need to make sure that the HTML template and the properties file are in the same location, because I am using the StandardMessageResolver - see below.
I created a standalone Thymeleaf template engine class:
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.context.Context;
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
import org.thymeleaf.messageresolver.IMessageResolver;
import org.thymeleaf.messageresolver.StandardMessageResolver;
import java.util.Map;
public class HtmlTemplateEngine {
private final TemplateEngine templateEngine;
public HtmlTemplateEngine() {
templateEngine = new org.thymeleaf.TemplateEngine();
templateEngine.addDialect(new Java8TimeDialect()); // optional extra
ClassLoaderTemplateResolver templateResolver
= new ClassLoaderTemplateResolver(Thread
.currentThread().getContextClassLoader());
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setPrefix("/thymeleaf/");
templateResolver.setCacheTTLMs(3600000L); // one hour
templateResolver.setCacheable(true);
templateResolver.setCharacterEncoding("UTF-8");
templateEngine.setTemplateResolver(templateResolver);
IMessageResolver messageResolver = new StandardMessageResolver();
templateEngine.setMessageResolver(messageResolver);
}
// use this if you do not want to pass a Context (see below). It basically does
// the same as the Context-based example, by transferring Map data to a Context:
public String getTemplate(String templateName, Map<String, Object> parameters) {
Context ctx = new Context();
if (parameters != null) {
parameters.forEach((k, v) -> {
ctx.setVariable(k, v);
});
}
return this.templateEngine.process(templateName, ctx).trim();
}
public String getTemplate(String templateName, Context ctx) {
return this.templateEngine.process(templateName, ctx).trim();
}
}
Some of the entries are default values - so they could be removed without affecting anything (for example the StandardMessageResolver will be used by default if you do not provide one).
I chose to use a ClassLoaderTemplateResolver. There are other choices you can make.
I use the above artifacts as follows:
private void processSimpleReport() {
Map<String, Object> model = new HashMap<>();
model.put("note", "my note here");
Context ctx = new Context();
//ctx.setLocale(Locale.US); // optional for i18n
ctx.setVariables(model);
HtmlTemplateEngine engine = new HtmlTemplateEngine();
String s = engine.getTemplate("test.html", ctx);
System.out.println(s);
}
Your question only uses a message - the model variables are just optional extras I added for this demo.
The resulting HTML is:
<!DOCTYPE html>
<html>
<p>This is a message to you.</p>
<p>my note here</p>
</html>

Related

Checking HTML document with REST Assured

I'm trying to use REST Assured to check some properties on an HTML document returned by my server. An SSCCE demonstrating the problem would be as follows:
import static com.jayway.restassured.path.xml.config.XmlPathConfig.xmlPathConfig;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import com.jayway.restassured.path.xml.XmlPath;
public class HtmlDocumentTest {
#Test
public void titleShouldBeHelloWorld() {
final XmlPath xml = new XmlPath("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head><title>Hello world</title></head><body></body></html>")
.using(xmlPathConfig().with().feature("http://apache.org/xml/features/disallow-doctype-decl", false));
assertThat(xml.get("//title[text()]"), is("Hello world"));
}
}
Now, this attempt ends in com.jayway.restassured.path.xml.exception.XmlPathException: Failed to parse the XML document caused by, off all the possible errors, java.net.ConnectException: Connection timed out after some 30 seconds or so!
If I remove the line with the xmlPathConfig().with().feature(...) the test fails immediately due to DOCTYPE is disallowed when the feature "http://apache.org/xml/features/disallow-doctype-decl" set to true..
If I remove the doctype line from the document the parsing succeeds but the test fails on an assertion error, "Expected: is "Hello world" but: was <Hello worldnull>" -- however, that's a different problem, obviously (but feel free to give instructions on that one, too...). And removing the doctype isn't an option for me anyway.
So, question: how do you check properties of an HTML document with a doctype using REST Assured? It says in the documentation that "REST Assured providers predefined parsers for e.g. HTML, XML and JSON.", but I cannot seem to find any examples on how exactly to activate and work with that HTML parser! There's no "HtmlPath" class like there's XmlPath, for example, and that timeout exception is very puzzling...
I checked your code. The thing is that XmlPath of Restassured isn't Xpath, but uses a property access syntax. If you add a body content to your sample HTML you will see that your XPath doesn't do much. The actual name of the query language is GPath. The following example works, note also the use of CompatibilityMode.HTML, which has the right config for you need:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.jayway.restassured.path.xml.XmlPath;
import com.jayway.restassured.path.xml.XmlPath.CompatibilityMode;
public class HtmlDocumentTest {
#Test
public void titleShouldBeHelloWorld() {
XmlPath doc = new XmlPath(
CompatibilityMode.HTML,
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head><title>Hello world</title></head>"
+ "<body>some body"
+ "<div class=\"content\">wrapped</div>"
+ "<div class=\"content\">wrapped2</div>"
+ "</body></html>");
String title = doc.getString("html.head.title");
String content = doc.getString("html.body.div.find { it.#class == 'content' }");
String content2 = doc.getString("**.findAll { it.#class == 'content' }[1]");
assertEquals("Hello world", title);
assertEquals("wrapped", content);
assertEquals("wrapped2", content2);
}
}
If you're using the DSL (given/when/then) then XmlPath with CompatibilityMode.HTML is used automatically if the response content-type header contains a html compatible media type (such as text/html). For example if /index.html contains the following html page:
<html>
<title>My page</title>
<body>Something</body>
</html>
then you can validate the title and body like this:
when().
get("/index.html").
then().
statusCode(200).
body("html.title", equalTo("My page"),
"html.body", equalTo("Something"));
Here is sample code with the latest rest assured apis, i.e. io.restassured and not the older jayway.restassured. The explanation for the code is in the code comments.
//Demo for an api which returns a json string inside html. The json string is just an array of objects.
import io.restassured.RestAssured;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import java.util.List;
import static io.restassured.RestAssured.*;
public void testMyApi() {
Response response =
when().
get("www.myapi.com/data").
then().
extract().
response();
String bodyTxt = response.htmlPath().getString("body");//Get the body element of the html response.
JsonPath jsonObj = new JsonPath(bodyTxt);//helps us to find things in a json string.
List<String> rootItems = jsonObj.getList("$");//get root element of the json part.
System.out.println(rootItems);
}

Orchard - Querying the Part On A Layout File

I’ve gotten stuck on an “Object reference not set to an instance of an object” error message.
We’re trying to use a PagePart field that is attached to the Page type to dynamically link a CSS file in the HEAD of a layout file. See below code.
<!-- DYNAMIC CSS-->
var contentItem = Model.ContentItem;
var pagePart = (PagePart)contentItem.PagePart;
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName))
{
<link ref="#Url.Content("/Themes/MyTheme/Styles/festival-programs/" + pagePart.FestivalProgramName + ".css")" rel="stylesheet" type="text/css">
}
This is in a file called:
Layout.cshtml
Something is wrong about this (obviously) since pagePart is “null” when I Attach to Debugger and look. I get that the Layout file doesn’t know that it’s associated with a “Page” Content Type but this layout is only used with Pages. Anyway, this is very similar to code that works elsewhere in our Orchard site. Any help or advice is hugely appreciated!
Thanks, T
In the Layout, the Model is the Layout object. It has nothing to do whatsoever with whatever content is going to get rendered into the Content zone.
I think what you are trying to do should be done by overriding the Page template (Content-Page.Detail.cshtml). (Note the Detail part, you probably don't want to import every css when displaying multiple pages in summary)
In there you can do:
#{
var contentItem = Model.ContentItem; // The Page content item
var pagePart = contentItem.Page; // Note that casting to PagePart won't work, because it does not exist
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName.Value))
{
// "Orchard's" way to include styles
Style.Include("festival-programs/" + pagePart.FestivalProgramName.Value + ".css");
}
}
EDIT:
What you probably should do (I assume not every page is a festival page) is create a new Content Type: FestivalPage. Then attach the following parts to this content type (same as the Page content type):
Common
Publish later
Title
Autoroute
Body (Orchard 1.8.1 and lower)
Layout (Orchard 1.9.x)
Tags
Localization
Menu
And your field:
FestivalProgramName
Then create an alternate Content-FestivalPage.Detail.cshtml with the following content:
#using Orchard.Utility.Extensions;
#{
if (Model.Title != null) {
Layout.Title = Model.Title;
}
Model.Classes.Add("content-item");
var contentTypeClassName = ((string)Model.ContentItem.ContentType).HtmlClassify();
Model.Classes.Add(contentTypeClassName);
var tag = Tag(Model, "article");
var contentItem = Model.ContentItem; // The FestivalPage content item
var pagePart = contentItem.FestivalPage; // The FestivalPage part with the FestivalProgramName field
if (!String.IsNullOrWhiteSpace(pagePart.FestivalProgramName.Value))
{
// "Orchard's" way to include styles
Style.Include("festival-programs/" + pagePart.FestivalProgramName.Value + ".css");
}
}
// -- Default Orchard content --
#tag.StartElement
<header>
#Display(Model.Header)
#if (Model.Meta != null) {
<div class="metadata">
#Display(Model.Meta)
</div>
}
</header>
#Display(Model.Content)
#if(Model.Footer != null) {
<footer>
#Display(Model.Footer)
</footer>
}
#tag.EndElement
// ----
This way you won't get in the way with the normal pages of the application.
Thank you so much for responding. Your solution works but not in my case since I use the Layout Selector module and can't override at the Page.Detail level since that would imply one layout - or at least from my perspective it seems that way. I found another option though that does the trick.
We already take advantage of the Handlers class to insert META stuff into the HEAD of the page and thanks to your feedback and this thread Adding an element to page <head> in Orchard CMS, it dawned on me to use the same Handler to insert the CSS link.
PagePartHandler.cs.
using System;
using MyModuleName.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Title.Models;
using Orchard.Data;
using Orchard.UI.Resources;
using Orchard.Utility.Extensions;
namespace MyModuleName.Handlers {
public class PagePartHandler : ContentHandler {
private readonly IResourceManager _resourceManager;
public PagePartHandler(
IRepository<PagePartRecord> repository,
IResourceManager resourceManager) {
_resourceManager = resourceManager;
Filters.Add(StorageFilter.For(repository));
OnGetDisplayShape<PagePart>(RegisterFestivalProgramStyle);
}
private void RegisterFestivalProgramStyle(BuildDisplayContext context, PagePart part) {
if (context.DisplayType != "Detail")
return;
if (String.IsNullOrWhiteSpace(part.FestivalProgramName))
return;
_resourceManager.RegisterLink(new LinkEntry
{
Rel = "stylesheet",
Type = "text/css",
Href = "/Themes/Bootstrap/Styles/festival-programs/" + part.FestivalProgramName + ".css"
});
}
}
}
This uses the tradition link style, not ResourceManifest.cs, but WORKS!

Add a URL in faces message JSF 2.0 [duplicate]

I want to embed a link in a JSF message, is this possible?
When I try it, the rendered html of the h:messages tag escapes the html characters. I tried setting the escape attribute of the h:messages tag to false, but that didn't help.
Unfortunately, this is not possible in the standard JSF implementation. The component and the renderer doesn't officially support this attribute. You can however homegrow a renderer which handles this.
Since this is a pretty common requirement/wish, I thought to take a look what's all possible.
First some background information: JSF by default uses ResponseWriter#writeText() to write the tag body, which escapes HTML by default. We'd like to let it use ResponseWriter#write() instead like as with <h:outputText escape="false" />. We'd like to extend the MessagesRenderer of the standard JSF implementation and override the encodeEnd() method accordingly. But since the MessagesRenderer#encodeEnd() contains pretty a lot of code (~180 lines) which we prefer not to copypaste to just change one or two lines after all, I found it better to replace the ResponseWriter with a custom implementation with help of ResponseWriterWrapper wherein the writeText() is been overriden to handle the escaping.
So, I ended up with this:
package com.example;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.context.ResponseWriterWrapper;
import javax.faces.render.FacesRenderer;
import com.sun.faces.renderkit.html_basic.MessagesRenderer;
#FacesRenderer(componentFamily="javax.faces.Messages", rendererType="javax.faces.Messages")
public class EscapableMessagesRenderer extends MessagesRenderer {
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
final ResponseWriter originalResponseWriter = context.getResponseWriter();
try {
context.setResponseWriter(new ResponseWriterWrapper() {
#Override
public ResponseWriter getWrapped() {
return originalResponseWriter;
}
#Override
public void writeText(Object text, UIComponent component, String property) throws IOException {
String string = String.valueOf(text);
String escape = (String) component.getAttributes().get("escape");
if (escape != null && !Boolean.valueOf(escape)) {
super.write(string);
} else {
super.writeText(string, component, property);
}
}
});
super.encodeEnd(context, component); // Now, render it!
} finally {
context.setResponseWriter(originalResponseWriter); // Restore original writer.
}
}
}
In spite of the #FacesRenderer annotation, it get overriden by the default MessagesRenderer implementation. I suspect here a bug, so I reported issue 1748. To get it to work anyway, we have to fall back to the faces-config.xml:
<render-kit>
<renderer>
<component-family>javax.faces.Messages</component-family>
<renderer-type>javax.faces.Messages</renderer-type>
<renderer-class>com.example.EscapableMessagesRenderer</renderer-class>
</renderer>
</render-kit>
Then, to trigger it, just do:
<h:messages escape="false" />
And it works! :)
Note: the above affects <h:messages> only. To do the same for <h:message>, just do the same, but replace anywhere "Messages" by "Message" (component family, renderer type and classnames).
The escape="false" attributed you need is provided by the OmniFaces <o:messages> component. The OmniFaces utility library is available for JSF 2.
I posted this solution mentioned by #BalusC's comment as an answer since this is the most straightforward solution.

Putting parameters in velocity context in Jira 4.4

I'm developing a plugin to display additional information related to a project.
So I'm developing a Project Tab Panel module but my page does not display the paramenters I put in the velocity context.
Here is the a part of plugin xml:
<project-tabpanel key="stats-tab-panel" name="Stats Tab Panel" i18n-name-key="stats-tab-panel.name" class="it.pride.jira.plugins.StatsTabPanel">
<description key="stats-tab-panel.description">The Stats Tab Panel Plugin</description>
<label key="stats-tab-panel.label"></label>
<order>10</order>
<resource type="velocity" name="view" location="templates/tabpanels/stats-tab-panel.vm"/>
Here instead the useful part of my class:
public class StatsTabPanel extends GenericProjectTabPanel {
public StatsTabPanel(JiraAuthenticationContext jiraAuthenticationContext,
FieldVisibilityManager fieldVisibilityManager) {
super(jiraAuthenticationContext, fieldVisibilityManager);
// TODO Auto-generated constructor stub
}
public String testvalue="112002";
#Override
public boolean showPanel(BrowseContext context){
return true;
}
#Override
public Map<String, Object> createVelocityParams (BrowseContext context) {
Map<String, Object> contextMap = createVelocityParams(context);
contextMap.put("testvalue", testvalue);
return contextMap;
}
}
So, as in this case, when i write `$testvalue in my template the number doesn't show up.
What am I doing wrong?
The problem is that the getHtml method that is used to return the HTML for the tab has a Map that is the Velocity context but only contains the params in the BrowseContext object. The way to fix this is to override createVelocityParams in your class, e.g.
protected Map createVelocityParams(final BrowseContext ctx) {
Map params = super.createVelocityParams(ctx);
params.put("myparam", "some value for it");
return params;
}
The problem is that method is protected so I ended up also overriding getHtml which calls createVelocityParams in a parent class.
Some Project tabs extend GenericProjectTabPanel but some such as SummaryProjectTabPanel.java seem to use UI Fragments. I suspect that Fragments is what things are moving towards.

ASP.NET MVC HtmlHelper extensions for YUI controls (Yahoo User Interfaces)?

Has anyone written any HTMLHelper classes for MVC that help with Yahoo's User Interface Library?
For instance I have written a helper method to convert a 'menu model' into the HTML markup needed to support the Yahoo Menu Control. The MVC pattern works well here because obviously if I chose to switch to a different menu implementation I can just write a new helper and not touch the model.
This code works for me but isn't fully tested and you're welcome to use it.
First we need a simple data structure for the menu model itself. You would add this to your page model with the normal MVC conventions. For instance I access a list of menu items from my view via ViewData.Model.MainMenu.MenuOptions.
public class MenuItem
{
public string Text { get; set; }
public string Description { get; set; }
public string RouteURL { get; set; }
public bool SeparatorBefore { get; set; }
public List<MenuItem> MenuItems { get; set; }
}
Extension method. Put in a namespace that is accessible to your view.
public static class YUIExtensions
{
public static string RenderMenu(this HtmlHelper html, string id, List<MenuItem> menuItems)
{
// <div id="mnuTopNav" class="yuimenubar yuimenubarnav">
// <div class="bd">
// <ul class="first-of-type">
// <li class="yuimenubaritem first-of-type"><a class="yuimenubaritemlabel" href="#store">Store</a></li>
// <li class="yuimenubaritem"><a class="yuimenubaritemlabel" href="#products">Products</a>
// <div id="communication" class="yuimenu">
// <div class="bd">
// <ul>
// <li class="yuimenuitem"><a class="yuimenuitemlabel" href="http://360.yahoo.com">360°</a></li>
// <li class="yuimenuitem"><a class="yuimenuitemlabel" href="http://mobile.yahoo.com">Mobile</a></li>
// <li class="yuimenuitem"><a class="yuimenuitemlabel" href="http://www.flickr.com">Flickr Photo Sharing</a></li>
// </ul>
// </div>
// </div>
// </li>
// </ul>
// </div>
//</div>
int menuId = 0;
HtmlGenericControl menuControl = CreateControl(html, id, 0, ref menuId, menuItems);
// render to string
StringWriter sw = new StringWriter();
HtmlTextWriter tw = new HtmlTextWriter(sw);
tw.Indent = 1;
menuControl.RenderControl(tw);
return sw.ToString();
}
private static HtmlGenericControl CreateControl(HtmlHelper html, string id, int level, ref int menuId, List<MenuItem> currentItems)
{
var menu = new HtmlGenericControl("div");
menu.Attributes["class"] = (level == 0) ? "yuimenubar yuimenubarnav" : "yuimenu";
menu.Attributes["id"] = id;
var div_bd = new HtmlGenericControl("div");
menu.Controls.Add(div_bd);
div_bd.Attributes["class"] = "bd";
HtmlGenericControl ul = null;
int i = 0;
foreach (var menuItem in currentItems)
{
if (ul == null || menuItem.SeparatorBefore)
{
ul = new HtmlGenericControl("ul");
div_bd.Controls.Add(ul);
if (i == 0)
{
ul.Attributes["class"] = "first-of-type";
}
}
var menuItem_li = new HtmlGenericControl("li");
menuItem_li.Attributes["class"] = (level == 0) ? "yuimenubaritem" : "yuimenuitem";
if (i == 0)
{
menuItem_li.Attributes["class"] += " first-of-type";
}
ul.Controls.Add(menuItem_li);
var href = new HtmlGenericControl("a");
href.Attributes["class"] = (level == 0) ? "yuimenubaritemlabel" : "yuimenuitemlabel";
href.Attributes["href"] = menuItem.RouteURL;
href.InnerHtml = menuItem.Text;
menuItem_li.Controls.Add(href);
if (menuItem.MenuItems != null && menuItem.MenuItems.Count > 0)
{
menuItem_li.Controls.Add(CreateControl(html, id + "_" + (menuId++), level + 1, ref menuId, menuItem.MenuItems));
}
i++;
}
return menu;
}
}
Stick this code where you want to generate the menu in your view (I have this in a master page):
<%= Html.RenderMenu("mnuTopNav", ViewData.Model.MainMenu.MenuOptions) %>
If you're lazy, or don't know about YUI you'll need this too in your <HEAD>
<!-- Combo-handled YUI CSS files: -->
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/combo?2.6.0/build/menu/assets/skins/sam/menu.css">
<!-- Combo-handled YUI JS files: -->
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.6.0/build/yahoo-dom-event/yahoo-dom-event.js&2.6.0/build/container/container_core-min.js&2.6.0/build/menu/menu-min.js"></script>
This currently generates markup for top nav style navigation bar - but it could be easily modified.
I was hoping somebody else was doing the same for some of the other controls.
Seems like a good candidate for an open source project - but I dont have time to start that.
Implementation advice welcomed!
Last night I did some thinking about this and am wondering if there's even more opportunity here to make general purpose HTMLHelpers using YUI or whatever other Javascript/HTML widgets you want.
For instance, if there was an interface for IMenu and one for ITextBox, ICheckBox, IRichTextEditor, ICarousel, etc. much like your class for a MenuItem, then you could have a YUI implementation of each of those interfaces, one for JQuery, one for MooTools or one for just straight HTML/CSS.
Part of what sparked this is the generalization that articles like this: http://designingwebinterfaces.com/essential_controls are taking to UI controls on the web for rich web apps.
Those interfaces would contain all of the basic stuff that is obvious at first glance: Id, Name, Value, List, Style, OnChange, OnClick, etc. as well as less obvious stuff like ValidationRegex, HelpText, etc.
That would let you have a layer that converts a model object or model property into an ITextBox and not worry about which one of the implementations of the interface will actually be handling it. You could also easily switch to a new implementation if one came along that was better/faster/cooler.
You'd have to deal with what should happen if you give something like ValidationRegex to a barebones HTML implementation and it has no way to deal with it, but I think it's a path worth thinking about. I also think it might make more sense to implement this as separate from the existing HTMLHelper namespace by inheriting it or just reimplementing it, but I am often wrong at this kind of early idea stage of coming up with a solution.
The YUIAsp.NET stuff is interesting, but is more oriented to WebForms and user controls than the direction that ASP.NET MVC and even moreso with Fubu MVC recently are going.
I tinkered with this idea a little bit and am really intrigued with the possibilities.
Simon,
I'm not sure this is helpful with respect to the MVC question, but there is a good open-source project that aims to simplify working with YUI within .NET:
http://www.yuiasp.net/
Menu is one of the controls that they include.
At the very least, this may be a project you can contribute back to if your work adds a new dimension to what's already there.
-Eric

Resources