I am using Liferay 6 for the Portal Development .
By going through Liferay Developer Guide the author explains that there are Two phases of Portlet Execution
Action Phase
Render Phase
public class DateTimePortlet extends GenericPortlet
{
public void doView(RenderRequest req, RenderResponse res) throws IOException, PortletException
{
Object actionAttribute = req.getAttribute("datetime");
res.getWriter().println("Date Time:" + (actionAttribute != null ? actionAttribute :"Unavailable"));
res.getWriter().println("<BR/>");
PortletURL u = res.createActionURL();
res.getWriter().println("<A href=" + u + ">Trigger an action.");
res.getWriter().close();
}
public void processAction(ActionRequest req, ActionResponse res) throws PortletException
{
req.setAttribute("datetime",new Date());
}
}
My understanding is that the doView method is known as "Render Phase" and the processAction Method is known as "Action Phase".
And if there are 5 portlets displayed on a page, the "Render Phase" (That is the code inside the doView Method) is executed for every Page refresh.
Please let me know if i am correct.
Yes, correct: There's max. 1 portlet handling an action per request, but all of the portlets on the page will have a render request running (unless the output is cached, but let's put aside this rather advanced stuff)
There can also be 0 action handling on a request, when just rendering is done (this is the most common operation typically executed on a portlet. You must (and can) not change any state in this phase.).
Following an Action, the event phase can be triggered (see Inter-Portlet-Communication, IPC) that can be executed on any number of portlets.
If you don't want a full page reload, you'll have to look into the resource-phase where you can handle AJAX calls and serve all kinds of different resources other than the usual page fragments that a portlet is meant to serve.
Related
I recently started learning liferay(7.1.2 ga3). my requirement is to change the login code(i.e write my own code for login) not look and feel in login.jsp.
I created a hook file with the following steps(in liferay developer studio) New -> Liferay Module Project -> Project Name as CustomLogin -> Build Type as Maven -> Project Template Name as war-hook -> then Finish.
After This folder was created with the name as CustomLogin and in that src -> main -> java -> CustomLogin -> I can see two Files CustomLoginLoginPostAction.java and CustomLoginStartupAction.java
in CustomLoginStartupAction.java
package CustomLogin;
import com.liferay.portal.kernel.events.ActionException;
import com.liferay.portal.kernel.events.SimpleAction;
public class CustomLoginStartupAction extends SimpleAction {
#Override
public void run(String[] lifecycleEventIds) throws ActionException {
for (String eventId : lifecycleEventIds) {
System.out.println("Startup event ID " + eventId);
}
}
}
in CustomLoginLoginPostAction.java
package CustomLogin;
import com.liferay.portal.kernel.events.Action;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.service.UserLocalServiceUtil;
import com.liferay.portal.kernel.util.PortalUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CustomLoginLoginPostAction extends Action
{
#Override
public void run(HttpServletRequest request, HttpServletResponse response)
{
long userId = PortalUtil.getUserId(request);
User user = UserLocalServiceUtil.fetchUser(userId);
System.out.println(user.getFirstName() + " has logged in.");
}
}
But I dont't know what to do after this. please help. or give some sample code.
Strong recommendations:
As you start learning Liferay, start with the current version, not with one from January 2019 (original 7.1.x release in 2018)
Start with OSGi plugins - they deploy a lot quicker than WAR archives and require a lot less memory at runtime
think about the requirement: You're using a platform that gives you the option to ignore all of the user management worries, and the first thing you do is to take back control of this delicate issue. Rather: Use the platform to your advantage. Consider using LDAP or SSO if you need to authenticate against another user database: That's configuration, not code - so nothing for you to maintain
That being said, the code that you posted adds to the login procedure. E.g. the LoginPostAction is executed after Liferay's own login code is already executed. This enables you to intercept a login and impose custom rules when you know what user you deal with (and that they authenticated correctly). Similarly, there's a similar LoginPreAction, that's allowing you to intercept the login process before the user's login is applied.
(A StartupAction is the wrong path - nothing to do with the login process - don't follow that path for this purpose)
What you typically do in these actions is to either deny access based on unconfigurable criteria (e.g. may the user log in from that IP? At this time?) or initialize some additional environments (e.g. triggering some backend action, initialize some information in the session). For this you can determine to do this before or after the login succeeded.
If - against my recommendation - you decide that you'll still want to customize the login process: This code for 7.0 should still work on 7.1 (and likely even on 7.4, the current version). It involves writing a custom login portlet, and the relevant code is in this class, after retrieving the HttpServletRequest (I'm omitting the presentation layer from the linked sample)
#Component(
property = {
"javax.portlet.name=MyLoginPortlet",
"mvc.command.name=/login/login"
},
service = MVCActionCommand.class
)
public class MyLoginMVCActionCommand extends BaseMVCActionCommand {
#Override
protected void doProcessAction(ActionRequest actionRequest,
ActionResponse actionResponse) throws Exception {
ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
WebKeys.THEME_DISPLAY);
HttpServletRequest request = PortalUtil.getOriginalServletRequest(
PortalUtil.getHttpServletRequest(actionRequest));
HttpServletResponse response = PortalUtil.getHttpServletResponse(
actionResponse);
String login = ParamUtil.getString(actionRequest, "login");
String password = actionRequest.getParameter("password");
boolean rememberMe = ParamUtil.getBoolean(actionRequest, "rememberMe");
String authType = CompanyConstants.AUTH_TYPE_EA;
AuthenticatedSessionManagerUtil.login(
request, response, login, password, rememberMe, authType);
actionResponse.sendRedirect(themeDisplay.getPathMain());
}
}
But really: Use your time to add business value and work on business problems. Be happy that the infrastructure layer is taken care of already.
I am new in OAF. I am writing a small program for displaying data in the browser window. But as per the OAF Documentation, when a page loads processRequest() should be called automatically. But in my case the processRequest() method is not called. So any one please help me to get the processRequest() method to be called when page is loaded.
This is my Controller code. Note I associate this controller to a page. While loading the page, processRequest() method is not called.
public class MyController extends OAControllerImpl
{
public static final String RCS_ID = "$Header$";
public static final boolean RCS_ID_RECORDED =
VersionInfo.recordClassVersion(RCS_ID, "%packagename%");
/**
* Layout and page setup logic for a region.
* #param pageContext the current OA page context
* #param webBean the web bean corresponding to the region
*/
public void processRequest(OAPageContext pageContext, OAWebBean webBean)
{
/* The below code line is used to initialize the application module */
System.out.println("inside processRequest");
OAApplicationModule am =
(OAApplicationModule)pageContext.getApplicationModule(webBean);
// am.invokeMethod("execVO");
/* The below code line is used to initialize VO*/
OAViewObject vo = (OAViewObject)am.findViewObject("EmpView1");
/* DataDisplayVO1 is the instance name in AM which is the original name of the VO */
vo.executeQuery();
RowSetIterator rowsetIterator = vo.createRowSetIterator(null);
while (rowsetIterator.hasNext())
{
Row r = rowsetIterator.next();
System.out.println("Empno is ... " + r.getAttribute("Empno"));
}
}
This is impossible. In my experience, till now I have never faced any issues like this.
On a second thought, just thinking if the controller is not assigned to the page. It may happen although in rarest case. And the case is you have run the page before attaching the controller and the page xml is stored in your classes directory. This directory is refreshed on each run, but rarely it doesn't get refreshed.
Try to rebuild your application, if possible delete the classes folder content of your relevant package. Hopefully it may help.
Suppose I want to pass one object (reference) through several pages. I can navigate and pass parameters via Frame.Navigate(typeof(FirstPage), object). But how to pass the reference back on back press properly?
protected override void OnNavigatedTo(NavigationEventArgs e) {
if (e.Parameter is SomeClass) {
this.someObject = (SomeClass)e.Parameter;
}
else {
this.someObject = new SomeClass();
}
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
SystemNavigationManager.GetForCurrentView().BackRequested += OnHardwareButtonsBackPressed;
base.OnNavigatedTo(e);
}
private void OnHardwareButtonsBackPressed(object sender, BackRequestedEventArgs e) {
// This is the missing line!
Frame.Navigate(typeof(FirstPage), this.someObject);
}
But when I press back button it goes back to the FirstPage OnNavigatedTo with no parameter, and then back to the SecondPage OnHardwareButtonsBackPressed and then back to FirstPage OnNavigatedTo with filled parameter.
Could you please advice me some better approach?
In your back handler, don't navigate forwards again, just call GoBack -- and it's typically easier if you handle that at a global level rather than at a page level.
You can store your application state (the things you want to persist across page navigations) in global / static objects, or you could directly modify the object that was passed from the initial navigation (if the calling page still has a reference, it will be able to see the changes).
I would consider doing a search for "MVVM Windows Apps" and looking at some of the results to learn about a common way of building XAML apps.
I am invoking a method from rendered attribute, where I noticed that the method is triggered multiple times in RENDER_RESPONSE phase.
It was also noticed that the method was trigered many times in other phases (APPLY_REQUEST_VALUES, PROCESS_VALIDATIONS etc.) also.
I saw a related query (Why is the getter called so many times by the rendered attribute?) where the reason behind these calls were told.
Is there a way where we can control this, so that the method is invoked only once.
My usage
<rich:panelMenuItem label="Menu1" actionListener="#{testMenuMB.panelMenuClickedAjax}" rendered="#{testMenuMB.checkForRendering('RoleA,RoleB')}"></rich:panelMenuItem>
public boolean checkForRendering(String rolesString){
System.out.println("Roles-->"+rolesString+FacesContext.getCurrentInstance().getCurrentPhaseId());
boolean authorized = false;
String [] rolesArray = rolesString.split(",");
for (String string : rolesArray) {
if(string!=null && accesibleRolesMap.containsKey(string)){
authorized = true;
break;
}
}
return authorized;
}
You cannot control the number of times the method is called, its framewroks lifecycle. You should find appropriate place to set the boolean value e.g. on command click action method, use this boolean value in render method so that logic is executed once in action method and only boolean is returned.
Other thing is you can protect the logic with if condition by probing for current lifecycle phase for render response.
if(FacesContext.getCurrentInstance().getRenderResponse()){
//logic
}
But I'll prefer first option.
I have a publishing website build with MOSS 2007 and I need to get the page content programmatically. I know that I can use WebClient to sent a request to the page and then parse the response text. But I want to do it in SharePoint model since the scale is quiet large.
The web parta I want to render are DataFormWebPart and they display fine if viewed in a browser. But I'm getting exceptions when trying to render them programmatically.
The code:
var partMgr = siteCollection.RootWeb.GetLimitedWebPartManager(pageUrl, PersonalizationScope.Shared);
var sr = new StreamWriter(#"d:\temp\test.txt", false);
var htr = new HtmlTextWriter(sr);
foreach (WebPart part in partMgr.WebParts)
{
if (part.GetType() == new DataFormWebPart().GetType())
{
try
{
htr.WriteLine("");
part.RenderBeginTag(htr);
htr.WriteLine("");
part.RenderControl(htr);
htr.WriteLine("");
part.RenderEndTag(htr);
htr.WriteLine("");
}
catch (Exception exc)
{
htr.WriteLine("Message: " + exc.Message);
htr.WriteLine("StackTrace: " + exc.StackTrace);
htr.WriteLine("InnerException: " + (exc.InnerException == null).ToString());
}
}
}
sr.Close();
htr.Close();
The output:
<div id="g_1722aa69_d0d7_4804_83fa_c8f4a250080a">
Message: Value cannot be null. Parameter name: page
StackTrace: at System.Web.UI.WebControls.WebParts.WebPartManager.GetCurrentWebPartManager(Page page)
at Microsoft.SharePoint.WebPartPages.WebPart.Render(HtmlTextWriter writer)
at ExtractPageData.Program.Main(String[] args) in D:\DNR-Playground\ExtractPageData\ExtractPageData\Program.cs:line 49
InnerException: False
<div id="g_1115045e_b0d7_41ce_ad19_15aab0f3871d">
Message: Value cannot be null. Parameter name: page
StackTrace: at System.Web.UI.WebControls.WebParts.WebPartManager.GetCurrentWebPartManager(Page page)
at Microsoft.SharePoint.WebPartPages.WebPart.Render(HtmlTextWriter writer)
at ExtractPageData.Program.Main(String[] args) in D:\DNR-Playground\ExtractPageData\ExtractPageData\Program.cs:line 49
InnerException: False
As you can see in the output, the begin tag is rendered but the control can't be rendered due to part.Page is null and it it's a readonly attribute.
This just won't work. Web parts require proper SPContext which is setup during HTTP request handling in the ASP.NET pipeline. There's no easy answer how to overcome it. You would essentially have to simulate an HTTP request to the page that you need to render in-memory.
Also, this expression part.GetType() == new DataFormWebPart().GetType() is far from ideal. Rather use part.GetType() == typeof(DataFormWebPart) to prevent instantiation and possible unwanted side effects.