MS AJAX call on pageUnload - web-services

I have a legacy WebService referenced in a ScriptManager
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/AJAX_SERVICE.asmx" />
</Services>
</asp:ScriptManager>
And defined like
<System.Web.Script.Services.ScriptService()> _
Public Class CAjaxService : Inherits System.Web.Services.WebService
<WebMethod(enableSession:=True)> _
Public Sub SomeMethod()
...
End Sub
...
End Class
After that to call the method from JavaScript client code I can simple do
CAjaxService.SomeMethod()
I need to execute this call when user navigates away from the current page, so I placed it into page unload event:
function pageUnload(sender, args) {
CAjaxService.SomeMethod()
}
The problem is - the call is async and page navigates away before call is complete (if I place alert() after the call - it goes thru). I've seen similar problems with jQuery ajax calls (and recommended solution to make a synchronous call) but I am not sure if this is possible in MS Ajax.
What would be the way to execute (and complete) an AJAX call upon user navigating away from current page?

You should attach to the onBeforeUnload event instead of the onUnload event.
The onUnload event actually fires when the first byte of the next page is downloaded. At this time the entire context of the previous page starts to get destroyed, and there's a very low chance that you will be able to create network connections. The best that you could do at this point is set a cookie via JavaScript.
The onBeforeUnload event, however, fires before the request for the next page is made, and you have enough time to make a network call to your server to pass it some information. Make your Ajax call within this method.

I try to reply your requirements I create a simple web service that sleep for 5 seconds
[WebService(Namespace = "http://tempuri.org/")]
[ScriptService]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CAjaxService : System.Web.Services.WebService {
[WebMethod(true)]
public string SomeMethod() {
Thread.Sleep(5000);
return DateTime.Now.ToString(CultureInfo.InvariantCulture);
}
}
I try to call it synchronusly but the only way to do it using the MS Ajax Library is to extends the Sys.Net.XMLHttpExecutor you can see the post
I can make the page to wait in the unload event using jquery and make the ajax call synchronously with this code
<script src="Scripts/jquery-2.0.3.js"></script>
<script>
window.onbeforeunload = function () {
$.ajax({
type: "POST",
url: "CAjaxService.asmx/SomeMethod",
async: false,
});
};
</script>
I hope this will help you

I ended up implementing my own function
CAjaxService.SomeMethodSync = function() {
...
}
where I do a manual synchronous post request. Not the most elegant method, but it works.

Related

How to check Livewire event is completed

I have 3 components Map, Field & Section. Map is the parent component. Field & Section are child components.
There is an action called testField in Field component. Inside the test function first I want to trigger an event SaveSection on Section component and then continue other stuff.
// In field component
public function testField(): void
{
// Save section
$this->emitTo('section', 'saveSection');
Log::debug('Testing.....');
}
// In section component
public function saveSection()
{
Log::debug('saveSection+++++');
$this->emitTo('map', 'storeSection', $this->section, $this->sectionIndex);
}
// In map component
public function storeSection(array $section, int $sectionIndex)
{
Log::debug('storeSection-----');
// Store to DB
....
}
But it prints Testing..... before storeSection-----. Is there way I can wait for events to finish before continue.
Hi based on our conversation, I think you would want to go with hooking into Livewire and listening for message processed events.
The first step would be to wrap Log::debug('Testing.....'); in a function like this. The original idea came from #Prospero Livewire show loader when an event is emitted
public function testLog()
{
Log::debug('Testing.....');
}
You might want to tweak this to your satisfaction as this is just a recommendation.
Now you need to prepare your application to stack scripts in the base view right before the closing body tag. eg in app.blade.php like this
<body>
...//Whatever thats before it
#stack('scripts')
</body>
This would help you stack the custom script tag you would define in the component view.
After this you now use the Livewire hook. Do this properly to avoid DOM diffing issues as Livewire would cry tears in your developer console:
<script>
document.addEventListener('livewire:load', function () {
Livewire.hook("message.processed", (message, component) => {
if (message.updateQueue[0].payload.event === "storeSection") {
// I have not implemented it this way so try any of them
//#this is a brilliant decorator that finds the component
#this.emit('testLog') or #this.call('testLog')
}
});
})
</script>
This would show Testing....`` after storeSection``` is processed.
Please do let me know if this does not work as I might have to implement changes.

Is Qooxdoo protected against XSS

I'm looking for informations about security on Qooxdoo.
I want to check my app vs OWASP top 10
A point to review is the XSS OWASP A3 XSS
How can I be sure that Qooxdoo is secure against XSS attacks ?
Does Qooxdoo use some sanitizer tools ?
SOLVED
A short answer from all the discussions. Yes Qooxdoo is XSS safe. By default, no javascript value in any field will be executed.
But, if you use rich=true, you have to check input/output
A common XSS attack vector are situations where an attacker somehow inputs JS code into a web application, such that this code then shows up in the DOM of a webpage and gets thus activated.
To protect against this kind of XSS, you must make sure that the backend server does not send user generated (un-cleaned) html towards the browser ... (this has nothing to do with qooxdoo).
That said, the regular qooxdoo widgets do not in general display data as html so you are reasonably safe even without a clever server. The exception is the qx.ui.basic.Label widget and its descendants. The Label widget has the ability to display HTML directly if you set the rich property. The rich property is set to false by default, but if you enable it, you have to make sure you don't display 'dangerous' html content.
Only very few (non essential) qooxdoo widgets allow you to insert HTML code into the DOM. In these instance you have to take care to sanitize the data. The widgets in question are:
qx.ui.embed.Html
qx.ui.table.cellrenderer.Html
qx.ui.progressive.renderer.table.cell.Html
qx.ui.virtual.cell.Html
qx.ui.virtual.layer.HtmlCell
qx.ui.virtual.layer.HtmlCellSpan
If you do use qx.html.* and qx.bom.*and qx.dom.* objects to work with the DOM directly, you are beyond the reach of qooxoo and have to take care to act accordingly.
Another important attack vector are authentication cookies. Most of the attacks work by getting the browser to send a request together with the cookie to its server without the user being aware it.
Qooxdoo itself does not require you to use cookies at all. Since qooxdoo applications by design run in a single browser window, you can work without ever using cookies. An easy way of implementing something like this is to have a 'server access singleton' which takes care of all the communication with the backend and supplies the access token in a special header added to every request.
The code below could serve as a guide ... for the cookie problem.
qx.Class.define('myapp.Server', {
extend : qx.io.remote.Rpc,
type : "singleton",
construct : function() {
this.base(arguments);
this.set({
timeout : 60000,
url : 'QX-JSON-RPC/',
serviceName : 'default'
});
},
properties: {
sessionCookie: {
init: null,
nullable: true
}
},
members : {
/**
* override the request creation, to add our 'cookie' header
*/
createRequest: function() {
var req = this.base(arguments);
var cookie = this.getSessionCookie();
if (cookie){
req.setRequestHeader('X-Session-Cookie',this.getSessionCookie());
}
return req;
}
}
});
and if you provide a login popup window in myapp.uiLogin you could replace
the standard callAsync by adding the following to popup a login window if the backend is unhappy with your request.
/**
* A asyncCall handler which tries to
* login in the case of a permission exception.
*
* #param handler {Function} the callback function.
* #param methodName {String} the name of the method to call.
* #return {var} the method call reference.
*/
callAsync : function(handler, methodName) {
var origArguments = arguments;
var origThis = this;
var origHandler = handler;
var that = this;
var superHandler = function(ret, exc, id) {
if (exc && exc.code == 6) {
var login = myapp.uiLogin.getInstance();
login.addListenerOnce('login', function(e) {
var ret = e.getData();
that.setSessionCookie(ret.sessionCookie);
origArguments.callee.base.apply(origThis, origArguments);
});
login.open();
return;
}
origHandler(ret, exc, id);
};
if (methodName != 'login') {
arguments[0] = superHandler;
}
arguments.callee.base.apply(this, arguments);
},
take a look at the CallBackery application to see how this works in a real application.

Tracking a Page Event from ASHX Handler

Im currently trying to track a PageEvent within a ASHX Handler. My code basically looks like this:
public class GetProductPdf : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (!Tracker.IsActive)
{
Tracker.Initialize();
Tracker.StartTracking();
}
//Track PageEvent here...
}
public bool IsReusable
{
get
{
return false;
}
}
}
The Tracker is always inactive and Tracker.Current == null. On method call "Tracker.StartTracking();" the following Exception is thrown:
[InvalidOperationException: Tracker.Current is not initialized]
Sitecore.Analytics.Pipelines.StartAnalytics.StartTracking.Process(PipelineArgs args) +317
(Object , Object[] ) +83
Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +445
Project.Web.Handler.PdfCreation.GetProductPdf.ProcessRequest(HttpContext context) in d:\Project\Website\Handler\PdfCreation\GetProductPdf.ashx.cs:69
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +913
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
I tried all possible solutions suggested here.
When doing the same in a mvc controller the Tracker is active and Tracker.Current != null.
Does anyone has an idea, what could cause this or are there any other suggestions for a solution?
Thanks in advance.
I am not certain that your Ashx Handler can be executed within the necessary Sitecore Context so that Tacker.Current will not be valid nor can be started via Tracker.StartTracking(). Someone might be able to confirm but I have another solution you can try which works for me.
As nice as it would be for the Ashx Handler to register the Event for you, instead you can fire a JavaScript function on the link to the file. So that when the link is clicked the JavaScript makes a web request to a MVC Controller and the controller registers the event for you.
I have implemented this myself using WebApi Controllers. Data Attributes were on the a tag, JavaScript posted those attributes to the controller, the controller used those attributes to determine which Event to register and the description to use on the Event.
<asp:HyperLink runat="server" data-goalid="{08030449-A811-428B-95F0-59FCD42B8DEB}" data-goaldescription="Product 0112 brochure">
[System.Web.Mvc.HttpPost]
public JsonResult RegisterGoal(string goalId, string goalDescription)
{
Item eventItem = Sitecore.Context.Database.GetItem(goalId);
var goal = new PageEventItem(eventItem);
var eventData = Tracker.Current.PreviousPage.Register(goal);
eventData.Data = goal["Description"] + " " + goalDescription;
Tracker.Current.Interaction.AcceptModifications();
return Json(new PageEventRequestResult()
{
Success = true,
Message = "Successfully registered goal",
});
}
It works really well. The only downside is having to add it to the various links that lead to the files you want to track.
I wrote a blog about tracking various interactions on a site and registering Sitecore Events / Goals you might want to look at, scroll down to the 'Storing custom data in xDB' section.

test of angularjs controller: unsatistifed post request

I'm testing an angularjs controller, using also mocks, but it raises the error 'Error: Unsatisfied requests: POST /myurl
My file for test contains a beforeEach method like this
httpBackend.whenPOST('/myurl')
.respond( 200,obj1 );
httpBackend.expectPOST('/myurl')
scope = $rootScope.$new();
MainCtrl = $controller('MyCtrl', {
$scope:scope
});
and my test case is:
it('scope.mymethod should work fine', function(){
httpBackend.flush()
// verify size of array before calling the method
expect(scope.myobjs.length).toEqual(2)
// call the method
scope.saveNewPage(myobj)
// verify size of array after calling the method
expect(scope.myobjs.length).toEqual(3)
})
The method saveNewPage looks like:
function saveNewPage(p){
console.log('Hello')
$http.post('/myurl', {
e:p.e, url:p.url, name:p.name
}).then(function (response) {
otherMethod(new Page(response.data.page))
}, handleError);
}
Note that console.log('Hello') is never executed (in karma console it's never printed).
EDIT:
In the meanwhile I'm studying the doc about httpBackend, I tried to change the position of httpBackend.flush(). Basically, i'm executing a first flush(), to initialize data in the scope, then I execute the method, and then I execute an other flush() for the pending request. Specifically, in this situation the test case look like:
it('scope.saveNewPage should work fine', function(){
var p=new Object(pages[0])
httpBackend.flush()
httpBackend.whenPOST('/myurl',{
url:pages[0].url,
existingPage:new Object(pages[0]),
name:pages[0].name
}).respond(200,{data:pages[0]})
httpBackend.expectPOST('/myurl')
scope.saveNewPage(p)
httpBackend.flush()
expect(scope.pages.length).toBe(3)
})
But now it raises Error: No response defined !, like if I didn't specified the mock for that url
I solved in this way:
I put the calls of whenPOST and expectPOST before calling the method to test
I put httpBackend.flush() after calling the method to test, such that, invoking the method it generates pending request, and by httpBackend.flush() it satisfies the pending requests
I adjusted the parameter of respond method. Basically it didn't need to associate the response to a data key of the response
Assuming the POST is supposed to come from saveNewPage, you will need to call httpBackend.flush() between saveNewPage and the line where you inspect the result. flush only flushes the responses that have already been requested by your code.
it('scope.mymethod should work fine', function(){
expect(scope.myobjs.length).toEqual(2)
scope.saveNewPage(myobj)
expect(scope.myobjs.length).toEqual(2)
httpBackend.flush()
expect(scope.myobjs.length).toEqual(3)
})

Attaching a cookie to a view in Symfony2

I've found a few questions and pages dealing with cookies in Symfony2 but there doesn't seem to be any clear consensus on exactly how this is supposed to work. I can, of course, just fall back to using PHP's native setcookie function but I feel that it should be an easy thing to do with Symfony2 as well.
I have an action in my controller from which I simply want to return a view with a cookie attached. Thus far I have seem examples basically like this:
use Symfony\Compentnt\HttpFoundation\Response;
public function indexAction() {
$response = new Response();
$response->headers->setCookie(new Cookie('name', 'value', 0, '/');
$response->send();
}
The problem with this is that it sends the response... and doesn't render the view. If I set the cookie without sending the headers the view is rendered but the header (cookie) is not sent.
Poking around I found the sendHeaders() method in the Response object so I'm now manually calling that in my action before returning and that seems to work:
public function indexAction() {
...
$response->sendHeaders();
return array('variables' => 'values');
}
But is this really the expected pattern to use? In previous versions of symfony I could set the headers in my controller and expect the view controller to handle sending whatever I had sent. It seems now that I must manually send them from the action to get it to work, meaning I have to call this from any action that I set headers in. Is this the case or is there something that I'm missing that's so obvious that no one has bothered to even mention it in any of the documentation?
I think you're on the right lines with:
$response->headers->setCookie(new Cookie('name', 'value', 0, '/'));
If you're trying to render a template then check out the docs here:
Symfony2 Templating Service
If you look at the line:
return $this->render('AcmeArticleBundle:Article:index.html.twig');
basically the render method is returning a response (which the controller then returns) which has the content of the twig template, all you have to do is intercept this:
$response = $this->render('AcmeArticleBundle:Article:index.html.twig');
$response->headers->setCookie(new Cookie('name', 'value', 0, '/'));
return $response;
I think that's it anyway...