I am (trying to) implementing a Chrome Extension, that uses the Tumblr API. For that to work I need to authorize via OAuth (1.0a).
I managed to get most of the authorization to work but I think I am missing something...
My manifest.json of the Chrome Extension I am writing looks like this for now:
{
"name": "Tumblr - Tiled Dashboard",
"version": "0.1.1",
"manifest_version": 2,
"description": "This extension modifies the look of your Tumblr dashboard.",
"icons": {
"16": "images/icon_16.png",
"48": "images/icon_48.png",
"128": "images/icon_128.png"
},
"background": {
"page": "background.html"
},
"content_scripts": [
{
"matches": [ "*://*.tumblr.com/dashboard" ],
"css": [ "styles.css" ],
"js": [ "jquery-2.1.3.min.js", "masonry.min.js", "code.js" ]
}
],
"permissions": [
"https://www.google-analytics.com/",
"https://api.tumblr.com/v2/*",
"webRequest",
"storage",
"tabs",
"https://www.google.com/accounts/OAuthGetRequestToken", "https://www.google.com/accounts/OAuthAuthorizeToken", "https://www.google.com/accounts/OAuthGetAccessToken"
],
"web_accessible_resources": [ "chrome_ex_oauth.html", "injectedCode.js" ],
"content_security_policy": "script-src 'self' https://api.tumblr.com/v2/ https://ssl.google-analytics.com; object-src 'self'",
"homepage_url": "http://desvre.tumblr.com/",
"author": "Franz Spitaler"
}
...I think this should be ok. In my background.html there are in fact only the included scripts (google analytics and the three oauth files I got from here: https://developer.chrome.com/extensions/tut_oauth . Those three files are also included in the fourth file I downloaded from the previous source ("chrome_ex_oauth.html").
Now when I reload the Chrome Extension in the Extensions (Ctrl + r) the Redirecting page opens and redirects me to the Tumblr authorization page, where I can allow the access.
Since I also added the "chrome_ex_oauth.html" to the "web_accessible_resources" in the manifest.json this works.
The problem occurs after the click on the 'allow' button. I simply get back to the redirecting page ("chrome_ex_oauth.html") and nothing more happens. When I open up the console, I can see an error message like the following:
GET https://www.tumblr.com/oauth/access_token?oauth_consumer_key=MY_CONSUMER_KEY&oauth_nonce=D3VeV&oauth_signature=kvhL%2F9GSMuiODoPR%2FyUrUiqzqF0%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1424250463&oauth_token=6khqzjiMFbM7hcqqnNf8hm9ttDELKUVYo2TBQmyLOtepGN9KhJ&oauth_verifier= 400 (Bad Request)
As described in the OAuth tutorial page from Google I use this to initialize the 'background page' (which leads to the error message):
var oauth = ChromeExOAuth.initBackgroundPage({
'request_url': 'https://www.tumblr.com/oauth/request_token',
'authorize_url': 'https://www.tumblr.com/oauth/authorize',
'access_url': 'https://www.tumblr.com/oauth/access_token',
'consumer_key': 'MY_CONSUMER_KEY',
'consumer_secret': 'MY_SECRET_CONSUMER_KEY',
'app_name': 'Tumblr Tiled Dashboard'
});
Did I miss something important here? I think the manifest.json file is ok (permissions, web_accessible_resources?!).
Thank you for any help. There is really no really great tutorial for OAuth out there for Google Extensions (except that linked page)...
As #abraham pointed out, there was a missing parameter, as seen in my posted error.
I was able to track down the problem and found it in the function of the chrome_ex_oauth.js file. I changed the function from:
ChromeExOAuth.formDecode = function(encoded) {
var params = encoded.split("&");
var decoded = {};
for (var i = 0, param; param = params[i]; i++) {
var keyval = param.split("=");
if (keyval.length == 2) {
var key = ChromeExOAuth.fromRfc3986(keyval[0]);
var val = ChromeExOAuth.fromRfc3986(keyval[1]);
decoded[key] = val;
}
}
return decoded;
};
to this:
ChromeExOAuth.formDecode = function(encoded) {
var params = encoded.split("&");
var decoded = {};
for (var i = 0, param; param = params[i]; i++) {
var keyval = param.split("=");
if (keyval.length == 2) {
var key = ChromeExOAuth.fromRfc3986(keyval[0]);
var val = ChromeExOAuth.fromRfc3986(keyval[1]);
decoded[key] = val;
}
else if (keyval.length == 3){
var key = ChromeExOAuth.fromRfc3986(keyval[0]);
var val = ChromeExOAuth.fromRfc3986(keyval[1].split("#")[0]);
decoded[key] = val;
}
}
return decoded;
};
Where the last parameter was not identified correctly because the ending of the url I got looks like this: #_=_
With the keyval[1].split('#')[0] I get the exact part of the parameter, that I need!
Thank you for the help, everything seems to work now. A request that needs OAuth authorization did, at least!
Related
I'm working on a private chrome extension which is designed to rake data from one tab, sanitize it, and populate the form data in another tab.
The logic flows like this:
The background script gets the tabids of the two tabs of interest and populates variables named feedId and targetId - background.js line 7ff
The Background script listens for the indication that the icon is clicked - background.js line 30
The background script sends a message to the content.js script to "Rake the Data." - background.js line 38
The content.js receives the message and looks for the text "Rake the Data" then rakes the data and puts the data into an object - content.js line 17ff
The content.js sends a message back to the background script with an object of the data - content.js line 49
The background script receives the message with the object and then works to sanitize the data - background.js line 43ff
The background script then sends the sanitized data (I'm only sending one element at this stage of the script) to the targetId tab to begin the process of populating the fields by utilizing chrome.tabs.sendMessage - background.js line 73ff
I am expecting the message "Populate the Form" to show in the alert box in content.js line but it never gets alerted.
What am I doing wrong in this last stage of the process?
Here is my manifest.js:
{
"manifest_version":
2,
"name":
"Rake and Populate",
"version":
"0.3",
"description" :
"Rake data and populate a web form." ,
"permissions" : [
"tabs"
],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js"
]
}
],
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
"icons" : {
"16" : "rake16.png" ,
"32" : "rake32.png" ,
"48" : "rake48.png" ,
"128" : "rake128.png"
},
"browser_action" : {
"default_icon" : "rake16.png"
}
}
Here is my background.js script:
// Let define some parameters for the query
// These are the websites we want to work with
var players = ["*://*.domain-one.com/*", "*://*.domain-two/add.html*"];
feedId = targetId = 0;
// query the tabs to get the Ids of the two tabs of interest
chrome.tabs.query({url: players}, mytabs);
// take the returned data from the query and find the ID for each
function mytabs(myinfo){
var regex = /domain-one\.com/;
if (myinfo[0].url.match(regex)){
window.feedId = myinfo[0].id;
window.targetId = myinfo[1].id;
console.log('matched');
console.log(window.feedId);
} else {
window.feedId = myinfo[1].id;
window.targetId = myinfo[0].id;
};
};
// let's listen for the button in the addres bar being clicked
// when the button is clicked, the function "iChooseToRake" will get executed
chrome.browserAction.onClicked.addListener(iChooseToRake);
// This function is called when the button is onClicked
function iChooseToRake(tab) { //the tab object is passed into the function
//chrome.tabs.sendMessage(tab.id, "Rake the Data");
var rakeMessage = {message: "Rake the Data"};
chrome.tabs.sendMessage(tab.id, rakeMessage);
}
// Set up a listener to receive the message from the rake
// saying "I have the data"
chrome.runtime.onMessage.addListener(
function(unsanitizedData, sender){
// The data has been sent back in the object. Sanitize the data in the next few lines
// Let's get the zip from the end of the full address
// Check to see if there is a zip+4 code in the address
var regex = /[0-9]{5}-[0-9]{4}/; // does it match the pattern nnnnn-nnnn
if (unsanitizedData.physical_address_full.match(regex)){
var tempAddress = unsanitizedData.physical_address_full.match(regex)[0]; //populates tempAddress if matched
} else {
// We get here if the zip+4 matched failed.
// Check to see if there is a match for a 5 digit zip
var regex = /[0-9]{5}$/; //the pattern nnnnn at the end of the string
var tempAddress = unsanitizedData.physical_address_full.match(regex)[0]; //populates tempAddress if matched
console.log('I matched the 5 digit zip pattern: ' +tempAddress);
};
//pack the sanitized data into an object
var address = {
zip: tempAddress
};
sendNeededData(targetId, address);
});
// send the opject back to the correct tab
function sendNeededData(targetId, address){
// append the correct message in the object
address.message = "Populate the Form";
console.log(address);
chrome.tabs.sendMessage(targetId, address);
};
Here is my content.js script:
// Function to Convert text to Initial Caps
function titleCase(str) {
return str.toLowerCase().split(' ').map(function(word) {
return (word.charAt(0).toUpperCase() + word.slice(1));
}).join(' ');
}
// The content script listens for a message from the background script
// which tells the browser to rake the data
chrome.runtime.onMessage.addListener(messageRecieved);
function messageRecieved(txt, sender, sendResponse) {
alert(txt.message);
if (txt.message == "Rake the Data"){
console.log('I was just told to rake the data');
// Let's first read the data from the website
var owner_name = document.getElementsByTagName("li")[6].innerText.trim(); // Owner Name
owner_name = titleCase(owner_name); // Give it initial Caps
var owner_address_full = document.getElementsByTagName("li")[7].innerText.trim(); // owner_full_address
owner_address_full = titleCase(owner_address_full); // Give it initial Caps
var physical_address_full = document.getElementsByTagName("li")[10].innerText.trim(); // Situs Address Content:
physical_address_full = titleCase(physical_address_full); // Give it initial Caps
var area_land = document.getElementsByTagName("li")[11].innerText.trim(); // Land Area: e.g. 11,260 Sq.Ft.
var area_roof = document.getElementsByTagName("td")[7].innerText.trim(); // Gross Area: e.g. 2,709
var area_air = document.getElementsByTagName("td")[8].innerText.trim(); // Living Area: e.g. 1,952
// Now let's pack this data into an object and send it back to
// the background script to be processed and fed to the form
var parcelInfo = {
owner_name: owner_name,
owner_address_full: owner_address_full,
physical_address_full: physical_address_full,
area_land: area_land,
area_roof: area_roof,
area_air: area_air
};
console.log(parcelInfo);
// send the message with the data packed into the object named "parcelInfo"
chrome.runtime.sendMessage(parcelInfo);
};
};
I am writing a chrome extension to block certain pages on a website.
I do this by loading a .txt file full of the pages to be blocked and then comparing each to the url loaded by the user and then redirecting to a blocking page if the url matches any in the file. However, everything works apart from the actual redirect.
manifest.json
{
"name": "Reddit Subreddit Blocker",
"description": "Blocks access to specified subreddits.",
"version": "1.0",
"manifest_version": 2,
"background": {
"scripts":["background.js"],
"pages": ["blocked.html"],
"persistent": true
},
"permissions": [
"webRequest",
"*://*.reddit.com/r/*",
"webRequestBlocking"
]
}
background.js
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
var r = new XMLHttpRequest();
r.open("GET", chrome.extension.getURL("test.txt"), true);
r.onreadystatechange = function(e){
if (r.readyState == 4 && r.status == 200){
var subreddits = r.responseText.split("\n");
var index;
var url = details.url;
var found = false;
for (index = 0; index < subreddits.length && found == false; ++index){
if (subreddits[index] === url){
console.log("Subreddit found.");
found = true;
}
}
if (found == false){
console.log("not found");
return {cancel: false};
}else{
console.log("redirecting");
return {redirectUrl: chrome.extension.getURL("blocked.html")};
}
}
};
r.send(null);
}, {
urls: ["https://*.reddit.com/r/*"]
}, ["blocking"]
);
The urls to be blocked are stored on seperate lines in the .txt file.
When I run the code with a url that should be blocked, I simply get the output:
Subreddit found.
redirecting
Therefore, the subreddit was found and the if statement to redirect was executed but it was not actually redirected.
I am trying to develop a form abandonment heat mapper
function newMarkup(id, uniqueHits){
var min = Math.min.apply(null,uniqueHits);
var max = Math.max.apply(null,uniqueHits);
var med = max / 2;
for(var num = 0; num < id.length; num++){
styleElement(id[num], uniqueHits[num], min, max, med);
}}
function styleElement(element, value, min, max, med){
var el = $("[id$=" + element + "]");
if(el.prop('nodeName') === "INPUT"){
if(value == max){
el.addClass('very-good');
}
if(value < max && value > med){
el.addClass('good');
}
if(value == med){
el.addClass('average');
}
if(value < med && value > min){
el.addClass('not-so-good');
}
if(value == min){
el.addClass('poor');
}
} else {
el = el.next();
if(value == max){
el.addClass('very-good');
}
if(value < max && value > med){
el.addClass('good');
}
if(value == med){
el.addClass('average');
}
if(value < med && value > min){
el.addClass('not-so-good');
}
if(value == min){
el.addClass('poor');
}
}
}
and I am wondering if it is possible to do the call to the api from a chrome extension?
Essentially I am trying to do this call to get the data:
gapi.client.analytics.data.ga.get({
'ids': 'ga:' + profileId,
'start-date': '7daysAgo',
'end-date': 'today',
'metrics': 'ga:uniqueEvents',
'dimensions': 'ga:eventLabel',
'sort':'-ga:uniqueEvents',
'filters': "ga:eventCategory==Form Field Tracking - /join"
})
I tried do create my heatmapper by having an iFrame in an MVC web application but because of the trouble of trying to send data across domains, I gave up on that.
I would like to know if it's possible to get data from the analytic's API from a chrome extension? I'd imagine it would have to be done in a similar fashion as how google scripts uses the analytics API (using a service account) but I have been unable to find any documentation or find out if this is even possible.
(Example of code in scripts)
function runReport(profileId) {
var today = new Date();
var oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
var startDate = Utilities.formatDate(oneWeekAgo, Session.getTimeZone(), 'yyyy-MM-dd');
var endDate = Utilities.formatDate(today, Session.getTimeZone(), 'yyyy-MM-dd');
var tableId = 'ga:' + profileId;
var metric = 'ga:uniqueEvents';
var options = {
'dimensions': 'ga:eventLabel',
'sort':'-ga:uniqueEvents',
'filters': "ga:eventCategory==Form Field Tracking - /join"
};
var report = Analytics.Data.Ga.get(tableId, startDate, endDate, metric, options);
}
I'd really appreciate any help or advice anyone has for me. I am trying to query the google analytic's API from a chrome extension. Is this possible?
Not tested on GA specifically but this is the general method of working with scripts that require external server for the API:
The content script can't make requests to other sites so it should ask the background page to do it by sending a message
The background page includes ga.js script referenced in the manifest, this file must be present in the extension, it's not a remotely loaded script.
The background page can communicate with the external API server after we add the corresponding CSP rule (added in the manifest)
Now the message from step 1 is received by the background page listener, which invokes GA and sends the results back to the content script of the original tab (tabId was saved when the first message was received).
manifest.json:
"content_scripts": [
{
"matches": ["<all_urls>"],
"run_at": "document_start",
"all_frames": true,
"js": ["content.js"]
}
],
"background": {
"scripts": ["background.js", "ga.js"]
},
"content_security_policy":
"script-src 'self' https://ssl.google-analytics.com; object-src 'self'",
.............
content.js:
chrome.runtime.sendMessage({type: "getGA", data: some_data_for_GA});
chrome.runtime.onMessage.addListener(function(msg) {
if (msg.type == "GA") {
// apply msg.data to the page elements
...................
}
});
background.js:
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
var tabId = sender.tab.id, frameId = sender.frameId;
if (msg.type == "getGA") {
gapi.client.analytics.data.ga.get({
..................... // use msg.data if needed
}).execute(function(response) {
sendMessageToFrameId(tabId, frameId, {type: "GA", data: response});
});
}
});
function sendMessageToFrameId(tabId, frameId, msg) {
// 1. frameId works since Chrome 41 and throws on prior versions
// 2. without frameId Chrome 45 sends the message to wrong tabs
try { chrome.tabs.sendMessage(tabId, msg, {frameId: frameId}); }
catch(e) { chrome.tabs.sendMessage(tabId, msg); }
}
I'm developing a chrome extension , and I want it to get the selected text by user , the code works fine when I run it on a similar non-extension page but it doesn't work in my background page extension . even when I try to get the value of the selection in the console.log it doesn't return anything . here's my javascipt file :
window.onload = myOnload;
function myOnload(){
var button = document.getElementById("button");
chrome.contextMenus.create({
"title":"MissLang Fix",
"contexts":["all"],
"onclick": myClick2
});
document.getElementById("input").innerHTML = "mojtaba";
button.onclick= function(){myClick()};
}
function myClick2(){
var txt = '';
if (chrome.extension.getBackgroundPage().window.getSelection)
txt = chrome.extension.getBackgroundPage().window.getSelection();
else if (chrome.extension.getBackgroundPage().document.getSelection)
txt = chrome.extension.getBackgroundPage().document.getSelection();
else if (chrome.extension.getBackgroundPage().document.selection)
txt = chrome.extension.getBackgroundPage().document.selection.createRange().text;
console.log("this is the console message");
console.log("txt: " + txt);
}
function myClick(){
var myString = document.getElementById("input").value;
for(var i=0;i<myString.length;i++){
myString = myString.replace("q","ض");
myString = myString.replace("w","ص");
}
document.getElementById("output").innerHTML = myString;
}
and here's my manifest.json file :
{
"name": "MissLang Fix extension",
"manifest_version": 2,
"version": "1.0",
"description": "fixing english to farsi typings.",
"icons":{
"16":"icon_16.png",
"128":"icon_128.png"
},
"background":{
"scripts":["script.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "missLangFix.html"
},
"permissions": [
"tabs",
"contextMenus"
]
}
I'm new to this so please bear with me. I am trying to write a chrome extension that does the following:
Detect www.website.com/anypage.html. If this website is detected, then do the following.
Don't load the URL.
Instead, write a blank document with a hyperlink to www.website.com/anypage.html?ie=UTF8
The script is set to run at document start (in the manifest).
Here is my code:
Detect URL:
var regExp = /website.com/gi;
var match = 0;
testString = window.location.href.toString();
if(regExp.test(testString) {
match = 1;
Write blank document with link to the URL with the UTF8 encoding tag:
document.write("<a href=" + window.location.href + "?ie=UTF8>Title of Link</a>");
This doesn't work as expected, and just shows a blank page. Anyone have any ideas?
Thanks!
EDIT: Here is the full code:
checklink(); // If there is a match, then checklink will return a 1. If it's already tagged, it will return a 5.
var matchLink = null;
if (checklink() === 1) {
matchLink = window.location.href.toString();
if (checklink() != 1) {
matchLink = null;
function checklink() { //checks to see if the current URL matches website.com
var regExp = /website.com/gi,
testString = window.location.href.toString(),
match = 0,
tagged = 0;
if (regExp.test(testString)) { //if there is a match, returns 1
match = 1;
var regExp2 = /UTF8/gi;
if (regExp2.test(testString)) { //if UTF8 is found, then it returns 5
tagged = 5;
return(match + tagged);
function tagUTF() {
if (matchLink) {
var newLink = matchLink + "?ie=UTF8";
document.write("Link");
if (matchLink) {
tagUTF();
}
The chrome content script has access to the DOM, so you could just replace the contents of the body element of the current page with a new node that has your anchor tag either using dom manipulation methods or innerHTML:
document.body.innerHTML = "<a href=" + window.location.href + "?ie=UTF8>Title of Link</a>";
Please note, this assumes that the JavaScript that is doing the DOM manipulation was properly added for your Chrome extension as a "content script":
http://code.google.com/chrome/extensions/content_scripts.html
EDIT:
Here is the code I used to make it work for me locally:
manifest.json
{
"name": "Test",
"version": "1.0",
"description": "Test",
"permissions": [
"<all_urls>"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_end"
}
]
}
content-script.js
document.body.innerHTML = "<a href='test'>test</a>";