Why migrating Liferay projects to a newer version is not easy v2

By Rafał Pydyniak | 2021-01-10

Hello! In first part Why migrating Liferay projects to a newer version is not easy I introduced you to the topic of Liferay migrations and I showed you a few issues you might encounter during database migration process. In this part I'd like to show you few issues you might have during migration of the code of your custom modules for example portlets.

How to migrate the code

For starters - how do you even start the code migration process? Well first of all if you decided to migrate your project to higher version you should start with the official documentation. There are few options how do you even start migration - for example you will start differently if you're migrating from 6.2 (or earlier) and differently if you're already on version 7.x. Of course the general rule is if you have higher version already then the migration will go easier for you. Also if you use more Liferay's API then it will be harder. Anyway you should start with documentation because it should give you some idea of how it all goes. Obviously after going through that documentation you might feell a little overwhelmed because there're a lot of informations but don't be too stressed about it, it won't be that bad!

Issues that we can encounter

Lets go back to the main thing I wanted to share with you in this post. What can go wrong while migrating the code? Well the biggest issue is that not everything is documented well enough. You can find so called "Breaking changes" pages for each version but sadly not all informations are there and because of that you might find yourself in a place that something stopped working and you can't find a clue why. What can we do? Well I think the only thing you can do is to just go through the migration process and fix unexpected and undocumentted issues one by one. Either by searching them on Liferay forum, or in Liferay blog or just by debugging the Liferay code (I hope it won't be neccessary for you but you never know)

The good news is that I want to show you four issues you might encounter so you can learn something on my mistakes instead on your owns :) These are not all issues I've encountered but I hope that will help you anyway. I also would like to remind you that I wrote about some other issues in previous post.

Note! I found these issues some time ago. It might happen that when you read that (or even at the publication date) some of them are already in breaking changes already - I haven't checked that. This could've happened though as even I wrote some posts in Liferay forum so perhaps someone added that at some point.

Web Contents fetching images API change

I'd like to start by showing you how the XML for image looked like in Web Content in version 7.0:

<dynamic-content language-id="en_US" alt="" name="XXX.png" title="XXX.png" type="document" [fileEntryId="12345"><![CDATA[/documents/111/222/XXX.png/QQQQQQ-WWWW-EEEE-CCCC-DDDDDDD?t=1588696601862]]></dynamic-content>

And then the XML for same (or almost the same) Web Content in version 7.2

<dynamic-content [language-id="en_US"><![CDATA[{"groupId":"126435","alt":"","name":"XXX.png","title":"XXX.png","type":"journal","uuid":"QQQQ-WWW-EEE-CCC-ec48068bbf39","fileEntryId":"716022","resourcePrimKey":"716460"}]]></dynamic-content>

Do you see any difference? Well you could notice that there is no path of the file now. Instead of that we have JSON with few informations about our image. What's the big deal you might ask? Well the thing is that common practise in 7.0 was to fetch the path of the image in following way:

final com.liferay.portal.kernel.xml.Document document = SAXReaderUtil.read(article.getContentByLocale(locale.toString()));
final String imageUrl = document.valueOf("//dynamic-element[@name='Image']/dynamic-content");

If you used the same in 7.2 you'd end up with fetching JSON instead of the image path so if you use that as your img source it will look like that:

Screen showing the JSON inside img tag
Screen showing the JSON inside img tag

In order not to let it happen you need to adjust your code for example in following way:

final com.liferay.portal.kernel.xml.Document document = SAXReaderUtil.read(article.getContentByLocale(locale.toString()));  
final String imageJsonString = document.valueOf("//dynamic-element[@name='Image']/dynamic-content");  
JSONObject imageJsonObject = JSONFactoryUtil.createJSONObject(imageJsonString);  
FileEntry fileEntry = PortletFileRepositoryUtil.getPortletFileEntry(imageJsonObject.getLong("fileEntryId"));  
String imageUrl = PortletFileRepositoryUtil.getDownloadPortletFileEntryURL(serviceContext.getThemeDisplay(),  
  fileEntry, StringPool.BLANK);

Please note you could have similiar situation in your FTL template but it should be easy for you to translate above Java code to FTL one.

It's worth to read that blog's entry in Liferay forum where you can read that this change was made because the Liferay developers thought not one uses this path. Unfortunately there's no better alternative (or wasn't back then). The Liferay developers didn't provide neat API you could use to fetch the Web Content's image so the above code was used - it was the solution you could found on Liferay's forum for example.

Anyway just keep that thing in mind as the images will just stop working - no error or anything in your IDE.

The fields in Search API change

In Liferay 7.2 (or 7.1) there was also a change in the naming of fields in Search API. The change was that in 7.0 the field name in


Looked like this:


In 7.2 on the other hand it looks like that:


See the difference? Well the _en_US part is gone. Of course it makes sense - we don't translate the PublicationDate field so why would we want to add the language key. Unfortunately you might've had a code that used the Search API and you used the name of the fields directly. You can look for such code by looking for "IndexSearchHelper", "Document" or "SearchContext" keywords and the example of such code could look like that:

final BooleanQuery publicationDateQuery = new BooleanQueryImpl();  
publicationDateQuery.addRangeTerm(String.format(PUBLICATION_DATE_TERM_FIELD_PATTERN, newsDescriptionStructureId),  
  sdf.format(from), sdf.format(to));  
indexSearcherHelper.search(searchContext, publicationDateQuery).getDocs()

Where our pattern is

private static final String PUBLICATION_DATE_TERM_FIELD_PATTERN = "ddm__keyword__%s__PublicationDate_%s";

So we can search by the field name (see addRangeTerm) and pass the field name with the language key part. The code will still compile but the results will be incorrect because we will search based on non existing field name as it was changed.

I guess this issue might be quite rare as I believe Search API is not that common but if you use that then you might have hard time finding why it stopped working.

View permission in Web Contents

Another change I couldn't find in breaking changes.

Image showing the permissions for Web Content
Image showing the permissions for Web Content

When you have a Web Content with permissions like on the screen above our Liferay portal will behave differently based on version. This is is connected to one of the Liferay settings available under (in 7.2):

Control panel → configuration → system settings → web content

The config is called "Article view permissions check enabled" and with it you can configure either the portal should verify the "View" permission for Web Contents or not. So:

  • If the the option is off - every one can see our Web Content even though the permissions (look on the screen above) suggest that only Owner should have that access.
  • If the option is on - there is a verification and for example Guest doesn't have access to our Web Content

The thing is the default value changed between versions. In 7.0 it was disabled by default and in 7.2 it's enabled by default.

So it's a suprise waiting for us - before everyone could see our Web Contents and now no one can. Of course if we defined the permissions then we will be fine but what if we did defined permissions for all web contents but one or two? Well that might lead to some page not working properly and since it's only one page for example then it can be easily overlooked.

Changed way of fetching current URL in FTL

In 7.0.x we could fetch current URL in FTL files using following code:

url = request.attributes['CURRENT_URL']

But after upgrading our Liferay Portal it won't work any longer. Instead we can use:

url = portalUtil.getCurrentURL(request)

it's a minor thing but undocumented one.

Is that all?

Well no. The above issues are just few examples which we might find. But still the issues especially with Search API and fetching the images are the perfect examples showing the unexpected issues that might happen to us during migration process. For contrast I also added two issues that are much easier to find just to show you that not all issues are that hidden (well the Web Content issue might be hard to find in some rare scenarios).

I hope this post will help you at least a little bit during your migration

Best regards!

Copyright: Rafał Pydyniak