Introduction:
A Client Extension is a way to add new features or customize existing ones in our Liferay site without modifying the core code. Think of it like adding apps or plugins to our smartphone to give it new capabilities.Client Extensions are a powerful way to customize and extend our Liferay site safely and flexibly. By keeping our customizations separate from the core code, We can easily manage updates and maintain our site.Prerequisites:
- Liferay
Key Feature Of Client Extension:
- Modular Architecture:
Client Extensions are designed as independent modules that we can add to our Liferay site.
- Separation of Concerns: Each extension is isolated, so it only focuses on a specific feature or functionality.
- Easy Management: We can update, test, or remove extensions without affecting the rest of the site.
- Scalability: We can build and deploy new features as separate modules, making our site more flexible and easier to scale.
Example: Imagine we have an online store. We can add a Client extension that handles a new payment method without touching the existing payment system.
- Decoupled from Core:
Client Extensions operate independently of Liferay’s core codebase.
- Safe Updates: We can update Liferay to the latest version without worrying about breaking our custom functionalities.
- Stable Customizations: Our customizations remain intact and functional even when the core platform changes.
- Less Risk: Reduced risk of conflicts or errors since extensions don’t modify core files.
Example: We add a custom search widget to our site as a Client Extension. When we upgrade Liferay, the core search functionality updates, but our custom widget remains unaffected and continues to work.
- Security
Client Extensions follow best practices for security and run in isolated contexts.
- Protection: Keeps the core system secure and reduces the risk of vulnerabilities.
- Isolated Environment: Even if one extension has an issue, it doesn’t compromise the entire system.
- Controlled Access: Extensions can be granted specific permissions, limiting their access to only what they need.
Example: A custom extension that handles sensitive user data can be designed to have limited access, ensuring that other parts of the system remain secure.
Types of Client Extensions:
- Frontend Client Extensions
Frontend client extensions are custom components or user interface elements that we can integrate into our Liferay.
- Batch Client Extensions
Batch client extensions handle large-scale data processing tasks that can be scheduled and run periodically within the Liferay platform.
Batch client extensions provide data entities to our Liferay instance, such as object definitions or workflow definitions. Create batch client extensions by exporting data via the batch engine framework.
- Configuration Client Extensions
Configuration client extensions provide a way to manage and customize various settings and configurations within the Liferay platform.
- Microservice Client Extensions
Microservice client extensions are independent services that perform specific tasks and communicate with the Liferay platform through APIs
Steps to create a custom client extension:
- Create a client extension directory.
- Create client-extension.yaml file.
- Create package.json and webpack.config.js.
- Enter the code in the assets folder.
- Deploy client extension.
Now in a client-extensions folder create a liferay-custom-element (Project Folder Name).
Then create an assets folder in the project directory.
STEP 2: Create a ‘client-extension.yaml’ file
A client extension project contains a single client-extension.yaml file that defines one or more client extensions. If a client extension project includes a package.json file with a defined build script, this script runs automatically when we build the project. This ensures that any necessary build steps occur before the files are copied, allowing us to specify the output location in the ‘assemble’ block for proper organization. The assemble block is a YAML array that can include multiple instructions for files to include. Each set of instructions follows this pattern:
– from: [some folder from project]
include: [single file or glob match]
into: [output location in archive]
Here is an example:
assemble:
- from: build/static
into: static
liferay-custom-element:
friendlyURLMapping: vanilla-counter
htmlElementName: vanilla-counter
instanceable: false
name: Liferay Custom Element
portletCategoryName: category.client-extensions
type: customElement
urls:
- index.*.js
useESM: false
- from: build/static: Source directory containing the built static files.
- into: static: Target directory within the deployment package
- friendlyURLMapping: vanilla-counter: Friendly URL mapping for accessing the custom element.
- htmlElementName: vanilla-counter: HTML tag name for the custom element.
- instanceable: false: Indicates if multiple instances can be added to the same page.
- name: Liferay Custom Element: Display the name of the custom element.
- portletCategoryName: category.client-extensions: Category in Liferay Control Panel.
- type: customElement: Type of client extension.
- URLs: – index.*.js: JavaScript files implementing the custom element.
- useESM: false: Whether to use ECMAScript modules.
- Create a package. json file in a liferay-custom-element folder.
package.json is the manifest file in a Node.js project. It holds metadata relevant to the project and is used to manage the project’s dependencies, scripts, and other configurations.
{
"devDependencies": {
"css-loader": "6.10.0",
"style-loader": "3.3.4",
"webpack": "5.90.1",
"webpack-cli": "5.1.4"
},
"name": "@liferay/liferay-custom-element",
"private": true,
"scripts": {
"build": "webpack"
},
"version": "0.0.0"
}
Let’s break the package.json
devDependencies:
Lists the development dependencies required for the project. These tools and libraries are used during the development and build process but are not included in the production bundle.
“css-loader”: Loads CSS files and resolves any dependencies within them (version 6.10.0).
“style-loader”: Injects CSS into the DOM via <style> tags (version 3.3.4).
“webpack”: module bundler that compiles JavaScript modules and other assets (version 5.90.1).
“webpack-cli”: Command-line interface for Webpack, providing commands for running and configuring Webpack (version 5.1.4).
- Now create a webpack.config.js file inside the liferay-custom-element.
This webpack.config.js file configures Webpack for a Liferay client extension.
It sets different settings based on the environment (development or production), enabling source maps in development for easier debugging.
The entry point is ./assets/index.js, and the build mode switches between development and production based on the NODE_ENV variable.
It uses loaders to handle CSS files and outputs bundled files to the build/static directory with hashed filenames for cache busting.
Additionally, it optimizes the build by minimizing files in production and limits the number of output chunks to one using the LimitChunkCountPlugin.
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const DEVELOPMENT = process.env.NODE_ENV === 'development';
module.exports = {
devtool: DEVELOPMENT ? 'source-map' : false,
entry: {
index: './assets/index.js',
},
mode: DEVELOPMENT ? 'development' : 'production',
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
optimization: {
minimize: !DEVELOPMENT,
},
output: {
clean: true,
filename: '[name].[contenthash].js',
path: path.resolve('build', 'static'),
},
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
};
STEP 4: Enter the code in the assets folder.
Here we are taking .js file to print the custom message in a liferay portlet using HTML element.
index.js
import './style.css';
(function () {
class VanillaCounter extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const message = document.createElement('div');
message.textContent = 'Hello Liferay Users :)';
shadow.appendChild(message);
}
}
customElements.define('vanilla-counter', VanillaCounter);
})();
Note: Make sure to define the class name that we declared in the client extension.
We can also add a style class in the asset folder and import that into our .js file.
style.css
vanilla-counter {
background-color: #f3e5ab;
display: block;
padding: 10px;
}
Now our folder structure something look like this
client-extensions/
└── liferay-custom-element/
├── assets/
│ ├── main.js
│ └── styles.css
├── client-extension.yaml
├── package.json
└── webpack.config.js
STEP 5: Deploy client extension.
Open the client-extensions folder in the terminal.
Now, enter this Gradle deploy command to deploy the client extension.
../gradlew deploy
Now we can find our client extension in the
Control Panel -> Applications -> Custom Apps -> Client-Extensions
Now to test the custom-created client-extension element.
Drop the Liferay custom-created module on the page. And it will look like this,
Conclusion:
Client Extensions provide a robust, secure, and flexible way to extend and customize your Liferay site without altering the core code. By utilizing a modular architecture, these extensions ensure that updates and customizations remain stable and manageable. Whether adding new features, integrating third-party services, or handling large-scale data processing, Client Extensions enable you to enhance your Liferay site efficiently. Follow the steps to create and deploy custom extensions, and explore the various types to best suit your needs. Embrace the power of Client Extensions to keep your site scalable, secure, and tailored to your specific requirements.