Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,11 @@ lib/
yalc.lock
.idea/

dist/
dist/
/.env
/package-lock.json

certificates

/.next
/.env.sandbox
102 changes: 64 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,83 @@
# Adobe Universal Editor Sample App
# Adobe Universal Editor Sample App (Next.js)

## Using the Sample App
The Sample App is hosted at https://ue-remote-app.adobe.net.
Per Default the content is retrieved and written back to the Adobe Experience Manager as a Cloud Service ( Production ) Demo Environment:
This is a sample Next.js application demonstrating how to integrate and use the Adobe Universal Editor with an AEM headless backend.

The default settings from [.env](.env) can be overwritten using Query parameters:
* `authorHost`: host to retrieve data from and update content to; default=https://author-p7452-e12433.adobeaemcloud.com
* `service`: Universal Editor Service endpoint; default Universal Editor default
* `protocol`: protocol to use with backend, can be `aem`, `aem65`, `aemcsLegacy`; default: `aem`
* `cors`: defining which cors.js - connection between Universal Editor and application shall be used. Can be `stage` or empty; default `null/empty`. `stage` will use the cors library hosted on stage, else it will use the production version
## Local Development with Universal Editor

To retrieve content from another environment add `authorHost` as query parameters, e.g.
To run this application locally and edit it using the Universal Editor, follow these steps:

[https://ue-remote-app.adobe.net?authorHost=https://author-p7452-e12433.adobeaemcloud.com](https://ue-remote-app.adobe.net?authorHost=https://author-p7452-e12433.adobeaemcloud.com)
### Prerequisites

Similarly, if running the Universal Editor App on local dev environment, add `authorHost` as query parameters like this:
1. **AEM Local Instance**: An AEM 6.5 or AEM as a Cloud Service (AEMCS) local SDK instance running locally.
2. **HTTPS Configuration**: AEM must be configured to run on HTTPS (e.g., `https://localhost:8443`).
3. **Content**: Ensure you have the latest WKND Site or the appropriate headless models/content installed on your local AEM instance.
4. **Universal Editor CORS Proxy**: You must install the AEM Universal Editor CORS proxy package (bundle/jar) on your local AEM instance. You can download the latest Universal Editor local proxy package from the [Adobe Software Distribution portal](https://experience.adobe.com/#/downloads/content/software-distribution/en/aem.html). This is required to bypass CORS restrictions when the editor runs locally.

[https://localhost:3000?authorHost=https://localhost:8443&service=https://localhost:8443/universal-editor](https://localhost:3000?authorHost=https://localhost:8443&service=https://localhost:8443/universal-editor)
### Environment Configuration

## Run locally
For local development, the application uses the `.env.local` file. Ensure it contains the appropriate variables for your local setup. Example:

- AEM 6.5 or AEMCS instance
- Latest WKND Content installed on the AEM instance[https://github.com/adobe/aem-guides-wknd/releases/latest](https://github.com/adobe/aem-guides-wknd/releases/latest)
- AEM configured to run on HTTPS [https://experienceleague.adobe.com/en/docs/experience-manager-learn/foundation/security/use-the-ssl-wizard](https://experienceleague.adobe.com/en/docs/experience-manager-learn/foundation/security/use-the-ssl-wizard)
- `Adobe Granite Token Authentication Handler` configured to set `token.samesite.cookie.attr=Partitioned`
- Remove `X-FRAME-Options=SAMEORIGIN` from `Apache Sling Main Servlet`'s `sling.additional.response.headers` attribute if run locally
- Add policy for `https://localhost:3000` to `Adobe Granite Cross-Origin Resource Sharing Policy`. The default `adobe` configuraiton can be used as blueprint if run local copy of the app
- Follow configuration on [https://github.com/maximilianvoss/universal-editor-service-proxy](https://github.com/maximilianvoss/universal-editor-service-proxy) for local development set up
- Open Universal Editor either
- under AEM domain for AEMCS, e.g. [https://author-p7452-e12433.adobeaemcloud.com/ui#/aem/universal-editor/canvas/](https://author-p7452-e12433.adobeaemcloud.com/ui#/aem/universal-editor/canvas/)
- or on [https://experience.adobe.com/#/aem/editor/canvas/](https://experience.adobe.com/#/aem/editor/canvas/)
- For experience.adobe.com use the `Local Developer Login` to authenticate against your local AEM instance when using a local SDK or AEM 6.5
```env
NEXT_PUBLIC_AEM_ACCESS_TOKEN="admin:admin"
NEXT_PUBLIC_AEM_HOST="https://localhost:8443"
NEXT_PUBLIC_UE_SERVICE="https://localhost:8000"
NODE_TLS_REJECT_UNAUTHORIZED=0
```

## Available Scripts
- `NEXT_PUBLIC_AEM_HOST`: Points to your local AEM author instance.
- `NEXT_PUBLIC_AEM_ACCESS_TOKEN`: The credentials (e.g., Basic Auth or Bearer token) needed to fetch content from your local AEM instance.
- `NEXT_PUBLIC_UE_SERVICE`: Points to the local Universal Editor service if you are running it locally.

In the project directory, you can run:
### Running the App Locally

To start the Next.js development server specifically configured for a local AEM instance:

```bash
npm run dev:local
```

**Note:** The `dev:local` script automatically includes a local CA certificate (`NODE_EXTRA_CA_CERTS=certificates/localhost.pem`) and enables experimental HTTPS in Next.js (`--experimental-https`). This is required to resolve local SSL certificate errors when fetching data from AEM over HTTPS on localhost.

The app will be available at [https://localhost:3000](https://localhost:3000).

### `yarn start`
### Local Universal Editor Service Proxy

Runs the app in the development mode.\
Open [https://localhost:3000](https://localhost:3000) to view it in your browser.
If you are running the Universal Editor service proxy locally, you must create and trust a local certificate before starting the service:

The page will reload when you make changes.\
You may also see any lint errors in the console.
1. **Generate a local certificate:**
```bash
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out certificate.pem -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
```
2. **Trust the certificate on your system (macOS):**
```bash
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain certificate.pem
```
3. **Start the local Universal Editor service:**
```bash
node universal-editor-service.cjs
```

### `yarn build`
### Opening in Universal Editor

1. Open the Universal Editor. Ensure your local Universal Editor service proxy is running (e.g., at `https://localhost:8000`).
2. Point the Universal Editor to your local Next.js app URL: `https://localhost:3000`.
3. You can now edit the Next.js application in context. The changes will be pushed back to your local AEM instance at `https://localhost:8443`.

## Available Scripts

In the project directory, you can run:

Builds the app for production to the `dist` folder.
### `npm run dev:local`
Runs the app in development mode, tailored for localhost. It injects local certificates to bypass SSL connection errors when communicating with local AEM over HTTPS.

### `yarn preview`
### `npm run dev:sandbox`
Runs the app using configurations defined in `.env.sandbox`. Useful when connecting to a remote sandbox AEM environment instead of localhost.

Run the built app in production mode locally to verify the build.
### `npm run build`
Builds the Next.js app for production to the `.next` folder.

### `yarn deploy`
### `npm run start`
Starts the built Next.js application in production mode.

Build the application and push it to GitHub pages
### `npm run deploy`
Builds the application and deploys it to GitHub Pages.
36 changes: 0 additions & 36 deletions index.html

This file was deleted.

22 changes: 22 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: false,
async headers() {
debugger;
return [
{
source: "/:path*",
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "https://experience.adobe.com" },
{ key: "Access-Control-Allow-Methods", value: "GET, POST, OPTIONS, PUT, PATCH, DELETE" },
{ key: "Access-Control-Allow-Headers", value: "Content-Type, Authorization, X-Requested-With" },
// CRITICAL: This allows a public site to access your local network
{ key: "Access-Control-Allow-Private-Network", value: "true" },
],
},
]
}
};

export default nextConfig;
24 changes: 11 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,25 @@
],
"type": "module",
"scripts": {
"start": "vite",
"build": "vite build",
"preview": "vite preview",
"deploy": "npm run build && gh-pages -d dist --cname ue-remote-app.adobe.net"
"start": "next start",
"dev:sandbox": "env-cmd -f .env.sandbox next dev --experimental-https",
"build": "next build",
"deploy": "next build && gh-pages -d out --cname ue-remote-app.adobe.net",
"dev:local": "NODE_EXTRA_CA_CERTS=certificates/localhost.pem next dev --experimental-https"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^7.9.6",
"@adobe/jwt-auth": "^2.0.0",
"next": "^16.2.4",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"sass": "^1.94.1"
},
"devDependencies": {
"@adobe/aem-headless-client-js": "^4.0.0",
"@adobe/aem-headless-client-nodejs": "^2.0.0",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-basic-ssl": "^2.1.0",
"@vitejs/plugin-react": "^4.2.1",
"gh-pages": "^6.1.0",
"react-helmet-async": "^2.0.4",
"vite": "^5.0.8"
"env-cmd": "^11.0.0",
"gh-pages": "^6.1.0"
},
"eslintConfig": {
"extends": [
Expand Down
25 changes: 25 additions & 0 deletions public/static/component-definition.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@
}
}
}
},
{
"title": "Carousel Slide",
"id": "carousel-item",
"model": "carousel-item",
"plugins": {
"aem": {
"page": {
"resourceType": "wknd/components/container"
}
}
}
}
]
},
Expand Down Expand Up @@ -106,6 +118,19 @@
}
}
}
},
{
"title": "Carousel",
"id": "carousel",
"model": "carousel",
"filter": "carousel",
"plugins": {
"aem": {
"page": {
"resourceType": "wknd/components/carousel"
}
}
}
}
]
}
Expand Down
14 changes: 10 additions & 4 deletions public/static/filter-definition.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
[
{
{
"id": "container",
"components": [
"text", "image", "title", "accordion", "container", "richtext"
"text", "image", "title", "accordion", "carousel", "container", "richtext"
]
},
},
{
"id": "accordion",
"components": [
"accordion-item"
]
},
{
"id": "carousel",
"components": [
"carousel-item"
]
}
]
]
52 changes: 52 additions & 0 deletions public/static/model-definition.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,57 @@
"required": true
}
]
},
{
"id": "carousel",
"fields": [
{
"component": "boolean",
"name": "autoplay",
"label": "Auto-play slides",
"valueType": "boolean"
},
{
"component": "boolean",
"name": "showIndicators",
"label": "Show slide indicators",
"valueType": "boolean",
"value": true
},
{
"component": "boolean",
"name": "showNavigation",
"label": "Show navigation arrows",
"valueType": "boolean",
"value": true
}
]
},
{
"id": "carousel-item",
"fields": [
{
"component": "text",
"name": "cq:panelTitle",
"value": "",
"label": "Slide Title",
"valueType": "string",
"required": true
},
{
"component": "select",
"name": "overlayStrength",
"value": "medium",
"label": "Image Overlay Strength",
"description": "Darkens the image so text overlay is readable. Applies only when an image and other content share a slide.",
"valueType": "string",
"options": [
{ "name": "None", "value": "none" },
{ "name": "Subtle", "value": "subtle" },
{ "name": "Medium (default)", "value": "medium" },
{ "name": "Strong", "value": "strong" }
]
}
]
}
]
Loading