I have a problem related to the monitorDownload functionality, more specifically, it seems that the complete method is never triggered and as a result, the entire page is blocked.
I have a commandButton that once pressed, triggers the download:
<p:commandButton id="commandButtonCI"
value="#{labels['target_checkout']}"
actionListener="#{contentBean.checkOut()}" ajax="false"
onclick="PrimeFaces.monitorDownload(start, updatemapcourse, 'mapCourse')"
styleClass="size_button">
<p:fileDownload value="#{contentBean.file}"/>
</p:commandButton>
The start function calls:
PF('uiBlocker').show();
which blocks the screen, while the complete function (updatemapcourse) is a remoteCommand, but I never reach this point. Initially, I thought that the monitorDownload is not creating the 'primefaces.download' cookie after the checkOut() method is executed, but I can see it in the Cookies section of Chrome DevTools. The problem is that the cookie is always marked as httpOnly (I tried to create a custom cookie, similar to 'primefaces.download', mark it as httpOnly = false, but it is always set to httpOnly = true). From what I understood, JS cannot read httpOnly cookies and that is the reason the monitorDownload never calls the complete method. I tried to implement a custom monitorDownload without using cookies:
PrimeFaces.monitorDownload = function(start, complete, monitorKey) {
if(this.cookiesEnabled()) {
if(start) {
start();
}
window.downloadMonitor = setInterval(function() {
var downloadComplete = false;
if(monitorKey == 'mapCourse') {
downloadComplete = document.getElementById("targetTableauxTab:mapcourseForm:mapCourseDownloadFlag").value;
}
if(downloadComplete === true) {
if(complete) {
complete();
}
clearInterval(window.downloadMonitor);
downloadComplete = false;
}
}, 1000);
}
}
Instead of cookies, I thought that I could use an hiddenInput that would be updated by the checkOut() method:
<h:inputHidden id="mapCourseDownloadFlag" value="#{contentBean.primefacesDownloadFlag}"/>
The problem that I now have is that the hidden input is never updated with the value set in checkOut() because of the ajax="false". I also cannot use oncomplete or onsuccess events because of this. Unfortunately, I don't have any other ideas on how to handle this error. I thought that maybe someone from here had the same problem and found a workaround.
The Primefaces version I use is 8.0.3.
Related
Help, I'm Stuck! I am playing with a CRUD setup with Node Express but with AJAX post request. I have the read form working fine.
The form has one input filed which is a lookup email. AJAX post the form data with the following code
if ($("#rsvp-search-form").length) {
$("#rsvp-search-form").validate({
rules: {
...
},
messages: {
...
},
submitHandler: function (form) {
$("#loader").css("display", "inline-block");
$.ajax({
type: "POST",
url: "/",
data: $(form).serialize(),
success: function (data, textStatus, jqXHR){
if (typeof data.redirect == 'string')
window.location = data.redirect;
}
,
error: function() {
$( "#loader").hide();
$( "#error").slideDown( "slow" );
setTimeout(function() {
$( "#error").slideUp( "slow" );
}, 5000);
}
});
return false; // required to block normal submit since you used ajax
}
});
}
I have a express post route to '/' that returns a status with res.status(#).send() and the proper success/error block is executed based on the whether status # is 400 or 200.
Now on the update form I have the same basic setup with many more form inputs, but the AJAX code does not process the res.status(#).send() response by executing the proper success or error block, instead it is just loading a new page with the same url as the request was processed from.
The AJAX code request is similar to the top with difference of url:
submitHandler: function (form) {
$("#loader").css("display", "inline-block");
$.ajax({
type: "POST",
//The website when loaded has an invitation
//object that is passed by express
url: "/rsvp/" +invitation._id,
data: $(form).serialize(),
dataType: 'application/json'
I verified that the proper post route is running and receiving the invitation._id. It returns res.staus(#).send() but the ajax does not process the success or error block it just redirects to the requesting url but does not actually render the url.
I don't know if it is just that the form is still processing the default action, if the response from express is not correct, etc etc
I hope I have been clear on my issue and someone knows what I am doing wrong here.
Regards!
Update!
I got it working. Though the url with variable was correct and express was receiving the proper id, JS was throwing an error causing everything to crash. I never caught the error because the page would reload to the blank page and clear the console. I fixed it by saving the id in a hidden field when rendering the form and used that instead. Seems to have fixed the problem.
Thanks for looking!
I am using the Synchronizer Token Pattern for standard forms (useToken = true) but I cannot find any recommended method of dealing with this over AJAX.
EDIT
Since posting this, I have rolled my own solution incorporating Grails existing pattern from above.
In the jQuery ajax I post the entire form (which will include Grails' injected SYNCHRONIZER_TOKEN and SYNCHRONIZER_URI hidden fields) such that the withForm closure can perform as expected in the controller.
The problem is, on successful response, there is no new token set (as the page is not reloaded and the g:form taglib is not evoked) and so I do this manually in the controller calling into the same library as the g:form taglib, and return it in the ajax response, and then reset the hidden field value.
See below:
var formData = jQuery("form[name=userform]").serializeArray();
$.ajax({
type: 'POST',
url: 'delete',
data: formData,
success: function (data) {
// do stuff
},
complete: function (data) {
// Reset the token on complete
$("#SYNCHRONIZER_TOKEN").val(data.newToken);
}
})
in the Controller:
def delete(String selectedCommonName) {
def messages = [:]
withForm {
User user = User.findByName(name)
if (user) {
userService.delete(user)
messages.info = message(code: 'user.deleted.text')
} else {
messages.error = message(code: 'user.notdeleted.text')
}
}.invalidToken {
messages.error = message(code: 'no.duplicate.submissions')
}
// Set a new token for CSRF protection
messages.newToken = SynchronizerTokensHolder.store(session).generateToken(params.SYNCHRONIZER_URI)
render messages as JSON
}
Can anyone identify if I have unknowingly introduced a security flaw in the above solution. It looks adequate to me but I don't like hand rolling anything to do with security.
Nice!
IMO, you'd better reset the token at the same time.
SynchronizerTokensHolder.store(session).resetToken(params.SYNCHRONIZER_URI)
and if you have multiple forms in the same page, define a variable to hold tokens returned from each ajax request.
btw, why not implement the token pattern on your own?
Generate a token, e.g., UUID.randomUUID().toString(), and store it into session with the url as the key.
Check and reset the token at the satrt of post actions.
I have an XAgent I have created that works just fine via window.location but I can't get it to work via AJAX. This agent is called from a delete button on a popup div, so rather than writing to my responseStream in my XAgent, I'd prefer to just run my agent and close my popup via javascript when it is finished.
My XAgent is called by the URL doc.$DBPath.value + "/xAgent_DeleteDemand.xsp?open&id=" + doc.$DocUNID.value and looks like this:
javascript:importPackage(foo);
try {
var url:java.lang.String = context.getUrl().toString();
print(url);
if (param.containsKey("id")) {
var unid = param.get("id");
} else {
throw "No unid given";
}
XAgent.deleteDemand(unid);
} catch (e) {
print(e);
}
My actual code is in the foo package but that doesn't seem relevant because I'm not even getting my URL printed. I can say the the URL being generated and called works just fine using window.location so it is safe to assume that the problem is elsewhere.
I have a sneaking suspicion that maybe context doesn't have any meaning when called via AJAX from a non XPage app, but I don't know for sure.
I don't think there is anything special about my AJAX code but here it is just in case. It has been working fine for a long time.
function createAJAXRequest(retrievalURL, responseFunction) {
if (window.ActiveXObject) {
AJAXReq = new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
AJAXReq = new XMLHttpRequest();
}
showHideIndicator("block")
var currentTime = new Date()
AJAXReq.open("GET", retrievalURL + "&z=" + currentTime.getTime());
AJAXReq.onreadystatechange = eval(responseFunction);
AJAXReq.send(null);
}
I'm not sure what the immediate problem would be, but as some troubleshooting steps:
The resultant URL is just server-relative and not on a different server+protocol combination, right?
Do you see anything on the browser's debug console when clicking the button?
Is there an entry in the browser's debug Network panel for the request at all?
I have a simple app I'm building using Play + AngularJS that requires authentication before most routes can be accessed. The login flow includes a "remember me" feature that stores a session ID in to the browser local storage and gets mapped to a valid authorized database session entry on the server side any time a user returns to the app.
The problem I'm having is that I do the session checking (extract cookie & compare against server) in the run() function of the module:
.run(function ($rootScope, $http, $cookieStore, $location) {
// <snip>
// check if there is already a session?
var sessionId = window.localStorage["session.id"];
if (sessionId == null) {
sessionId = $cookieStore.get("session.id");
}
if (sessionId != null) {
$http.get("/sessions/" + sessionId)
.success(function (data) {
$http.defaults.headers.common['X-Session-ID'] = data.id;
$cookieStore.put("session.id", data.id);
$rootScope.user = data.user;
})
.error(function () {
// remove the cookie, since it's dead
$cookieStore.remove("session.id");
window.localStorage.removeItem("session.id");
$location.path("/login");
});
} else {
if ($location.path() != "/login" && $location.path() != "/signup") {
$location.path("/login");
}
}
});
The problem is that this function executes an AJAX call and I don't know if the session is valid until it completes. However, the controller that loads (via the route selected by $routeProvider) can fire away another AJAX call that often kicks off before the other one finishes, resulting in a race condition and the initial request getting a 401 response code.
So my question is: how can I force run (with its associated $http call) to complete before any other part of the app runs? I have tried using $q/promise here and it doesn't seem to make a difference (perhaps run functions don't honor promises). I've been advisor to use resolve feature in $routeProvider but I don't know exactly what to do and I'm not super execited about having to put that in for every route anyway.
I assume this is a pretty common use case and it gets solved every day. Hopefully someone can give me some direction with my code, or share their approaches for "remember me" and AngularJS.
You need to manual bootstrap your app after you get session from server.It's easy if you use jQuery for example you can do, or even without jQuery you can use injector to access $http before bootstrapping
$.get(server,function(){
//success , set variable.
}).fail(function (){
//failed :( redirect to login or set session to false etc... null
})
.always(function(){
//alwyas bootstrap in both case and set result as a constant or variable Angular.module('app').variable('session',sessionResult);
});
I'm on phone right now, but this should give u the idea
I need to detect unsaved data in form when user leaves the page without submitting the form. I would like to implement that without adding a value change listener to each input.
This is the functional requirement:
"User open a page than click on any link if values in the page changed an alert message popup to notify user that he need to save changed data, but if did not change any thing system continue without notify user".
I tried to compare array method to compare both DTO coming for DB and the bind DTO, but it gives me a lot of problems in array length, and byte comparison.
This is normally implemented in the client side with help of JavaScript, because it's not nicely possible to intercept on beforeunload event from the server side on when the enduser leaves the page.
Here's a concrete example with help of the JavaScript library jQuery (otherwise you would end up with 10 times as much code to make it properly crossbrowser compatible and work seamlessly together with ajax re-renders):
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(function() {
// Set the unload message whenever any input element get changed.
$(':input').on('change', function() {
setConfirmUnload(true);
});
// Turn off the unload message whenever a form get submitted properly.
$('form').on('submit', function() {
setConfirmUnload(false);
});
});
function setConfirmUnload(on) {
var message = "You have unsaved data. Are you sure to leave the page?";
window.onbeforeunload = (on) ? function() { return message; } : null;
}
</script>
Just paste this in your <h:head> template or just put it in some script.js file which you include by <h:outputScript>.
This worked for me.
$(function() {
// Set the unload message whenever any input element get changed.
$('input').change(function() {
setConfirmUnload(true);
});
// Turn off the unload message whenever a form get submitted properly.
$('form').submit(function() {
setConfirmUnload(false);
});
});
function setConfirmUnload(on) {
var message = "You have unsaved data. Are you sure to leave the page?";
window.onbeforeunload = (on) ? function() { return message; } : null;
}