data-sly-resource error handling in sightly - AEM - aem

data-sly-resource tag allows to refer resource in another component in following way.
<article data-sly-resource="path/to/resource"></article>
We have path/to/resource being taken as part of dialog field by content authors. If the content authors select the path which is not existing by mistake, page becomes inaccessible to content authors, giving HTTP 500 error and there is no other way to correct it without going to CRX. On environments where access to CRX is not there, we cannot delete the incorrect node. Exception in logs is
Caused by: org.apache.sling.scripting.sightly.SightlyException: org.apache.sling.api.resource.ResourceNotFoundException: No resource found
at com.adobe.cq.sightly.WCMScriptHelper.includeResource(WCMScriptHelper.java:143)
at com.adobe.cq.sightly.WCMScriptHelper.includeResource(WCMScriptHelper.java:86)
at com.adobe.cq.sightly.internal.extensions.ResourceExtension.call(ResourceExtension.java:99)
at org.apache.sling.scripting.sightly.impl.engine.runtime.RenderContextImpl.call(RenderContextImpl.java:89)
The out of the box reference component (/libs/foundation/components/reference/reference.jsp) handles this by having a catch block, Content Author can correct path. But that's JSP based not sightly based.
Is there a way to handle this in sightly?

The proper way to handle this is through an Use-API object that will attempt to find the resource and handle any exceptions:
<article data-sly-use.helper="myHelper" data-sly-test="${helper.resource}" data-sly-resource="${helper.resource}"></article>
Passing an actual org.apache.sling.api.resource.Resource to data-sly-resource is possible since SLING-5811, for older versions of HTL/Sightly you will need to pass a path.

Try this syntax, path should given in EL expression:
<article data-sly-resource="${ # path='path/to/resource'}"></article>

Related

Make twig plugins by dynamically horizontal reusing via "use" (not "include")

Need: A dynamic template loaded from a DB
I need to present a JSON object via "template plugins" that will come from a database (defined outside the application itself). The plugin will be applied only if a plugin exists.
For this example, let's assume I have this object of type "Reservation" that contains a sub-object of type "Flight":
{
"id": "ABC-XYZ",
"reservationDate": "2020-09-23",
"state": "paid",
"flight":
{
"origin": "BCN",
"destination": "MAD",
"airline": "VY"
}
}
Rules
The controller will call a page template passing multiple objects.
The page template will "use" or "include" (see later more info) an object.html.twig and will display it.
The object.html.twig will do this:
If there's not any known plugin able to handle this type of object, the template will display a <pre> with the object converted to YAML. This will be mainly act as a "default" plugin or "fallback" plugin.
If there's a known plugin able to handle the object, the object will be sent to the plugin (which is not anything else than another twig template).
In this case, the plugin should be able to separate "parts" of "interpretable" results to make them "nice" and leave the rest into an object that will be, in turn, displayed again with the original "default" plugin.
If there are parts of the object that are in turn interpretable, they will be in turn passed to other plugins.
Example "desired" outputs
Sample 1. No plugin available
Sample 2. Plugin 'reservation' available. Interprets the state in green. Also removes the redundant data of the ID
Sample 3. Same than 2 but also plugin 'flight' available, able to process the flight block. Formats the texts and makes a lookup of the airline full-name
Sample 4. Plugin 'flight' available, able to process the flight, that in turn knows that the origin and the destination are "airports" and passes them into the corresponding nested 'airport' plugin, because they are "reusable" objects not only in the "flight" plugin but also in many other places in the application, so they are defined appart
What I have already explored
I know that doing it via 'include' it could work. But let's take a look at the differences between 'use' and 'include':
The major difference between include and use in twig is that:
When you include a template, it's direct HTML 'inserted there' where you can use the {{ }} operator for printing and {% %} for control flow (set variables, ifs, fors, etc.). The renderer will process it straight forward. But defining a new block via {% block myNiceBlock %} is forbidden.
When you 'use' a template, it's pre-loaded, and blocks are permitted. There's no rendering of the included block. Then, from the caller, you use a {{ block( 'whatever' ) }} to tell the renderer to go and render that specific block.
The 'include' is more rudimentary. The use allows horizontal reusing and allows itself to auto-organize itself with other sub-blocks called by the parent block, all in one single file.
For example, in the airports example, if there are N images, in a include you should put the wrapper HTML directly in the file, do a loop and inside the loop write the inner HTML.
Instead in the use approach you'd do an airport block which in turn loops over the images and just calls the block airportImage which is defined in another block in the same file, thus facilitating clean-coding.
Requirement
The application should not be re-deployed when new plugins are created. They must be loaded from a DB or any other dynamic system, as the plugins will be written by "users of the application" as they need it. No deploy allowed.
The plugins should be written in terms of a "block-able" twig template, so need to be 'use'-able.
Question
Discovering "which" plugin to call is not a problem. Assume that whoever (the controller, the twig itself, whoever really) can discover for this example that a "reservation" plugin exists somewhere. How can I use it from the page? When the reservation is rendering it "asks if a flight plugin" is available. If not, all to the YAML. If it is, how can then dynamically tell the reservation to use the flight?
In short: How do I force a template to dynamically use (not include) templates that, in turn, comes from the database (not from fixed files)?
NOTE: If this info is useful: I'm using Symfony 5 with webpack.
Thanks!

Content altered in HTL/ Sightly in AEM 6

This is the weirdest issue I've ever faced in a long time. I have a URL that is authored inside a multifield. The URL has an underscore eg. http://example.net/_pinkPanther_is_pink it is currently inside ${item.link}
When I do Click <br> ${item.link} and inspect, it renders as
Click
<br> http://example.net/_pinkPanther_is_pink
If you notice both values are coming from the same variable in Sightly still when the link is used inside href of anchor tag there is double underscore added by God know who after example.net/
Does anybody have a clue as to what on earth is going on ?
That's caused by the display context aware XSS protection. Sightly/HTL automatically detects the display context of a HTL expression, using its location within the structure of the HTML page to detect it.
For example, if the expression appears in a place that would produce a text once rendered, then it is said to be in a text context. If it is found within the value of an attribute, then it is said to be in an attribute context, and so forth. More about contexts in the htl specification page.
In your example, the implicit context inside the href attribute is uri while in the later case is text.
In order to overwrite this behaviour, you may explicitly set the context like href="${item.link # context='text'}.

AEM 6.x: How to pass an HTL variable to clientlib/JS?

So I have the following lines which loads my javascript.
<sly data-sly-use.clientLib="${'/libs/granite/sightly/templates/clientlib.html'}" data-sly-unwrap />
<sly data-sly-call="${clientLib.js # categories='myhost.mycustomJS'}" data-sly-unwrap />
I have an HTL property (example: ${properties.myCustomProperty}) that I want to pass to myCustomJS.
Any ideas how it can be done?
I've looked around the net but didn't find anything useful.
Thank you.
You are trying to access a server side property with client side script. As you may realize sightly executes at server end and rendered output is returned to browser. In your case you need to send the properties to browser to make it available for clientside scripts to consume.
Technique 1: (Recommended) Data attributes -
This is easiest to send since DOM structure doesnt change. Pass the values as data elements and retrieve using jquery. For example:
var value = $('#mydiv').data('custom-property');
console.log(value);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv" data-custom-property="${properties.myCustomProperty}" />
Technique 2: (Old school) - Hidden variable - Add hidden variable into component rendering script; set the value of variable with HTL property and read the variable from clientside js using getElementById or jquery.
Technique 3: (Not recommended) - Make a trip to server. If you dont want to dilute your DOM (maybe property is secret or not SEO friendly), you may need to make an ajax call to a sling servlet that returns the property value. There are multiple examples available for sling servlet you can refer to.
ACS Sample, AEM 6.3 servlet, 1 more example.
But do remember its not worth to make a trip to server just for 1 property.

What is done here. What does this syntax means in sightly?

I am reading the docs about calling clientlibs in sightly.
I am not getting the below syntax
<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"
data-sly-call="${clientlib.all # categories='clientlib1,clientlib2'}"/>
why we are using category here? How does it related to clientlibs?
Lets break this down:
<sly> - is a sightly tag that does nothing :) So when you don't want to use an HTML tag you can use <sly> as a placeholder.
data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html - this line references the clientlib.html file that has templates marked with data-sly-template attributes. These templates are reusable piece of markup. Look at them as functions in htl.
data-sly-call - used to call one of the templates from the above clientlib.html
clientLib.all - "all" is the name of the template being called from clientlib.html which is referred using clientLib keyword (-use.clientLib)
# categories='clientlib1,clientlib2 - categories are used to identify cq:clientLibraryFolder that are used for client side code in AEM. If you check http://localhost:4502/libs/granite/ui/content/dumplibs.html it will show you the location of libraries clientlib1 & clientlib2
So in a nutshell, this line calls 2 libraries (containing js & css) with categories clientlib1 & clientlib2 and loads them on to the page/component

How to parse HTL content

I'm loading a resource in AEM using SlingRequestProcessor as the example available here.
My page/html file looks like:
<div data-sly-use.stepPlanItem="stepPlanItemTemplate.html"
data-sly-call="${stepPlanItem.step # step = step}"
data-sly-unwrap />
But, when I invoke requestProcessor.processRequest(req, resp, request.getResourceResolver()); nothing happens. I'm getting exactly the same content of the file. Nothing is being parsed.
My main question is: How can I parse sly tags from Java code?
Should I use filters? Which one? This is a page (not an AEM one) just an HTL snippet.
You do not need to parse HTL files, this is done by the Sling Scripting Engine implementation. You are always processing/loading a resource and it needs to be resolved according to the Sling resource resolution.
Assuming you are trying to load a resource at: /content/myapp/mypage.html that has a sling:resourceType=myapp/myfile, you would put the above code snippet in an /apps/myapp/myfile/myfile.html file so that the scripting engine can execute the HTL.