I've got a few problems with Google Picker that I just can't seem to solve.
Firstly, I have a problem with signing into my google account via the google picker window (as reported here https://groups.google.com/forum/#!topic/google-picker-api/3VXqKO1BD5g and elsewhere). In short, the picker works perfectly up until the point where it returns from the sign-in action. It fails to load the picker view once the account is signed in. The actions taken are as follows:
Open Google picker
Receive not signed in page, with sign in button.
Button opens a new window for google sign in.
Enter details and sign in. The sign in is successful.
Sign in window closes, and focus returns to the google picker, but it fails to recognise the sign in, receiving instead the above mentioned "The feature you requested is currently unavailable. Please try again later." error. With a js ReferenceError: init is not defined
Secondly, I have a problem in IE10 where the browser will show the "you are not signed in" screen even if I am. Clicking the button will open the sign-in window which closes immediately (sign in recognised?), but nothing happens on the google picker window.
The example found here: http://www-personal.umich.edu/~johnathb/misc/gpicker.html Seems to work just fine on IE10. So I am not sure what the problem is.. Possible differences are:
I have Https enabled on my site (but it didn't seem to make a difference when turned off).
I am currently running my app within an intranet (with internet access though).
Something to do with public IPs or such? But this wouldn't explain why the Google Picker works in Firefox etc.
The code used to load and handle the picker is shown below:
$('.googleDrivePicker').click(function () {
var inputControl = $(this).data('inputid');
// Google Picker API for the Google Docs import
google.load('picker',
'1',
{"language": '#Session["kieli"]',
"callback" : function () {
// Create and render a Picker object for searching images.
var picker = new google.picker.PickerBuilder().
addView(google.picker.ViewId.DOCS).
addView(google.picker.ViewId.IMAGE_SEARCH).
setCallback(function (data) {
// A simple callback implementation.
var url = '';
if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
url = doc[google.picker.Document.EMBEDDABLE_URL] || doc[google.picker.Document.URL];
$('#' + inputControl).val(url).change();
}
}).
build();
picker.setVisible(true);
$('.picker.modal-dialog-bg').css('z-index', 1101);
$('.picker.modal-dialog.picker-dialog').css('z-index', 1102);
}
});
});
Would really appreciate help with either of the above problems.
Related
How do I code my GreaseMonkey userscript to take me to an authorization/login window while on a different website (www.sharelatex.com)? My understanding was that GM_xmlhttpRequest would perform this function, and while I get an "OK" status after GM_xmlhttpRequest has successfully loaded, no login window presents itself. Should I be using a different function? This login must be done programmatically so that the userscript can "catch" the token number that gets attached to the redirect URL following successful login. This token number will then be used in a Mendeley API call to download the desired file from my Mendeley account (using implicit grant flow).
Background info: I am trying to build a GreaseMonkey userscript that will add a button to my www.sharelatex.com account that, when pushed, will automatically download a file from my account on www.mendeley.com using that site's API. The code associated with the button should also take of login and authentication requirements needed to use the API. I've registered my application with Mendeley, received a "client ID" number (0000, for purposes of illustration) which I have used to construct the following url:
var urlAuth = "https://api.mendeley.com/oauth/authorize?client_id=0000&redirect_uri=http://localhost&response_type=token&scope=all"
If I manually enter the above URL directly into my browser as a URL address, I am taken to a login/authorization page that looks like the below, which is exactly what I want to see, but programmatically instead of manually:
Click here to view authentication/login window
Below are the relevant bits of my malfunctioning GreaseMonkey userscript:
// ==UserScript==
// ... skipping over irrelevant lines ...
// #grant GM_xmlhttpRequest
// ==/UserScript==
var urlAuth = "https://api.mendeley.com/oauth/authorize?client_id=0000&redirect_uri=http://localhost&response_type=token&scope=all"
/* *************** CREATE BUTTON ************************* */
var input = document.createElement("input");
input.type = "button";
input.value="Update bibtex";
input.onclick = getBib;
input.setAttribute("style", "font-size:18px; position:absolute; bottom:10px;left:10px;");
document.body.appendChild(input);
/* ================================================================ */
function getBib()
{
GM_xmlhttpRequest({
method: 'GET',
url: urlAuth,
onload: function(reply) { alert(reply.statusText) }
}
the Alert indicates an OK status, but no login window presents itself. When I do an:
alert(urlAuth)
within the onload section and I manually copy/paste what appears in the alert box into the browser address area, the browser takes me to the appropriate login/authorization window, so the URL itself is fine.
Why isn't the GM_xmlhttpRequest taking me to the login screen? Am I misunderstanding the functionality of GM_xmlhttpRequest, and should instead be using a different function? I've spent about 2 solid months trying to figure this out, poring through hundreds of references on the topic of OAuth2, userscripts, Mendeley API, etc. A few examples: http://userscripts-mirror.org/scripts/review/292038 (was promising as it is the only GreaseMonkey/Mendeley userscript out there but unfortunately does not perform OAuth2 stuff),
https://salesforce.stackexchange.com/questions/76397/accessing-salesforce-rest-api-through-greasemonkey-script (the provided answer never addressed the question of how to get the login window).
While the topic of OAuth2 process involves multiple steps, this question was focused on one step: how to get the authorization / log-in window to present itself through a GreaseMonkey userscript. The answer (see comment by Brock Adams above) is given by an example provided in meta.stackexchange.com/a/293498/148310. More specifically, the log-in window is produced by the window.open function, as illustrated in the following example (see Line #21 below):
// ==UserScript==
<other needed info in the metadata block should be included here>
// #match https://stackexchange.com/oauth/login_success*
// ==/UserScript==
var rootUrl = "https://api.mendeley.com/oauth/authorize?"
// the rootUrl should point to whatever API service you are trying to use.
// I am using Mendeley, but could be i.e, facebook, YouTube, Twitter, Google, etc.
var clientId = "0000" // needs to be the number you got when you registered your app
// through whatever API service you want to use
var redirectUrl = "https://stackexchange.com/oauth/login_success"
// The above directs where the login page will be redirected after successful
// log-in/authorization.
// This URL needs to point to a real page.
// Also make sure that whatever is given for the redirectUrl is also
// listed in the #match statement in the metadata block section at the top,
// but with an astericks at the end (to allow for the token number to be
// attached as a hash fragment, as part of the OAuth2 process)
var other = "response_type=token&scope=all"
var urlAuth = rootUrl + clientId + "&" + redirectUrl + "&" + other
authWindow = window.open ( urlAuth, "Log in", "resizeable, scrollbars, status, toolbar,
dependent, width=660,height=480" )
// tailor window sizing, etc. to your own aesthetics
I've got a Chrome extension that creates a popup window that the user needs to use for a login system. You click on the extension icon and it opens up its application (AngularJS in my case). The user then clicks on a button which calls chrome.windows.create() to open a popup.
I would like the main extension app to monitor the URL of that popup for changes.
I create the popup from the extension this way:
chrome.windows.create(
{
url: 'https://some.external.url.com/whatever',
type: 'panel',
width: 600,
height: 600
},
function (windowReference) {
console.log('My Window:', windowReference);
// start monitoring URL of windowReference somehow
// could be as simple as a setInterval() loop
}
)
The problem is that the windowReference object passed to the callback doesn't have the current URL of the popup. Since the user can interact with the page in the popup (I'm pointing it at out OAuth2 system), the URL will change at times. I want to see that - either actively as changes are made, or by simply querying the current URL periodically.
This is what the windowReference object contains:
{
alwaysOnTop:false,
focused:false,
height:600,
id:1089,
incognito:false,
left:61,
state:"normal",
top:23,
type:"popup",
width:600
}
You can see that there is an ID there, and that, to me, suggest that I might be able to use it to call some other method to get the real URL information I'm after.
Any help would be appreciated.
So the answer turns out to be pretty simple. As Rob W mentioned in a comment, you use the chrome.tabs.query() method to do the search as you would for any other tab.
The missing part for me was that you can use the id from the window reference you get when the popup is created to get the desired results from the tabs query:
chrome.tabs.query(
{ windowId: windowReference.id },
function callback(tabs) {
var popup = tabs[0];
$log.debug("Popup URL:", popup.url);
}
);
You can see that I passed the ID as the value of the windowId parameter in the search query object.
So I have a system that essentially enabled communication between two computers, and uses a WebRTC framework to achieve this:
"The Host": This is the control computer, and clients connect to this. They control the clients window.
"The Client": The is the user on the other end. They are having their window controlled by the server.
What I mean by control, is that the host can:
change CSS on the clients open window.
control the URL of an iframe on the clients open window
There are variations on these but essentially thats the amount of control there is.
When "the client" logs in, the host sends a web address to the client. This web address will then be displayed in an iframe, as such:
$('#iframe_id').attr("src", URL);
there is also the ability to send a new web address to the client, in the form of a message. The same code is used above in order to navigate to that URL.
The problem I am having is that on, roughly 1 in 4 computers the iframe doesn't actually load. It either displays a white screen, or it shows the little "page could not be displayed" icon:
I have been unable to reliably duplicate this bug
I have not seen a clear pattern between computers that can and cannot view the iframe content.
All clients are running google chrome, most on an apple powermac. The only semi-link I have made is that windows computers seem slightly more susceptible to it, but not in a way I can reproduce. Sometimes refreshing the page works...
Are there any known bugs that could possibly cause this to happen? I have read about iframe white flashes but I am confident it isn't that issue. I am confident it isn't a problem with jQuery loading because that produces issues before this and would be easy to spot.
Thanks so much.
Alex
edit: Ok so here is the code that is collecting data from the server. Upon inspection the data being received is correct.
conn.on('data', function(data) {
var data_array = JSON.parse(data);
console.log(data_array);
// initialisation
if(data_array.type=='init' && inititated === false) {
if(data_array.duration > 0) {
set_timeleft(data_array.duration); // how long is the exam? (minutes)
} else {
$('#connection_remainingtime').html('No limits');
}
$('#content_frame').attr("src", data_array.uri); // url to navigate to
//timestarted = data_array.start.replace(/ /g,''); // start time
ob = data_array.ob; // is it open book? Doesnt do anything really... why use it if it isnt open book?
snd = data_array.snd; // is sound allowed?
inititated = true;
}
}
It is definitele trying to make the iframe navigate somewhere as when the client launches the iframe changes - its trying to load something but failing.
EDIT: Update on this issue: It does actually work, just not with google forms. And again it isn't everybody's computers, it is only a few people. If they navigate elsewhere (http://www.bit-tech.net for example) then it works just fine.
** FURTHER UPDATE **: It seems on the ones that fail, there is an 'X-Frames-Origin' issue, in that its set the 'SAMEORIGIN'. I dont understand why some students would get this problem and some wouldn't... surely it depends upon the page you are navigating to, and if one person can get it all should be able to?
So the problem here was that the students were trying to load this behind a proxy server which has an issue with cookies. Although the site does not use cookies, the proxy does, and when the student had blocked "third party cookies" in their settings then the proxy was not allowing the site to load.
Simply allowed cookies and it worked :)
iframes are one of the last things to load in the DOM, so wrap your iframe dependent code in this:
document.getElementById('content_frame').onload = function() {...}
If that doesn't work then it's the document within the iframe. If you own the page inside the iframe then you have options. If not...setTimeout? Or window.onload...?
SNIPPET
conn.on('data', function(data) {
var data_array = JSON.parse(data);
console.log(data_array);
// initialisation
if (data_array.type == 'init' && inititated === false) {
if (data_array.duration > 0) {
set_timeleft(data_array.duration); // how long is the exam? (minutes)
} else {
$('#connection_remainingtime').html('No limits');
}
document.getElementById('content_frame').onload = function() {
$('#content_frame').attr("src", data_array.uri); // url to navigate to
//timestarted = data_array.start.replace(/ /g,''); // start time
ob = data_array.ob; // is it open book? Doesnt do anything really... why use it if it isnt open book?
snd = data_array.snd; // is sound allowed?
inititated = true;
}
}
}
Edit : this is the windows behaviour, with linux it just fails.
First, if you succeeded navigate on gmail with casper (without random waiting time -from 20sec to 5min-), please tell me.
I want to register on our site, then validate my registration automatically with Gmail (an entire register step). Did someone do that before?
I have no problem to register, and I can login on my mailbox (Gmail) but after i have some troubles to navigate and validate my registration in Gmail, and i observe different behaviors between phantomJS and slimerJS.
In phantom it will work (without special commands), but it may take until 5 minutes before pass in the next step (waitForSelector). And with slimerjs it just stays stuck on the mailbox page.
EDIT : A strange thing : if i click manually (slimer) on a link which opens a popup, it stops being blocked and my navigation continues, it's like it can't detect the end of the step itself and can't perform the waitFor after the submit click without another interaction. Is it a refresh/reload problem?
Try that to see yourself :
casper.thenOpen('https://accounts.google.com/ServiceLogin?service=mail&continue=https://mail.google.com/mail/&hl=en', function(){
this.sendKeys("input#Email","your mail");
this.sendKeys("input#Passwd","your password");
this.click("input#signIn.rc-button-submit");
console.log(this.getCurrentUrl());
this.waitForSelector(".aeF",function(){//fail with linux -> timeout
this.test.pass("ok"); //windows -> stuck in slimer, several times in phantom
this.test.assertExists(".T-I.J-J5-Ji.T-I-KE.L3","Gmail Home ok");
console.log("url "+this.getCurrentUrl());
});
And i don't get any timeOut error. In slimerjs it just keeps the page opened.
If i do a waitForPopup instead of a waitForUrl, i have the error (timeout -> did not pop up), so why does a waitForUrl/waitForSelector... stay stuck ? I tried --web-security=no,--ignore-ssl-errors=true commands too (not linked but i tried --output-encoding=ISO 8859-1 too which doesn't work).
Here the differences between phantom and slimer (doc) :
http://docs.slimerjs.org/0.8/differences-with-phantomjs.html
(useless in this issue i think)
Well, we finally found a way to do it : the problem is by default gmail loop on ajax requests, to check some new mails, etc... see Page polls remote url, causing casper to block in step.
Fortunately google proposes a way to avoid that, using the simplified HTML version (you can for example use a special gmail address for your tests using this version) :
That way the script works as it should.
Bonus :
/*
* Click on an element specified by its selector and a part of its text content.
* This method resolves some problem as random space in textNode / more flexible too.
* Need to fix one bug though : when there is a tag in textContent of our selector.
*/
casper.clickSelectorHasText = function (selector, containsText){
var tmp = this.getElementsInfo(selector)
,i
,l
,bool=false
;
for (i=0,l=tmp.length;i<l; i++){
if(tmp[i].text && tmp[i].text.indexOf(containsText)!==-1){
this.clickLabel(tmp[i].text);
bool=true;
break;
}
}
casper.test.assert(bool, "clickSelectorHasText done, text and selector found -> click selector " + selector +" which contains text " + containsText);
};
casper.thenOpen('https://accounts.google.com/ServiceLogin?service=mail&continue=https://mail.google.com/mail/&hl=en', function scrapeCode(){
//log in
this.sendKeys("input#Email","your email");
this.sendKeys("input#Passwd","your password");
this.click("input#signIn.rc-button-submit");
//wait to redirect to our mailbox
this.waitForSelector("form[name='f']",function(){
//check main block
this.test.assertExists("form[name='f']","Gmail Home ok");
this.test.assertSelectorHasText("span", "Your gmail title message");
this.clickSelectorHasText("font", "one string which appears in font tag");
//wait inscription message appears
this.waitForSelector("div.msg",function(){
this.test.assertSelectorHasText("a","the message which activates your account--> in <a>");
});
})
//validate our account
.then(function(){
this.clickLabel("the message which activates your account--> in <a>");
this.waitForPopup(/mail/, function(){
this.test.pass("popup opened");
});
this.withPopup(/mail/, function(){
this.viewport(1400,800);
this.test.pass("With Popup");
//wait something on your website (for me selector .boxValid)
this.waitForSelector(".boxValid", function(){
/*
* Here your code after validation
*/
});
});
})
It might be possible to do it with normal gmail using event, see resource.received.
(works fine in Chrome on the iPhone)
I get this error:
TypeError: 'undefined' is not an object (evaluating 'win.location') in dg.js line 3
And the lightbox does not open.
The code in question inside PayPal's dg.js is:
startFlow: function (url) {
var win = that._render();
if (win.location) {
win.location = url;
} else {
win.src = url;
}
}
So does mobile Safari not understand that._render()? How do I get around this?
If it matters, I'm using Adaptive Payments, calling it like so:
var dg = new PAYPAL.apps.DGFlow({
trigger: null,
expType: 'light'
});
dg.startFlow('https://www.paypal.com/webapps/adaptivepayment/flow/pay?expType=light&payKey=' +data.paykey);
I don't have any problems getting the payKey & the entire payflow works on desktops and in mobile browsers other than Safari (it works on desktop Safari). It also does not work when our site is run as an iOS web app, which I assume is just a shell for Safari anyway.
I can explain why you are seeing this error.
Safari on iOS only allows a window to be opened as a result of a user click/touch event.
The DGFlow._render() function executes:
window.open('', "PPDG");
which returns null if triggered by anything other than a user click/touch event.
I am guessing you are issuing an XMLHttpRequest to generate a PayRequest/PayKey on the server and then in the onsuccess callback you are calling DGFlow.startFlow().
The solution is two split the process into two steps:
When the user is ready to checkout, issue the call to the server to
generate the pay key.
Then, present the user with a button to Checkout with PayPal and when that is clicked, call DGFlow.startFlow()
Found a couple of ways to get around this...location.replace with the PayPal URL or using your own lightbox. I used easybox.
// Replace
dg.startFlow('https://www.paypal.com/webapps/adaptivepayment/flow/pay?expType=light&payKey=' +data.paykey);
// with
var RUNNING_AS_WEB_APP = (window.navigator.standalone == true ? true : false);
if (RUNNING_AS_WEB_APP === false) {
dg.startFlow('https://www.paypal.com/webapps/adaptivepayment/flow/pay?expType=light&payKey=' +data.paykey);
} else {
location.replace('https://www.paypal.com/webapps/adaptivepayment/flow/pay?expType=light&payKey=' +data.paykey);
// Or, lightbox option:
// $.easybox([{url: 'https://www.paypal.com/webapps/adaptivepayment/flow/pay?expType=light&payKey=' +data.paykey, width: 320, height: 480}]);
}
Try using the mini browser experience where expType=mini. Seems to work better than the lightbox on mobile devices.
Adaptive Payments without modal box or popups?