Personal AEM Notes
I just recently started a project at work using Adobe Experience Manager (AEM). Here's my random, rambling collection of notes. If you know of a better way to do any of these examples please let me know by adding a comment.
Good resources:- Jeff's Youtube series Programmer Vs World AEM Series
- HTL guide: https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/master/SPECIFICATION.md#225-test
- https://github.com/pankajchhatri/AEM
General Tips for Working with AEM
- Save often when creating objects - all the time. Create an object, then "Save All", or Ctl-S. All the time. Delete something? Save immediately.
- Remember to always to a hard reload of a browser after changing AEM content, Ctl-Shift-R.
- Coming from Visual Studio, I found the IntelliJ "Live Edit" plugin and the Chrome plugin very useful.
Jeff's overview of AEM components:
Apache Felix - osgi framework - forces modular sw development - service orentation Jackrabbit - JCR content repo, JSR-170,283, workspaces, nodes, properties Sling - Restful Web Framework Sencha EXT JS 4 - js application framework Lucene - search engine Quartz Scheduler - cron system programmatically accessable CRX - Tar Journal, Package Manager, deploy and share, Servlet Engine CQSE CRXDE Lite - ide WCM (Web Content Manager) - this is what content writers use
Folder Structure
/apps: custom templates, components, /content: stores all content /etc: client library, design dialog info /libs: standard components - do not modify example: /libs/wcm/foundation/components/page/ /conf: All configuration /var: locks, /home: info about users and groups /oak:index: Jackrabbit Oak index definitions
Wierd Strings to Put in the URL
Strings to put after "http://localhost:4502" to do wonderful things:
/cf#/ - sets the old interface, e.g., http://localhost:4502/cf#/content/...
/editor.html/ - uses the new interface, e.g., http://localhost:4502/editor.html/content/...
/crx/de - goes straight to the CRXDE page e.g., http://localhost:4502/crx/de
/welcome - go to the Websites/DAM page e.g., http://localhost:4502/libs/cq/core/content/welcome.html
/siteadmin - go to the Websites e.g., http://localhost:4502/siteadmin
/system/console/bundles - shows versions and the console - e.g., http://localhost:4502/system/console/bundles
How to limit use of a template?
allowed paths: /content(/.*)? /* anywhere under content */ allowed paths: /content/sports/baseball /* only under baseball */
How to get rid of <p> </p>?
Make sure the text you entered into the "text" element does not contain an extra carriage return.
How to only iterate a fixed number of times?
<ul data-sly-list="${thelist}"> <li data-sly-test="${itemList.index < 4}">${itemList}</li> </ul>
Set default values with the 'or' operator
${properties.pageTitle || properties.jcr:title || resource.name}
How to find what version of AEM you're running
http://localhost:4502/system/console/status-productinfo
To skip the AEM link check
add this class
<a x-cq-linkchecker="skip" href="..." />
Chrome screen creeping up?
In Chrome do you ever get CRX/DE screen creeping up the bottom left panel til it takes over the whole screen? The best solution I've found is to right-click and select "inspect" and make sure the Chrome browser is not in full screen mode.
How to get a list of tags and iterate over them
var categories = []; var tags = resolver.getResource("/etc/tags/mysection").getChildren(); for (var i=1;i<tags.length;i++) { categories.push(tags[i].name);//do something }
What is a "Client Library"?
It organizes resources that we send to the browser (client side) - things like css and JavaScript.
How to include a resource in a page
<div data-sly-resource="${ 'myname' @ resourceType='wcm/foundation/components/parsys'}"></div>
To show a page title
${properties.jcr:title}
How to include other files
<sly data-sly-include="header.html"></sly>
How to iterate over properties
<ul data-sly-list.child="${currentPage.getProperties}"> <li>${child}</li> </ul> or <ul data-sly-repeat.child="${currentPage.getParent.getProperties}"> <li>${child}</li> </ul>
How to place a page property on the HTML page
Here's an example of reading a property from the page dialog in JavaScript and putting the value on the page.
<div data-sly-use.bannerTitle="getBannerTitle.js"> ${bannerTitle} </div>
getBannerTitle.js "use strict"; use(function () { return resourcePage.getProperties().get("bannerTitle"); });
To See the Actual jcr Nodes
/content/....
To show an array as text with a seperator
${['apple', 'pear', 'papaya'] @ join=' - '} generates apple - pear - papaya
How to format strings
${'person {0} of {1}' @ format=[7,9]}
How to encode a string for a URL
This will replace non-URL friendly characters like ' ' with '%20'.
${properties.jcr:title @ context='uri'} or ${'This is a test' @ context='uri'}
How to encode a string for simple html content
This will replace non-URL friendly characters like ' ' with '%20'.
${'Texas A & M University ' @ context='text'} generates <x>Texas A & M University</x>
How to put in an AEM HTL comment
<!--/* This is an example comment that will not appear in the browser. */-->
How to use Sly Element so no "div" element is sent to the browser
Use the "sly" element when you don't want to have the extra "div" element
<div data-sly-include="head.html"></div> /* this produces: <div>myhead.html</div> */ use only the "sly" tag and the surrounding divs are not created. <sly data-sly-include="head.html"></sly> /* this produces: myhead.html */
How to replace content of HTML Element
<div data-sly-text="${currentPage.title}">This will not be seen</div>
Simple Example of "use" to Call Server-side JavaScript and Pass Parameters
For example, suppose we need a social icon component, "sharethis", which will accept two variables, url encode them, and return an object with two url-encoded variables. The returned object is named "site". ( I have since learned the right way to do this is with the "context='uri'"):
${'This is a test' @ context='uri'} //url:This%20is%20a%20test
In sharethis.html:
<div class="iconContainer" data-sly-use.site="${'sharethis.js' @ title=currentPage.title, url=request.requestURL.toString}"> <p class="shareThisTitle">Share this:</p> <a href="https://www.facebook.com/sharer/sharer.php?href=${site.url}&title=${site.title}" alt="share on facebook" target="_blank" class="nodecoration"> <img src="/content/dam/design/facebook.png" title="share on facebook" alt="share on facebook" /> </a> ... </div>
The variables passed in are accessed by prefacing them with "this.". In the file sharethis.js:
"use strict"; use(function () { return { title: encodeURIComponent(this.title), url: encodeURIComponent(this.url) } });
To Have AEM Automatically Bundle Css And Javascript Files
clientlib create node / cq:clientlibraryfolder create clientlib named zurbfoundation create property 'categories' string[], zurb.foundation create property 'dependencies' "+" cq.jquery js.txt #base=style //subdirectory name css.txt #base=source <html data-sly-use.clientlib='/libs/granite/slightly/templates/clientlib.html'> <sly data-sly-call="${clientlib.css @ categories='zurb.foundation'}" /> <sly data-sly-call="${clientlib.js @ categories='zurb.foundation'}" /> or to do both: <sly data-sly-call="${clientlib.all @ categories='zurb.foundation'}" />
How to do a REST call to get json objects in server-side JavaScript within a proxy
"use strict"; function geturlcontents(url) { //put the following values in a config file var proxyHost = "proxy.district13.gov", proxyPort = 80, username = "katniss", password = "ilovearchery"; var method = new org.apache.commons.httpclient.methods.GetMethod(url), client = new org.apache.commons.httpclient.HttpClient(), status = new org.apache.commons.httpclient.HttpStatus(), inputStream; var hostConfiguration = new org.apache.commons.httpclient.HostConfiguration(); hostConfiguration.setProxy(proxyHost,proxyPort); client.setHostConfiguration(hostConfiguration); var credentials = new org.apache.commons.httpclient.UsernamePasswordCredentials(username, password); var authScope = new org.apache.commons.httpclient.auth.AuthScope(proxyHost, proxyPort); client.getState().setProxyCredentials(authScope, credentials); try { var statusCode = client.executeMethod(method); if (statusCode == status.SC_OK) { inputStream = method.getResponseBodyAsString(); } else { console.log(':::Failed to execute http method. status code = '+statusCode + ' for url '+ url); } } catch (e) { console.log(":::exception: "+e); } finally { method.releaseConnection(); } return inputStream; }; use(function() { var inputStream = geturlcontents(this.api); return JSON.parse(inputStream).slice(0, this.maxitems); });
How to do a REST call to get json objects in server-side in JSP within a proxy
<%@include file="/libs/foundation/global.jsp"%> <%@page import="org.apache.commons.httpclient.*, org.apache.commons.httpclient.methods.*, org.apache.commons.httpclient.params.HttpMethodParams, org.apache.commons.httpclient.auth.*, org.slf4j.Logger, org.slf4j.LoggerFactory, org.apache.sling.commons.json.*, java.util.Date, java.io.*"%> <% String proxyHost = "myproxy.mycompany.com"; int proxyPort = 80; String username = "steven"; String password = "spielburg"; String url = "http://www.omdbapi.com/?t=alien&plot=full"; HttpClient client = new HttpClient(); GetMethod method = new GetMethod(url); HttpStatus status = new HttpStatus(); HostConfiguration hostConfiguration = new HostConfiguration(); hostConfiguration.setProxy(proxyHost, proxyPort); client.setHostConfiguration(hostConfiguration); UsernamePasswordCredentials credentials = new org.apache.commons.httpclient.UsernamePasswordCredentials(username, password); AuthScope authScope = new AuthScope(proxyHost, proxyPort); client.getState().setProxyCredentials(authScope, credentials); String jsonString = "[]"; try { int statusCode = client.executeMethod(method); application.log(" *** statusCode : " +statusCode); if (statusCode != HttpStatus.SC_OK) { application.log(" *** Method failed: " + method.getStatusLine()); throw new Exception(" *** client.executeMethod failed for url "+ url + " with http error code " + statusCode); } // Read the response body. byte[] responseBody = method.getResponseBody(); jsonString = new String(responseBody); } catch (HttpException e) { application.log(" *** Fatal protocol violation: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { application.log(" *** Fatal transport error: " + e.getMessage()); e.printStackTrace(); } catch (Exception e) { application.log(" *** Fatal error: " + e.getMessage()); e.printStackTrace(); } finally { method.releaseConnection(); } JSONArray jsonArray = new JSONArray(jsonString); String countofmoviesString = (String)properties.get("countofmovies"); int countofmovies = Integer.parseInt(countofmoviesString); %> <div class="movieTileContainer"> <ul class="movieTileList" > <% int maxMovies = jsonArray.length() > countofmovies ? countofmovies : jsonArray.length(); for(int i=0;i<maxMovies;i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); String title = (String)jsonObject.get("title"); String jurl = (String)jsonObject.get("url"); JSONObject image = (JSONObject) jsonObject.get("movieEntryImage"); String thumbnailLocation = (String)image.get("thumbnailLocation"); JSONArray prices = (JSONArray) jsonObject.get("price"); String price = (String)prices.getJSONObject(0).get("price"); %> <li class="movieTileListItem"> <div class="content"> <div class="movieTileBox"> <a class="movieTileLink" title="<%= title %> " href="https://www.MyMovieSite.com<%= jurl %>"> <p class="movieTileTitle"><%= title %> </p> <img class="movieTileImage" width="185" height="101" src="https://www.MyMovieSite.com<%= thumbnailLocation %>" alt="<%= title %>"> <p class="movieTilePrice" <%= price %> </p> </a> </div> </div> </li> <% } %> </ul> </div>
Common Resource Types:
text field: granite/ui/components/coral/foundation/form/textfield
number: /libs/granite/ui/components/foundation/form/numberfield
DAM file: granite/ui/components/foundation/form/pathbrowser
How to lowercase a variable
More generally, how to invoke a JavaScript method on an htl variable. Call the method without parenthesis.
<a href="${myvariable.toLowerCase}/mypage.html">
Important AEM directories
/libs/granite/ui/components/foundation - new components /libs/wcm/foundation/components/
To Add An Editable Area
<div data-sly-resource="${ 'content' @ resourceType='wcm/foundation/components/parsys'}"></div>To Show AEM Quickstart Frame
add this immediately after body tag and it will present the AEM quickstart frame
<div data-sly-call="/libs/wcm/core/components/init/init.jsp"></div>
How to Create an Editable Button
/components/materialize-button/dialog/items/items/tab1/items fieldDescription / What do you want the button to say fieldLabel / Button Text jscr:primaryType cq:Widget name / ./buttontext xtype / textfield <a>${properties.buttontext}</a>
Unorganized Snippets of Forgotten Thoughts
on components, "group" is category. to add dialog: rt-click/create...dialog on tab1, create a node with name of "items" and type of cq:WidgetCollection inside items create another node named "buttontext" of type cq:Widget in Properties add "xtype/String/textfield" add "fieldDescription/String/What do you want the button to say" add "fieldLabel/String/Button Text" add "name/String/./buttontext" --name is where it puts the value from component: ${properties.buttontext} mainpage.tidy.4.json (to get a list of entities, API Documentation/CQ/Ext/form)