We are happy to announce the release of Liferay Portal 7.3 CE GA2!

{ "actions": { "approveRegistration": { "method": "PUT", "href": "http://localhost:8080/o/c/courseregistrations/ by-external-reference-code/6f704673-30af-a1f3-5ea2-0e6f95b6f7ed/ object-actions/approveRegistration" }, "denyRegistration": { "method": "PUT", "href": "http://localhost:8080/o/c/courseregistrations/ by-external-reference-code/6f704673-30af-a1f3-5ea2-0e6f95b6f7ed/ object-actions/denyRegistration" }, "permissions": { "method": "GET", "href": "http://localhost:8080/o/c/courseregistrations/34905/permissions" }, "get": { "method": "GET", "href": "http://localhost:8080/o/c/courseregistrations/34905" }, "replace": { "method": "PUT", "href": "http://localhost:8080/o/c/courseregistrations/34905" }, "update": { "method": "PATCH", "href": "http://localhost:8080/o/c/courseregistrations/34905" }, "delete": { "method": "DELETE", "href": "http://localhost:8080/o/c/courseregistrations/34905" } }, "creator": { "additionalName": "", "contentType": "UserAccount", "familyName": "Test", "givenName": "Test", "id": 20122, "name": "Test Test" }, "dateCreated": "2024-03-16T04:32:35Z", "dateModified": "2024-04-14T23:44:58Z", "externalReferenceCode": "6f704673-30af-a1f3-5ea2-0e6f95b6f7ed", "id": 34905, "keywords": [], "scopeKey": "Masterclass", "status": { "code": 0, "label": "approved", "label_i18n": "Approved" }, "taxonomyCategoryBriefs": [], "registrationStatus": { "key": "denied", "name": "Denied" }, "notes": "Nothing to say here.", "r_courseAttendee_userERC": "aa3313fd-42a0-267b-8366-8c38ef011432", "r_courseAttendee_userId": 20122, "course": { "key": "projectManagerCertificateJuly2024", "name": "Project Manager Certificate - July, 2024" }, "courseAttendeeERC": "aa3313fd-42a0-267b-8366-8c38ef011432" }
I would then display the Approve and Deny buttons and, depending
upon which one you clicked, I'd issue a PUT call to the
relevant action URL.
Now when I did this, I would always get an error back, a
409 Conflict as the result. But this made no sense to me
because nothing that I was doing should have triggered any kind of conflict.
Hence the need to debug the situation. I needed to find out why
the PUT actions were failing.
Debugging The Call
So actually I set up breakpoints on all of the classes I discussed earlier.
In ObjectDeployerImpl I set a breakpoint on
deploy() so I could see what was being registered from my
Object Definition.
In ObjectEntryOpenAPIContributor, I set a
breakpoint on contribute() so I could see what was
being added to the schema and would appear on the
/o/api page for my application.
And, in ObjectEntryResourceImpl I set a breakpoint
on
putScopeScopeKeyByExternalReferenceCodeObjectActionObjectActionName()
because this was the method that should be invoked for either the
approve or deny action URLs.
On startup, the first breakpoint I hit was the deploy() method. I could see that my JaxRS application was being properly registered and everything seemed to be wired correctly, so no problem there.
I then tested my React code. In my api() method
(which you'll get to see when I publish the blog), I added a
console.log() statement to show what I was invoking, and
I could see that it was using the URL
http://localhost:8080/o/c/courseregistrations/by-external-reference-code/6f704673-30af-a1f3-5ea2-0e6f95b6f7ed/object-actions/approveRegistration,
but it was still getting the 409 failure response.
At this point I'm thinking maybe there is something wrong in how
the approveRegistration is implemented, so I open a tab
to /o/api to try it there manually. As soon as I did
this, I hit the next breakpoint in the contribute()
method, so I could verify that yes, in fact my two standalone actions
were being added correctly to the OpenAPI schema.
At this point I had verified the JaxRS application was registered correctly, and also the right things were being added to the OpenAPI schema, so from a registration perspective everything was looking fine.
So the next step was to find one of the actions and try to run
it on /o/api, and that's what I did:

I plugged in my key values and clicked Execute,
and wouldn't you know it, it worked just fine and had a return code of 204!
Oh, and the debugger stopped in the
putScopeScopeKeyByExternalReferenceCodeObjectActionObjectActionName()
call so clearly it was getting to the right method to handle the
action URL...
When I tried invoking the action from React, I only got the
409 and never hit the breakpoint, so something before the
resource impl was failing and returning the 409. I used
the stack trace in the debugger to set breakpoints at every method in
the stack hoping that I'd be able to find one that triggered the
failure, but that was kind of fruitless. First, the stack was in the
bowels of CXF (the implementation Liferay uses for the JaxRS
applications), and second I couldn't trace exactly where in CXF it was
kicking out and returning the 409.
Now I'm starting to go a little crazy...
I'm wondering why the /o/api call would work but it
would fail for my React app.
I eliminated perms as an issue because I'm logged in as the Test
admin account and besides, the 409 didn't have anything
to do with a permissions issue. Plus I checked the headers that I was
sending with the React call and they matched exactly the headers used
in the curl example, so the call should have worked.
The Clue
So it was the checking of the headers between my React call and the curl example from /o/api that gave me the clue to the mystery...
My header values matched exactly, but I noticed that the URLs didn't match up...
The curl URL example is
http://localhost:8080/o/c/courseregistrations/scopes/32815/by-external-reference-code/6f704673-30af-a1f3-5ea2-0e6f95b6f7ed/object-actions/approveRegistration,
but the URL in the approveRegistration action href is
http://localhost:8080/o/c/courseregistrations/by-external-reference-code/6f704673-30af-a1f3-5ea2-0e6f95b6f7ed/object-actions/approveRegistration.
It's missing a scopes portion in the path!
The Bug
So yes, I had actually been beating my head against the wall on this for days. Not straight through, I had code to finish and, even though I knew the failure was still there, I could think about it while wrapping the other code up.
But I had found a bug. The action URLs are supposed to be HATEOAS so I expected them to just work, but for site-scoped objects, the action URLs are wrong because they don't include the necessary scope portion of the path.
I opened a bug on this issue: https://liferay.atlassian.net/browse/LPD-23177
I also changed my code so it wouldn't use the action URLs, instead I'd manually construct the proper, complete URL to invoke the action. This was all it took to have my React code successfully invoke the standalone actions.
Conclusion
Did I need to debug the Objects headless APIs to find the problem here that turned out to be a product bug?
Maybe not, I mean if my first thought had been to check what URL
was being used and whether it matched what was used in
/o/api, maybe I could have identified the bug without all
of the debugging.
Since that wasn't my first thought, going through the steps to debug the process was really my only option. And, had the failure been something different, debugging may have been the only way to find the issue and either resolve it or report it.
Anyways, I thought this was a useful exercise and that sharing it with you might provide some insight if you ever find yourself needing to debug the bowels of the Objects headless APIs.
The moral of this story, if there is one - always check your URL to ensure that they are really valid!
Liferay Portal 7.3 CE GA2 is our second release using our new Rolling Release cycle. For more information on our new release cycle please find the announcement here.Docker
Official images can be found on Docker Hub and can be used for deployments on any system that is running Docker. For more information on configuration options for the image see the overview page. To get started with docker run the following:
docker run -it -p 8080:8080 liferay/portal:7.3.1-ga2
Download
You can find the 7.3 release on the download page. If you need additional files (for example, the source code, or dependency libraries), visit the release page.
Improved Dependency Management
Beginning with Liferay Portal 7.3 CE GA2 it’s now easier than ever before to manage dependencies within projects. Instead of declaring a dependency for each api, all dependencies can now be defined with a single declaration. When using an IDE such as Eclipse or IntelliJ all apis are immediately available in autocomplete for immediate use.
To get started, update the projects build.gradle file from:

To contain the following dependency:
compileOnly group: "com.liferay.portal", name: "release.portal.api", version: "7.3.1-ga2-3"
For example the build.grade file from above we become:
New Features
Liferay Portal CE 7.3 GA2 includes several new features mainly in Experience Management and some in Platform improvements.
Experience Management
We are continuing to empower designers and marketers to create user-friendly, beautiful and engaging experiences, without code freeing developers to focus on advanced interactive experiences through modern front-end tooling and APIs. Here are some of the new features you can find in this release.
Nesting layouts and more flexible layouts - The non technical users can now nest a layout into another one, and thus creating sophisticated page layouts using drag and drop. In GA1 the user could create horizontal layouts with multiple columns. But now it is possible to combine, split, nest, duplicate the layouts and thus unleashing the users’ creativity.
More info: LPS-102328

Workflows for Content Pages - Users can now define a workflow process for approval of creation and changes for content pages. Just like for Web Content articles and other content types, administrators can specify a different workflow process at different levels: virtual instance level, site level and all the other entities (see workflow documentation). Once the workflow is enabled for the Content Pages, every new Content Page will go through the approval process before publication. Thus, the content reviewer can preview a page with pending status and approve, reject or request additional changes.
More info: LPS-103815
Support configuring permissions for Widgets in fragment-based pages - This feature allows users to configure permissions for widgets added to fragment-based pages (including: content pages, master pages, page templates and display pages). This will allow users to control which visitors can view the widget, so that some visitors may see it while for others it's fully hidden. It may also be used to configure other permissions such as allowing certain logged in visitors to configure the widget.
We’ve taken page templates and master pages into account as well. If you create a page based on a page template, all the widget permissions are copied to the page. The permissions in widgets from the master pages are set in the master page, so you cannot change them in the page itself.
More info: LPS-102583
One of our goals with the Rolling Releases is to respond faster to feedback so we want to mention some of the feature requested that we have implemented in this release. Feature requests are new features our community members and customers would like to see added to our products, and also an opportunity to have your voice heard by using the voting system to support ideas that you would like to see implemented. The following feature requests were implemented in GA2:
Developers and Sysadmins
- Allowing to enter longer values (255) for category names
- feature request LPS-104562 resolved by LPS-107730
- Adding remote public Layout fetchLayout method
- feature request LPS-101824 resolved by LPS-108427
- Ability to have autocomplete in the fragment editor for variables, taglibs and resources.
-
feature request LPS-95904 resolved by LPS-108566
-
Page Designers / Administrators:
- Visual identification of empty portlet-columns in Widget pages
- More flexible configuration of the number of items display with RSS Publisher
- feature request LPS-107217 resolved by LPS-107942
- Ability to hide/show portlet controls on the Widget pages
- feature request LPS-106498 resolved by LPS-108216
- Ability to define navigation menus in Global to share them across all sites
- feature request LPS-106861 resolved by LPS-107833
- Ability to store custom information relevant to my sites on the items of a navigation menu
- feature request LPS-105289 resolved by LPS-108262
Introducing Asset Libraries - with this new feature, it is possible to create dedicated libraries for content that make sense together and have better-isolated control on them. Asset libraries make it easier to reuse resources across different sites, connecting them only to the sites where you need to provide access.
Asset Libraries support the storage of documents and web content allowing a marketing team to organize collateral used in a campaign in an asset library and connecting it to the sites where the campaign will be run. While creating a page, or writing a blog post, content authors can access the connected asset libraries and use images or content uploaded.
This feature is experimental, which means that before the launch of the Liferay DXP 7.3 GA1 some features may change, from the way to interact with the libraries, to the types of content that can be stored and reused. Additionally, you may find some differences in behavior that may affect existing applications. If you don’t want the new behavior, or are not interested in Asset Libraries, you can disable the feature in Control Panel -> Configuration -> System settings -> Asset Libraries. More info on (LPS-102412, LPS-102471, LPS-102496)
Platform Improvements
Support for batch operations in the headless APIs: Now the headless APIs support batch operations allowing to perform requests over multiple elements in a single request asynchronously. (LPS-98648)
Retrieve all translations of localized content in a single request in the headless APIs thanks to the new custom header “X-Accept-All-Languages” (set to true). For the scenarios where it is important to retrieve all the translations of content in order to maximize the number of requests, we have introduced a new set of properties that include this information. (LPS-106757)
Documentation
All documentation for Liferay Portal/DXP 7.2 and above can now be found on our new documentation site called learn.liferay.com. For more information on our new documentation initiative see the official announcement here.
Compatibility Matrix
Liferay's general policy is to test Liferay Portal CE against newer major releases of operating systems, open source app servers, browsers, and open source databases (we regularly update the bundled upstream libraries to fix bugs or take advantage of new features in the open source we depend on).
Nothing has been removed from the matrix, however a couple things have changed. Liferay Portal 7.3 CE was tested extensively for use with the following Application/Database Servers:
Application Server
-
Tomcat 9.0
-
Wildfly 16.0 (Previously 11.0)
Database
-
HSQLDB 2 (only for demonstration, development, and testing)
-
MySQL 5.7, 8.0
-
MariaDB 10.2
-
PostgreSQL 11.2 (Previously 10)
JDK
-
IBM J9 JDK 8
-
Oracle JDK 8
-
Oracle JDK 11
-
All Java Technical Compatibility Kit (TCK) compliant builds of Java 11 and Java 8
Source Code
Liferay Portal CE's source is available as a zip archive on the release page, or on its home on GitHub. If you're interested in contributing, take a look at our contribution page.
Bug Reporting
If you believe you have encountered a bug in the new release you can report your issue by following the bug reporting instructions found on the Liferay Portal CE site.
Getting Support
Support is provided by our awesome community. Please visit helping a developer page for more details on how you can receive support.
Fixes and Known Issues
Kudos
A special thanks goes out to our Engineering, QA and Release teams who helped make this release a reality. Also thank you to everyone in our community who spent many hours reporting bugs and providing feedback on new features that made it into this release.


