Upgrading Enonic apps from XP7 to XP8
Contents
This section describes the steps required to upgrade an XP application from 7.x to 8.0
| To run this upgrade automatically with an AI coding agent, use the Enonic AI Agents Skills repository — it includes a skill that drives Claude Code (or a compatible agent) through the procedure below. The skill reads this page as its source, so the steps documented here are authoritative whether you upgrade by hand or via an agent. |
Terminology and concept changes
XP 8 renames or reframes several XP 7 concepts. Use this map when migrating — and when reconciling older XP 7-era examples or AI-generated code with current XP 8 conventions. Each change is detailed further down this page.
- Engine → Service
-
The site, webapp, and admin "engines" are now services — Additionally XP8 introduces the /api service for a total of four (
/site,/webapp,/admin,/api) dispatched by the Web endpoint. - Controller → HTTP function
-
There are no "controllers". A module (implementation file) exports functions (
GET,POST, …) — the implementation is often paired with a descriptor. Controllers may be mentioned in descriptors to indicate that a function has a specific role in the request processing, but the function is still just a function. Function also aligns with contemporary JavaScript/TypeScript terminology, and avoids confusion with the "controller" role in MVC frameworks. - Service → Universal API
-
The XP7 HTTP servies / services located at
/_/service/<app>/<name>are replaced by Universal APIs in XP8, reachable contextually at/_/<app>:<api>. - Assets service → lib-asset
-
The built-in assets service is deprecated; serve static assets with the
lib-assetlibrary (available on Enonic Market) instead. - Widgets → Admin extensions
-
Admin widgets are now part of the Admin Extensions API; the
admin/widgetsdirectory becomesadmin/extensions. -
site/→cms/ -
The
src/main/resources/sitedirectory is renamed tocms. - Mixin → Form fragment
-
XP 7 mixins are renamed to form fragments; the
mixinsdirectory becomesform-fragments. - X-data → Mixin
-
XP 7 x-data is renamed to mixin; the
x-datadirectory becomesmixins(note this collides with the old name — see above). Renaming is made as mixins a more intuitive name, used by editors and developers alike. The data is still stored under thexproperty in content, aligning with miXin. -
displayName→title -
In descriptors (
enonic.yamland others), thedisplayNameproperty is nowtitle. This solves the confusion for content type definitions, where every content item has a special displayName field, which also has related configuration. displayName now remains exclusively a data field, and title is used for the human friendly name of the content type, visible in Content Studio and elsewhere in the UI. -
delete→deleteContent/deleteProject/ … -
Library methods named
deletewere renamed to avoid the reserved word (the olddeleteexports remain for backward compatibility).
Preparations
Before you start the upgrade procedure:
-
Make sure you have Enonic CLI installed
-
Check out the project source code to a local folder i.e. myapp/
-
Ensure you have an XP8 sandbox for the application using Enonic CLI
enonic project sandbox
App migrator tool
The xp8migrator is a small CLI that converts an XP 7 project’s XML descriptors into the XP 8 YAML form. Run it from your project root.
Linux / macOS
curl -fsSL https://raw.githubusercontent.com/enonic/xp8migrator/main/migrator-install.sh | sh
./migrator
Windows
irm https://raw.githubusercontent.com/enonic/xp8migrator/main/migrator-install.ps1 | iex
.\migrator.exe
The migrator binary can be removed after a successful migration.
Build system
The standard build system must be updated.
Gradle Wrapper
XP 8.0 standardizes on the use of Gradle 9.x. We recommend bundling Gradle wrapper of version 9.5.0 or later in your project.
For simple installations, from your project root, run:
./gradle wrapper --gradle-version 9.5.0
This will install or update your project with a new Gradle wrapper.
Please refer to Grade documentation for more details https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:upgrading_wrapper
build.gradle
If you are upgrading an application, you need to use a version greater or equal to '4.0.0' of the XP Gradle plugin (com.enonic.xp.app).
The new plugin no longer requires the app {} section in build.gradle Read more about it in the plugin documentation.
| Syntax for adding plugins to Gradle may have changed for your project, we recommend the following updates to your build files: |
plugins {
id 'com.enonic.xp.app'
}
repositories {
mavenCentral()
xp.enonicRepo()
}
dependencies {
implementation xplibs.api.script
include xplibs.content
include xplibs.portal
}
The default dev task is now registered automatically by the plugin. If your project defines its own dev task, either remove it to use the default, or set createDefaultDevTask = false in the app {} section to keep your custom task. |
Remove any java.toolchain.languageVersion (and sourceCompatibility / targetCompatibility) configuration from your build.gradle. The XP Gradle plugin sets the correct Java toolchain automatically — manual settings can conflict with what the plugin expects. |
gradle.properties
The new plugin only uses appName and xpVersion properties.
displayName, url, vendorName and vendorUrl must be moved to the new src/main/resources/enonic.yaml
displayName is now called title in enonic.yaml). |
# Gradle Project settings
projectName = myproject
version = 1.0.0-SNAPSHOT
# XP App values
appName = com.acme.something.myproject
xpVersion = 8.0.0
appName is only used for application projects, as well as app config in build.gradle |
settings.gradle
For the plugin to work, you must add the following to your settings.gradle:
plugins {
id("com.enonic.xp.settings") version "4.0.0"
}
Library upgrade
If you are upgrading a library, you don’t need to use com.enonic.xp.app plugin or have app {} section in build.gradle. Below is a sample content of build.gradle and gradle.properties files for a library:
plugins {
id 'java'
id 'maven-publish'
id 'com.enonic.xp.base'
}
repositories {
mavenCentral()
xp.enonicRepo()
}
| You only need to use com.enonic.xp.base plugin if you are using XP dependencies and need to shortlink to Enonic repo via xp.enonicRepo() shortcut |
group=com.mycompany.lib
projectName=mylib
xpVersion=8.0.0
version=1.0.0-SNAPSHOT
TypeScript definitions (@enonic-types) for apps and libraries
If your project uses TypeScript, update all @enonic-types packages in package.json to XP 8 compatible versions. This applies to both applications and libraries.
XP 7
{
"devDependencies": {
"@enonic-types/global": "^7.0.0",
"@enonic-types/lib-content": "^7.0.0"
}
}
XP 8
{
"devDependencies": {
"@enonic-types/global": "^8.0.0",
"@enonic-types/lib-content": "^8.0.0"
}
}
Verify
After completing the steps above, you should now be able to test that your build is working, using the Enonic CLI:
enonic project deploy
This command proxies the Gradle wrapper, but also connects with the project sandbox. You may also use enonic project build to build without deploying
| Projects containing Java code might get build errors at this point, otherwise the build should complete successfully. |
JDK
Java 25 is bundled with XP 8.0.0, and is the minimum required version for building and running XP8 applications.
Testing API
If you are using Enonic testing API (com.enonic.xp:testing), you’ll need to add Junit 5 dependency with the corresponding platform launcher.
dependencies {
testImplementation "com.enonic.xp:testing:${xpVersion}"
testImplementation(platform("org.junit:junit-bom:6.0.1"))
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
test {
useJUnitPlatform()
}
| Junit 4 is not supported in XP 8.0.0 |
API Deprecations and Breaking Changes
Default context
In XP8 the contextual repository and branch may return null if they are not set explicitly. Previously the default context was set with com.enonic.cms.default repository and draft branch.
Symptom. Code that previously relied on the implicit defaults — typically contextLib.run({ … }, callback) without a branch: or repository: field — now throws at runtime, e.g. Error: branch is required. The same applies to node.connect({ … }) calls that don’t pass branch / repoId.
Fix. Set branch (and repository, where relevant) explicitly on every run or connect call that needs to operate against a known repo and branch.
// Before — relied on default 'draft' branch
import {run} from '/lib/xp/context';
run({}, () => doStuff());
// After — branch (and usually repository) set explicitly
import {run} from '/lib/xp/context';
run({repository: 'com.enonic.cms.default', branch: 'draft'}, () => doStuff());
Roles
The legacy role role:cms.cm.app (display name "Content Manager App") is deprecated. Fresh XP 8 installs no longer create this role, and it is no longer added to the default ACLs of content, issue, or archive root nodes when new projects are initialized.
Existing installations upgrading from XP 7 keep the role definition and any pre-existing ACEs that reference it. Those entries are harmless but obsolete and should not be relied on going forward. Apps that previously granted cms.cm.app to a user or group should switch to the per-project role hierarchy that XP 8 manages per content project:
-
role:cms.project.<name>.owner -
role:cms.project.<name>.editor -
role:cms.project.<name>.author -
role:cms.project.<name>.contributor -
role:cms.project.<name>.viewer
Legacy dumps (model 8) imported into XP 8 are upgraded to model 9 automatically. During that upgrade, cms.cm.app is stripped from the legacy com.enonic.cms.default repository ACLs and replaced with the per-project role hierarchy. No manual action is required for that case. |
Identity keys, names, and IDs
XP 8 tightens the validation rules for identity keys, node/content names, and IDs. Apps that programmatically generate these values (custom ID providers, importers, migration scripts, etc.) must produce values matching the rules below.
All identity keys must satisfy the following common rules:
-
must not be an empty string
-
must not be a single underscore
_, dot., or double dot.. -
must not include
/or\ -
must not include any whitespace characters, except the regular space
U+0020 -
must not start or end with the space character
U+0020
Node, Content, and Issue names and IDs
-
Names follow the common rules above.
-
IDs are limited to 256 bytes/characters and no longer accept uppercase letters:
[a-z0-9_.:-]+
IdProviderKey and PrincipalKey
Follow the common rules with the additional restriction of not including the space character U+0020, nor any of: <, >, :, ", |, ?, *, &, '.
DescriptorKey
Applies to:
-
All CMS schema descriptor keys, including MacroKey
-
Task key
-
API key
-
HTTP service key
-
Admin Tool key
-
Admin Extension key
Additional restrictions:
-
must not include any of:
<,>,:,",|,?,*,&,' -
must not include the space character
U+0020 -
limited to 64 characters
ApplicationKey
-
must match the regex
\w(?:\.\w+)*+ -
limited to 63 characters
RepositoryId
-
must match the regex
[a-z0-9][a-z0-9._-]* -
limited to 63 characters
Branch
-
must match the regex
[a-z0-9][a-z0-9.-]* -
limited to 63 characters
ProjectName
-
must match the regex
[a-z0-9][a-z0-9_-]* -
limited to 48 characters
Application Descriptor
The application.xml descriptor must be converted to enonic.yaml.
| Use the xp8migrator tool to automate the conversion of XML descriptors to YAML. |
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="urn:enonic:xp:model:1.0">
<description>My app description</description>
</application>
kind: "Application"
description: "My app description"
Admin Tools
Admin Tool descriptors must be converted from XML to YAML.
<?xml version="1.0" encoding="UTF-8"?>
<tool xmlns="urn:enonic:xp:model:1.0">
<display-name>My Admin Tool</display-name>
<description>An admin tool</description>
<allow>
<principal>role:system.authenticated</principal>
</allow>
</tool>
kind: "AdminTool"
title: "My Admin Tool"
description: "An admin tool"
allow:
- "role:system.authenticated"
Widgets / Admin Extensions
Widgets are now part of the Admin Extensions API. The admin/widgets directory has been renamed to admin/extensions, and descriptors must be converted from XML to YAML.
<widget xmlns="urn:enonic:xp:model:1.0">
<display-name>Settings</display-name>
<description>Configure Projects</description>
<interfaces>
<interface>contentstudio.menuitem</interface>
</interfaces>
</widget>
kind: "AdminExtension"
title: "Settings"
description: "Configure Projects"
interfaces:
- "contentstudio.menuitem"
Unlike the widgets API in XP 7, the admin:extension API is not automatically mounted on admin tools. It must be explicitly mounted in the admin tool descriptor in order for extensions to work. |
In admin extensions, use lib-static instead of lib-asset. Both lib-asset and admin extensions are implemented as Universal APIs, so serving assets from an extension via lib-asset will not work without additional configuration. lib-static + is designed specifically for serving static files from admin extensions and does not require any additional configuration. |
admin:extension API in an admin tool
kind: "AdminTool"
title: "My Admin Tool"
allow:
- "role:system.admin"
apis:
- "admin:extension"
Task
Task descriptors must be converted from XML to YAML.
| Task descriptor forms do not support form-fragments. |
<?xml version="1.0" encoding="UTF-8"?>
<task xmlns="urn:enonic:xp:model:1.0">
<description>Background job</description>
<form>
<input type="Long" name="count">
<label>Number of items to process</label>
<default>42</default>
<occurrences minimum="1" maximum="1"/>
</input>
</form>
</task>
kind: "Task"
description: "Background job"
form:
- type: "Long"
name: "count"
label: "Number of items to process"
occurrences:
min: 1
max: 1
default: 42
ID Provider
ID Provider descriptors must be converted from XML to YAML.
<?xml version="1.0" encoding="UTF-8"?>
<id-provider xmlns="urn:enonic:xp:model:1.0">
<mode>MIXED</mode>
<form>
<input name="appClientId" type="TextLine">
<label>Client ID</label>
<occurrences minimum="1" maximum="1"/>
</input>
</form>
</id-provider>
kind: "IdProvider"
mode: "MIXED"
form:
- type: "TextLine"
name: "appClientId"
label: "Client ID"
occurrences:
min: 1
max: 1
Authentication scope
Authentication no longer searches across ID providers for the first matching user. A request resolves against the vhost’s default ID provider, or an ID provider you specify explicitly. If your app relied on cross-provider lookup, configure the relevant vhosts explicitly in your XP deployment.
Site descriptor split
The site.xml descriptor has been split into two separate files in XP 8:
-
site.yaml- contains processors, mappings, and apis -
cms.yaml- contains mixin references (previously x-data) and the site form configuration
Additionally, the site directory has been renamed to cms.
<?xml version="1.0" encoding="UTF-8"?>
<site xmlns="urn:enonic:xp:model:1.0">
<x-data name="myapp:my-mixin"/>
<form>
<input type="TextLine" name="some-name">
<label>Textline</label>
<occurrences minimum="0" maximum="1"/>
</input>
</form>
<processors>
<response-processor name="filter1" order="10"/>
</processors>
<mappings>
<mapping controller="/controllers/preview.js" order="50">
<match>type:'.*:person'</match>
</mapping>
</mappings>
</site>
kind: "Site"
processors:
- name: "filter1"
order: 10
mappings:
- controller: "/controllers/preview.js"
order: 50
match: "type:'.*:person'"
apis:
- "apiname" # belongs to the current application
- "com.enonic.app.myapp:apiname"
kind: "CMS"
mixin:
- name: "myapp:my-mixin"
optional: false
form:
- type: "TextLine"
name: "some-name"
label: "Textline"
occurrences:
min: 0
max: 1
Filter mappings now chain
In XP 7, when several filter mappings matched the same request, only the lowest-order filter ran and next() went straight to rendering; higher-order matches were silently dropped. In XP 8, all matching filters run as a chain in ascending order, and rendering happens after the last next(). A filter that does not call next(), or a controller mapping that wins on order, still short-circuits the chain.
Page descriptor
<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="urn:enonic:xp:model:1.0">
<display-name>Main</display-name>
<description>Contains a single main region</description>
<form/>
<regions>
<region name="main"/>
</regions>
</page>
kind: "Page"
title: "Main"
description: "Contains a single main region"
form: []
regions:
- "main"
Layout descriptor
<?xml version="1.0" encoding="UTF-8"?>
<layout xmlns="urn:enonic:xp:model:1.0">
<display-name>2 columns</display-name>
<description>Provides left and right regions</description>
<form/>
<regions>
<region name="left"/>
<region name="right"/>
</regions>
</layout>
kind: "Layout"
title: "2 columns"
description: "Provides left and right regions"
form: []
regions:
- "left"
- "right"
Part descriptor
<?xml version="1.0" encoding="UTF-8"?>
<part xmlns="urn:enonic:xp:model:1.0">
<display-name>My Part</display-name>
<description>A simple part</description>
<form>
<input name="title" type="TextLine">
<label>Title</label>
<occurrences minimum="1" maximum="1"/>
</input>
</form>
</part>
kind: "Part"
title: "My Part"
description: "A simple part"
form:
- type: "TextLine"
name: "title"
label: "Title"
occurrences:
min: 1
max: 1
Content type descriptor
<?xml version="1.0" encoding="UTF-8"?>
<content-type xmlns="urn:enonic:xp:model:1.0">
<display-name>Article</display-name>
<super-type>base:structured</super-type>
<form>
<input name="title" type="TextLine">
<label>Title</label>
<occurrences minimum="1" maximum="1"/>
</input>
</form>
</content-type>
kind: "ContentType"
superType: "base:structured"
title: "Article"
form:
- type: "TextLine"
name: "title"
label: "Title"
occurrences:
min: 1
max: 1
Mixins renamed to Form Fragments
Mixins (XP 7) have been renamed to Form Fragments in XP 8. The mixins directory should be renamed to form-fragments.
<?xml version="1.0" encoding="UTF-8"?>
<mixin xmlns="urn:enonic:xp:model:1.0">
<display-name>Address</display-name>
<form>
<input type="TextLine" name="street">
<label>Street</label>
<occurrences minimum="0" maximum="1"/>
</input>
</form>
</mixin>
kind: "FormFragment"
title: "Address"
form:
- type: "TextLine"
name: "street"
label: "Street"
occurrences:
min: 0
max: 1
X-data renamed to Mixin
X-data has been renamed to Mixin in XP 8. The x-data directory should be renamed to mixins, and descriptor files updated accordingly.
<?xml version="1.0" encoding="UTF-8"?>
<x-data>
<display-name>My Attachment</display-name>
<form>
<input name="myAttachment" type="AttachmentUploader">
<label>My Attachment</label>
<occurrences minimum="1" maximum="1"/>
</input>
</form>
</x-data>
kind: "Mixin"
title: "My Attachment"
form:
- type: "AttachmentUploader"
name: "myAttachment"
label: "My Attachment"
occurrences:
min: 1
max: 1
Macro descriptors
Macro descriptors must be converted from XML to YAML.
<macro>
<display-name>Current user</display-name>
<description>Shows currently logged user</description>
<form>
<input name="defaultText" type="TextLine">
<label>Default text</label>
</input>
</form>
</macro>
kind: "Macro"
title: "Current user"
description: "Shows currently logged user"
form:
- type: "TextLine"
name: "defaultText"
label: "Default text"
occurrences:
min: 0
max: 1
Styles
Style descriptor must be converted from XML to YAML.
<styles css="styles/styles.css" xmlns="urn:enonic:xp:model:1.0">
<image name="editor-image-skyscraper">
<display-name>Skyscraper (9:21)</display-name>
<aspect-ratio>9:21</aspect-ratio>
</image>
<image name="editor-image-sepia">
<display-name>Old photo</display-name>
<filter>sepia(25)</filter>
</image>
</styles>
kind: "Style"
styles:
- name: "editor-image-skyscraper"
type: "Image"
label: "Skyscraper (9:21)"
aspectRatio: "9:21"
editor:
css: |
<Here you should specify the CSS selectors for this style from the styles/styles.css file.>
- name: "editor-image-sepia"
type: "Image"
label: "Old photo"
filter: "sepia(25)"
editor:
css: |
<Here you should specify the CSS selectors for this style from the styles/styles.css file.>
The styles/styles.css file must be removed.
DateTime and Instant input types
The XP 7 DateTime input type with <timezone>true</timezone> has been split into a separate Instant type in XP 8. DateTime is now used exclusively for local date-time values (without timezone), while Instant represents a point in time.
<input name="publishDate" type="DateTime">
<label>Publish date</label>
<config>
<timezone>true</timezone>
</config>
</input>
- type: "Instant"
name: "publishDate"
label: "Publish date"
<input name="eventDate" type="DateTime">
<label>Event date</label>
</input>
- type: "DateTime"
name: "eventDate"
label: "Event date"
Script Globals
resolve function no longer uses src/main/resources/site as its first root folder. Only src/main/resources is used as the root. If your scripts relied on resolving paths relative to the site folder, update them to use full paths from the resources root.
HTTP functions
HTTP function method names were changed to uppercase to avoid conflicts with JavaScript reserved words and imported library methods.
The lowercase method names (get, post, delete, etc…) are deprecated but still supported for backward compatibility. It is recommended to migrate to the new uppercase names (GET, POST, DELETE, etc…).
The all function remains lowercase and has not been changed to uppercase. |
exports.get = function(req) {
return {
body: 'Hello World',
contentType: 'text/plain'
};
};
exports.post = function(req) {
// Handle POST
};
exports.delete = function(req) {
// Handle DELETE
};
exports.all = function(req) {
// Handle all other methods
};
exports.GET = function(req) {
return {
body: 'Hello World',
contentType: 'text/plain'
};
};
exports.POST = function(req) {
// Handle POST
};
exports.DELETE = function(req) {
// Handle DELETE
};
exports.all = function(req) {
// Handle all other methods
};
System libraries
lib-admin
getAssetsUri method is removed. This method was never in use since XP 7.
getBaseUri method is removed. Use getHomeToolUrl instead as a drop-in replacement.
getLocale method is removed without replacement. This method was causing issues when first user preference did not match any localization files and English was chosen as default.
getLocales method is removed. Use request.locales instead.
getPhrases method is removed. Use lib-i18n getPhrases instead.
getLauncherUrl and getLauncherPath methods are removed. Launcher is a widget now and available via widgets API.
lib-admin no longer depends on lib-portal. You need to explicitly add lib-portal dependency to your project if you are using it.
XP 7
dependencies {
include "com.enonic.xp:lib-admin:${xpVersion}"
}
XP 8
dependencies {
include xplibs.admin
include xplibs.portal
}
lib-node
_inheritsPermissions node property is removed. It can only be used as an argument in create method - to copy permissions from the parent node.
setRootPermissions method is removed. Use applyPermissions instead.
modify method is deprecated. Use update instead.
editor of deprecated modify method can no longer edit node permissions. Use separate call to applyPermissions method instead.
lib-content
Reading the project’s root content (path /) via lib-content (get, getByPath, etc.) now returns null. The root is reserved for the platform; use lib-project for project metadata (get({id}), list()) and create/move content under named paths only.
move and rename methods are merged into a single move method.
setChildOrder and reorderChildren methods are merged into a single sort method.
The hasChildren property has been removed from content.
setPermissions method is removed. Use applyPermissions instead.
applyPermissions method applies permissions on published and draft content simply.
editor of deprecated modify method can no longer edit content permissions. Use separate call to applyPermissions method instead.
modify method is deprecated. Use update instead.
modifyMedia method is deprecated. Use updateMedia instead.
delete method is renamed to deleteContent to avoid conflicts with the JavaScript reserved word. The old delete export is still available for backward compatibility, but it’s recommended to use the new name directly.
XP 7
import {delete as deleteContent} from '/lib/xp/content';
XP 8
import {deleteContent} from '/lib/xp/content';
lib-project
delete method is renamed to deleteProject to avoid conflicts with the JavaScript reserved word. The old delete export is still available for backward compatibility, but it’s recommended to use the new name directly.
XP 7
import {delete as deleteProject} from '/lib/xp/project';
XP 8
import {deleteProject} from '/lib/xp/project';
readAccess: { public } → publicRead: boolean. create({readAccess: {public: true}}) is now create({publicRead: true}). The Project shape returned by get / list / create carries publicRead instead of readAccess.public. The nested-object form has been removed entirely; there is no backward-compat shim.
modifyReadAccess → setPublicRead. Same flat shape:
// XP 7
import {modifyReadAccess} from '/lib/xp/project';
modifyReadAccess({id: 'p', readAccess: {public: true}});
// XP 8
import {setPublicRead} from '/lib/xp/project';
setPublicRead({id: 'p', publicRead: true});
modify now takes an editor function instead of optional setter-style fields. Fields the editor doesn’t touch are left unchanged; setting description, language, or siteConfig to null clears them. displayName cannot be cleared.
// XP 7
import {modify} from '/lib/xp/project';
modify({id: 'p', displayName: 'New name', language: 'no'});
// XP 8
import {modify} from '/lib/xp/project';
modify({
id: 'p',
editor: (project) => {
project.displayName = 'New name';
project.language = 'no';
return project;
}
});
lib-repo
delete method is renamed to deleteRepo to avoid conflicts with the JavaScript reserved word. The old delete export is still available for backward compatibility, but it’s recommended to use the new name directly.
XP 7
import {delete as deleteRepo} from '/lib/xp/repo';
XP 8
import {deleteRepo} from '/lib/xp/repo';
create method settings param is removed. This was a direct exposure of internal implementation details.
lib-scheduler
delete method is renamed to deleteJob to avoid conflicts with the JavaScript reserved word. The old delete export is still available for backward compatibility, but it’s recommended to use the new name directly.
XP 7
import {delete as deleteJob} from '/lib/xp/scheduler';
XP 8
import {deleteJob} from '/lib/xp/scheduler';
lib-cluster
isMaster method is deprecated. Use isLeader instead.
lib-i18n
localize method no longer accepts application parameter - because application load order is not guaranteed, trying to load foreign application localization might fail. Required localize phrases should be copied over the applications instead.
Methods no longer use the undocumented /site/i18n/phrases bundle location by default. Only the standard /i18n/phrases location is used. If your application relied on /site/i18n/phrases, move the files to /i18n/phrases or use your own custom bundle location.
lib-task
submitNamed method is removed. Use submitTask instead.
submit method is removed. Use executeFunction instead.
lib-auth
login method idProvider parameter now defaults to system if not provided, instead of trying all available id providers. It is also not possible to specify multiple values anymore. This change ensures consistent id provider selection during login.
lib-context
run method context.user.idProvider parameter now defaults to system if not provided, instead of trying all available id providers.
lib-export
exportNodes
includeNodeIds and includeVersions parameters are removed.
Exports are now stored as ZIP archives. The output file is named {exportName}.zip in the exports directory, replacing the previous folder-based structure.
| Folder-based exports from XP 7 should be converted to a ZIP archive before they can be imported in XP 8. |
Other libraries
lib-util
Lib util has been deprecated. If your app dependes on any of the methods from lib-util, you should migrate to using the corresponding standard TypeScript or JavaScript code where possible, or inline the relevant logic directly in your code.
Get more details at https://github.com/enonic/lib-util/issues/196
lib-guillotine
Lib guillotine has been deprecated for a long time, and is not supported for XP8. If your app uses lib-guillotine you need to migrate to the standard Guillotine app available on the Enonic Market: https://market.enonic.com/vendors/enonic/guillotine before upgrading to XP8.
HTTP Services
HTTP Services are now deprecated, but continues to work in XP 8. We recommend upgrading to Universal API instead.
Migrating to Universal API means the existing service endpoint URLs will change. Services were previously mounted under //service/<app-name>/<service-name>, while Universal APIs are contextually available directly under //<app-name>:<api-name> in addition to the new /api mount point.
HTTP Services were "unsecure by default" by automatically mounting on admin tools, web-apps, and sites, while Universal APIs must be explicitly mounted via the respective descriptors where you need it.
src/
main/
resources/
services/
coolservice/
coolservice.xml
coolservice.js
myservice/
myservice.js
myservice.xml
src/
main/
resources/
apis/
coolservice/
coolservice.yaml
coolservice.js
myservice/
myservice.js
myservice.yaml
<service>
<allow>
<principal>role:system.admin</principal>
</allow>
</service>
kind: "API"
allow:
- "role:system.admin"
Use portal.apiUrl() instead of portal.serviceUrl() to generate URLs pointing to APIs.
kind: "AdminTool"
title: "My Admin Tool"
allow:
- "role:system.admin"
apis:
- "coolservice"
Assets service
The built-in assets service has been deprecated since XP 7.15. Replace it with one of:
-
lib-asset — best fit for site applications, where it integrates with the site service and content context.
-
lib-static — designed for serving static files from contexts where lib-asset does not fit, such as ID providers and admin extensions.
Widgets go Web Components
Dashboard and Context Panel Widgets were required to wrap their HTML with the <widget/> tag. This requirement is now obsolete as widgets are now embedded as Web Components. However, this also means that each widget resides in its own Shadow DOM, so it no longer has access to styles in the main DOM of Content Studio. If your widget piggybacked on Content Studio’s styles, you’ll have to change the implementation so that the widget is using its own styles.
Assets
It’s no longer allowed to use lib-asset for generating URLs of widget assets, due to security reasons. Use lib-static (and lib-router) instead.
XP 7:
import {render} from '/lib/mustache';
import {assetUrl} from '/lib/enonic/asset';
export function get() {
const view = resolve('./widget.html');
const params = {
jsUri: assetUrl({
path: 'js/extensions/widget.mjs'
}),
stylesUri: assetUrl({
path: 'styles/extensions/widget.css'
})
};
return {
contentType: 'text/html',
body: render(view, params)
};
XP 8:
import {render} from '/lib/mustache';
import {Request, Response} from '@enonic-types/core';
import {mappedRelativePath, requestHandler} from '/lib/enonic/static';
import Router from '/lib/router';
const STATIC_BASE_PATH = `/_static`;
type WidgetRenderer = (staticBaseUrl: string, req?: Request) => Response;
function createWidgetRouter(renderer: WidgetRenderer) {
const router = Router();
router.get('', (req: Request) => renderer(`${req.contextPath}${STATIC_BASE_PATH}`, req));
router.get(`${STATIC_BASE_PATH}/{path:.*}`, (req: Request) => requestHandler(req, {
index: false,
root: '/asset',
relativePath: mappedRelativePath(STATIC_BASE_PATH),
}));
return router;
}
const router = createWidgetRouter((staticBaseUrl: string, req: Request) => {
const view = resolve('./widget.html');
const params = {
jsUri: `${staticBaseUrl}/js/widgets/widget.mjs`,
stylesUri: `${staticBaseUrl}/styles/widgets/widget.css`
};
return {
contentType: 'text/html',
body: render(view, params)
};
});
export const all = (req: Request) => router.dispatch(req);
Javax to Jakarta
Most of the Java EE APIs have been migrated to Jakarta EE APIs. For instance javax.servlet is replaced with jakarta.servlet.
Jetty was updated to version 12.x and now uses Jakarta EE APIs.
FasterXML Jackson JSON API
Java FasterXML Jackson JSON API is no longer a transitive dependency of XP Core API.
If you need to serialize or deserialize JSON in Java, you need to add the dependency to your project, preferably using a library different from XP internal one (FasterXML Jackson) to avoid conflicts.
We recommend using https://github.com/FasterXML/jackson-jr
FasterXML Jackson Annotations com.fasterxml.jackson.core:jackson-annotations transitive dependency is still available in JAX-RS API.
JAX-RS API
Java JAX-RS API is deprecated. It was not documented and used internally to implement legacy REST APIs.
If you have used JAX-RS API in your application, you need to migrate to the new XP 8 Universal API.
To know if you are using JAX-RS API, check for com.enonic.xp:jaxrs-api dependency in the project’s Gradle file.
Dropwizard Metrics
Java Dropwizard Metrics is no longer available in XP Core API. Migrate to com.enonic.xp.metrics Core API.
Java APIs
There are many changes in Java APIs, multiple classes were moved or renamed, as well as their methods. The intention was to keep the libraries as compatible as possible, while improving Java API design and removing unnecessary or confusing methods and classes.
The most noticeable changes are:
-
Java methods and classes marked as
@Deprecatedwere removed. -
Many classes and methods that were only used for internal implementation details and not intended for public use were removed.
-
Methods that had names that could cause confusion with the libraries were renamed, for instance
ContentService.deleteWithoutFetchrenamedContentService.delete. -
Classes that have direct replacement in Java 25 standard library were removed.
-
ContentService.updatemethod was split into 3 separate methods:ContentService.update,ContentService.updateMediaandContentService.updatePermissions. -
Java Portal View Functions are no longer part of XP Core API. They were incorporated into lib-thymeleaf and lib-xslt libraries.
-
com.enonic.xp.page.DescriptorKeymoved tocom.enonic.xp.descriptor.DescriptorKey. -
Concrete collection types like
LinkedList,HashMap, etc. were replaced with corresponding interfaces likeList,Map, etc. -
Javax Mail is no longer a transitive dependency of XP Core API.
-
OSGi classes are no longer part of API. OSGi is still used internally in XP.