EUROPE'S LEADING AEM DEVELOPER CONFERENCE
25
th
27
th
SEPTEMBER 2023
Extending AEM authoring experience
through Javascript Extension
Félix Delval, Adobe
About me
2
Félix Delval
Sr. Software Engineer
AEM Sites, background in Commerce
Agenda
3
AEM Authoring Experience
UI Extensibility Framework
Implementation of a use case
Q&A
4
AEM AUTHORING EXPERIENCE
AEM Content Fragment Console
5
AEM Content Fragment Editor
6
7
UI Extensibility Framework
UI Extensions
8
What?
UI Apps
embedded in
Adobe Experince
Cloud UI
How?
JS App using
Adobe
AppBuilder
Why?
Personalize
the authoring
experience
What is AppBuilder
9
I/O Runtime
CLI &
SDKs
Spectrum UI
Framework
Custom
Events
Cloud
Services
Developer
Tools
API
Mesh
Extending with AppBuilder
10
Middleware
Extensibility
Connect external
systems with Adobe
applications building
custom connectors
Core Services
Extensibility
Extend core applications
capabilitie by extending
the default behavior
User Experience
Extensibility
Extend core experience
to support personalized
workflow
In-Process Extensibility
11
Disparate Technologies
Slows down time to value
Maintenance and Releases are complex
Out-of-proccess Extensibility
12
Easy maintenance and releasing
Common stack of technologies
Flexibility
Less Code
Reduced Total Cost of Ownership
13
Extension Points
Console Extension points
14
Header buttons (eg. Create)
Action button for Fragments (eg. Delete)
Modal on click events
Columns with custom renderer
Drop-down menus
React Spectrum icons customization
Editor Extensions Point
15
Variables
Toolbar
Header Menu
Badges
16
Use case implementation
How to create an extension?
17
Setup
Create a
console project
Init
Init an
extension app
for a template
Code
Register the
extensions, craft
UI and serverless
backend
Run
Test the app
locally
Deploy
Push to
production
Extension Design
18
1 - Generate an image
form text using
OpenAI API
2 Upload the image
to AEM DAM
3 Update Content
Fragments image
property
19
Extension Flow
20
CF Admin Console UI extension
Adobe I/O Runtime
Action Bar button
Extension modal
Generate Image
Upload Image
Update CF
manifest.yml
21
operations:
view:
- type: web
impl: index.html
web: web-src
actions: actions
runtimeManifest:
packages:
aem-cf-console-admin-1:
license: Apache-2.0
actions:
generate-image:
function: actions/generate-image
web: 'yes’
runtime: nodejs:16
inputs:
LOG_LEVEL: debug
OPENAI_API_KEY: $OPENAI_API_KEY
Declaration of the App
with UI Extension
Serverless declaration
Extension Registration
22
function ExtensionRegistration() {
const init = async () => {
const guestConnection = await register({
id: extensionId,
methods: {
actionBar: {
getButton() {
return {
'id': 'generate-image', // Unique ID for the button
'label': 'Generate Image', // Button label
'icon': 'PublishCheck' // Button icons name
}
},
// Click handler for the extension button
onClick(selections) {
// Code in next slides
}
},
}
})
}
init().catch(console.error)
return <Text>IFrame for integration with Host (AEM)...</Text>
}
Declaration
of the button
Show modal
code
onClick
23
onClick(selections) {
// Collect the selected content fragment paths
const selectionIds = selections.map(selection => selection.id);
const modalURL = "/index.html#" + generatePath(
"/content-fragment/:selection/generate-image-modal",
{
// Set the :selection React route parameter to an encoded,
// delimited list of paths of the selected content fragments
selection: encodeURIComponent(selectionIds.join('|')),
});
// Open the route in the extension modal using the constructed URL
guestConnection.host.modal.showUrl({
title: "Generate Image",
url: modalURL
})
}
Building
the URL
Display the
modal
App Structure
24
function App() {
return (
<Router>
<ErrorBoundary onError={onError} FallbackComponent={fallbackComponent}>
<Routes>
<Route index element={<ExtensionRegistration />} />
<Route
exact path="index.html"
element={<ExtensionRegistration />}
/>
<Route
exact path="content-fragment/:selection/generate-image-modal"
element={<GenerateImageModal />}
/>
</Routes>
</ErrorBoundary>
</Router>)
Extension
Registration
Modal
The modal
25
function renderImgGenerationForm() {
return (
<Provider theme={defaultTheme} colorScheme="light">
<Content width="100%">
<Flex width="100%">
<Form width="100%">
<TextField
label="Image Description"
description="The image description in natural language, for e.g. Alaskan adventure in wilderness, animals, and flowers."
isRequired
validationState={validationState?.propertyName}
onChange={setImageDescription}
contextualHelp={(
<ContextualHelp>
<Heading>Need help?</Heading>
<Content>
<Text>
The <strong>description of an image</strong> {' ‘}
you are looking for in the natural language, for e.g. &quot;Family vacation on the beach with blue ocean, dolphins, boats and drink&quot;
</Text>
</Content>
</ContextualHelp>
)}/>
<ButtonGroup align="end">
<Button variant="accent" onPress={onSubmitHandler}>Use Credits</Button>
<Button variant="accent" onPress={() => guestConnection.host.modal.close()}>Close</Button>
</ButtonGroup>
</Form>
</Flex>
</Content>
</Provider>
);
Text Field
Two
buttons
IO Runtime Actions
26
// main function that will be executed by Adobe I/O Runtime
async function main(params) {
// ...
// Call OpenAI (DALL.E 2) API to generate an image using image description
const generatedImageURL = await generateImageUsingOpenAI(params);
logger.info(`Generated image using OpenAI API and url is : ${generatedImageURL}`);
// Upload the generated image to AEM-CS
const uploadedImagePath = await uploadGeneratedImageToAEM(params, generatedImageURL, token);
logger.info(`Uploaded image to AEM, path is: ${uploadedImagePath}`);
// Update Content Fragment with the newly generated image reference
const updateContentFragmentPath = await updateContentFragmentToUseGeneratedImg(params, uploadedImagePath, token);
logger.info(`Updated Content Fragment path is: ${updateContentFragmentPath}`);
// ...
}
Image
generation
Upload
asset
Link to Content
Fragment
Key takeaways
27
New AEM Authoring offers UI Extensibility
AppBuilder extensions can help you create
new experience
What’s coming next
28
More extension points are coming
UI Extensions as first class citizen
AEM Labs
Submit ideas at uix@adobe.com
AppBuilder is now available
29
All AEM as Cloud Service client have acces to
AppBuilder
Contact your sales team for more information
Useful links
30
Github repo with code samples
Experience League Tutorial
UI Extensibility Docs Homepage
Adobe Developer Live in November