Ask Questions and Find Answers
Important:
Ask is now read-only. You can review any existing questions and answers, but not add anything new.
But - don't panic! While ask is no more, we've replaced it with discuss - the new Liferay Discussion Forum! Read more here here or just visit the site here:
discuss.liferay.com
RE: Clay UI dropdown direction?
I have embedded the Language Selector portlet into my header to make it show on the upper right corner of my template. However, when I click on the Language Selector button, its dropdown appears left side of the button. How do I make the dropdown direction bottom of the button? The position of the dropdown seems to be auto-generated by Clay UI.
Hi Jason,
Is the positioning done using CSS? If yes, then you should be able to simply target and override the styles in your own theme.
Is the positioning done using CSS? If yes, then you should be able to simply target and override the styles in your own theme.
The dropdown element has an ID like "yui_patched_v3_18_1_1_1555291221427_480", so it feels kind of hack-ish to target it that way.
Hi Jason,
Right, I certainly wouldn't target that -- but if there is a "less dynamic" node higher up like say ..
Right, I certainly wouldn't target that -- but if there is a "less dynamic" node higher up like say ..
div.clay > ul > li
then that would certainly be more acceptable. Maybe if you share the markup we can help figure out a reasonable path? Given the nature of CSS, I wouldn't consider that a hack, but yeah, definitely void any of the "yui_..." elements because they are not necessarily the same all the time anyway.
Unfortunately, it is generated directly under body. I believe the classes are shared with other dropdowns (if any).
<div id="yui_patched_v3_18_1_1_1555291610955_21932" class="yui3-widget overlay yui3-widget-positioned yui3-widget-modal yui3-widget-stacked overlay-hidden" style="left: 1450.7px; top: 8px; z-index: 5000; height: 42px; width: 160px;">...</div>
That sucks. Well, if you know which taglib it is, you could always create a JSP fragment hook and override the jsp with whatever styles you want. Look at those inline styles -- yuck!

Thanks Andrew. As I'm fairly new to Liferay, I've never done a fragment hook before.
How would you go about doing that using Eclipse?
How would you go about doing that using Eclipse?
Hi Jason,
We are all new to Liferay at some point. I'm been at this for nearly decade and I still learn something new almost every day
.
The first step is to figure out which tag is being used. You mentioned it was a clay taglib, but not which exactly. if you can figure that out and share first, then we can take it from there.
We are all new to Liferay at some point. I'm been at this for nearly decade and I still learn something new almost every day

The first step is to figure out which tag is being used. You mentioned it was a clay taglib, but not which exactly. if you can figure that out and share first, then we can take it from there.
Hi Andrew,
I'm not 100% sure how to check for that. Do you mean this?
I'm not 100% sure how to check for that. Do you mean this?
<@liferay_portlet["runtime"]
defaultPreferences=default_preferences
portletProviderAction=portletProviderAction.VIEW
portletProviderClassName="com.liferay.portal.kernel.servlet.taglib.ui.LanguageEntry"
/>
Ah! ok -- that's a taglib, but what it is doing is embedding a portlet. I don't think that is the right tag though, but that code you are sharing is embedding the LanguageEntry which looks like the language toggle option.
Can you send me a screenshot of what it is you are trying to change?
Can you send me a screenshot of what it is you are trying to change?
Hi Andrew, sorry for the delay. What I'm trying to achieve is a language selector dropdown that, well, drops down lol. This portlet, as you've pointed out, is embedded into my theme's header.
The generated dropdown appears to the left instead, as shown below:

The generated dropdown appears to the left instead, as shown below:

Hi Jason,
I thought maybe that is what it was! I'm glad I asked for a screenshot. The good news is that you don't have to override anything, you can actually change this look and feel using configuration. The not so great news is that there are several steps you have to perform so it's not for the faint of heart. I think you mentioned being new to Liferay so I'll try to outline all the steps, but be forewarned that this is off the cuff.
There are multiple steps, so i'll outline them one at a time.
Explanation
You're basically embedding a portlet in the theme so that you don't have to add it manually to every page. What you are doing is fine, but there is an alternative that is available for a few of Liferay's out of the box portlets where Liferay provides for you a macro that you can use to accomplish the same goal. In this case you could use the macro like this --
.. be sure to include the "s" on the end. There is a version without the s can be used to translate language keys. Ok, so this macro will basically do what you are doing manually, embed the portlet whereever you put it. Not he good news is that the Language portlet is not ADT enabled. This means that the portlet was updated so that the template that is used to render the portlet is something that can be controlled at runtime through configuration, including creation of the template. This feature was introduced in 6.2 and is one of my favourites
. So what you can do is define a custom ADT for this portlet with the markup you want to use to render the drop down. Then you will look create a preferences string which you can pass as part of the macro tag above and as you navigate from page to page (including new ones you add) the portlet will automatically be added and configured for you.
Step #1 - Generating the Preferences
The first thing I do here is I go to a page and I manually add the portlet to the page. Then I configure the portlet and grab the preferences out of the database so I am sure not to make an error with my embedded string. Sooo..
1. Create a new page
2. Go to that page
3. Add the Language Selector portlet from the widgets to the page
4. Hover on the portlet to access the stop light menu and then choose Configuration from the options
5. You will see a link that says "Manage Templates" -- click it
6. Then you Add a new template
This is the internal one that Liferay provides, so you can start with it and make your changes as you need.
7. Ok, now save, save, save ... basically close it all out.
8. Go back into the configuration and choose your custom template and save
Step #2 - Retrieveing the Preferences
This is a trick I like to use. There may be other ways, but I have found this to work well enough.
1. Open up some tool that you can use to query your database
2. select * from Group_ where groupKey like '%Guest%'; -- this is assuming of course you are using the default site /web/guest or /group/guest
3. Make note of the groupId
4. select * from layout where groupId = <groupId> and friendlyURL like '%/my-page%' -- use the friendly url you set for your page
5. Make note of the plid
6. select * from PortletPreferences where plid = <plid>;
in the results you want to find the row for the language portlet and then copy the value in the preferences column. You can see in the XML string the key/value pairs that are used -- we're going to use those with the macro.
Step #3 - Embedding your Portlet
1. Go back to your theme
2. Update your code so it looks like this
3. .. making sure to change the "key" and "value" items there to match the ones you grabbed from the DB in the last step.
4. save and deploy your code.
And that should be it! Now there is a gotcha here. A couple actually. But let's start with this and work our way through any issues you have and then we can talk about the gotchas.
I thought maybe that is what it was! I'm glad I asked for a screenshot. The good news is that you don't have to override anything, you can actually change this look and feel using configuration. The not so great news is that there are several steps you have to perform so it's not for the faint of heart. I think you mentioned being new to Liferay so I'll try to outline all the steps, but be forewarned that this is off the cuff.
There are multiple steps, so i'll outline them one at a time.
Explanation
You're basically embedding a portlet in the theme so that you don't have to add it manually to every page. What you are doing is fine, but there is an alternative that is available for a few of Liferay's out of the box portlets where Liferay provides for you a macro that you can use to accomplish the same goal. In this case you could use the macro like this --
<@liferay.languages/>
.. be sure to include the "s" on the end. There is a version without the s can be used to translate language keys. Ok, so this macro will basically do what you are doing manually, embed the portlet whereever you put it. Not he good news is that the Language portlet is not ADT enabled. This means that the portlet was updated so that the template that is used to render the portlet is something that can be controlled at runtime through configuration, including creation of the template. This feature was introduced in 6.2 and is one of my favourites

Step #1 - Generating the Preferences
The first thing I do here is I go to a page and I manually add the portlet to the page. Then I configure the portlet and grab the preferences out of the database so I am sure not to make an error with my embedded string. Sooo..
1. Create a new page
2. Go to that page
3. Add the Language Selector portlet from the widgets to the page
4. Hover on the portlet to access the stop light menu and then choose Configuration from the options
5. You will see a link that says "Manage Templates" -- click it
6. Then you Add a new template
This is the internal one that Liferay provides, so you can start with it and make your changes as you need.
<#if entries?has_content>
<#assign languageId = localeUtil.toLanguageId(locale) />
<style>
.taglib-language-option {
background: none no-repeat 5px center;
padding-left: 25px;
}
<#list entries as entry>
.taglib-language-option-${entry.getW3cLanguageId()} {
background-image: url(${themeDisplay.getPathThemeImages()}/language/${entry.getLanguageId()}.png);
}
</#list>
</style>
<@liferay_aui["form"]
action=formAction
method="post"
name='${namespace + formName}'
useNamespace=false
>
<@liferay_aui["select"]
changesContext=true
id='${namespace + formName}'
label=""
name='${name}'
onChange='${namespace + "changeLanguage();"}'
title="language"
>
<#list entries as entry>
<@liferay_aui["option"]
cssClass="taglib-language-option taglib-language-option-${entry.getW3cLanguageId()}"
disabled=entry.isDisabled()
label=entry.getLongDisplayName()
lang=entry.getW3cLanguageId()
selected=entry.isSelected()
value=entry.getLanguageId()
/>
<!--#list-->
<!--@-->
<!--@-->
<@liferay_aui["script"]>
function ${namespace}changeLanguage() {
var languageId = AUI.$(document.${namespace + formName}.${name}).val();
submitForm(document.${namespace + formName});
}
<!--@-->
<!--#if-->
7. Ok, now save, save, save ... basically close it all out.
8. Go back into the configuration and choose your custom template and save
Step #2 - Retrieveing the Preferences
This is a trick I like to use. There may be other ways, but I have found this to work well enough.
1. Open up some tool that you can use to query your database
2. select * from Group_ where groupKey like '%Guest%'; -- this is assuming of course you are using the default site /web/guest or /group/guest
3. Make note of the groupId
4. select * from layout where groupId = <groupId> and friendlyURL like '%/my-page%' -- use the friendly url you set for your page
5. Make note of the plid
6. select * from PortletPreferences where plid = <plid>;
in the results you want to find the row for the language portlet and then copy the value in the preferences column. You can see in the XML string the key/value pairs that are used -- we're going to use those with the macro.
Step #3 - Embedding your Portlet
1. Go back to your theme
2. Update your code so it looks like this
<@liferay.languages default_preferences=freeMarkerPortletPreferences.getPreferences({"key": "value", "key": "value"}) />
${freeMarkerPortletPreferences.reset()}
3. .. making sure to change the "key" and "value" items there to match the ones you grabbed from the DB in the last step.
4. save and deploy your code.
And that should be it! Now there is a gotcha here. A couple actually. But let's start with this and work our way through any issues you have and then we can talk about the gotchas.

Hi Andrew,
First of all, thank you for your continuous guidance. It is indeed very helpful for beginners and hopefully more people can learn from your answers.
I've managed to follow up to this point :
At that point, in Squirrel SQL, I cannot locate the plid that is same as the one for friendly url "/test" (test being the name of the page I created in Step 1).



Any idea what I've missed out?
First of all, thank you for your continuous guidance. It is indeed very helpful for beginners and hopefully more people can learn from your answers.
I've managed to follow up to this point :
6. select * from PortletPreferences where plid = <plid>;
At that point, in Squirrel SQL, I cannot locate the plid that is same as the one for friendly url "/test" (test being the name of the page I created in Step 1).



Any idea what I've missed out?
Hey Jason,
Hmm.. I have a couple thoughts. First is that perhaps you are not actually using the guest site as I had originally assumed
. I can see from your first screenshot that you have a record for a group called NVIS -- is it possible that the site you are working with is that one and not the default (Guest) one? ... maybe share with me the full URL you are using for your page?
.. If you are SURE that it is Guest though and it is still not working then the next thing I would try is adding another portlet or two to the page. Just make sure that the page you are using is one that you didn't have in place already and had accessed BEFORE you removed the embedded portelt from your theme. It should be pretty obvious mind you since the Language Selector can only be added to any given page one time.
My money is on the site reference.
Hmm.. I have a couple thoughts. First is that perhaps you are not actually using the guest site as I had originally assumed

.. If you are SURE that it is Guest though and it is still not working then the next thing I would try is adding another portlet or two to the page. Just make sure that the page you are using is one that you didn't have in place already and had accessed BEFORE you removed the embedded portelt from your theme. It should be pretty obvious mind you since the Language Selector can only be added to any given page one time.
My money is on the site reference.
Hi Andrew, sorry for the delay. I had problem starting up and figured I'd go with a fresh copy.
I've identified the new language select and its preference as below:
<portlet-preferences><preference><name>displayStyle</name><value>ddmTemplate_37521</value></preference><preference><name>languageIds</name><value>en_US,ca_ES,zh_CN,nl_NL,fi_FI,fr_FR,de_DE,iw_IL,hu_HU,ja_JP,pt_BR,es_ES</value></preference><preference><name>displayCurrentLocale</name><value>true</value></preference><preference><name>displayStyleGroupId</name><value>20126</value></preference></portlet-preferences>
However, I don't see the XML string with the key/value pairs. Is the code above the correct one?
I've identified the new language select and its preference as below:
<portlet-preferences><preference><name>displayStyle</name><value>ddmTemplate_37521</value></preference><preference><name>languageIds</name><value>en_US,ca_ES,zh_CN,nl_NL,fi_FI,fr_FR,de_DE,iw_IL,hu_HU,ja_JP,pt_BR,es_ES</value></preference><preference><name>displayCurrentLocale</name><value>true</value></preference><preference><name>displayStyleGroupId</name><value>20126</value></preference></portlet-preferences>
However, I don't see the XML string with the key/value pairs. Is the code above the correct one?
Hey Jason,
Yep, that looks right to me. So you have --
.. and the "name" nodes become your "keys" and the "value" nodes become your values of course. So try the last step with that and let me know how it goes. Once you see the embeeded portlet using your template we'll look at the next step -- the gotchas that you need to work through.
Yep, that looks right to me. So you have --
<!--?xml version="1.0" encoding="UTF-8"?--> <portlet-preferences> <preference> <name>displayStyle</name> <value>ddmTemplate_37521</value> </preference> <preference> <name>languageIds</name> <value>en_US,ca_ES,zh_CN,nl_NL,fi_FI,fr_FR,de_DE,iw_IL,hu_HU,ja_JP,pt_BR,es_ES</value> </preference> <preference> <name>displayCurrentLocale</name> <value>true</value> </preference> <preference> <name>displayStyleGroupId</name> <value>20126</value> </preference> </portlet-preferences>
.. and the "name" nodes become your "keys" and the "value" nodes become your values of course. So try the last step with that and let me know how it goes. Once you see the embeeded portlet using your template we'll look at the next step -- the gotchas that you need to work through.
Hi Andrew, great! I do see the embedded Language Selector now. Do tell me about the gotchas.
Hi Jason,
Good stuff.
OK -- Gotcha #1. When you want to change the preferences.
Let's say for example you make a new template and you want to change the preferences map to reference the new guy. The problem is that because the portlet is already embedded, you can change the string, but it won't get picked up. What you need to do is remove the preferences so that they get re-generated. To do this you need to go back to the portlet preferences table and this time you want to select all the rows where the portlet id matches your portlet reference. I always do a select first and just eyball the details and when I am happy that I am seeing what I no longer need, I run a delete. Now, because you ran a direct query the cache has not been updated so yo uprobably still have the old stuff in it. Once you run this query, go to the Control Panel > Configuration > Server Administration and clear the caches (most importantly the database cache)
NOTE!!! You should pretty much always avoid direct queries in the database. Liferay does not use foreign keys so you can really mess things up if you don't know what you are doing.
Gotcha #2 -- Removing the portlet
Let's say you decide that you don't want to provide your site in multiple languages anymore. So you go into your theme and you remove the embedded code. That's works, BUT the problem is that it doesn't actually trigger a "delete portlet from page". All it does is stop triggering the render. All the records are still there (database wise) .. but since you can't see the portlet it is orphaned. Ok, so now, let's say you do this on your "test" page (try this out). Go to the Pages section for your site in the control panel. Click on the Test page from the miller columns. Click on the stop light menu on the end and choose Orphaned something or other. It will open a window and you can see all the portlets that are associated with the page that are embedded or static. You would delete it from here. Now, go back to the test page (on the site). Because that portlet (which is not instanceable -- meaning only one per page) is no longer associated with the page, you can manually add the portlet from the widgets. So in this very round about example, you could decide not to make your whole site language switchable, but you might have a page that is.
Gotcha #3 -- ADT id
This is the tricky one. As you move from one environment to the next, how can you be sure that your ADT Template (DDMTemplate) id will be the same? Ugh. I am going from memory here, but I am pretty sure that the ID values is in fact the template key. By default thought, Liferay is configured to auto-generate these keys -- but youcan change that so that you can control the key and in each environment give it the same name. Go to
1. Control Panel > Configuration > System Settings > Dynamic Data Mapping
2. Uncheck the Autogenerate etc template.
Now for your exercise
1. Go back to your portlet and add a NEW template for it
2. Notice that this time you have an editable field where you can enter the template key -- give it a name -- and use a template that you can identify as different from the one you are using now
3. Go back to your theme and change your preference to use your NEW key
4. Then do gotcha #1 (delete the preferences, clear the cache)
5. Deploy your theme and make sure it works
Let me know how it goes.
the portlet, AND ALSO the preferences. Now, the portlet is embedded, so how the heck are you supposed to r
Good stuff.
OK -- Gotcha #1. When you want to change the preferences.
Let's say for example you make a new template and you want to change the preferences map to reference the new guy. The problem is that because the portlet is already embedded, you can change the string, but it won't get picked up. What you need to do is remove the preferences so that they get re-generated. To do this you need to go back to the portlet preferences table and this time you want to select all the rows where the portlet id matches your portlet reference. I always do a select first and just eyball the details and when I am happy that I am seeing what I no longer need, I run a delete. Now, because you ran a direct query the cache has not been updated so yo uprobably still have the old stuff in it. Once you run this query, go to the Control Panel > Configuration > Server Administration and clear the caches (most importantly the database cache)
NOTE!!! You should pretty much always avoid direct queries in the database. Liferay does not use foreign keys so you can really mess things up if you don't know what you are doing.
Gotcha #2 -- Removing the portlet
Let's say you decide that you don't want to provide your site in multiple languages anymore. So you go into your theme and you remove the embedded code. That's works, BUT the problem is that it doesn't actually trigger a "delete portlet from page". All it does is stop triggering the render. All the records are still there (database wise) .. but since you can't see the portlet it is orphaned. Ok, so now, let's say you do this on your "test" page (try this out). Go to the Pages section for your site in the control panel. Click on the Test page from the miller columns. Click on the stop light menu on the end and choose Orphaned something or other. It will open a window and you can see all the portlets that are associated with the page that are embedded or static. You would delete it from here. Now, go back to the test page (on the site). Because that portlet (which is not instanceable -- meaning only one per page) is no longer associated with the page, you can manually add the portlet from the widgets. So in this very round about example, you could decide not to make your whole site language switchable, but you might have a page that is.
Gotcha #3 -- ADT id
This is the tricky one. As you move from one environment to the next, how can you be sure that your ADT Template (DDMTemplate) id will be the same? Ugh. I am going from memory here, but I am pretty sure that the ID values is in fact the template key. By default thought, Liferay is configured to auto-generate these keys -- but youcan change that so that you can control the key and in each environment give it the same name. Go to
1. Control Panel > Configuration > System Settings > Dynamic Data Mapping
2. Uncheck the Autogenerate etc template.
Now for your exercise

1. Go back to your portlet and add a NEW template for it
2. Notice that this time you have an editable field where you can enter the template key -- give it a name -- and use a template that you can identify as different from the one you are using now
3. Go back to your theme and change your preference to use your NEW key
4. Then do gotcha #1 (delete the preferences, clear the cache)
5. Deploy your theme and make sure it works
Let me know how it goes.
the portlet, AND ALSO the preferences. Now, the portlet is embedded, so how the heck are you supposed to r
Hi Andrew,
I've gone back to the language selector's Configuration > Manage Template > Add. However, I'm not seeing the template key field mentioned when creating a new template
I've gone back to the language selector's Configuration > Manage Template > Add. However, I'm not seeing the template key field mentioned when creating a new template
2. Notice that this time you have an editable field where you can enter the template key -- give it a name -- and use a template that you can identify as different from the one you are using nowI just see Name, Language, Description and Small Image followed by the script editor.
Are you sure you got that checkbox? .. that or maybe the Details section is closed and it hiding it on you. Here are some screenshots from my side.
Attachments:
Ahh, I was following these:
1. Go back to your portlet and add a NEW template for itI was assuming that gotcha #3 will come later lol. Alright, I understand gotcha #2 and #3 and I can now make the new template with custom key. However, I'm not 100% certain about this:
2. Notice that this time you have an editable field where you can enter the template key -- give it a name -- and use a template that you can identify as different from the one you are using now
3. Go back to your theme and change your preference to use your NEW key
4. Then do gotcha #1 (delete the preferences, clear the cache)
5. Deploy your theme and make sure it works
select all the rows where the portlet id matches your portlet referenceWhat exactly am I looking for here? I didn't want to delete any wrong preference and screw up, lol!
Hey Jason,
So the way it works is every portle/page (layout) combination will have it's own preferences. That is why i was asking you to filter the portlet preferencs on the plid (plid = Page Layout ID) so that you could see the page where the portlet was embedded.
Now, if you created a bunch of other page and then started navigating to them, then your Language portlet woul dbe embedded in those as well, and as a result you woul dhave a whole whack of rows in the PortletPreferences table with you embedded preferences. When you want to CHANGE the embedded preferences though, you need to nuke these records.
Now going and querying page by page would be painful. So instead you can run the select to get all the records where the portletId (column) matches the id for your language portlet. Running this query will give you all the records across all the pages where it has been embedded. In this case, because the language portlet is not intanceable (it can only be added to any given page one time) there is virtually no risk that you will delete the wrong records (because you are only working with one site). I still like to run the select first though and look at the row count (usually I know roughly how many pages I have) and then just scan the records.
Once you are satified, change your select * to a delete from .. and then clear your cache, and re-hit your page and the new settings should be stored in the DB. You can verify with the same queries of course.
So the way it works is every portle/page (layout) combination will have it's own preferences. That is why i was asking you to filter the portlet preferencs on the plid (plid = Page Layout ID) so that you could see the page where the portlet was embedded.
Now, if you created a bunch of other page and then started navigating to them, then your Language portlet woul dbe embedded in those as well, and as a result you woul dhave a whole whack of rows in the PortletPreferences table with you embedded preferences. When you want to CHANGE the embedded preferences though, you need to nuke these records.
Now going and querying page by page would be painful. So instead you can run the select to get all the records where the portletId (column) matches the id for your language portlet. Running this query will give you all the records across all the pages where it has been embedded. In this case, because the language portlet is not intanceable (it can only be added to any given page one time) there is virtually no risk that you will delete the wrong records (because you are only working with one site). I still like to run the select first though and look at the row count (usually I know roughly how many pages I have) and then just scan the records.
Once you are satified, change your select * to a delete from .. and then clear your cache, and re-hit your page and the new settings should be stored in the DB. You can verify with the same queries of course.
Hi Andrew,
I see. Alright I think I get it now. So now, all that's left to do is to edit the template to my liking, correct?
You've been really helpful and I hope this thread will help more beginners in the future. Thank you so much!
I see. Alright I think I get it now. So now, all that's left to do is to edit the template to my liking, correct?
You've been really helpful and I hope this thread will help more beginners in the future. Thank you so much!
Yep -- and now that you control the template key, you shouldn't have to worry about the migration from environment to environment. If you used the export/import, I'm pretty sure that kind of stuff is maintained, but my own experience with the export/import process has been less than stellar so I often find myself simpy recreating artifacts as part of the deployment.
Happy to help -- let me know if you need anything else.
Happy to help -- let me know if you need anything else.
Hi Andrew,
I've updated the template to use the Icon Menu code as the base, as the UI is closer to what I want. However, it does not seem to have any function to update the portal's language when clicking on a choice, nor does it render the same as when selecting Icon Menu from the display template.
Your sample earlier, which is select box style, has the function included in the template. How do I invoke the language update in my custom template?
I've updated the template to use the Icon Menu code as the base, as the UI is closer to what I want. However, it does not seem to have any function to update the portal's language when clicking on a choice, nor does it render the same as when selecting Icon Menu from the display template.
Your sample earlier, which is select box style, has the function included in the template. How do I invoke the language update in my custom template?
You've been really great Andrew

Copyright © 2025 Liferay, Inc
• Privacy Policy
Powered by Liferay™