Liferay Portlet and JSF : Redirect during Render Phase - redirect

I have a problem to implement a simple HTTP redirection.
I use Liferay 6.0.6, our portlets are build with JSF2.0 / PortletFaces.
I want to call a redirection when a view is loaded (and not when an action is triggered). Currently, my function is called by the PreRenderView listener.
<f:metadata>
<f:event listener="#{myControler.dispatch}" type="preRenderView" />
</f:metadata>
In this function, i can check the permissions, do other stuff, and in some cases I want to redirect the user to a new page (not another view).
I tried several methods, unsuccessfully.
Specifically, I thought that this method would work :
getFacesContext().getExternalContext().redirect(url);
getFacesContext().responseComplete()
// => Can only redirect during ACTION_PHASE
This error is logical, but is there a solution to force the redirection.
It could be realized in another function, called otherwise, I only need the Hibernate Session (set at the beginning of the Render Phase)
Have you ideas to resolve this problem?
Thanks!
ps : <redirect /> or ?faces-redirect don't work with the portlets.

You can't do this in the render phase by design. Reasons:
It's possible that portlets are rendered asynchronously, so the page might already be displayed when your portlet is being rendered
It's possible that parts of the page are already delivered to the client, so that the HTTP Headers are already sent - for this reason, by design you don't have access to them in the render phase
What would be the expected outcome if two portlets rendered on the same page would decide that they'd like to forwards to another page? Who would win?
A hacky workaround is to render some javascript redirect, but this is veeeery un-portal-like and can mess up other's expectations (plus, parts of the page might already be rendered, causing your users to fill a form only to be redirected by your javascript routine.
Please rethink the problem and come up with a different solution - it's really worth doing this in a portal environment.

I use this and it works for me:
public void preRenderView() throws IOException {
if (!checkUtente()) {
FacesContext fc = FacesContext.getCurrentInstance();
NavigationHandler navigationHandler = fc.getApplication().getNavigationHandler();
navigationHandler.handleNavigation(fc, null, "errore.xhtml?faces-redirect=true");
fc.renderResponse();
}
}

Use the below method it will work
public static void redirect(final String url) throws IOException {
final javax.portlet.PortletResponse portletResponse
= getPortletResponse();
if (portletResponse instanceof ActionResponse) {
final javax.portlet.ActionResponse actionResponse
= (javax.portlet.ActionResponse) portletResponse;
actionResponse.sendRedirect(url);
} else if (portletResponse instanceof ResourceResponse) {
final FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx.getPartialViewContext().isPartialRequest()) {
final ResourceResponse portletResResp
= (ResourceResponse) portletResponse;
PartialResponseWriter pwriter;
final ResponseWriter writer = ctx.getResponseWriter();
if (writer instanceof PartialResponseWriter) {
pwriter = (PartialResponseWriter) writer;
} else {
pwriter = ctx.getPartialViewContext()
.getPartialResponseWriter();
}
portletResResp.setContentType(Constants.CONTENT_TYPE);
portletResResp.setCharacterEncoding(Constants.ENCODING_TYPE);
// addResponseHeader("Cache-Control", "no-cache");
pwriter.startDocument();
pwriter.redirect(url);
pwriter.endDocument();
ctx.responseComplete();
} else {
throw new UnsupportedEncodingException(
"Can only redirect during RESOURCE_PHASE "
+ "if a Partial-(JSF AJAX)-Request has "
+ "been triggered");
}
} else {
throw new UnsupportedEncodingException(
"Can not redirect during the current phase: "
+ portletResponse.getClass().getSimpleName());
}
}

Related

Why does the Facebook Graph API respond with the same "next" URL over and over?

I am using Volley to submit POST requests to the Facebook Graph API in order to retrieve information about photos and videos from a user account using their BATCH facility so I get it all in one go (rather than making one call for photos, one for videos). The first call works perfectly:
request = new StringRequest(Request.Method.POST,
"https://graph.facebook.com",
future,
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Timber.e("Got VolleyError: %s", error.getMessage());
}
}) {
#Override
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
JSONArray batchRequest = new JSONArray();
JSONObject photoRequest = new JSONObject();
JSONObject videoRequest = new JSONObject();
try {
photoRequest.put("method", "GET");
photoRequest.put("relative_url",facebookUserID + String.format("?fields=photos.limit(%1$s){id,created_time,images{source},picture}",batchSize));
videoRequest.put("method", "GET");
videoRequest.put("relative_url",facebookUserID + String.format("?fields=videos.limit(%1$s){id,created_time,source,picture}",batchSize));
batchRequest.put(photoRequest);
batchRequest.put(videoRequest);
} catch (JSONException e) {
Timber.d("Lifecycle: Exception constructing batch request: %s", e.getMessage());
return null;
}
//try {
// Timber.d("Lifecycle: batchRequest: %s", batchRequest.toString(2));
//} catch (JSONException e) {
// e.printStackTrace();
//}
params.put("batch", batchRequest.toString());
params.put("include_headers", "false");
params.put(FB_BASE_ACCESSTOKEN_KEY, facebookToken);
return params;
}
};
InTouchUtils.addToRequestQueue(request);
// Using a blocking volley request, this chain has been called on a separate async task
// See SO: https://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley
facebookRetval = future.get(VOLLEY_REQUEST_DEFAULT_TIMEOUT, TimeUnit.SECONDS);
returnResult = parseBatchRequest(facebookRetval);
The returned JSON has all the fields I've requested, as well as the pagination block with cursors, and a "next" and/or "previous" url, per the Facebook documentation.
A "next" URL looks something like:
https://graph.facebook.com/v7.0/FACEBOOK_ID_HERE/photos?access_token=ACCESS_TOKEN_HERE&fields=id,created_time,images{source},picture&limit=5&after=AFTER_TOKEN_HERE
There is one of these that gets passed back from the batch operation for each of the original GET operations (assuming both photos and videos have greater than LIMIT items).
Again, this part works fine.
But when I try and use that "next" URL to create another BATCH call, it fails with an "unsupported GET operation" error. This is true even though I can use a standard Volley GET using that exact same URL and it works perfectly.
I have tried using the "https://graph.facebook.com" portion of the above URL as the root of the POST (like what worked in the initial call), and everything after that as the "relative_url" parameter. No go.
Then I tried parsing out just the "after" portion of the "next" url, and constructing a new relative_url that was exactly like the first one, but tacking on a "&after=" + AFTER_VALUE to it as the relative_url. No go - in fact, while this succeeded in making the call, I keep getting the initial batch over and over and over. It is like it is ignoring the "&after=" parameter.
For now I am back to making two GET calls (one for photos, one for videos) just using the NEXT url as long as it keeps being passed back to me. This works fine, but obviously I'm making two network calls instead of the single batch one.
Ideas?
A little more examination revealed that I had made a string parsing error on the subsequent batch operation, and was inadvertently including a forward slash when I should not have been.
For those new to using the batch API, the lesson is that you need "https://graph.facebook.com" as the POST url (no trailing forward slash), then your relative url should NOT start with a forward slash. So the URL I was trying to utilize on calls 2..N like this:
https://graph.facebook.com/v7.0/FACEBOOK_ID_HERE/photos?access_token=ACCESS_TOKEN_HERE&fields=id,created_time,images{source},picture&limit=5&after=AFTER_TOKEN_HERE
should be broken out as:
photoRequest.put("relative_url", "v7.0/FACEBOOK_ID_HERE/photos?access_token=ACCESS_TOKEN_HERE&fields=id,created_time,images{source},picture&limit=5&after=AFTER_TOKEN_HERE");
The API handles putting in the forward slash between the root and the relative url.

Wicket 6.x https with other host

I have a webpage with a area where users can login. This area
www.host.com/mypage/myarea
should be under https.
The problem is that my https is running on a another host:
www.something-foo.host.com/mypage/myarea
. (loadbalancer stuff...??? I dont know why)
My try is to annotate the Pages with #RequireHttps, an than rewrite the urls of the Pages.
But how and where? Has someone please an example?
Thanks for your help.
Well if you really want to this with Wicket your best option would be to write an implementation of IRequestMapperDelegate and set them during the onInit() process of your WicketApplication.
To give you an idea how to do this I've written an example of raping the HttpsMapper of Wicket:
setRootRequestMapper(new HttpsMapper(getRootRequestMapper(), new HttpsConfig(8080, 8443)) {
private final static String SUBDOMAIN = "www.something-foo.";
#Override
protected Scheme getSchemeOf(Request request) {
HttpServletRequest req = (HttpServletRequest) ((WebRequest) request).getContainerRequest();
// well that's basically cheating and not so nice... but we're not allowed to overwrite mapRequest()
// but that means that every request that doesn't start with the subdomain will be treated as HTTP aka
// insecure.
if (req.getServerName().startsWith(SUBDOMAIN) == false) {
return Scheme.HTTP;
}
return super.getSchemeOf(request);
}
#Override
protected String createRedirectUrl(IRequestHandler handler, Request request, Scheme scheme) {
// stolen from super implementation
HttpServletRequest req = (HttpServletRequest) ((WebRequest) request).getContainerRequest();
String url = scheme.urlName() + "://";
// except the part where we insert the subdomain
url += SUBDOMAIN;
url += req.getServerName();
if (!scheme.usesStandardPort(getConfig())) {
url += ":" + scheme.getPort(getConfig());
}
url += req.getRequestURI();
if (req.getQueryString() != null) {
url += "?" + req.getQueryString();
}
return url;
}
});
Depending on your question I can't really determine if this is a good solution ... it really depends on how many frameworks are working on top of Wicket. Since you didn't mention anything else I'm assuming none.

Trouble posting comments with the LWUIT Facebook API

I'm working on a mobile project and decided to try out the LWUIT framework for development. So far it has been quite interesting although I wish the documentation was a bit better.
I run into a problem trying to post content to facebook using the recently released Facebook API. I'm able to authenticate without issues. However, when I try to post comments to the user's wall, I get a http 404 error.
Has anyone else had this sort of challenge. Below is an excerpt from my code;
protected boolean onShareScreenPost() {
// If the resource file changes the names of components this call will break notifying you that you should fix the code
//boolean val = super.onShareScreenPost();
Form shareForm = Display.getInstance().getCurrent();
final TextField shareField = findShareField(shareForm);
String postText = shareField.getText();
try {
makeFacebookAuthenticationRequest();
FaceBookAccess.getInstance().postOnWall(me.getId(), postText);
} catch (IOException ex) {
ex.printStackTrace();
//Include a dialog saying unable to post or connect to the internet or whatever
}
return true;
}
private void makeFacebookAuthenticationRequest() throws IOException {
FaceBookAccess.getInstance().authenticate("125527160846284", "http://a.b.c/", new String[]{ "publish_stream"});
me = new User();
FaceBookAccess.getInstance().getUser("me", me, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("returned user");
}
});
}
Seeing this question 24 hours later makes me feel a bit silly.
The answer was quite simple and staring me in the face all along. I needed to wait for the Facebook API to return a User object before making additional calls to the API. Failure to do this resulted in a null reference for my user object and this was being used in the wall post request causing the facebook api to return a http 404.
Hope this helps someone...

How to get the complete request that calls my MVC2 controller?

Newbie question … sorry ;-)
I have to write and to integrate a new website in a complex web application.
My new (MVC2) website will be hosted on a separate server and only called when the user clicks on a link in the already existing, complex website.
Means I(!) define the URL which calls my(!) new website.
But “they” (the calling, already existing, complex web application/website) will add an attribute to the url. This attribute is the sessionID.
Ok, I think I understand already that this calls my (MVC2) controller.
But how can I get in my (MVC2) controller the “calling URL” (which include the added sessionID)?
Hopefully that someone understand what I ask ;-)
Thanks in advance!
I want just share my little parser - hopefully it helps someone. ;-)
Also requests like
(Request.Url.Query =) "?sessionID=12345678901234567890123456789012&argumentWithoutValue&x=1&y&z=3"
will be well parsed.
Here my code:
Hashtable attributes = new Hashtable();
string query = Request.Url.Query;
string[] arrPairs = query.Split('&'); // ...?x=1&y=2
if (arrPairs != null)
{
foreach(string s in arrPairs)
{
if (!String.IsNullOrEmpty(s))
{
string onePair = s.Replace("?", "").Replace("&", "");
if (onePair.Contains("="))
{
string[] arr = onePair.Split('=');
if (arr != null)
{
if (arr.Count() == 2)
{
attributes.Add(arr[0], arr[1]);
}
}
}
else
{
// onePair does not contain a pair!
attributes.Add(onePair, "");
}
}
}
You really should set your URL and Route to be more MVC-Like. The URL you are calling should be:
newapp/controller/action/sessionId
Then set your route up:
routes.MapRoute(
"sessionId",
"{controller}/{action}/{sessionId}",
new { controller = "controller", action = "action", sessionId = 0 });
Then in your controller:
public ActionResult Action(int sessionId)
{
}
In your controller you still have direct access to the Request object, so you can use Request.Url, etc.
Does that answer your question, or is it something else that you need?

ajaxcontroltoolkit setting hidden value after asyncfileupload has completed

I have an asyncfileupload control that I'm using from the ajaxcontroltoolkit. On the file complete in the code behind I process the file and write the information in the file to a database. I get the id of the record from the database, and this needs to be written to an asp hidden field. I've tried just setting the value:
fldImageID.Value = pimg.IdImageGroup.ToString();
I've tried Registering a script like I've seen in an example on a website:
ScriptManager.RegisterClientScriptBlock(
ImageFileUploader,
ImageFileUploader.GetType(),
"script1",
"alert('hi'); top.document.getElementById('"
+ fldImageID.ClientID
+ "').value='"
+ pimg.IdImageGroup.ToString()
+ "'; top.document.getElementById('"
+ lblError.ClientID
+ "').innerHTML = 'image uploaded'",
true);
I've just tried embedding javascript in a response.Write call from the method I've set to process the uploaded file. Nothing I've done has worked so far. After I've done everything the hidden field still does not contain the required value.
This is pretty easy with jQuery. Have an html hidden input control placed in your page, not the asp:hidden input control. Add a class lets say "hiddenPhoto" to your html hidden control.
so lets say our control html is like this
<input type="hidden" class="hiddenPhoto" runat="server" id="hdPhotoName" />
Now access it using class selector in your OnClientUploadComplete js method and set its value. Have it declared runat="server" in order to access its value on the server side.
Regards
I found an acceptable solution back when I was working on this. And since then I've received emails from people who have had the same problem and have been asking if I found a solution. So I'm presenting it here, stripping out any extraineous code:
From the user control that has the FileUpload control I first set the session variable on the back side in the FileUploadComplete handler:
*in the ascx file (upload_chart.ascx) I have the AsyncFileUpload, what is important is the OnUploadComplete and the OnClientUploadComplete:*
<ajaxToolkit:AsyncFileUpload
OnUploadedComplete="FileUploadComplete1"
OnClientUploadComplete="UploadComplete1"
ID="ImageFileUploader"
runat="server" />
*in the code behind of the ascx file (upload_chart.ascx.cs) I handle the FileUploadComplete:*
public void FileUploadComplete1(object sender, EventArgs e)
{
try
{
if (ImageFileUploader.FileBytes.Length > 0)
{
// File data is in ImageFileUploaded.FileBytes
// Save it however you need to
// I saved it to a database, in a DBImage Object class I created
// DBImage is specific to my application
ODS.Entity.DBImage pimg =
ODS.Data.DataRepository.SaveImageBytes(ImageFileUploaded.FileBytes);
// Set the ImageID1 in the session
Session["ImageID1"] = pimg.IdImageGroup.ToString();
}
else
{
// error handling for an empty file, however you want to handle it
}
}
catch (Exception Ex)
{
// error handling for an unhandled exception, whatever you want to do here
}
}
Javascript and script methods are used to set the value on the page, here is my codebehind for the script method:
// on the aspx page code behind (chartofthedayadmin.aspx.cs) I have the webmethod:
[System.Web.Services.WebMethod]
public static string GetImageID1()
{
System.Web.SessionState.HttpSessionState Session = System.Web.HttpContext.Current.Session;
String retval = Session["ImageID1"].ToString();
Session["ImageID1"] = null;
return retval;
}
Here is the javascript:
// on the aspx front end (chartofthedayadmin.aspx) I have the javascript
// to call the Web method and the javascript failed message:
function UploadComplete1() {
var str = PageMethods.GetImageID1(uploadSuccess1, uploadFailed);
}
function uploadFailed() {
alert('error occurred or some meaningfull error stuff');
}
*// javascript on the user control (upload_chart.ascx) to set the value of the hidden field*
function uploadSuccess1(result) {
document.getElementById('<%= fldImageID.ClientID %>').value = result;
}
note: Make sure your scriptmanager has EnablePageMethods="true".
The better and more simple solution is in code behind:
string script = String.Format("top.document.getElementById('hdnFilename').value='{0}';", safeFilename);
ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "hdnFilenameFromCodeBehind", script, true);
In my case, safeFilename is the unique filename, after handling duplicate filename, i.e. sample_5.png in the 5th upload of sample.png.
See http://forums.asp.net/t/1503989.aspx