Skip to content

Commit

Permalink
Merge commit '72841a434a072b302415e43fe315a2875a1fc58a' as 'plugins/a…
Browse files Browse the repository at this point in the history
…em-assets-plugin'
  • Loading branch information
Satya Deep Maheshwari committed Nov 12, 2024
2 parents 89d30fe + 72841a4 commit 1db4372
Show file tree
Hide file tree
Showing 19 changed files with 5,911 additions and 0 deletions.
1 change: 1 addition & 0 deletions plugins/aem-assets-plugin/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
blocks/video/videojs
18 changes: 18 additions & 0 deletions plugins/aem-assets-plugin/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
extends: 'airbnb-base',
env: {
browser: true,
},
parser: '@babel/eslint-parser',
parserOptions: {
allowImportExportEverywhere: true,
sourceType: 'module',
requireConfigFile: false,
},
rules: {
'import/extensions': ['error', { js: 'always' }], // require js file extensions in imports
'linebreak-style': ['error', 'unix'], // enforce unix linebreaks
'no-param-reassign': [2, { props: false }], // allow modifying properties of param
},
};
130 changes: 130 additions & 0 deletions plugins/aem-assets-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
3 changes: 3 additions & 0 deletions plugins/aem-assets-plugin/.stylelintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["stylelint-config-standard"]
}
189 changes: 189 additions & 0 deletions plugins/aem-assets-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
:construction: This is an early access technology and is still heavily in development. Reach out to us over Slack / Discord before using it.

# AEM Edge Delivery Services Assets Plugin
The AEM Assets Plugin helps you quickly integrate with AEM Assets for your AEM project. It is currently available to customers in collaboration with AEM Engineering via co-innovation VIP Projects. To implement your use cases, please reach out to the AEM Engineering team in the Slack channel dedicated to your project.

## Features
- A collection of blocks to use AEM Assets in Edge Delivery Services based websites
- Utility functions to generate markup for delivering assets from AEM Assets

## Prerequisites
- You need to have an AEM Assets as a Cloud Service subscription
- You need to have access to [Dynamic Media Open API](https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/assets/dynamicmedia/dynamic-media-open-apis/dynamic-media-open-apis-overview)

And you need to have pre-configured:
- [AEM Assets Sidekick plugin](https://www.aem.live/docs/aem-assets-sidekick-plugin)

## Installation

Add the plugin to your AEM project by running:
```sh
git subtree add --squash --prefix plugins/aem-assets-plugin [email protected]:adobe-rnd/aem-assets-plugin.git main
```

If you later want to pull the latest changes and update your local copy of the plugin
```sh
git subtree pull --squash --prefix plugins/aem-assets-plugin [email protected]:adobe-rnd/aem-assets-plugin.git main
```

If you prefer using `https` links you'd replace `[email protected]:adobe-rnd/aem-assets-plugin.git` in the above commands by `https://github.com:adobe-rnd/aem-assets-plugin.git`.

If the `subtree pull` command is failing with an error like:
```
fatal: can't squash-merge: 'plugins/aem-assets-plugin' was never added
```
you can just delete the folder and re-add the plugin via the `git subtree add` command above.

## Project instrumentation

To properly connect and configure the plugin for your project, you'll need to edit both the `aem.js` and `scripts.js` in your AEM project and add a new file `aem-assets-plugin-support.js` in the `scripts` folder.

> **Note:** All the changes described below can also be seen consolidated in this [git commit](https://github.com/hlxsites/franklin-assets-selector/commit/f512e9b10d752971136fef476402826b61d07f45).

Here's typically how `scripts/aem-assets-plugin-support.js` would look:

```
// The based path of the aem-assets-plugin code.
const codeBasePath = '/plugins/aem-assets-plugin';
// The blocks that are to be used from the aem-assets-plugin.
const blocks = ['video'];
// Initialize the aem-assets-plugin.
export default async function assetsInit() {
const { loadBlock, createOptimizedPicture } = await import(`${codeBasePath}/scripts/aem-assets.js`);
window.hlx = window.hlx || {};
window.hlx.aemassets = {
codeBasePath,
blocks,
loadBlock,
createOptimizedPicture,
};
}
```

You'd need to add the following code in `createOptimizedPicture` in `aem.js` to call the overidden version of this function
```
if (window.hlx?.aemassets?.createOptimizedPicture) {
return window.hlx.aemassets.createOptimizedPicture(src, alt, eager, breakpoints);
}
```

Here's the complete code for `createOptimizedPicture` in `aem.js` with the above lines of code added
```
function createOptimizedPicture(
src,
alt = '',
eager = false,
breakpoints = [{ media: '(min-width: 600px)', width: '2000' }, { width: '750' }],
) {
// Add below lines of code //
if (window.hlx?.aemassets?.createOptimizedPicture) {
return window.hlx.aemassets.createOptimizedPicture(src, alt, eager, breakpoints);
}
// Add above lines of code //
const url = new URL(src, window.location.href);
const picture = document.createElement('picture');
const { pathname } = url;
const ext = pathname.substring(pathname.lastIndexOf('.') + 1);
// webp
breakpoints.forEach((br) => {
const source = document.createElement('source');
if (br.media) source.setAttribute('media', br.media);
source.setAttribute('type', 'image/webp');
source.setAttribute('srcset', `${pathname}?width=${br.width}&format=webply&optimize=medium`);
picture.appendChild(source);
});
// fallback
breakpoints.forEach((br, i) => {
if (i < breakpoints.length - 1) {
const source = document.createElement('source');
if (br.media) source.setAttribute('media', br.media);
source.setAttribute('srcset', `${pathname}?width=${br.width}&format=${ext}&optimize=medium`);
picture.appendChild(source);
} else {
const img = document.createElement('img');
img.setAttribute('loading', eager ? 'eager' : 'lazy');
img.setAttribute('alt', alt);
picture.appendChild(img);
img.setAttribute('src', `${pathname}?width=${br.width}&format=${ext}&optimize=medium`);
}
});
return picture;
}
```

You'd need to add the following code in `loadBlock` in `aem.js` to call the overidden version of this function
```
if (window.hlx?.aemassets?.loadBlock) {
return window.hlx.aemassets.loadBlock(block);
}
```

Here's the complete code for `loadBlock` in `aem.js` with the above lines of code added
```
async function loadBlock(block) {
// Add below lines of code //
if (window.hlx?.aemassets?.loadBlock) {
return window.hlx.aemassets.loadBlock(block);
}
// Add above lines of code //
const status = block.dataset.blockStatus;
if (status !== 'loading' && status !== 'loaded') {
block.dataset.blockStatus = 'loading';
const { blockName } = block.dataset;
try {
const cssLoaded = loadCSS(`${window.hlx.codeBasePath}/blocks/${blockName}/${blockName}.css`);
const decorationComplete = new Promise((resolve) => {
(async () => {
try {
const mod = await import(
`${window.hlx.codeBasePath}/blocks/${blockName}/${blockName}.js`
);
if (mod.default) {
await mod.default(block);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(`failed to load module for ${blockName}`, error);
}
resolve();
})();
});
await Promise.all([cssLoaded, decorationComplete]);
} catch (error) {
// eslint-disable-next-line no-console
console.log(`failed to load block ${blockName}`, error);
}
block.dataset.blockStatus = 'loaded';
}
return block;
}
```

In `scripts/scripts.js` you'd need to add the following lines of code:

Import `aem-assets-plugin-support.js`
```
import assetsInit from './aem-assets-plugin-support.js';
```

Initialize `aem-assets-plugin-support.js`
```
await assetsInit(); // This to be done before loadPage() function invocation
loadPage();
```

## FAQ

Q. Why should I use this plugin?

A. The plugin provides a quick way to start using AEM Assets in your website. It abstracts and implements the groundwork to be able to consume AEM Assets delivery URLs in your wbsite, thus helps avoiding additional work to implement everything from scratch. This plugin also has a collection of standard blocks to consume AEM Assets which can be reused as they are, or copied over in your website for adaptations.
50 changes: 50 additions & 0 deletions plugins/aem-assets-plugin/blocks/secure-assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Secure Asset Block

## Feature Overview:

In standard image transformation from <a> tags to <picture> tags in EDS through [external sources](https://github.com/hlxsites/franklin-assets-selector/blob/ext-images/EXTERNAL_IMAGES.md), the presence of secure assets coming from DM with OpenAPI on the page isn't considered, treating all assets as if they are public. This block addresses that gap by embedding a delivery token while rendering secure assets.

## Advantages:

This block allows content authors to embed secure assets directly into their EDS pages. It can also be extended to facilitate campaigns like member-exclusive deals by using placeholder images to prompt unauthorized users to become members.

## Functionality:

The block reduces overhead by filtering secure assets using HEAD requests into a local map before making reload calls to DM OpenAPI. When a user provides a valid role or principal, secured images are refreshed accordingly, while public images remain unaffected.

## Usages

### Section Metadata Semantics

* `Placeholder Image` : Set any public image reference or base64 encoded image.

## Knowledge Base

### How the asset are identifed as secure?

It checks for 404 response status code with the help of HEAD HTTP request to identify any secure asset in DM with OpenAPI.

### How to maintain a page score of ~100 on LHS with secure assets on the page?

Identifying an asset as secure involves two requests within the block. The first, a HEAD request, determines if the asset is secure, followed by a request to render the secure asset's binary. This process can cause delays in LCP (Largest Contentful Paint), especially when numerous secure assets are present. To mitigate this, use the 'secure asset' block judiciously and only for specific use cases.

### How to update the fallback image?

Just update the section metadata with property - `Placeholder Image` with desired value

### How does a content author get the URL of a secure asset?

Author can select the target asset through Asset picker.

### How is the asset actually marked ‘secure’ on AEM author?

Can be set using asset property page followed by editing roles (aka dam:roles)

### How to refresh images via passing auth token for secure assets

The secure asset block listens to `auth-token-available` event to get the authentication token needed to fetch the secure images. A sample event with token valid till 10/27/2025 is illustrated below :
```
const customEvent = new CustomEvent('auth-token-available', { detail: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6ImFkbWluIiwiZXhwaXJ5IjoiMjAyNS0xMC0yOFQwMjozMzoyNC42NjhaIn0.El7r66ngrDXneNJWkLsLFQRESk-e4bhvDFHSoUJNo0k' });
document.dispatchEvent(customEvent);
```
Loading

0 comments on commit 1db4372

Please sign in to comment.