adobe / htl-spec Goto Github PK
View Code? Open in Web Editor NEWHTML Template Language Specification
License: Apache License 2.0
HTML Template Language Specification
License: Apache License 2.0
The data-sly-unwrap
block element should be added to the TOC.
data-sly-dataset
:
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.dataset
identifier.The data-sly-dataset
attribute would serve two functions.
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>
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
I analysed HTL grammar and it seems to me, that there are two issues:
properties['key']
) - where is it? Maybe it's implicitly included, but I tried to find it without success.:
, 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-include uses the property name file instead of path as documented in the https://helpx.adobe.com/experience-manager/htl/using/block-statements.html#include page.
Therefore, file should be renamed to path as in the provided documentation.
At least within the starting and closing <script>
it seems that Sightly Block Statements are just ignored. Please clarify in the specification where exactly Sightly Block Statements are supported.
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.
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 [...]
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).
Label is not supported on data-sly-element even though it is valid html tag.
example code:
<div data-sly-element="${'label'}">
foo
</div>
Why the Unwrap block statement data-sly-unwrap
doesn't have an Attribute identifier so the result of the test can be used later on?
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.
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.
Similar to what <c:redirect> does for JSPs (http://www.tutorialspoint.com/jsp/jstl_core_redirect_tag.htm) Sightly should have a block element which allows to send a redirect back to the browser.
Currently this is only possible with Javascript/Java objects called via the Use-API.
We should create a changelog of everything that has changed in the spec between versions.
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.
In https://github.com/Adobe-Marketing-Cloud/sightly-spec/blob/1.1/SPECIFICATION.md#227-repeat the example is saying
data-sly-list.childResource="${resource.listChildren}"
where it should say data-sly-repeat.childResource="${resource.listChildren}"
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?
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 )
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]-->
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.
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
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.
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=', '}
For some reason the SLY element tag is not working in Sightly. See spec:
https://github.com/Adobe-Marketing-Cloud/sightly-spec/blob/master/SPECIFICATION.md#31-sly
It renders the sly element in the HTML and is not unwrapping the element automatically. Tested it with multiply block statements.
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?
Considering its name we should also allow java.util.Locale
objects in the i18n locale
option:
${"Foo" @ i18n, locale=resource.locale}
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.
Comparison operators should be added to the specification, in the Operators section.
When I look at the i18n implementation, there is also the option the specify 'basename', however this isn't mentioned in the spec.
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.
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.
Hello,
For : ${'https://www.adobe.com/fr/' @ extension ='html'}
it will generate : https://www.adobe.com/fr/.html
should be : https://www.adobe.com/fr/
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.
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
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 0step
- iteration will only process every step items of the collection, starting with the first oneend
- 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>
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
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"ar"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
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.
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.
The current @format
option offers support only for string templates. Similar support would be nice for date and number formatting.
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
:
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 -> []
.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.
The in
relational operator can be used to:
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 */-->
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 */-->
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 */-->
As mentioned in #20, the current 'number' validation context only handles whole numbers. We should add a 'double' validation context to handle fractional numbers.
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:
Is this an error in the specification or is this a bug in my specific version and one of the bundles requires an update?
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.
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>
When I do:
${'<div></div>' @context='html'}
It outputs invalid HTML, it self-closes the element:
<div/>
Only a set number of elements are permitted to self-close - http://xahlee.info/js/html5_non-closing_tag.html.
The following examples are incorrect:
example.com
is not a valid domain in this context:${'example.com/path/page.html' @ scheme='http'}
<!-- outputs: http://example.com/path/page.html -->
${'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 -->
Per https://issues.apache.org/jira/browse/SLING-5053, there is desire to change the casting behavior from Enums to Strings. This should be reflected in the spec.
The link destination is as follows: https://github.com/Adobe-Marketing-Cloud/sightly-spec/blob/1.1/SPECIFICATION.md
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.