Git Product home page Git Product logo

htl-spec's Introduction

HTML Template Language Specification

The HTML Template Language (HTL), formerly known as Sightly, has been introduced with Adobe Experience Manager 6.0 and takes the place of JSP (JavaServer Pages) as the preferred and recommended server-side template system for HTML.

This is the HTL Specification that defines the syntax and the behavior of the language.

A Java-based reference implementation has been donated to the Apache Sling project and can be checked out from https://github.com/apache/sling/tree/trunk/bundles/scripting/sightly.

The Technology Compatibility Kit can be run on implementations to check their compliance with the specification.

An implementation of version 1.4 of the language specification is available in AEM 6.3 SP3 and AEM 6.4 SP1.

htl-spec's People

Contributors

gabrielwalt avatar karollewandowski avatar kptdobe avatar paulochang avatar raducotescu avatar tripodsan avatar vladbailescu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

htl-spec's Issues

Add `data-sly-dataset` block statement to export data-* attributes to element scope

Dataset

data-sly-dataset:

  • Exports any preceding data-* attributes defined on the element as a (String -> String) hash table akin to HTML5 dataset, but excludes data-sly-* attributes. For subsequent data-sly-use attributes defined on the same element, the entries of the hash table would be merged with any options specified in the data-sly-use attribute.
  • Element: always shown.
  • Attribute value: optional; hint(s) for post-processor transformations of the dataset.
  • Attribute identifier: optional; customised identifier name to access the variable within expressions in nested scopes. If the identifier is omitted, the hash table values will not be exported beyond the scope of the element start tag. Expressions within subsequent attributes on the same element would be able to access the hash table using the default dataset identifier.

The data-sly-dataset attribute would serve two functions.

Export Data Attributes as Use Options

First, it would take advantage of native HTML syntax to provide an alternative to expression options for providing narrowly-scoped input arguments to the Use API.

For example:

<div data-parent-path="${resource.path}"
        data-filter="solutions"
        data-link-target="_blank"
        data-sly-dataset
        data-sly-use.articles="QueryArticles"
></div>

Would be equivalent to:

<div data-sly-use.articles="${'QueryArticles' @ parentPath=resource.path, filter='solutions', linkTarget='_blank'}"
></div>
Provide a Post-Process Hook for Contextual Rewriting

Second, it would provide a much narrower hook for a transformation stage than would a full-output SAX transformer to perform link rewriting or other similar environment- or context-sensitive function on attributes that are also used, by convention, to pass such contextual data to client-side scripts.

For example, a component may need to provide its client-side script the URL to a search results page, but this URL needs to be rewritten according to Sling Mapping rules:

<div class="quick-results" data-search-page="/content/mysite/en/us/searchresults.html"></div>

A transformation pipeline can be configured to handle all div:data-search-page start element SAX events, but this comes with a performance penalty on every page where this component appears and also introduces a config management challenge to keep the full list of every data attribute containing an href up to date.

Instead of using a SAX pipeline against the entire response, a more discrete transformation API can be defined that only deals with Map<String, String> as input and output, and that is triggered only after the data-sly-dataset attribute is evaluated on an element.

<div class="quick-results" data-search-page="/content/mysite/us/en/searchresults.html" data-sly-dataset></div>

Could then be transformed to:

<div class="quick-results" data-search-page="/us/en/searchresults"></div>

I imagine there would be a couple constraints

  1. Expression option values, which are typed, would take precedence over data attribute values, which would be coerced to string before being exported or rendered.
  2. Post-process transformations would need to occur only at the point when the data- attributed is rendered. HTL expressions should only be evaluated using the pre-transformation values.

data-sly-repeat

I want to propose a new operation in Sightly: data-sly-repeat.
This to overcome situations where you want to loop through items, but where you don't have a container element in your markup.

This is possible with data-sly-list and data-sly-unwrap, but that forces you to add markup.

<div data-sly-list="${resource.listChildren}" data-sly-unwrap>
   <div>${child.path}</div>
</div>

With data-sly-repeat you would be able to write it like this:

<div data-sly-repeat="${resource.listChildren}">${child.path}</div>

Allow array join to passthrough strings

I propose to modify the Sightly spec so that the array join works on simple strings by passing them through: ${'test' @ join=', '} will output test

Right now we must wrap the string into an ugly single-element array: ${['test'] @ join=', '}

data-sly-list and data-sly-repeat iteration control

Both data-sly-list and data-sly-repeat should be enhanced to provide iteration control through the following additional expression options:

  • begin - iteration begins at the item located at the specified index; first item of the collection has index 0
  • step - iteration will only process every step items of the collection, starting with the first one
  • end - iteration ends at the item located at the specified index (inclusive)

Example usage:

<!--/* Iterate the collections starting from the element at index 5 (6th element), jumping every 5 elements, until index 100 */-->
<ul data-sly-list="${collection @ begin = 5, step = 5, end = 100}">
    <li>${item}</li>
</ul>
<section data-sly-repeat="${collection @ begin = 5, step = 5, end = 100}">${item}</section>

IE Conditional comments wouldn't be working

Not sure about importance of this issue but anyway code below couldn't be parsed with HTL correctly:

<!--[if lt IE 9]>
    <sly data-sly-call="${clientlib.js @ categories='html5shiv-3.7.0'}"/>
<![endif]-->

Output:

<!--[if lt IE 9]>
    <sly data-sly-call=""></sly>
<![endif]-->

Questions about grammar

I analysed HTL grammar and it seems to me, that there are two issues:

  1. Lack of bracket notation property access (properties['key']) - where is it? Maybe it's implicitly included, but I tried to find it without success.
  2. Why only spaces are allowed around colon in ternary branches separator? I understand the reason for spaces - identifiers can contain :, but I think that it's nothing uncommon to format HTML files with tabs and have something like:
${someVal ? "Lorem ipsum dolor sit amet"
<tab><tab>: ""}

Branches separator can't be properly parsed in such code and it can be confusing for developers ("why the hell it doesn't work?"). In my opinion all white spaces should be allowed.

data-sly-alias

I want to propose a new block statement for HTL: data-sly-alias. With this, you could store an intermediate result of a long getter chain. Currently, you must use the full chain of getters when accessing a deeply nested value:

<div data-sly-use.object="SomeObject">
  <div>${object.child.grandChild.greatGrandChild.value1}</div>
  <div>${object.child.grandChild.greatGrandChild.value2}</div>
  <div>${object.child.grandChild.greatGrandChild.value3}</div>
</div>

With data-sly-alias it would be much more terse, by storing the intermediate evaluation of object.child.grandChild.greatGrandChild:

<div data-sly-use.object="SomeObject"
     data-sly-alias.greatGrandChild="${object.child.grandChild.greatGrandChild}">
  <div>${greatGrandChild.value1}</div>
  <div>${greatGrandChild.value2}</div>
  <div>${greatGrandChild.value3}</div>
</div>

Note:
For a workaround that isn't a hack using data-sly-test, see my comment below

Add Sightly context for XML content

I propose to add a new Sightly context for XML content. This will remove/encode markup that contains XSS risks, similar to context="html" and allow better semantics.

Right now the same result can be achieved using context="html" but this is not obvious and might encourage developers to use unsafe context.

basename option in i18n

When I look at the i18n implementation, there is also the option the specify 'basename', however this isn't mentioned in the spec.

Add Sightly context for JSON inline data

I propose to add a new context for inline JSON data. This will encode HTML entities in order to prevent XSS attacks.

At the moment the only way to add inline JSON data is by using context="unsafe". This is both semantically incorrect and dangerous (no XSS protection out-of-the-box).

@ context is not escaping single quotes

This code example where properties.titleLeft = GET 50% OFF ANY M'EDIUM OR L"AR"GE PIZZA!

<a href="#" onclick="trackPromoCta('${properties.titleLeft @ context='html'}')">Text</a>
<a href="#" onclick="trackPromoCta('${properties.titleLeft @ context='attribute'}')">Text</a>
<a href="#" onclick="trackPromoCta('${properties.titleLeft @ context='text'}')">Text</a>

Produces

<a href="#" onclick="trackPromoCta('Get 50% off any m'edium or l&quot;ar&quot;ge pizza!')">Text</a>

The single quote is not encoded as an HTML entity, therefore this function causes an error on button click as it ends too soon at the single quote in the what should be escaped content.

Producing error Uncaught SyntaxError: missing ) after argument list

Document data-sly-include "file" option

Add documentation for the "file" option in data-sly-include.

data-sly-include="${ @ file='/a/b/c' }"

( Example in tck: github Adobe-Marketing-Cloud/sightly-tck blockstatements/include/include.html )

Add negative numbers to the HTL grammar

I'm wondering why negative numbers like: -5, -8.2 are not allowed in grammar. Is there any motivation for this or it was just overlooked? It could be sometimes useful eg. for comparing values with -1 (it's often returned from Java API methods as "not found" code).
If it is something that can be added, but you just don't have time, please let me know. I can create changes here and in Sling project.

Clarify 'number' validation context

The number validation context should probably have been named 'long' as it only accepts whole numbers per the TCK. But IMHO this is not worth changing at this point for backwards compatibility reasons. I suggest just adding some clarifying language to the specification.

Should the language spec include specific treatment on implicit unwrapping of optional values for backend languages that support them?

We are encouraging the use of Java 8 Streams in our service and model code, which had led to prolific use of java.util.Optional return types for many model getters and service methods, which are treated opaquely as non-null runtime Objects if accessed in HTL. From my personal perspective, though, java Optional values should be treated as a special case, just like null or Map:

  1. Optional.empty() in the context of a single value is implicitly unwrapped to null and treated the same way, i.e. toBoolean(value) -> false, toString(value) -> '', toCollection -> [].
  2. Optional.of(foo) in the context of a single value is implicitly unwrapped to foo and subjected to the same rules described above, and with subsequent property expressions applied only to foo and not to the Optional wrapper.

My question though is whether such behavior should be left up to the backend languages to define, or if this is something that needs to be explicitly defined by the HTL spec.

Trivial mistake in logical operator

In section 2.2.5 there is:

<p data-sly-test="${!editOrDesign and pageProperties.jcr:title}">show this when disabled...</p>

but should be (&& instead of and):

<p data-sly-test="${!editOrDesign && pageProperties.jcr:title}">show this when disabled...</p>

Are pull requests with such fixes welcomed?

Add 'double' validation context

As mentioned in #20, the current 'number' validation context only handles whole numbers. We should add a 'double' validation context to handle fractional numbers.

Clarify how objects in expressions are evaluated

From the HTL expression it is not clear to me what should be the proper output for
${resource} where resource is a simple POJO.

My expectation would be that it follows the JSP EL language rules which indicates that the toString() method is called in such cases. [0]

The apache implementation currently seems to print nothing, which is counterintuitive to me.

Could you please clarify how this should work?

0- section 1.2.1.1 http://download.oracle.com/otn-pub/jcp/jsp-2.1-fr-spec-oth-JSpec/jsp-2_1-fr-spec-el.pdf?AuthParam=1499856940_8baf9732bece797be47d8353c416c0d2

Not consistent behavior while using @query

Please consider the following example:

<a href="${'###' @ context='attribute'}">

The result is <a href="###"></a>

Now let's a add a query (assume controller.model.query is valid and evaluates to ?foo=bar)

<a href="${'###' @ context='attribute', query=controller.model.query}">

The result is a SightlyException & stack trace on a page :

The provided path does not represent a valid URI: ###

So, i was able to break the page on author without being able any more to correct the invalid value '###' which I deliberately set in the component dialog.

To me it looks as not very consistent behaviour, would it be maybe better to return ###?foo=bar in second case or return empty string in both cases?

The specification's simplified grammar assigns the same priority to the && and || operators

When compared to the majority of programming languages available, HTL's expression language can lead to some confusing bugs. Consider the example:

<sly data-sly-test.combinedVars="${var1 || var2 && var3}"/>

When provided with the following inputs:

var1: true
var2: false
var3: false

one could reasonably expect the value of combinedVars to be true because of the value of var1. This is because most languages give higher precedence to the && operator, than ||.

In HTL, combinedVars is false because both && and || are the same precedence. This means the statement above is evaluated as if it were written:

<sly data-sly-test.combinedVars="${(var1 || var2) && var3}"/>

This interpretation is counter-intuitive to any programming language I have ever used. Though this may be confirmation bias showing through, but given that this was not raised in an issue, as far as I can tell, means very few other people have encountered this issue. If that is true (or close enough) maybe the EL grammar can be changed to separate out this fact, to give && a higher precedence.

I am no grammar writing expert, but if my memory serves me well, this would be:

exprNode = orBinaryOp , '?' , orBinaryOp , ws , ':' , ws , orBinaryOp
         | orBinaryOp ;

orBinaryOp = andBinaryOp , '||' , andBinaryOp
           | andBinaryOp ;

andBinaryOp = factor , '&&' , factor
            | factor ;

instead of the original

exprNode = binaryOp , '?' , binaryOp , ws , ':' , ws , binaryOp
         | binaryOp ;

binaryOp = factor { , operator , factor} ;

operator = '&&'
         | '||' ;

The rest of the grammar remains unchanged, and the grammar continues to be LL. This would have impact on existing code, but I think it would be reasonable to make the change to give developers a more natural feel for HTL EL.

Feedback is appreciated.

Add "since" version for each feature

Looking at the spec, it is currently unclear for the reader when new features got introduced. All features should document since which version of Sightly they are available.

E.g.:
3.1. <sly>
Since: Sightly 1.1
The HTML tag can be used to remove the current element [...]

data-sly-resource scope

Hi,

What is the reasoning behind the following quote?

The scope of the data-sly-resource statement isn't passed to the template of the included resource.

If I understand correctly it's not possible to do component composition using data-sly-resource because the scope is not shared between a parent and a child component.

e.g. passing a variable as a session attribute is not possible

Define reserved options

Sightly's expressions accept options that allow to change the evaluated result through filters (e.g. context, format, i18n, locale, hint, join). The same expression options though can be interpreted as template parameters or Use-API parameters for initialising use objects.

Given that the filters' options are standard language features, template and use objects should not use these option names as names for their parameters.

Clarify block statements priorities

I think that current description of block statements is not sufficient. When I read it for the first time I was curious what will happen when we use test and then use. Will use be invoked only if test evaluated to true? What if we use list and repeat on the same element? Those questions still appear in my team from time to time.
If you agree it could be improved, but don't have time for it and don't mind if I do it, please let me know.

suffixSelectors and suffixExtention URI manipulation support

I propose the ability to treat suffixes as their own URI (limited to selectors and extensions). A suffix can point to another page and it would be nice to be able to easily modify selectors/extensions for that page.

Currently to get this functionality I have to spit off my suffix and manipulate it then appends it onto the base url. (Or do it in Java with the suffix as a new PathInfo object and append that)

// here request.pathInfo already has suffix stripped off and copied in hostPath
<a href="${request.pathInfo @ selectors='host'}${requestUtil.attributes['hostPath'] @ selectors=item.selector}">${item.title}</a>

It would be great to have expression options to do this directly like this:

<a href="${request.pathInfo @ selectors='host', suffixSelectors=item.selector}">${item.title}</a>

Maybe this should be a separate issue but I will mention it here. It would also be great to be able to do request.pathInfo.suffix, request.pathInfo.path, etc. in htl.

Variables scopes clarifications

I think that block variables scopes should be clearly described. Specification does not mention what happens when variable identifier is reused, eg:

<div data-sly-use.logic="logic.js">
    <div data-sly-test.test="${logic.property1}">
        ${test}
    </div>
    <div data-sly-test.test="${logic.property2}">
        ${test} <!-- is test overriden? -->
        <div data-sly-list.test="${logic.property3}"> 
            ${test} <!-- is test overriden? -->
            <div data-sly-list.test="${logic.property3}"> 
                ${test} <!-- is test overriden again? -->
            </div>
        </div>
        ${test} <!-- what "test" holds here? -->
    </div>
</div>

I made some experiments and it seems that element scoped variables defined by data-sly-list and data-sly-repeat always override other variables (also template parameters and parent data-sly-list/data-sly-repeat variables with the same name). "Global" variables defined by data-sly-use and data-sly-test lives to the next global "redeclaration" or are overridden by data-sly-list/data-sly-repeat and are again available out of redeclaring data-sly-list/data-sly-repeat block. The data-sly-template seems to be visible even before declaration, but only when there is no other variable with the same name declared in file - if there is any, then template is not visible. For me it looks pretty complex, but maybe there are simpler rules explaining those behaviours.

I know that reusing variable names should be avoided, but it's important information for HTL plugin developers - we have to know how to highlight references, when to highlight undeclared variables, etc.

Void elements and data-sly-test

I'm not sure if it is HTL specification or rather implementation issue. Please tell me if issue should created in Sling project.

There is an issue with HTML void elements. Eg.

<link data-sly-test="${condition}" rel="canonical" href="http://example.com">
<!--  a lot of additional markup here -->
<!--  end of file -->

<link> element is void element and can be used without closing. Above markup leads to the bug, because data-sly-test will be applied to the end of file as there is no closing link tag. If it is expected behavior I think specification should include notice to close such elements to avoid bugs.

Ternary operator not functioning properly when used in option element

Test case:

<select name="test">
    <option value="1" ${properties.value == '1'?'selected':'' @ context='unsafe'}>One</option>
    <option value="2" ${properties.value == '2'?'selected':'' @ context='unsafe'}>Two</option>
    <option value="3" ${properties.value == '3'?'selected':'' @ context='unsafe'}>Three</option>
</select>

HTL is not processed and rendered in page markup.

data-sly-text not rendering HTML

We are upgrading to AEM 6.3 and have noticed this problem.

<div data-sly-text=${properties.desc @ context='html'}></div>

is outputting the HTML escaped.

If I do :

<div>${properties.desc @ context='html'}</div>

it is working as intended.

Is this a new feature for 6.3? If there is a way to fix this, please let me know as this requires quite some refactoring effort to fix.

Thanks.

Introduce the "in" relational operator

The in relational operator can be used to:

  1. Check if a String is contained by another String:
${'a' in 'abc'} <!--/* returns true */-->
${'ab' in 'abc'} <!--/* returns true */-->
${'bc' in 'abc'} <!--/* returns true */-->
${'abc' in 'abc'} <!--/* returns true */-->
${'d' in 'abc'} <!--/* returns false */-->
  1. Check if an Array or a List contains an object:
<!--/*
  Assuming myArray would be in scope and would have the following content:
  [100, 200, 300, 400, 500]
*/-->
${100 in myArray} <!--/* returns true */-->
${300 in myArray} <!--/* returns true */-->
${1 in myArray} <!--/* returns false */-->
  1. Check if an object has a property or a Map has a key:

    Assuming the following use object (more details in the Use-API section) provided by a logic.js file:

    use(function () {
        return {
            a: true,
            b: 'two',
            c: 3
        };
    });
    <sly data-sly-use.logic="logic.js" />
    ${'a' in logic} <!--/* returns true */-->
    ${'b' in logic} <!--/* returns true */-->
    ${'c' in logic} <!--/* returns true */-->
    ${'two' in logic} <!--/* returns false */-->

Some examples from the URI manipulation section are not correct

The following examples are incorrect:

  1. example.com is not a valid domain in this context:
${'example.com/path/page.html' @ scheme='http'}
<!-- outputs: http://example.com/path/page.html -->
  1. selectors should not be merged, but rather appended
${'path/page.woo.foo.html' @ addSelectors='foo.bar'}
<!-- outputs: path/page.woo.foo.bar.html -->

${'path/page.woo.foo.html' @ addSelectors=['foo', 'bar']}
<!-- outputs: path/page.woo.foo.bar.html -->

Rendering of ${['']} in an attribute inconsistent with the spec

According to the specification, attributes containing an array with just an empty string should be rendered with an empty value:

<!--/* But an array containing just an empty string doesn't get removed: */-->
<div title="${['']}"></div>
<!--/* outputs: */-->
<div title=""></div>

However, when I try the same code on my AEM instance, I get the following output:

<div></div>

I'm using AEM 6.2 and the following bundles:

  • Apache Sling Scripting Sightly Engineorg.apache.sling.scripting.sightly 1.0.18
  • Apache Sling Scripting Sightly JavaScript Use Providerorg.apache.sling.scripting.sightly.js.provider 1.0.10 sling
  • Apache Sling Scripting Sightly Models Use Providerorg.apache.sling.scripting.sightly.models.provider 1.0.0
  • Adobe Experience Manager Sightly Extensioncom.adobe.cq.sightly.cq-wcm-sightly-extension 1.3.8
  • Adobe Granite Sightly Template Engine (compatibility)io.sightly.bundle 1.1.72

Is this an error in the specification or is this a bug in my specific version and one of the bundles requires an update?

Remove octal escape sequences from grammar

Grammar includes octal escape sequences in string definition, but Sling HTL implementation omits it. Is it going to be implemented in future? I don't know whether I should support it in IntelliJ plugin I'm working on.
If it should be removed and you don't mind if I do it and create PR, please let me know.

Allow i18n to be turned on/off by a boolean expression

Pretty self explanatory, allow the construct ${'some text' @ i18n=someCondition}, defaulting to true if =someCondition is not present. This would treat ${'some text' @ i18n} as ${'some text' @ i18n=true}

I have a few situations in a legacy application migration that would benefit from this ability.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.