Liferay Hook - Manipulation request parameters - liferay

I ran into a little problem with a hook. Szenario:
The Hook should override struts action /document_library/edit_file_entry which is called, whenever a user uploads a document into the document library.
The goal is to check the title of the document and rename it following a given naming-scheme.
My solution:
#Override
public void processAction(
StrutsPortletAction originalStrutsPortletAction,
PortletConfig portletConfig, ActionRequest actionRequest,
ActionResponse actionResponse)
throws Exception {
//Get old title - set new title
String oldTitle = ParamUtil.getString(actionRequest, "title");
String newTitle = "Test";
//wrap request to set param
DynamicActionRequest actionRequestNew = new DynamicActionRequest(actionRequest);
actionRequestNew.setParameter("title", newTitle );
//call original struts action with modified title
originalStrutsPortletAction.processAction(originalStrutsPortletAction, portletConfig, actionRequestNew, actionResponse);
}
The Problem is that the original Struts action in portal-impl/src/com/liferay/portlet/documentlibrary/action/EditFileEntryAction.java uses PortalUtil.getUploadPortletRequest(actionRequest); which expects a PortletRequestImpl.
But DynamicActionRequest cannot be cast to PortletRequestImpl.
See:
12:07:04,466 ERROR [http-bio-8082-exec-44][render_portlet_jsp:154] java.lang.ClassCastException: com.liferay.portal.kernel.portlet.DynamicActionRequest cannot be cast to com.liferay.portlet.PortletRequestImpl
at com.liferay.portal.util.PortalImpl.getUploadPortletRequest(PortalImpl.java:4067)
at com.liferay.portal.util.PortalUtil.getUploadPortletRequest(PortalUtil.java:1253)
at com.liferay.portlet.documentlibrary.action.EditFileEntryAction.updateFileEntry(EditFileEntryAction.java:653)
at com.liferay.portlet.documentlibrary.action.EditFileEntryAction.processAction(EditFileEntryAction.java:129)
at com.liferay.portal.struts.StrutsPortletActionAdapter.processAction(StrutsPortletActionAdapter.java:51)
at com.liferay.portal.kernel.struts.BaseStrutsPortletAction.processAction(BaseStrutsPortletAction.java:42)
at com.foo.hook.portlet.sites.action.MyEditFileEntryAction.processAction(MyEditFileEntryAction.java:83)
at com.liferay.portal.kernel.bean.ClassLoaderBeanHandler.invoke(ClassLoaderBeanHandler.java:67)
at com.liferay.portal.struts.PortletActionAdapter.processAction(PortletActionAdapter.java:55)
at com.liferay.portal.struts.PortletRequestProcessor.process(PortletRequestProcessor.java:169)
at com.liferay.portlet.StrutsPortlet.processAction(StrutsPortlet.java:212)
at com.liferay.portlet.FilterChainImpl.doFilter(FilterChainImpl.java:70)
at com.liferay.portal.kernel.portlet.PortletFilterUtil.doFilter(PortletFilterUtil.java:48)
at com.liferay.portlet.InvokerPortletImpl.invoke(InvokerPortletImpl.java:548)
at com.liferay.portlet.InvokerPortletImpl.invokeAction(InvokerPortletImpl.java:579)
at com.liferay.portlet.InvokerPortletImpl.processAction(InvokerPortletImpl.java:294)
at com.liferay.portal.action.LayoutAction.processPortletRequest(LayoutAction.java:944)
at com.liferay.portal.action.LayoutAction.processLayout(LayoutAction.java:688)
at com.liferay.portal.action.LayoutAction.execute(LayoutAction.java:249)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)
How can I change the parameter without using DynmicActionRequest? Any suggestions?
I'm running Liferay-Portal 6.1.20 EE.
Thanks in advance.

There are two approaches I can think of.
1) Create ActionRequestWrapper object and add a parameter. This would probably solve your issue.
2) Create a subclass of LR's action class. In that make the needed code changes. Create a hook and make the new action class available to LR.
I hope this helps.

You can set the parameter in existing actionRequest:
actionRequest.setParameter("title", newTitle);
It will updated with new value.

Related

shiro web step by step example not work,stormpath is moved

Shiro web example
I follow this, but in step 2, the stormpath is moved to another site okta, so I don't know what shall I do.
There is an exception:
java.lang.IllegalStateException: Unable to load credentials from any provider in the chain.
So I wrote a reamls by myself.
public class CustomSecurityRealm extends JdbcRealm{
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
char[] psw = upToken.getPassword();
String username = upToken.getUsername();
return new SimpleAuthenticationInfo(username, psw, getName());
}
#Override
public void setDataSource(DataSource dataSource) {
// TODO Auto-generated method stub
DruidDataSource ds=new DruidDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE");
ds.setUsername("root");
ds.setPassword("root");
dataSource=ds;
}
}
And in shiro.ini I change securityManager.realm = $stormpathRealm to securityManager.realm = realm.CustomSecurityRealm
But exception is the same. Or sometimes no error when I delete it from tomcat and add again, but home page is not found --404.
I hate this, I just want to see how to use shiro in web project,why it is so hard?
I have no jndi, so I didn't copy from this example, I just want to make things simple. How can I run the web sample?
Take a look at the examples in https://github.com/apache/shiro/tree/master/samples
We will get that tutorial updated too.

Liferay Action Hook: how to specify a tiles redirect?

I create a Liferay 6.2 hook by following official documentation, and everything works fine.
Now I need that, in the render phase and under several conditions, my user will be redirect to a specific tiles.
Original Liferay render method says:
return actionMapping.findForward("portlet.journal.error");
By the way in my class (extending BaseStrutsPortletAction) the render method can't access to any actionMapping instance.
So... how can I obtain the same behaviour in my class?
My code says:
#Override
public String render(
StrutsPortletAction originalStrutsPortletAction,
PortletConfig portletConfig, RenderRequest renderRequest,
RenderResponse renderResponse)
throws Exception {
Boolean myCondition = .......;
if (myCondition) {
// WHAT SHOULD I DO HERE?
//return actionMapping.findForward("portlet.journal.error");
}
return originalStrutsPortletAction.render(
null, portletConfig, renderRequest, renderResponse);
}
Thank you
All you need is to return the forward name.
Instead of actionMapping.findForward("portlet.journal.error"), return just "portlet.journal.error".
BaseStrutsPortletAction#render method is called from com.liferay.portal.struts.PortletActionAdapter#render, which uses the result to call actionMapping.findForward(...).

Using liferay dockbar notifications

I’d like to use the liferay notification feature following the tutorial http://www.codeyouneed.com/liferay-custom-notifications/. And as many people before, I succeeded in increasing the number of notifications, but the notification message is not displayed.
I tried to check by adding log-output whether the methods (getBody, getLink, …) of the UserNotificationHandler are called, and they are not called at all, not even the constructor of the UserNotificationHandler is called.
So I conclude that my notification is written to the database, but my UserNotificationHandler class is not found.
In my project, I have put the
user-notification-definitions into
project/src/main/resources.
They look like:
<?xml version="1.0"?>
<!DOCTYPE user-notification-definitions PUBLIC "-//Liferay//DTD User Notification Definitions 6.2.0//EN" "http://www.liferay.com/dtd/liferay-user-notification-definitions_6_2_0.dtd">
<user-notification-definitions>
<definition>
<notification-type>${com.myproject.portal.notifications.UserNotificationHandler.PORTLET_ID}</notification-type>
<description>receive-a-notification-when-triggered</description>
<delivery-type>
<name>email</name>
<type>${com.liferay.portal.model.UserNotificationDeliveryConstants.TYPE_EMAIL}</type>
<default>true</default>
<modifiable>true</modifiable>
</delivery-type>
<delivery-type>
<name>website</name>
<type>${com.liferay.portal.model.UserNotificationDeliveryConstants.TYPE_WEBSITE}</type>
<default>true</default>
<modifiable>true</modifiable>
</delivery-type>
</definition>
</user-notification-definitions>
The liferay-portlet.xml is in
project/src/main/webapp/WEB-INF.
And the UserNotificationHandler in
project/src/main/java/com/myproject/portal/notifications
in the package com.myproject.portal.notifications.
I wrote something like that into the liferay-portlet.xml:
<portlet-name>example</portlet-name>
<icon>/icon.png</icon>
<user-notification-definitions>
user-notification-definitions.xml
</user-notification-definitions>
<user-notification-handler-class>
com.myproject.portal.notifications.UserNotificationHandler
</user-notification-handler-class>
</portlet>
This is my UserNotificationHandlerClass (so far, I am just trying to get it work before adding the actual content):
package com.myproject.portal.notifications;
import ...//all necessary imports
public class UserNotificationHandler extends
BaseUserNotificationHandler {
public static final String PORTLET_ID = "example_WAR_myprojectportlet";
private static final Logger log = Logger.getLogger(UserNotificationHandler.class);
public UserNotificationHandler() {
log.info("UserNotificationHandler - Constructor");
setPortletId(UserNotificationHandler.PORTLET_ID);
}
#Override
protected String getBody(UserNotificationEvent userNotificationEvent,
ServiceContext serviceContext) throws Exception {
log.info("in getBody");
return "";
}
#Override
protected String getLink(UserNotificationEvent userNotificationEvent,
ServiceContext serviceContext) throws Exception {
log.info("in getLink");
return "";
}
protected String getBodyTemplate() throws Exception {
log.info("in getBodyTemplate");
return "";
}
}
I trigger the notification in my portlet like this:
ServiceContext serviceContext = ServiceContextFactory.getInstance(request);
JSONObject payloadJSON = JSONFactoryUtil.createJSONObject();
payloadJSON.put("userId", userId);
payloadJSON.put("yourCustomEntityId", 12345);
payloadJSON.put("additionalData", "success");
UserNotificationEventLocalServiceUtil.addUserNotificationEvent(userId,
UserNotificationHandler.PORTLET_ID,
(new Date()).getTime(),
userId,
payloadJSON.toString(),
false,
serviceContext);
What is the problem here?
Do you literally have public static final String PORTLET_ID = "myportlet"; in your code? If so, note the extra information in the tutorial that you link:
NB Important Information: The com.example.notifications.ExampleUserNotificationHandler.PORTLET_ID string that you use as your notification type has to match an actual portlet ID. It doesn’t actually need to be YOUR portlet ID but that would be the right thing to have there. The reason being that Notifications display portlet uses it to display a small portlet icon next to your notification to help the user identify the source of the notification. Providing a bad Portlet ID or something like null leads to a hard-to-trace NullPointerException in the JSP. Took me an hour to track it down.
Most likely the portlet ID looks rather like "example_WAR_myportlet", this indicates that it's deployed in a plugin named example.war and the portlet id (in portlet.xml) is myportlet. Try if it works then - Liferay might need to find the portlet in order to find, instanciate and use its NotificationHandler. (Note: This is currently a guess - I didn't try the full code posted)
In your liferay-portlet.xml you wrote
<user-notification-handler-class>
UserNotificationHandler
</user-notification-handler-class>
It should be:
<user-notification-handler-class>
com.myproject.portal.notifications.UserNotificationHandler
</user-notification-handler-class>
You should also check if this part is good
<user-notification-definitions>
user-notification-definitions.xml
</user-notification-definitions>
"user-notification-definitions.xml" file should be on WEB-INF/classes in the final WAR

Customize CreateAccountAction in Liferay Portal

I'm trying to add "User Group" Selection field while creating a user (Self User Registration form - create_account.jsp).
Here Custom fields is not helpful because Usergroup is already exists in db. I want to insert into existing Users_UserGroups table.
I'm using below hook:
But User is not added in the group and no exception is printed.
Please suggest me for any other way to achieve this.
public class CustomCreateAccountAction extends BaseStrutsPortletAction
{
public void processAction(StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse)
throws Exception
{
System.out.println("My Custom Process Action Method is Called");
String emailid=ParamUtil.getString(actionRequest, "emailAddress");
long[] userGroupIds = null;
originalStrutsPortletAction.processAction(originalStrutsPortletAction, portletConfig, actionRequest,actionResponse);
System.out.println("This is after user is registered");
if (SessionErrors.isEmpty(actionRequest))
{
ThemeDisplay themeDisplay =(ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
long newlyCreatedUserId=UserLocalServiceUtil.getUserIdByEmailAddress(themeDisplay.getCompanyId(), emailid);
long userIds[]={newlyCreatedUserId};
long dummygroupid=16206;
System.out.println("TEST UserID="+newlyCreatedUserId);
System.out.println("TEST GroupID="+dummygroupid);
//Everything went well until here.
UserServiceUtil.addUserGroupUsers(dummygroupid, userIds);
//below sysout is not printed. and no exception or user in group db created.
System.out.println("user added to group");
}
}
public String render(StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, RenderRequest renderRequest, RenderResponse renderResponse) throws Exception
{
System.out.println("My Custom Render Method is Called");
return originalStrutsPortletAction.render(null, portletConfig, renderRequest, renderResponse);
}
}
for more info please have a look at this thread.
Using UserLocalServiceUtil instead of UserServiceUtil worked. Basically, the difference is that *ServiceUtil checks permissions and *LocalServiceUtil does not.
I'm not sure this is the best idea, but you can use the hook for both modify the user creación jsp and save the value via model listener on user create/modify.
Regsrds

How to get portlet context in config (Liferay)?

I'd like to access the Context of the portlet in config mod (in my implement of ConfigurationAction interface).
I try since hours to get the same Context in my ConfigurationActionImpl.processAction(PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse) as I have in my doView(RenderRequest renderRequest, RenderResponse renderResponse), but without any good result.
In my doView(), I can access my portlet Context using getPortletContext() (same as getPortletConfig().getPortletContext()) and renderRequest.getPortletSession() (it's NOT the same Context instances), but I don't know how I can access one of those objects from my processAction().
Can somebody help me, please ?
This is the method I ended up using:
PortletBag portletBag = PortletBagPool.get(portletId);
DispatcherPortlet portlet = (DispatcherPortlet)portletBag.getPortletInstance();
PortletContext pCtx = portlet.getPortletContext();
In case you want to compute the portletId, you can do this:
String portletResource = ParamUtil.getString(renderRequest, "portletResource");
String portletId;
if (portletResource.contains("_INSTANCE")) {
portletId = portletResource.substring(0, portletResource.indexOf("_INSTANCE"));
} else {
portletId = portletResource;
}
As a side note, I want to mention that I needed it in order to be able to get the Spring ApplicationContext of the portlet, which I did with this:
PortletContext pCtx = portlet.getPortletContext();
ApplicationContext portletAppContext = (ApplicationContext)pCtx.getAttribute(FrameworkPortlet.PORTLET_CONTEXT_PREFIX + portlet.getPortletName());

Resources