Skip to content

Commit

Permalink
Qute: fix rendered=false if a fragment includes nested fragment
Browse files Browse the repository at this point in the history
- problem occurs if a fragment is rendered separately and that fragment
includes a template that declares another fragment like `{#fragment
rendered=false}` - the `rendered=false` is ignored
- fixes #44281
  • Loading branch information
mkouba committed Nov 4, 2024
1 parent c9ef6de commit 47215b4
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public class FragmentSectionHelper implements SectionHelper {

private static final String ID = "id";

// the generated id of the template that declares this fragment section
private final String generatedTemplateId;
private final String identifier;
private final Expression rendered;

FragmentSectionHelper(String identifier, Expression isVisible) {
FragmentSectionHelper(String identifier, Expression rendered, String generatedTemplateId) {
this.identifier = identifier;
this.rendered = isVisible;
this.rendered = rendered;
this.generatedTemplateId = generatedTemplateId;
}

public String getIdentifier() {
Expand All @@ -33,18 +36,25 @@ public String getIdentifier() {

@Override
public CompletionStage<ResultNode> resolve(SectionResolutionContext context) {
if (rendered == null
// executed from an include section
|| context.getParameters().containsKey(Template.Fragment.ATTRIBUTE)
// the attribute is set if executed separately via Template.Fragment
|| context.resolutionContext().getAttribute(Fragment.ATTRIBUTE) != null) {
if (isAlwaysExecuted(context)) {
return context.execute();
}
return context.resolutionContext().evaluate(rendered).thenCompose(r -> {
return Booleans.isFalsy(r) ? ResultNode.NOOP : context.execute();
});
}

private boolean isAlwaysExecuted(SectionResolutionContext context) {
if (rendered == null
// executed from an include section
|| context.getParameters().containsKey(Fragment.ATTRIBUTE)) {
return true;
}
Object attribute = context.resolutionContext().getAttribute(Fragment.ATTRIBUTE);
// the attribute is set if executed separately via Template.Fragment
return attribute != null && attribute.equals(generatedTemplateId + identifier);
}

public static class Factory implements SectionHelperFactory<FragmentSectionHelper> {

static final Pattern FRAGMENT_PATTERN = Pattern.compile("[a-zA-Z0-9_]+");
Expand Down Expand Up @@ -99,7 +109,7 @@ public FragmentSectionHelper initialize(SectionInitContext context) {
.build();
}
}
return new FragmentSectionHelper(id, context.getExpression(RENDERED));
return new FragmentSectionHelper(id, context.getExpression(RENDERED), generatedId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,9 @@ public Template getOriginalTemplate() {
@Override
public TemplateInstance instance() {
TemplateInstance instance = super.instance();
instance.setAttribute(Fragment.ATTRIBUTE, true);
// when a fragment is executed separately we need a way to instruct FragmentSectionHelper to ignore the "renreded" parameter
// Fragment.ATTRIBUTE contains the generated id of the template that declares the fragment section and the fragment identifier
instance.setAttribute(Fragment.ATTRIBUTE, TemplateImpl.this.getGeneratedId() + FragmentImpl.this.getId());
return instance;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,41 @@ public void testInvalidId() {
expected.getMessage());
}

@Test
public void testNestedFragmentRendered() {
Engine engine = Engine.builder().addDefaults().build();
Template alpha = engine.parse("""
OK
{#fragment id=\"nested\" rendered=false}
NOK
{/}
{#fragment id=\"visible\"}
01
{/fragment}
""");
engine.putTemplate("alpha", alpha);
assertEquals("OK01", alpha.render().replaceAll("\\s", ""));
assertEquals("NOK", alpha.getFragment("nested").render().trim());

Template bravo = engine.parse("""
{#include $nested}
{#fragment id=\"nested\" rendered=false}
OK
{/}
""");
assertEquals("OK", bravo.render().trim());
assertEquals("OK", bravo.getFragment("nested").render().trim());

assertEquals("NOK", engine.parse("{#include alpha$nested /}").render().trim());
Template charlie = engine.parse("{#include alpha /}");
assertEquals("OK01", charlie.render().replaceAll("\\s", ""));

Template delta = engine.parse("""
{#fragment id=\"nested\" rendered=false}
{#include alpha /}
{/}
""");
assertEquals("OK01", delta.getFragment("nested").render().replaceAll("\\s", ""));
}

}

0 comments on commit 47215b4

Please sign in to comment.