Skip to content

Commit

Permalink
Get dynamic title content working w/ limitations
Browse files Browse the repository at this point in the history
  • Loading branch information
ultraq committed Jan 22, 2024
1 parent 22e4b30 commit 6008579
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class LayoutDialect extends AbstractProcessorDialect {
return [
// Processors available in the HTML template mode
new StandardXmlNsTagProcessor(TemplateMode.HTML, dialectPrefix),
new DecorateProcessor(TemplateMode.HTML, dialectPrefix, sortingStrategy, autoHeadMerging),
new DecorateProcessor(TemplateMode.HTML, dialectPrefix, sortingStrategy, autoHeadMerging, newTitleTokens),
new IncludeProcessor(TemplateMode.HTML, dialectPrefix),
new InsertProcessor(TemplateMode.HTML, dialectPrefix),
new ReplaceProcessor(TemplateMode.HTML, dialectPrefix),
Expand All @@ -90,7 +90,7 @@ class LayoutDialect extends AbstractProcessorDialect {

// Processors available in the XML template mode
new StandardXmlNsTagProcessor(TemplateMode.XML, dialectPrefix),
new DecorateProcessor(TemplateMode.XML, dialectPrefix, sortingStrategy, autoHeadMerging),
new DecorateProcessor(TemplateMode.XML, dialectPrefix, sortingStrategy, autoHeadMerging, newTitleTokens),
new IncludeProcessor(TemplateMode.XML, dialectPrefix),
new InsertProcessor(TemplateMode.XML, dialectPrefix),
new ReplaceProcessor(TemplateMode.XML, dialectPrefix),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,39 +43,30 @@ class DecorateProcessor extends AbstractAttributeModelProcessor {

private final SortingStrategy sortingStrategy
private final boolean autoHeadMerging
private final boolean newTitleTokens

/**
* Constructor, configure this processor to work on the 'decorate' attribute
* and to use the given sorting strategy.
*
* @param templateMode
* @param dialectPrefix
* @param sortingStrategy
* @param autoHeadMerging
*/
DecorateProcessor(TemplateMode templateMode, String dialectPrefix, SortingStrategy sortingStrategy,
boolean autoHeadMerging) {
boolean autoHeadMerging, boolean newTitleTokens) {

this(templateMode, dialectPrefix, sortingStrategy, autoHeadMerging, PROCESSOR_NAME)
this(templateMode, dialectPrefix, sortingStrategy, autoHeadMerging, newTitleTokens, PROCESSOR_NAME)
}

/**
* Constructor, configurable processor name for the purposes of the
* deprecated {@code layout:decorator} alias.
*
* @param templateMode
* @param dialectPrefix
* @param sortingStrategy
* @param autoHeadMerging
* @param attributeName
*/
protected DecorateProcessor(TemplateMode templateMode, String dialectPrefix, SortingStrategy sortingStrategy,
boolean autoHeadMerging, String attributeName) {
boolean autoHeadMerging, boolean newTitleTokens, String attributeName) {

super(templateMode, dialectPrefix, null, false, attributeName, true, PROCESSOR_PRECEDENCE, false)

this.sortingStrategy = sortingStrategy
this.autoHeadMerging = autoHeadMerging
this.newTitleTokens = newTitleTokens
}

/**
Expand Down Expand Up @@ -119,7 +110,7 @@ class DecorateProcessor extends AbstractAttributeModelProcessor {
decorateTemplate = decorateTemplate.cloneModel()

// Extract titles from content and layout templates and save to the template context
def titleExtractor = new TitleExtractor(context)
def titleExtractor = new TitleExtractor(context, newTitleTokens)
titleExtractor.extract(contentTemplate, TitlePatternProcessor.CONTENT_TITLE_KEY)
titleExtractor.extract(decorateTemplate, TitlePatternProcessor.LAYOUT_TITLE_KEY)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@

package nz.net.ultraq.thymeleaf.layoutdialect.models

import nz.net.ultraq.thymeleaf.expressionprocessor.ExpressionProcessor

import org.thymeleaf.context.ITemplateContext
import org.thymeleaf.model.IModel
import org.thymeleaf.model.IProcessableElementTag
import org.thymeleaf.standard.StandardDialect
import org.thymeleaf.standard.processor.StandardTextTagProcessor
import org.thymeleaf.standard.processor.StandardUtextTagProcessor
Expand All @@ -29,10 +32,11 @@ import groovy.transform.TupleConstructor
*
* @author Emanuel Rabina
*/
@TupleConstructor(defaults = false)
@TupleConstructor
class TitleExtractor {

final ITemplateContext context
final boolean newTitleTokens

/**
* Locate and extract title data from the given template model, saving it to
Expand All @@ -49,28 +53,36 @@ class TitleExtractor {
return
}

def expressionProcessor = new ExpressionProcessor(context)
def titleModel = template?.findModel { event ->
return event.isOpeningElementOf('title')
}

if (titleModel) {
def titleTag = titleModel.first()
assert titleTag instanceof IProcessableElementTag
def modelBuilder = new ModelBuilder(context)
def standardDialectPrefix = context.getPrefixForDialect(StandardDialect)

// Escapable title from a th:text attribute on the title tag
if (titleTag.hasAttribute(standardDialectPrefix, StandardTextTagProcessor.ATTR_NAME)) {
context[(contextKey)] = modelBuilder.build {
'th:block'('th:text': titleTag.getAttributeValue(standardDialectPrefix, StandardTextTagProcessor.ATTR_NAME))
}
def titleExpression = titleTag.getAttributeValue(standardDialectPrefix, StandardTextTagProcessor.ATTR_NAME)
context[(contextKey)] = newTitleTokens ?
expressionProcessor.processAsString(titleExpression) :
modelBuilder.build {
'th:block'('th:text': titleExpression)
}
}

// Unescaped title from a th:utext attribute on the title tag, or
// whatever happens to be within the title tag
else if (titleTag.hasAttribute(standardDialectPrefix, StandardUtextTagProcessor.ATTR_NAME)) {
context[(contextKey)] = modelBuilder.build {
'th:block'('th:utext': titleTag.getAttributeValue(standardDialectPrefix, StandardUtextTagProcessor.ATTR_NAME))
}
def titleExpression = titleTag.getAttributeValue(standardDialectPrefix, StandardUtextTagProcessor.ATTR_NAME)
context[(contextKey)] = newTitleTokens ?
expressionProcessor.processAsString(titleExpression) :
modelBuilder.build {
'th:block'('th:utext': titleExpression)
}
}

// Title value exists within the <title>...</title> tags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class LayoutDialectTestExecutor extends JUnitTestExecutor {
def exclusions = [
'nz/net/ultraq/thymeleaf/layoutdialect/decorators/Decorate-DisabledHead.thtest',
'nz/net/ultraq/thymeleaf/layoutdialect/decorators/html/TitlePattern-AllowOtherProcessors.thtest',
'nz/net/ultraq/thymeleaf/layoutdialect/decorators/html/TitleTokens.thtest',
'nz/net/ultraq/thymeleaf/layoutdialect/decorators/html/TitleTokens-StaticContent.thtest',
'nz/net/ultraq/thymeleaf/layoutdialect/decorators/html/TitleTokens-DynamicContentThText.thtest',
'nz/net/ultraq/thymeleaf/layoutdialect/decorators/strategies/GroupingStrategy.thtest',

// Disabled, see test file for details
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

# Test using standard Thymeleaf expression syntax for title elements.

%TEMPLATE_MODE HTML

%CONTEXT
layoutTitle = "My website"
contentTitle = "My blog"

%INPUT
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{Layout}">
<head>
<title th:text="${contentTitle}" layout:title-pattern="|${layoutDialectContentTitle} - ${layoutDialectLayoutTitle}|">Content title</title>
</head>
<body>
<p th:text="${layoutDialectContentTitle}">I can use the content title here too!</p>
<p th:text="${layoutDialectLayoutTitle}">I can use the layout title here too!</p>
</body>
</html>

%INPUT[Layout]
<!DOCTYPE html>
<html>
<head>
<title th:text="${layoutTitle}">Layout title</title>
</head>
</html>

%OUTPUT
<!DOCTYPE html>
<html>
<head>
<title>My blog - My website</title>
</head>
<body>
<p>My blog</p>
<p>My website</p>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
</head>
<body>
<p th:text="${layoutDialectContentTitle}">I can use the content title here too!</p>
<p th:text="${layoutDialectLayoutTitle}">I can use the layout title here too!</p>
</body>
</html>

Expand All @@ -31,5 +32,6 @@
</head>
<body>
<p>Content title</p>
<p>Layout title</p>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,35 @@ import spock.lang.Specification
*/
class TitleTokens extends Specification {

def "New title tokens"() {
given:
def testExecutor = new TestExecutor(
'TitleTokens',
new WebProcessingContextBuilder(JavaxServletTestWebExchangeBuilder.create())
)
testExecutor.with {
dialects = [
new StandardDialect(),
new LayoutDialect(new AppendingStrategy(), true, true)
]
reporter = new JUnitTestReporter(new ConsoleTestReporter())
}
TestExecutor testExecutor

def setup() {
testExecutor = new TestExecutor(
'TitleTokens',
new WebProcessingContextBuilder(JavaxServletTestWebExchangeBuilder.create())
)
testExecutor.with {
dialects = [
new StandardDialect(),
new LayoutDialect(new AppendingStrategy(), true, true)
]
reporter = new JUnitTestReporter(new ConsoleTestReporter())
}
}

def "New title tokens - static content"() {
when:
testExecutor.execute(
'classpath:nz/net/ultraq/thymeleaf/layoutdialect/decorators/html/TitleTokens-StaticContent.thtest')

then:
assert testExecutor.reporter.lastResult.ok
}

def "New title tokens - dynamic content w/ th:text"() {
when:
testExecutor.execute('classpath:nz/net/ultraq/thymeleaf/layoutdialect/decorators/html/TitleTokens.thtest')
testExecutor.execute(
'classpath:nz/net/ultraq/thymeleaf/layoutdialect/decorators/html/TitleTokens-DynamicContentThText.thtest')

then:
assert testExecutor.reporter.lastResult.ok
Expand Down

0 comments on commit 6008579

Please sign in to comment.