In order for these fragments to function correctly, particularly for guest/anonymous users or for those requiring advanced FreeMarker features, the following Liferay configurations are necessary.
Many fragments (e.g., Content Map, Hero Assets, Public Comments) use
Headless APIs to fetch data. By default, these APIs are restricted for Guest
users.
Navigate to Control Panel -> Security -> Service Access Policy. Create or
update a policy (e.g., GUEST_READ) with the following signatures based on the
fragments you use:
- Content Map:
com.liferay.headless.delivery.internal.resource.v1_0.ContentSetElementResourceImpl#getContentSetContentSetElementsPagecom.liferay.headless.delivery.internal.resource.v1_0.ContentSetElementResourceImpl#getSiteContentSetByKeyContentSetElementsPagecom.liferay.headless.admin.taxonomy.internal.resource.v1_0.BaseTaxonomyCategoryResourceImpl#getTaxonomyVocabularyTaxonomyCategoriesPagecom.liferay.headless.delivery.internal.resource.v1_0.ContentTemplateResourceImpl#getSiteContentTemplate
- Hero Assets / Documents:
com.liferay.headless.delivery.internal.resource.v1_0.DocumentResourceImpl#getDocument
- Object Metadata Discovery (Meta-Object fragments):
com.liferay.object.admin.rest.internal.resource.v1_0.ObjectDefinitionResourceImpl#getObjectDefinitionByExternalReferenceCode- Note: This is required for Meta-Object Table, Form, and Record View fragments to discover fields at runtime. Without it, these fragments will trigger a 403 Forbidden error for Guest users.
- Object Integrations:
- Ensure the specific Object's REST API is enabled for Guest permissions in the Object's "Permissions" tab.
Some advanced fragments require access to Java utility classes (like
PortalUtil or ServiceContextThreadLocal). By default, Liferay restricts
these for security.
- Navigate to Control Panel -> System Settings -> Template Engines (under Platform) -> FreeMarker Engine.
- Locate the Restricted Variables list.
- Remove
staticUtilfrom the list. - Save the changes.
Note: This is specifically required for legacy versions of the Date Display
and some complex Object integrations.
Fragments in the Pulse and OAuth2 collections depend on JavaScript Client
Extensions to provide helper utilities.
- Pulse Integration: Requires the
pulse-helperJS client extension to be present on the page or included in the site's global JS. - OAuth2 / User Accounts: Requires a User Agent Application to be configured in Liferay (under OAuth2 Administration) and its "Reference Code" provided in the fragment configuration.
To showcase data-driven fragments like the Object-Linked Chart,
Activity Heatmap, and Interactive Event Timeline, multiple sample Object
definitions and datasets are provided.
Note: Liferay 2025.Q4.10 or later is required for the showcase data to work
correctly, as it utilizes site-scoping (siteExternalReferenceCode: L_GUEST).
- Direct Deployment: Build the showcase ZIPs using
./create-fragment-zips.shand deploy them using./deploy-fragment-zips.sh [TARGET_PATH] --showcase. - Organization: All showcase datasets are located in
/other-resources/showcase-data/, including:- Activity Log: For the
Activity Heatmap. - Product Showcase: For the
Dynamic Object Gallery. - Company Milestones: For the
Interactive Event Timeline. - Water Readings: For the
Meter ReadingandObject-Linked Chart.
- Activity Log: For the
To ensure the showcase datasets are correctly imported by the Liferay Batch Engine and compatible with the automated deployment scripts, please observe the following:
- Showcase ERC Convention (Project-Specific): Within this repository, the
External Reference Code (ERC) for sample Objects follows a naming
convention: uppercase with underscores (e.g.,
COMPANY_MILESTONE). This is for internal consistency and is not a Liferay platform requirement. taskItemDelegateName(Liferay CX Mandatory Rule): For the Liferay Batch Engine to process Object entry imports, thetaskItemDelegateNameproperty must be explicitly defined within theconfigurationobject of the JSON batch file. It must be set to the Object's name with aC_prefix (e.g.,"taskItemDelegateName": "C_CompanyMilestone").
The ./create-fragment-zips.sh script handles the packaging of fragments into
Liferay-compatible ZIP files. It includes several automated optimizations for
production readiness.
By default, the build script performs the following transformations:
- JS Obfuscation: Uses
terserto minify and mangle JavaScript files. - CSS Minification: Uses
clean-css-clito compress CSS. - JSON Minification: Uses
jqto strip whitespace from configuration and fragment metadata.
--debug: Skips all minimization and obfuscation steps. Use this during development to keep the source readable in the browser's developer tools../create-fragment-zips.sh --debug
If a specific fragment contains FreeMarker syntax within its .js or .css
files, automated minimization might corrupt the logic. To prevent this, place an
empty file named .no-transform in the fragment's root directory. The build
script will detect this file and skip all transformations for that fragment.
This project enforces a strict naming convention for primary template files to ensure correct Liferay deployment and optimal linting performance.
.ftl(FreeMarker): Must be used if the template contains ANY FreeMarker logic ([#if],[#list]), taglibs ([@clay]), or Liferay global variables (e.g.,${siteSpritemap})..html(HTML): Used only for strictly static HTML or fragments utilizing only basicdata-lfr-editablefields with no logic.
The htmlPath property within fragment.json must always match the chosen
extension:
{
"name": "My Fragment",
"htmlPath": "index.ftl",
...
}Modern versions of these fragments utilize the Liferay Fragment layoutMode API
('view', 'edit', 'preview'). If you are using an older version of Liferay
that does not support this API, you may need to "backport" the logic.
- Edit Mode Check: Replace
if (layoutMode === 'edit')withif (document.body.classList.contains('has-edit-mode-menu')). - Live/Runtime Check: Replace
if (layoutMode === 'view')withif (typeof fragmentNamespace !== 'undefined'). - Preview Detection: In legacy environments,
fragmentNamespacewas typically undefined during "Preview" but defined in "Edit" and "View" modes.