After restarting your Server you might get a:
ClassNotFoundException: org.apache.jsp.html.portlet.dockbar.view_jsp
This is a known issue in Liferay 6.2. The solution is quite simple.
Create a empty file view_user_account.portal.jspf in /webapps/ROOT/html/portlet/dockbar/.
Error: java.lang.ClassNotFoundException: org.apache.jsp.html.portlet.dockbar.view_jsp
Animated Loading Icon (Spinner / Progress Bar)
I just tried to show an animated loading icon (like a spinner or progress bar) while my portlet was loading.
A very simple way is to use the css-class "loading-animation", which is delivered with Alloy-Ui, I guess.
Adding a single div:
<div class="loading-animation" />
You will get an animated logo like this:
How to get and modify Portal-URL
import com.liferay.portal.kernel.util.HttpUtil; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.util.PortalUtil; // delivers url-path including parameters String currentUrlPathWithParamters = PortalUtil.getCurrentURL( request ); // --> Example: /group/guest/home?foo=test // delivers url-path without parameters String currentUrlPath = HttpUtil.getPath(currentUrlPathWithParamters); // --> Example: /group/guest/home // delivers complete url String currentUrl = PortalUtil.getPortalURL( request ) + currentUrlPath; // --> Example: http://localhost:8080/group/guest/home // adding parameters to url String urlWithCustomParameter= HttpUtil.addParameter(currentUrl,"foo", "faa"); // --> Example: http://localhost:8080/group/guest/home?foo=faa
portal-setup-wizard
After starting a clean liferay instance, you will see the portal-setup-wizard helping you to configure your portal. It provides help concerning your database-connection, default user,...
After you finished configuration portal-setup-wizard will not show up again. To enable portal-setup-wizard again, you can set the following property inside portal-setup-wizard.properties:
portal.setup.wizard.enabled=true
Hook- vs Ext-plugin
Using Hooks you can:
- provide a portal configuration (portal-ext.properties)
- define new or modify existing language key (language.properties)
- modify portal JSPs
- define servlet filters
- implement post indexers
- override Liferay-Services (especially by implementing a ServiceWrapper, like for example UserServiceWrapper)
- override any class inside Liferay (classes inside portal-iml.jar or portal-util)
- modify Liferay's core portlet's desciptors and configurations (like portelt.xml, liferay-portlet.xml,...)
Get PortletPreferences
import javax.portlet.PortletPreferences;
import com.liferay.portlet.PortletPreferencesFactoryUtil;
PortletPreferences portletSetup = PortletPreferencesFactoryUtil.getLayoutPortletSetup( layout , portletId);
String foo = portletSetup.getValue("foo", "defaultValue");
Get all portlets placed on a page (layout)
To get all portlets placed on a page (layout) you can use the following lines of code:
import com.liferay.portal.model.LayoutTypePortlet; import com.liferay.portal.model.Portlet; LayoutTypePortlet layoutTypePortlet = (LayoutTypePortlet) l.getLayoutType(); List< Portlet > portlets = layoutTypePortlet.getAllPortlets();
Liferay WebServices
Liferay offers the potential to use it's services via WebServices.
If you need an overview over all available WebServices just take a look at: http://*your.portal.url*/api/jsonws. Your custom Services created with Liferay's ServiceBuilder will also be listed here.
import com.liferay.portlet.asset.model.AssetEntrySoap;
import com.liferay.portlet.asset.model.AssetVocabularySoap;
import com.liferay.portlet.asset.service.persistence.AssetEntryQuery;
import java.util.List;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import org.json.JSONException;
import org.json.JSONObject;
public class WebServicesClient
{
private Client client;
private WebTarget target;
public WebServicesClient( final String portalUrl, final String username, final String password )
{
client = ClientBuilder.newClient();
client.register( new Authenticator( username, password ) );
client.register( GensonProvider.class );
target = client.target( portalUrl + "api/jsonws" );
}
public AssetEntrySoap requestAssetEntry( final String entryId )
{
Form form = new Form();
form.param( "entryId", entryId );
AssetEntrySoap assetEntry = target.path( "/assetentry/get-entry" )
.request( MediaType.APPLICATION_JSON_TYPE )
.post( Entity.entity( form, MediaType.APPLICATION_FORM_URLENCODED_TYPE ), AssetEntrySoap.class );
return assetEntry;
}
public List getAssetEntries(final String companyId )
{
Form form = new Form();
form.param( "companyId", companyId );
form.param( "start", "-1" );
form.param( "end", "-1" );
List< AssetEntrySoap > assetEntries = target.path( "/assetentry/get-company-entries" )
.request( MediaType.APPLICATION_JSON_TYPE )
.post( Entity.entity( form, MediaType.APPLICATION_FORM_URLENCODED_TYPE ), new GenericType< List< AssetEntrySoap > >() { } );
return assetEntries;
}
}
To perform a valid authentication you can implement an authenticator like this:
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.xml.bind.DatatypeConverter;
public class Authenticator implements ClientRequestFilter
{
private final String username;
private final String password;
public Authenticator( final String username, final String password )
{
this.username = username;
this.password = password;
}
public void filter( final ClientRequestContext requestContext ) throws IOException
{
MultivaluedMap headers = requestContext.getHeaders();
final String basicAuthentication = getBasicAuthentication();
headers.add( "Authorization", basicAuthentication );
}
private String getBasicAuthentication()
{
String token = this.username + ":" + this.password;
try{
return "BASIC " + DatatypeConverter.printBase64Binary( token.getBytes( "UTF-8" ) );
}catch ( UnsupportedEncodingException ex ){
throw new IllegalStateException( );
}
}
}
If you use Genson for Building your json String, you will have to set useDateAsTimestamp as true in your GensonBuilder, since Liferay uses timestamps as date-fields.
import com.owlike.genson.Genson;
import com.owlike.genson.GensonBuilder;
import javax.ws.rs.ext.ContextResolver;
public class GensonProvider implements ContextResolver< Genson >
{
private final Genson genson = new GensonBuilder().useDateAsTimestamp( true ).create();
@Override
public Genson getContext( final Class< ? > arg0 ){
return genson;
}
}
Testing with JUnit
If you want to write JUnit tests for your application, you can use the following TestRunner which will initialize a liferay context such that you are able to use for example liferay services. However this approach makes it necessary to add portal-impl.jar as a dependency.
public class LiferayJUnitTestRunner
extends BlockJUnit4ClassRunner {
public LiferayIntegrationJUnitTestRunner(Class clazz)
throws InitializationError {
super(clazz);
if (System.getProperty("external-properties") == null) {
System.setProperty("external-properties", "portal-test.properties");
}
InitUtil.initWithSpring();
_testContextHandler = new TestContextHandler(clazz);
}
@Override
protected Statement withAfters(
FrameworkMethod frameworkMethod, Object instance, Statement statement) {
Statement withAftersStatement = super.withAfters(frameworkMethod, instance, statement);
return new RunAfterTestMethodCallback(
instance, frameworkMethod.getMethod(), withAftersStatement,
_testContextHandler);
}
@Override
protected Statement withBefores(
FrameworkMethod frameworkMethod, Object instance, Statement statement) {
Statement withBeforesStatement = super.withBefores(frameworkMethod, instance, statement);
return new RunBeforeTestMethodCallback(
instance, frameworkMethod.getMethod(), withBeforesStatement,
_testContextHandler);
}
public static void run(Class clazz){
Request request=Request.classWithoutSuiteMethod(clazz);
Runner runner=request.getRunner();
RunNotifier notifier=new RunNotifier();
runner.run(notifier);
}
private TestContextHandler _testContextHandler;
}
A sample TestCase looks like that:
@RunWith(LiferayIntegrationJUnitTestRunner.class)
public class SampleTestCase{
@Test
public void testSample() throws Exception {
List users = UserLocalServiceUtil.getUsers(-1, -1);
Assert.assertNotNull(users);
}
}
Creating a service without service-builder
Create your service-project:
1) Create new Liferay-Portlet project
2) clear porlet.xml.
<portlet-app version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xsi:schemalocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"> </portlet-app>
2) create a service.xml file in docroot/WEB-INF
< ?xml version="1.0" encoding="UTF-8"? > < !DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd" > < service-builder package-path="test" > < author >test< /author > < namespace >test< /namespace > < exceptions > < /exceptions > < /service-builder >
3) Right click on service.xml and choose „Liferay - build services“. Service-Builder will create a directory „service“
4)Open project-properties ans choose the „service“-directory as additional source directory
Create Liferay Portlet URLs
You will often have to built a URL leading to a specific portlet without knowing on which page it was placed. In order to find the page (layout) containing your portlet and building a valid LiferayPortletURL you can use the following code:
ThemeDisplay themeDisplay = (ThemeDisplay)liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY); Layout targetLayout = themeDisplay.getLayout(); ListallLayouts = LayoutLocalServiceUtil.getLayouts(-1, -1); layoutloop: for(Layout l:allLayouts){ LayoutTypePortlet layoutTypePortlet = (LayoutTypePortlet)l.getLayoutType(); List portletIdList = layoutTypePortlet.getPortletIds(); for(String portletId: portletIdList ){ if(portletId.equalsIgnoreCase(*your portletId*)){ targetLayout = l; break layoutloop; } } } LiferayPortletURL targetUrl = PortletURLFactoryUtil.create(liferayPortletRequest, *your portletId*, targetLayout .getPlid(), PortletRequest.RENDER_PHASE);
Format file size
Liferay provides
com.liferay.portal.kernel.util.TextFormatter, which can be used to format the size of a file given in bytes and thus make it human readable. The relevant method is given with two different signatures;
static String formatStorageSize(double size, Locale locale)
static String formatStorageSize(int size, Locale locale)
Service Builder
In this post you will find a collection of informations concerning the service builder.
Maximum size of a database columns
For each model entity you define in service.xml a seperate database table will be created.
By default service builder will set the maximum size of a database column for each of a model's
fields to 75 characters. To configure the maximum size take a look at docroot/WEB-INF/src/META-INF/portlet-model-hints.xml.
For example if you want to set the maximum size of a field called "name" to 1000 characters you can change:
to
1000
Force ServiceBuilder to recreate database tables
ServiceBuilder uses the table "servicecomponents" to memorize if certain database tables have already been created. If you want to force ServiceBuilder to recreate database tables for some reason just remove the rows referencing your portlet from database table "servicecomponents".
Add custom portlet to control panel
In order to place your custom portlet to the control panel just add the following lines to your liferay-portlet.xml:
Liferay-Blog /icon.png apps 100 de.blogspot.blog.liferay.ControlPanelEntryHandler ...
Here are the explanations taken from liferay-portlet-app_6_2_0.dtd:
control-panel-entry-category: Set the control-panel-entry-category value to "my" to make this portlet available within the My Account administration of the user. Set the value to "apps", "configuration", "sites", or "users" to make it available in the Control Panel under that category. Set the value to "site_administration.configuration", "site_administration.content", "site_administration.pages" or "site_administration.users" to make it available in the Site Administration under that category. Legacy values from previous versions of Liferay will be automatically mapped to the new values: "content" to "site_administration.content", "portal" to "users", and "server" to "apps". [Source]
control-panel-entry-weight: Set the control-panel-entry-weight value to a double number to control the position of the entry within its Control Panel category. Higher values mean that the entry will appear lower in the Control Panel menu. [Source]
control-panel-entry-class: The control-panel-entry-class value must be a class that implements com.liferay.portlet.ControlPanelEntry and is called by the Control Panel to decide whether the portlet should be shown to a specific user in a specific context. [Source]
In your control-panel-entry-class you might for example check whether the user has a certain regular role or not. Here is a example where I check if the user has a regular role called "Liferay-Blog":
package de.blogspot.blog.liferay;
import com.liferay.portal.model.Group;
import com.liferay.portal.model.Portlet;
import com.liferay.portal.model.Role;
import com.liferay.portal.model.User;
import com.liferay.portal.security.permission.PermissionChecker;
import com.liferay.portal.service.RoleLocalServiceUtil;
import com.liferay.portal.util.PortalUtil;
import com.liferay.portlet.BaseControlPanelEntry;
public class ControlPanelEntryHandler extends BaseControlPanelEntry{
@Override
public boolean hasAccessPermission(PermissionChecker permissionChecker, Group group, Portlet portlet) throws Exception {
User user = permissionChecker.getUser();
Role liferayBlogRole = RoleLocalServiceUtil.getRole(PortalUtil.getDefaultCompanyId(), "Liferay-Blog");
boolean hasAccessPermission = RoleLocalServiceUtil.hasUserRole(user.getUserId(), liferayBlogRole.getRoleId());
return hasAccessPermission;
}
}
This site is inactive Please contact the administrator
When you try to access your portal and get the following message:
You probably deactivated your default guest site (group). Just run this query on your database ans restart your portal server:
UPDATE group_ SET active = 1 WHERE name = "group_name"
Setting portlet preferences in configuration mode
There two cases when the following post becomes interesting:
1. You hook some liferay portlet and want to modify a the portlets configuration jsp without hooking the appropriate struts action.
2. Creating a configuration view for your custom portlet by implementing a ConfigurationAction that extends com.liferay.portal.kernel.portlet.DefaultConfigurationAction and adding the following line to yoor liferay-portlet.xml:
... /icon.png de.blogspot.blog.liferay.ConfigurationAction true ...
When using AUI input fields just choose their names according the pattern "preferences--paramName--" and the DefaultConfigurationAction class will handle all parameters and store them in portlet preferences. There is no need to implement any line of code. Example input field:
< aui:input name="preferences--hideToolbar--" type="checkbox" value="<%= hideToolbar %>" />Choosing the name "preferences--hideToolbar--" for your input field, the DefaultConfigurationAction will store the "hideToolbar" value in your portlet preferences when the user submits the configuration form.
Custom startup actions
It is often necessary to define a startup action. For example to verify whether the portal is correctly configured. The following portal properties offer the chance to define custom startup actions which will be executed during portal startup and each deployment of your startup action.
global.startup.events application.startup.eventsUsing global.startup.events you can define a action running once while actions defined using application.startup.events run for every portal instance. Since global.startup.events describes an event, intercepting Liferay statup, you will need to define your class in an ext-plugin. Whereas application.startup.events can be defined in a hook-plugin. Your custom startup action has to extend com.liferay.portal.kernel.events.SimpleAction and override a run-method. Example: In portal.properties:
application.startup.events=de.blogspot.blog.liferay.StartupEventThe appropriate startup class:
package de.blogspot.blog.liferay;
import com.liferay.portal.kernel.events.ActionException;
import com.liferay.portal.kernel.events.SimpleAction;
public class StartupEvent extends SimpleAction{
@Override
public void run(String[] ids) throws ActionException {
System.out.println("Liferay-Blog.blogspot.de");
}
}
Asset Publisher: Custom Display Style
You can add custom display styles to the asset publisher. Let's say you want to add a display style called "MyDisplayTemplate".
First of all you add your style's name to the following portal property:
asset.publisher.display.styles= table,title-list,abstracts,full-content,MyDisplayTemplateNow you have to create a hook for the asset publisher. Add a jsp called "MyDisplayTemplate.jsp" in "docroot\custom_jsps\html\portlet\asset_publisher\display". Consider that the jsp file's name has to be exact the same as you defined it in portal.properties.
That's it!
Portal properties
In this post you will find a collection of frequently used portal properties.
Remove password reset on first login
passwords.default.policy.change.required=falseRemove terms of use on first login
terms.of.use.required=falseRemove Password reminder question on first login
users.reminder.queries.enabled=false users.reminder.queries.custom.question.enabled=false
Display styles for the asset publisher (HowTo:Add custom style):
asset.publisher.display.styles= table,title-list,abstracts,full-content
Expandos
Necessary classes:
com.liferay.portlet.expando.model.ExpandoTable com.liferay.portlet.expando.model.ExpandoValue com.liferay.portal.service.ClassNameLocalServiceUtil com.liferay.portlet.expando.service.ExpandoColumnLocalServiceUtil com.liferay.portlet.expando.service.ExpandoTableLocalServiceUtil com.liferay.portlet.expando.service.ExpandoValueLocalServiceUtilHere is a example how you get a expando-value programmatically. Lets say you have a expando named "foo" for User class. You can get a user's value like this:
long userClassNameId = ClassNameLocalServiceUtil.getClassNameId(User.class.getName()); ExpandoTable userExpandoTable = ExpandoTableLocalServiceUtil.getDefaultTable(PortalUtil.getDefaultCompanyId(), userClassNameId); ExpandoColumn userExpandoTableColumn = ExpandoColumnLocalServiceUtil.getColumn(userExpandoTable.getTableId(), "foo"); ExpandoValue expandoValue = ExpandoValueLocalServiceUtil.getValue(userExpandoTable.getTableId(),userExpandoTableColumn.getColumnId(), user.getUserId()); String foo= expandoValue.getString();There is also a shorter way based on ExpandoBridge. What you have to consider when using this approach, is that permission checking is enabled when using ExpandoBridge. For example when you try to access some expando value as a non-logged in user you will get some exception because of missing permissions to access expando-values.
String education =
(String)user.getExpandoBridge().getAttribute("foo");
Setting value by using ExpandoValueLocalServiceUtil
ExpandoValueLocalServiceUtil.addValue(userClassNameId, userExpandoTable.getTableId(), userExpandoTableColumn.getColumnId(), user.getUserId(), "some value");Setting the same value by using ExpandoBridge
user.getExpandoBridge().setAttribute("foo", "some value");
Checking if a certain expando exists. For example an expando "hobby" for Users:
user.getExpandoBridge().hasAttribute("hobby")


