Calling an XAgent from a traditional Domino web app via AJAX - xpages

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?

Related

setRedirectURLToSearchResults() not redirecting

I an new to NetSuite, and I am having a problem. I have created a button on a form through a user event script.
The button calls a client script, which executes a saved search. The search result should be displayed to the user.
Everything is located in one file:
function userEventBeforeLoad_AddSearchButton(type, form, request){
if(type == 'view' || type == 'edit'){
form.setScript('customscript_tvg_project_search_button');
var projectid = nlapiGetFieldValue('companyname');
form.addButton("custpage_search", "KHM Search", "performSearch('" + projectid + "')");
}
}
function performSearch(projectid) {
console.log('test in client');
alert('projectid = ' + projectid);
var obj = nlapiLoadSearch(null, 'customsearch_project_cost_detail_sublist');
obj.setRedirectURLToSearchResults();
}
I created a user event script record for userEventBeforeLoad_AddSearchButton(). and a client script record for performSearch().
In the code above, the button is created and the alert is being displayed. But no redirect is happening.
When I look at the result in Firebug it looks like this:
{"result":"\/app\/common\/search\/searchresults.nl?api=T","id":5}
Any ideas why the redirect is not happening, and what to do?
Edit: My code above was stripped down to simplify. The reason I am passing projectid over is that I actually need to filter the search result, suing the following two lines:
var searchFilter = new nlobjSearchFilter('job', null, 'anyof', projectid);
obj.addFilter(searchFilter)
Although the documentation does state that, "This method is supported in afterSubmit user event scripts, Suitelets, and client scripts", it seems from this NS User Group post by a Netsuite Employee in reply to someone who experienced the same issue as you, that the API does not actually perform the redirection client side:
Redirection works on server side. Use window.location.assign(url) to
navigate via script in client-side.
Testing this, I can see that setRedirectURLToSearchResults does appear to correctly "load the search into the session", so adding this line afterwards should fix your problem.
window.location = '/app/common/search/searchresults.nl?api=T';
setRedirectURLToSearchResults is not working for me either, but since you are using clientscript you might want to try this:
function performSearch(projectid) {
console.log('test in client');
alert('projectid = ' + projectid);
var searchid = 'customsearch_project_cost_detail_sublist';
location = '/app/common/search/searchresults.nl?searchid=' + searchid;
}

alert in SSJS Library

I have a function in SSJS Library. I would like get Client Side alert in Library. What i tried was not worked. I think I miss something. :(
function docAlive()
{
try
{
var otherDoc:NotesDocument= null;
if (funcDoc.getItemValueString("DocUNID")!="")
{
var otherDoc:NotesDocument = dbKontak.getDocumentByUNID(funcDoc.getItemValueString("DocUNID"))
if (otherDoc==null)
{
hataKod = "10001";
hataMsg = "There is no document :( Created One";
print (hataKod +": "+hataMsg);
view.postScript("alert('"+hataKod + " - " +hataMsg+"');");
}
}
return otherDoc;
}
catch (e)
{
e.toString();
}
}
view.postScript() will trigger a client-side alert, but as Tim Tripcony mentions, not in all events. And the alert will only be triggered after the function and any other code for the partial refresh has completed. At that point the HTML to trigger the (Client-Side) JavaScript alert will be posted back to the browser and the browser will action it.
If you want to throw an error back to the browser, I would strongly recommend XPages OpenLog Logger (and not just because I contribute and support it on OpenNTF). openLogBean.addError(e) will log the error to OpenLog and post an error message back to the browser.
The message is passed to the server using facesMessage.addMessage(), as documented here http://www.intec.co.uk/returning-error-messages-from-ssjs-through-the-facescontext/. I believe there are additional options for managing different message levels (e.g. WARNING, CONFIRMATION). FacesMessage is a standard Java (in this case, JSF) construct, so the documentation for it on the web is valid for XPages as well.

Firefox Extension Development

In Chrome Extension Development we have Background Page Concepts. Is any thing similar available in Firefox Extension Development also. While Developing Chrome Extensions I have seen methods like
window.Bkg = chrome.extension.getBackgroundPage().Bkg;
$(function () {
var view = null;
if (Bkg.account.isLoggedIn()) {
view = new Views.Popup();
$("#content").append(view.render().el);
} else {
$("#content").append(Template('logged_out')());
Bkg.refresh();
}
}...........
Where the main logic are written in Background Page(like isLoggedIn etc) and from the Extension Popup page we are calling Background page. Here for instance the background page is always loaded which manages the session. How can we have similar functionality in Firefox Extension Development.
All communication between the background page (main.js) and content scripts (your popup script) occurs via events. You cannot call functions immediately, so you won't receive any return values, but you can send an event from a content script to the background script that sends an event back to the content script and calls a new function, like so:
main.js partial
// See docs below on how to create a panel/popup
panel.port.on('loggedIn', function(message) {
panel.port.emit('isLoggedIn', someBoolean);
});
panel.port.on('refresh', function() {
// do something to refresh the view
});
popup.js
var view = null;
addon.port.on('isLoggedIn', function(someBool) {
if (someBool) {
// Obviously not code that's going to work in FF, just want you to know how to structure
view = new Views.Popup();
$("#content").append(view.render().el);
} else {
$("#content").append(Template('logged_out')());
addon.port.emit('refresh');
}
});
addon.port.emit('loggedIn', 'This is a message. I can pass any var along with the event, but I don't have to');
You should read this stuff:
Panel
Communicating between scripts

Chrome/FF/Safari extension: Load hidden web page in incognito-like mode

Is it possible to build an 'incognito mode' for loading background web-pages in a browser extension?
I am writing a non-IE cross-browser extension that periodically checks web-pages on the user's behalf. There are two requirements:
Page checks are done in the background, to be as unobtrusive as possible. I believe this could be done by opening the page in a new unfocussed browser tab, or hidden in a sandboxed iframe in the extension's background page.
The page checks should operate in 'incognito mode', and not use/update the user's cookies, history, or local storage. This is to stop the checks polluting the user's actual browsing behavior as much as possible.
Any thoughts on how to implement this 'incognito mode'?
It would ideally work in as many browser types as possible (not IE).
My current ideas are:
Filter out cookie headers from incoming/outgoing http requests associated with the page checks (if I can identify all of these) (not possible in Safari?)
After each page check, filter out the page from the user's history.
Useful SO questions I've found:
Chrome extension: loading a hidden page (without iframe)
Firefox addon development, open a hidden web browser
Identify requests originating in the hiddenDOMWindow (or one of its iframes)
var Cu = Components.utils;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/devtools/Console.jsm');
var win = Services.appShell.hiddenDOMWindow
var iframe = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
iframe.addEventListener('DOMContentLoaded', function(e) {
var win = e.originalTarget.defaultView;
console.log('done loaded', e.document.location);
if (win.frameElement && win.frameElement != iframe) {
//its a frame in the in iframe that load
}
}, false);
win.document.documentElement.appendChild(iframe);
must keep a global var reference to iframe we added.
then you can change the iframe location like this, and when its loaded it triggers the event listener above
iframe.contentWindow.location = 'http://www.bing.com/'
that DOMContentLoaded identifies all things loaded in that iframe. if the page has frames it detects that too.
to remove from history, into the DOMContentLoaded function use the history service to remove win.location from history:
https://developer.mozilla.org/en-US/docs/Using_the_Places_history_service
now to strip the cookies from requests in that page use this code:
const {classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
Cu.import('resource://gre/modules/Services.jsm');
var myTabToSpoofIn = Services.wm.getMostRecentBrowser('navigator:browser').gBrowser.tabContainer[0]; //will spoof in the first tab of your browser
var httpRequestObserver = {
observe: function (subject, topic, data) {
var httpChannel, requestURL;
if (topic == "http-on-modify-request") {
httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
var goodies = loadContextGoodies(httpChannel)
if (goodies) {
if (goodies.contentWindow.top == iframe.contentWindow.top) {
httpChannel.setRequestHeader('Cookie', '', false);
} else {
//this page load isnt in our iframe so ignore it
}
}
}
}
};
Services.obs.addObserver(httpRequestObserver, "http-on-modify-request", false);
//Services.obs.removeObserver(httpRequestObserver, "http-on-modify-request", false); //run this on shudown of your addon otherwise the observer stags registerd
//this function gets the contentWindow and other good stuff from loadContext of httpChannel
function loadContextGoodies(httpChannel) {
//httpChannel must be the subject of http-on-modify-request QI'ed to nsiHTTPChannel as is done on line 8 "httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);"
//start loadContext stuff
var loadContext;
try {
var interfaceRequestor = httpChannel.notificationCallbacks.QueryInterface(Ci.nsIInterfaceRequestor);
//var DOMWindow = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow); //not to be done anymore because: https://developer.mozilla.org/en-US/docs/Updating_extensions_for_Firefox_3.5#Getting_a_load_context_from_a_request //instead do the loadContext stuff below
try {
loadContext = interfaceRequestor.getInterface(Ci.nsILoadContext);
} catch (ex) {
try {
loadContext = subject.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
} catch (ex2) {}
}
} catch (ex0) {}
if (!loadContext) {
//no load context so dont do anything although you can run this, which is your old code
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var contentWindow = loadContext.associatedWindow;
if (!contentWindow) {
//this channel does not have a window, its probably loading a resource
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var aDOMWindow = contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
var gBrowser = aDOMWindow.gBrowser;
var aTab = gBrowser._getTabForContentWindow(contentWindow.top); //this is the clickable tab xul element, the one found in the tab strip of the firefox window, aTab.linkedBrowser is same as browser var above //can stylize tab like aTab.style.backgroundColor = 'blue'; //can stylize the tab like aTab.style.fontColor = 'red';
var browser = aTab.linkedBrowser; //this is the browser within the tab //this is where the example in the previous section ends
return {
aDOMWindow: aDOMWindow,
gBrowser: gBrowser,
aTab: aTab,
browser: browser,
contentWindow: contentWindow
};
}
}
//end loadContext stuff
}

How to open automatically downloaded Word document in the browser?

I use Aspose to generate a Word document. It must be opened in the browser automatically when it comes back from the server.
Here is my code:
Do Ajax call to get the document
$.ajax({
url: "Export/StreamWord",
data: { topicId: CurrentTopic.id },
success: function (result) {
//Nothing here. I think that the browser must open the file automatically.
}
});
Controller .NET MVC 3
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult StreamWord(string topicId)
{
var stream = new MemoryStream();
Document doc = exportRepos.GenerateWord(topicId); //Document is a Aspose object
doc.Save(stream, SaveFormat.Docx);
stream.WriteTo(Response.OutputStream);
return File(stream, "application/doc", "test.doc");
}
BUT when I run it from the browser nothing happen.
Response from the server you can see on the image. Document comes, but it is not been opened.
Any suggestions?
Do not use AJAX for this, just use a simple page redirect instead. If you use a page redirect it will prompt the user to download the file, it won't actually move them away from the current page.
The code would look like
document.location.href = "Export/StreamWord?topicId=" + CurrentTopic.Id;
What you're attempting is not possible with AJAX.

Resources