Thursday, April 21, 2022

Apache not starting - XAMPP on Windows

I was facing an issue while starting the Apache Server running in XAMPP on the Windows system; the server was not starting from the XAMPP control panel but was able to start the server directly by running the httpd.exe.

The start task was struck while doing through the XAMPP control panel

Also, was seeing the below exception in the error log.

VirtualProtect() failed [87] The parameter is incorrect

Port 80 was not in use; some configuration changes did not help.

Finally, the issue was due to the existing service entry for Apache2.4.


Properties



The service was created as part of the Bitnami WordPress installation and was not removed on uninstallation.

To resolve the issue, remove the Apace2.4 service entry.

Run the below command as an administrator through the command line

sc delete "Apache2.4."


Create a new service entry for the XAMPP Apache server

Run apache_installservice.bat from xampp/apache folder - this will create a new Apache2.4 service pointing to the XAMPP apache server



Now the Apache server can be managed through the XAMPP control panel.





Monday, April 11, 2022

What are Custom HTML Elements? | How to use Custom HTML Elements in AEM?

 In this post, let us explore what Custom HTML Elements are and how to use Custom HTML Elements in AEM.

Custom Elements allow web developers to define new types of HTML elements.Once a custom element is defined, we can use it on par with built-in HTML elements.

Some of the important features are enabled by Custom HTML elements.

  • Define new HTML/DOM elements
  • Create elements that extend from other elements
  • Logically bundle together custom functionality into a single tag
  • Extend the API of existing DOM elements

Web Components uses Custom HTML Elements as one of the building blocks to enable reusable custom elements(Components).

Rules on creating custom elements:

  • The name of a custom element must contain a dash (-). So <my-element>, and <my-awesome-app> are valid names.
  • You can’t register the same tag more than once. Attempting to do so will throw a DOMException
  • Custom elements cannot be self-closing(<my-element/>), Always write a closing tag <my-element> </my-element>

Steps for creating Custom HTML Elements:

  • Define a custom element e.g. <my-element> </my-element>
  • Create a class with special methods.
class MyElement extends HTMLElement {
constructor() {
super();
// element created
}
connectedCallback() {
// browser calls this method when the element is added to the document
// (can be called many times if an element is repeatedly added/removed)
}
disconnectedCallback() {
// browser calls this method when the element is removed from the document
// (can be called many times if an element is repeatedly added/removed)
}
static get observedAttributes() {
return [/* array of attribute names to monitor for changes */];
}
attributeChangedCallback(name, oldValue, newValue) {
// called when one of attributes listed above is modified
}
adoptedCallback() {
// called when the element is moved to a new document
// (happens in document.adoptNode, very rarely used)
}
// there can be other element methods and properties
}

Register the element:

// let the browser know that <my-element> is served by our new class customElements.define("my-element", MyElement);

Refer to https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements and https://web.dev/custom-elements-v1/ for more details.

Let us quickly see how to create Custom HTML Elements to format the data in HTML.

<time-formatted> </time-formatted>

Attributes(datetime and the other formating options for Intl.DateTimeFormat):

datetime
year
month
day
hour
minute
second
time-zone-name
<html>
<head>
<script>
class TimeFormatted extends HTMLElement { // Class to handle custom elements functionality
connectedCallback() {

let date = new Date(this.getAttribute('datetime') || Date.now());
this.innerHTML = new Intl.DateTimeFormat("default", {
year: this.getAttribute('year') || undefined,
month: this.getAttribute('month') || undefined,
day: this.getAttribute('day') || undefined,
hour: this.getAttribute('hour') || undefined,
minute: this.getAttribute('minute') || undefined,
second: this.getAttribute('second') || undefined,
timeZoneName: this.getAttribute('time-zone-name') || undefined,
}).format(date);
}
}
customElements.define("time-formatted", TimeFormatted); //Register custom HTML Element
</script>
</head>
<body>
<time-formatted datetime="2022-04-11"
year="numeric" month="long" day="numeric"
hour="numeric" minute="numeric" second="numeric"
time-zone-name="short"></time-formatted>
</body>
</html>

The formatted data is now displayed on the web page through custom elements and the specified attributes.

We can extend the Custom Element and the class to use additional callback methods and functionalities.

Let us quickly see how to enable the same custom elements to AEM.

Create a client library(e.g., wknd.site.custom-time-element) with the required JS

class TimeFormatted extends HTMLElement { // Class to handle custom elements functionalityconnectedCallback() {

let date = new Date(this.getAttribute('datetime') || Date.now());
this.innerHTML = new Intl.DateTimeFormat("default", {
year: this.getAttribute('year') || undefined,
month: this.getAttribute('month') || undefined,
day: this.getAttribute('day') || undefined,
hour: this.getAttribute('hour') || undefined,
minute: this.getAttribute('minute') || undefined,
second: this.getAttribute('second') || undefined,
timeZoneName: this.getAttribute('time-zone-name') || undefined,
}).format(date);
}
}
customElements.define("time-formatted", TimeFormatted); //Register custom HTML Element

Embed the new client library as part of the site level client library

Create a Component to enable the custom element

<p> Custom Element - Time Formater </p><time-formatted datetime=${properties.datetime}
year=${properties.yearformat} month=${properties.monthformat} day=${properties.dayformat}
hour=${properties.hourformat} minute=${properties.minuteformat} second=${properties.seconfformat}
time-zone-name=${properties.timezonename}></time-formatted>

Create the required dialogs to enable the needed configuration attributes for the custom element.

Now author the component to the required page. The user will now see the formatted data on the web page.

Maybe in the next post, let us see more on web components with additional features.ou cannot applaud your own story




Friday, April 8, 2022

AEM Experience Manager(AEM): Content Sharing Approaches

 In most cases, we need to share the CMS content with external systems to support the Omni channel's headless experiences or other use cases to support content sharing.

In this post, let us see the options available to share the AEM content with external systems.

The graph QL API shares the content fragments to enable the headless content experience with the external system. The structured data can be managed through Content Fragments in AEM and shared through Graph QL API to support the omnichannel experiences. The latest AEM 6.5 and AEM as a Clod Services versions support Graph QL API; the Graph QL API currently supports only exposing content fragments externally, not for the regular page content.

Refer to the following URL for more details on using Graph QL to API to share content fragments with external systems — https://medium.com/tech-learnings/how-to-deliver-headless-content-through-graphql-api-and-content-fragments-da2b2d22df06

Sling Model Exporter allows annotations — @Exporter(name = "jackson", extensions = "json") to be added to Sling Models that define how the Model can be exported as JSON. The page JSON data can be accessed by adding a model selector — this prints all the data exposed through different sling models.

http://localhost:4502/content/wknd/us/en.model.tidy.json (tidy selector formats the JSON response)

The sling exporter exposes all the components model data enabled on this page as a JSON response. The JSON response can render the page in an external channel to enable the headless experience.

The content as a service supports authoring of the Content fragments into a page and exposes the content through JSON exporter(by adding .model selector as discussed above)

Refer to https://experienceleague.adobe.com/docs/experience-manager-learn/foundation/development/understand-sling-model-exporter.html?lang=en for more details on Sling Model Exporter.

The Query Builder offers an easy way of querying the content repository of AEM. The REST API provides the functionality to execute JCR queries through HTTP requests and respond with JSON.

The JSON REST API can share the data to an external system, mainly the metadata about pages and assets, e.g., useful when the onsite search platform requires additional metadata for indexing Assets.

http://localhost:4502/bin/querybuilder.json?p.hits=selective&p.limit=-1&p.properties=jcr%3apath%20jcr%3acontent%2fmetadata%2fdc%3atitle%20jcr%3acontent%2fmetadata%2fdc%3adescription&path=%2fcontent%2fdam&type=dam%3aAsset

Change the query to fetch the required content properties on the JSON response.

{
"success": true,
"results": 5,
"total": 5,
"more": false,
"offset": 0,
"hits": [
{
"jcr:path": "/content/dam/sampledata",
"jcr:content": {
"metadata": {}
}
},
{
"jcr:path": "/content/dam/spaeditableareas/asset.jpg",
"jcr:content": {
"metadata": {}
}
},
{
"jcr:path": "/content/dam/spassr/asset.jpg",
"jcr:content": {
"metadata": {}
}
},
{
"jcr:path": "/content/dam/wknd/test.csv",
"jcr:content": {
"metadata": {}
}
},
{
"jcr:path": "/content/dam/wknd/asset.jpg",
"jcr:content": {
"metadata": {
"dc:description": "test description",
"dc:title": "test title"
}
}
}
]
}

Please note /bin/querybuilder.json URL will be blocked through the dispatcher for external-facing website domains due to security and performance impacts.

You can define a dedicated internal domain to share the data or directly access the Query builder JSON API from the Author server.

Refer to https://experienceleague.adobe.com/docs/experience-manager-64/developing/platform/query-builder/querybuilder-api.html?lang=en for more details on Query Builder API.

The AEM prints all the resources as Atom and RSS feeds; this will help share some basic data as feeds to the external systems.

The below feed URL's can be used to print the resources as a feed through the OOTB functionality.

http://localhost:4502/content/wknd/us/en.feed.atom.xml
http://localhost:4502/content/wknd/us/en.feedentry.atom.xml
http://localhost:4502/content/wknd/us/en.feed.rss.xml
http://localhost:4502/content/wknd/us/en.feedentry.rss.xml
<?xml version="1.0" encoding="UTF-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom">
<title type="html">en1</title>
<author>
<name>
<![CDATA[admin]]>
</name>
<email>admin@localhost</email>
</author>
<id>http://localhost:4502/content/wknd/us/en.feed.xml</id>
<link rel="alternate" type="text/html" href="http://localhost:4502/content/wknd/us/en.html"/>
<link rel="self" type="application/atom+xml" href="http://localhost:4502/content/wknd/us/en.feed.xml"/>
<updated>2021-12-28T17:22:58Z</updated>
<entry>
<title type="html">en</title>
<summary type="html"/>
<author>
<name>
<![CDATA[admin]]>
</name>
<email>admin@localhost</email>
</author>
<id>http://localhost:4502/content/wknd/us/en/test.feed.xml</id>
<updated>2021-12-28T17:22:58Z</updated>
<published>2021-12-28T17:22:58Z</published>
<category term=""/>
<link rel="alternate" type="text/html" href="http://localhost:4502/content/wknd/us/en/test.html"/>
<link rel="replies" type="text/html" href="http://localhost:4502/content/wknd/us/en/test.html#comments"/>
<content xml:base="http://localhost:4502/content/wknd/us/en" type="html"/>
</entry>
</feed>

QueryBuilder Feed: Query Builder feed helps print the feed data for the resources fetched through Query Builder API. Modify the query parameters to fetch the feed details for the required resources.

http://localhost:4502/bin/querybuilder.feed?p.limit=100&path=%2fcontent%2fdam%2fwknd&type=dam%3aAsset<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:os="http://a9.com/-/spec/opensearch/1.1/">
<title type="text">CQ Feed</title>
<id>http://localhost:4502/bin/querybuilder.feed?p.limit=100&amp;path=/content/dam/wknd&amp;type=dam:Asset</id>
<link href="http://localhost:4502/bin/querybuilder.feed?p.limit=100&amp;path=%2fcontent%2fdam%2fwknd&amp;type=dam%3aAsset" rel="self"></link>
<updated>2022-04-08T18:15:47.645Z</updated>
<os:itemsPerPage>100</os:itemsPerPage>
<os:totalResults>2</os:totalResults>
<os:startIndex>0</os:startIndex>
<entry
xmlns:media="http://search.yahoo.com/mrss/">
<title type="html">test.csv</title>
<link href="http://localhost:4502/content/dam/wknd/test.csv"></link>
<content type="html">&lt;img src="http://localhost:4502/content/dam/wknd/test.csv/jcr:content/renditions/cq5dam.thumbnail.140.100.png" alt="test.csv"/&gt;</content>
<media:content url="http://localhost:4502/content/dam/wknd/test.csv" type="text/csv"></media:content>
<id>http://localhost:4502/content/dam/wknd/test.csv</id>
<published>2021-12-28T23:44:16.614Z</published>
</entry>
<entry
xmlns:media="http://search.yahoo.com/mrss/">
<title type="html">test title</title>
<link href="http://localhost:4502/content/dam/wknd/asset.jpg"></link>
<content type="html">&lt;img src="http://localhost:4502/content/dam/wknd/asset.jpg/jcr:content/renditions/cq5dam.thumbnail.140.100.png" alt="test title"/&gt;</content>
<media:content url="http://localhost:4502/content/dam/wknd/asset.jpg" type="image/jpeg"></media:content>
<id>http://localhost:4502/content/dam/wknd/asset.jpg</id>
<published>2021-12-28T17:22:58.137Z</published>
</entry>
</feed>

The OOTB feed renderers(Servlets) can be overridden to share additional data through the feeds.

To obtain the page information, send a request to the PageInfo servlet to get the page metadata in JSON format. The PageInfo servlet returns information about resources in the repository. The servlet is enabled in https://<server>:<port>/libs/wcm/core/content/pageinfo.json; access the page info by sending the content path. Use the infinity selector to get the additional details about the page.

http://localhost:4502/libs/wcm/core/content/pageinfo.json?path=/content/wknd/us/enhttp://localhost:4502/libs/wcm/core/content/pageinfo.infinityjson?path=/content/wknd/us/en

Refer to https://experienceleague.adobe.com/docs/experience-manager-64/developing/components/pageinfo.html?lang=en for more details on the page info servlet.

Packages enable the importing and exporting of AEM repository content. For example, you can use packages to install new functionality, transfer content between instances, and back up repository content. Refer to the following URL https://experienceleague.adobe.com/docs/experience-manager-64/administering/contentmanagement/package-manager.html?lang=en for more details.

The content can also be exported in XML, XLIF, or JSON format as a package to support the manual translation workflow — Refer to the following URL https://medium.com/tech-learnings/adobe-experience-manager-aem-content-translation-deep-dive-part2-d4b75cade77b for more details on content translation.

Page Exporter:

The packages built through the Package Manager are more understandable by the AEM platform; The page exporter can be used to export a page as a complete web page including images, .js, and .css files.

You should enable the export configuration template for your site — the export configuration template exists for your site. The page can be exported as a ZIP once the export configurations are enabled by using the following URL format — <page-name>.export.zip, e.g http://localhost:4502/content/test.export.zip

Refer to The Page Exporter | Adobe Experience Manager for more details on Page Exporter.

Web scraping is an automatic method to obtain large amounts of data from websites. In this approach, we are not sharing the data directly from AEM, but you can use some scrapping tools/scripts to fetch the required content from any publically available websites, including AEM-hosted websites.

You can create custom servlets/services to share the required data in any format(e.g., JSON, XML, feed, etc.). Create custom servlets/services if non of the OOTB options support the required data sharing.

In AEM, any basic page data can be rendered as a JSON, e.g., http://localhost:4502/content/wknd/us/en.tidy.json (tidy selector formats the JSON while printing)

http://localhost:4502/content/wknd/us/en.tidy.infinity.json

The infinity selector can be used to print additional details about the page.

The Assets HTTP API allows for create-read-update-delete (CRUD) operations on digital assets, including metadata, renditions, comments, and structured content using Experience Manager Content Fragments. It is exposed at /api/assets and is implemented as REST API.

The API service document can be accessed through http://localhost:4502/api.json.

For example, the CURD operations can be performed in any assets, including the Content Fragments.

http://localhost:4502/api/assets/wknd/test.json (Content Fragment)

{
"links": [
{
"rel": [
"self"
],
"href": "http://localhost:4502/api/assets/wknd/test.json"
},
{
"rel": [
"parent"
],
"href": "http://localhost:4502/api/assets/wknd.json"
}
],
"class": [
"assets/asset"
],
"actions": [
{
"method": "DELETE",
"name": "delete",
"href": "http://localhost:4502/api/assets/wknd/test",
"title": "Delete"
},
{
"method": "PUT",
"name": "update-fragment",
"href": "http://localhost:4502/api/assets/wknd/test",
"type": "application/json",
"title": "Update Content Fragment"
}
],
"properties": {
"metadata": {},
"created": 1649447482461,
"description": "ssss",
"title": "test",
"contentFragment": true,
"createdBy": "admin",
"elementsOrder": [
"data1",
"data2"
],
"elements": {
"data2": {
"variationsOrder": [
"var1"
],
"translatable": false,
":type": "string",
"variations": {
"var1": {
":type": "string",
"dataType": "string",
"description": "",
"title": "var1",
"multiValue": false,
"value": "Test Var Data2"
}
},
"dataType": "string",
"title": "Data2",
"multiValue": false,
"value": "Test Data2"
},
"data1": {
"variationsOrder": [
"var1"
],
"translatable": false,
":type": "string",
"variations": {
"var1": {
":type": "string",
"dataType": "string",
"description": "",
"title": "var1",
"multiValue": false,
"value": "Test Var Data 1"
}
},
"dataType": "string",
"title": "Data1",
"multiValue": false,
"value": "Test Data 1"
}
},
"name": "test",
"modified": 1649447706954,
"modifiedBy": "admin",
"cq:model": {
"path": "/conf/global/settings/dam/cfm/models/sampledata"
},
"srn:paging": {
"total": 0,
"offset": 0,
"limit": 20
}
}
}

Retrieve an asset folder listing -http://localhost:4502/api/assets/wknd.json

Retrieve the properties of an asset — http://localhost:4502/api/assets/wknd/asset.jpg.json

Refer to https://experienceleague.adobe.com/docs/experience-manager-65/assets/extending/mac-api-assets.html?lang=en for more details on Asset HTTP API.

Adobe Experience Manager Assets lets you share assets, folders, and collections as a URL with members of your organization and external entities, including partners and vendors. Sharing assets through a link is a convenient way of making resources available to external parties without first logging into Assets.

Refer to https://experienceleague.adobe.com/docs/experience-manager-64/assets/administer/link-sharing.html?lang=en for more details on asset share functionality.

Please ensure the security and the performance are factored in before sharing the AEM repository content externally; as best practices, most of the JSON endpoints are recommended to be filtered by the dispatcher to secure the AEM publish server. Ensure the endpoints are exposed to intended users with proper authentications.

Considering the headless implementation based on the current status, use GraphQL API and Content Fragment to expose the structured content with the external systems. Also, use Content Service/Model Exporter to share any page content with external systems.

Also, the Experience Fragment can be used to define the shared experiences and share them as an HTML/JSON to any external systems.