How to create events with google calendar api using a chrome extension? - javascript

Goal: To create a google calendar event using a chrome extension.
I have been stuck for hours to find a solution to call the google calendar api from my popup script. However, I keep getting the following error:
Refused to load the script 'https://apis.google.com/js/client.js' because it violates the following Content Security Policy directive: "script-src 'self' https://apis.google.com/js/api.js". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
The calendar api works if I use the example given on the official docs (https://developers.google.com/calendar/api/quickstart/js). However, in this example the api script is loaded using the script tag in a html file and it does not encounter the above issue for reasons I do not fully understand.
My Manifest.json
{
"manifest_version": 2,
"name": "",
"description": "",
"version": "1.0",
"permissions": [
"identity","*://apis.google.com/*"
],
"content_scripts":[
{ "matches": ["<all_urls>"]
}
],
"browser_action":{
"default_icon":"icon.png",
"default_popup":"popup.html",
"default_title":"A popup will come here"
},
"content_security_policy": "script-src 'self' https://apis.google.com/js/api.js; object-src 'self'"
}
My popup.html
<!doctype html>
<html>
<head>
<title>GTmetrix Analyzer</title>
<script src="popup.js"></script>
</head>
<body>
<button id="authorize_button" style="display: none;">Authorize</button>
<button id="signout_button" style="display: none;">Sign Out</button>
</body>
</html>
My popup.js
// Client ID and API key from the Developer Console
var CLIENT_ID ="";
var API_KEY = "";
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES =
"https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.readonly";
var authorizeButton = document.getElementById("authorize_button");
var signoutButton = document.getElementById("signout_button");
function handleClientLoad() {
gapi.load("client:auth2", initClient);
}
function initClient() {
gapi.client
.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
})
.then(
function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
},
function (error) {
appendPre(JSON.stringify(error, null, 2));
}
);
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick(event) {
gapi.auth2.getAuthInstance().signIn();
}
/**
* Sign out the user upon button click.
*/
function handleSignoutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
authorizeButton.style.display = "none";
signoutButton.style.display = "block";
createEvent();
} else {
authorizeButton.style.display = "block";
signoutButton.style.display = "none";
}
}
function createEvent() {
var event = {
summary: "Google I/O 2015",
location: "800 Howard St., San Francisco, CA 94103",
description: "A chance to hear more about Google's developer products.",
start: {
dateTime: "2021-08-22T09:00:00-07:00",
timeZone: "America/Los_Angeles",
},
end: {
dateTime: "2021-08-22T17:00:00-07:00",
timeZone: "America/Los_Angeles",
},
};
gapi.client.calendar.events
.insert({
calendarId: "primary",
resource: event,
})
.then(function (response) {
appendPre("Event created: " + response.result.htmlLink);
console.log(response);
});
}
let script = document.createElement('script');
chrome.tabs.getSelected(null,function(tab){
script.src = "https://apis.google.com/js/api.js";
document.body.append(script);
})
script.addEventListener('load',function(){
handleClientLoad();
})
Note: I would also be interested to know if there is a better way of making this type of chrome extension than what I am currently doing. Thanks!

Related

How can I use google api in chrome extension content script?

I am trying to list labels using Gmail api. I want to use Gmail api in content script. Following is my manifest.json file and content script file:
{
"name": "Append Test Text",
"description": "Add test123 to body",
"version": "1.0",
"permissions": ["activeTab"],
"content_scripts": [
{
"matches": ["https://mail.google.com/*"],
"js": ["jquery-3.4.1.min.js", "gmail.js", "content-script.js"],
"all_frames": true
}
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"browser_action": {
"default_title": "Append Test Text"
},
"manifest_version": 2
}
content-script.js:
// Client ID and API key from the Developer Console
var CLIENT_ID = "<CLIENT_ID>";
var API_KEY = "<API_KEY>";
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = [
"https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"
];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/gmail.readonly";
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
console.log("hello");
gapi.load("client:auth2", initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
gapi.client
.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
})
.then(
function() {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
},
function(error) {
console.log(JSON.stringify(error, null, 2));
}
);
}
/**
* Called when the signed in status changes, to update the UI
* appropriately. After a sign-in, the API is called.
*/
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
listLabels();
} else {
gapi.auth2.getAuthInstance().signIn();
}
}
/**
* Print all Labels in the authorized user's inbox. If no labels
* are found an appropriate message is printed.
*/
function listLabels() {
gapi.client.gmail.users.labels
.list({
userId: "me"
})
.then(function(response) {
var labels = response.result.labels;
console.log("Labels:");
if (labels && labels.length > 0) {
for (i = 0; i < labels.length; i++) {
var label = labels[i];
console.log(label.name);
}
} else {
console.log("No Labels found.");
}
});
}
var script = $(
'<script async defer src="https://apis.google.com/js/api.js" onload="this.onload=function(){};handleClientLoad()" onreadystatechange="if (this.readyState === "complete") this.onload()"></script>'
);
$("body").append(script);
after running this there should at least be a "hello" in the console proving that the handleClientLoad() function is working. But nothing shows in the console.
Technically, Chrome won't allow you to do that. If you use content script to inject into a website then you can just send request only on that site, if you're trying to send a request into another site, Chrome will stop you due to CORS policy.
So to achieve it, you have to setup a background script which receive passed messages from your content script and then send request into Google API endpoint and then you can return the result into your content script via defined message channel. Here is how you can setup message passing in Chrome Extension.

Events created with Calendar API not showing up in the calendar

I'm experimenting with Google Calendar API in an attempt to set reminders for users. I think setting a calendar event and setting a reminder to it could do what I need.
I'm using this sample code from google to try the API, I'm getting Back the description of the event I created. But if I sign in to my Google calendar I can't see the event there.
<!DOCTYPE html>
<html>
<head>
<title>Google Calendar API Quickstart</title>
<meta charset='utf-8' />
</head>
<body>
<p>Google Calendar API Quickstart</p>
<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize-button" style="display: none;">Authorize</button>
<button id="signout-button" style="display: none;">Sign Out</button>
<pre id="content"></pre>
<script type="text/javascript">
// Client ID and API key from the Developer Console
var CLIENT_ID ='781531190408-u3gh34li8b0um74r11m8hsbcofbpj4jf.apps.googleusercontent.com';
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/calendar.readonly";
var authorizeButton = document.getElementById('authorize-button');
var signoutButton = document.getElementById('signout-button');
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
gapi.client.init({
discoveryDocs: DISCOVERY_DOCS,
clientId: CLIENT_ID,
scope: SCOPES
}).then(function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
});
}
/**
* Called when the signed in status changes, to update the UI
* appropriately. After a sign-in, the API is called.
*/
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
authorizeButton.style.display = 'none';
signoutButton.style.display = 'block';
listUpcomingEvents();
// Refer to the JavaScript quickstart on how to setup the environment:
// https://developers.google.com/google-apps/calendar/quickstart/js
// Change the scope to 'https://www.googleapis.com/auth/calendar' and delete any
// stored credentials.
var event = /*{
'summary': 'Google I/O 2015',
'location': '800 Howard St., San Francisco, CA 94103',
'description': 'A chance to hear more about Google\'s developer products.',
'start': {
'dateTime': '2017-07-28T09:00:00-07:00',
'timeZone': 'America/Los_Angeles'
},
'end': {
'dateTime': '2017-07-28T17:00:00-07:00',
'timeZone': 'America/Los_Angeles'
},
'recurrence': [
'RRULE:FREQ=DAILY;COUNT=2'
],
'attendees': [
{'email': 'lpage#example.com'},
{'email': 'sbrin#example.com'}
],
'reminders': {
'useDefault': false,
'overrides': [
{'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10}
]
}
};*/{
"reminders": {
"useDefault": false,
"overrides": [
{
"method": "email",
"minutes": 15
},
{
"method": "popup",
"minutes": 15
},
{
"method": "popup",
"minutes": 5
}
]
},
"start": {
"dateTime": "2017-07-12T10:30:00.0z"
},
"end": {
"dateTime": "2017-07-12T11:30:00.0z"
},
"description": "Just a test description "
};
var request = gapi.client.calendar.events.insert({
'calendarId': 'primary',
'resource': event
});
request.execute(function(event) {
appendPre('Event created: ' + event.description);
});
} else {
authorizeButton.style.display = 'block';
signoutButton.style.display = 'none';
}
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick(event) {
gapi.auth2.getAuthInstance().signIn();
}
/**
* Sign out the user upon button click.
*/
function handleSignoutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}
/**
* Append a pre element to the body containing the given message
* as its text node. Used to display the results of the API call.
*
* #param {string} message Text to be placed in pre element.
*/
function appendPre(message) {
var pre = document.getElementById('content');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
/**
* Print the summary and start datetime/date of the next ten events in
* the authorized user's calendar. If no events are found an
* appropriate message is printed.
*/
function listUpcomingEvents() {
gapi.client.calendar.events.list({
'calendarId': 'primary',
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 10,
'orderBy': 'startTime'
}).then(function(response) {
var events = response.result.items;
appendPre('Upcoming events:');
if (events.length > 0) {
for (i = 0; i < events.length; i++) {
var event = events[i];
var when = event.start.dateTime;
if (!when) {
when = event.start.date;
}
appendPre(event.description + ' (' + when + ')')
}
} else {
appendPre('No upcoming events found.');
}
});
}
</script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body>
</html>
Am I doing something wrong? Thanks.
I've tried the JavaScript Quickstart and insert an event using this sample code and I got a successful response. Maybe the problem is how you handle your authorization. You may check this tutorial.
Some additional references:
Need good example: Google Calendar API in Javascript
Events added through Google API PHP client not showing in Calendar
Google Calendar Api is not showing event list

API key not valid error when trying to get profile data

I'm trying to test Google login API. I want to retrieve some basic data after the client logs.
I created a Google API Console project and client ID (https://developers.google.com/identity/sign-in/web/devconsole-project)
Here is google official code (https://developers.google.com/api-client-library/javascript/samples/samples#LoadinganAPIandMakingaRequest):
<html>
<head>
</head>
<body>
<script type="text/javascript">
function handleClientLoad() {
// Loads the client library and the auth2 library together for efficiency.
// Loading the auth2 library is optional here since `gapi.client.init` function will load
// it if not already loaded. Loading it upfront can save one network request.
gapi.load('client:auth2', initClient);
}
function initClient() {
// Initialize the client with API key and People API, and initialize OAuth with an
// OAuth 2.0 client ID and scopes (space delimited string) to request access.
gapi.client.init({
apiKey: '5v2RzP7-xyQGNjxrD5suoPL9',
discoveryDocs: ["https://people.googleapis.com/$discovery/rest?version=v1"],
clientId: '298062822261-e5c09q8191mkho0o7n3n3obiq2eq2p3f.apps.googleusercontent.com',
scope: 'profile'
}).then(function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
});
}
function updateSigninStatus(isSignedIn) {
// When signin status changes, this function is called.
// If the signin status is changed to signedIn, we make an API call.
if (isSignedIn) {
makeApiCall();
}
}
function handleSignInClick(event) {
// Ideally the button should only show up after gapi.client.init finishes, so that this
// handler won't be called before OAuth is initialized.
gapi.auth2.getAuthInstance().signIn();
}
function handleSignOutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}
function makeApiCall() {
// Make an API call to the People API, and print the user's given name.
gapi.client.people.people.get({
resourceName: 'people/me'
}).then(function(response) {
console.log('Hello, ' + response.result.names[0].givenName);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
}
</script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
<button id="signin-button" onclick="handleSignInClick()">Sign In</button>
<button id="signout-button" onclick="handleSignOutClick()">Sign Out</button>
</body>
</html>
ClientId and apikey are from my google api.
I receive error 400
{
"error": {
"code": 400,
"message": "API key not valid. Please pass a valid API key.",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google developers console",
"url": "https://console.developers.google.com"
}
]
}
]
}
}
I don't understand what's wrong whit my api key. I followed all the steps from google documentation.
It seems you are using the client secret as API key. Isn't it?
To obtain an API key you must create it.
Under credentials (dev console) click on create credential, and then 'API Key'

Access Firefox' LocalStorage from extension

I build a Firefox Extension with an options page using this documentation
Implement a settings page
On this settings page, the user has the possibility to enter Username and Password which then are stored in localStorage.
function saveOptions() {
localStorage.setItem("hbz_username", document.querySelector("#username").value);
localStorage.setItem("hbz_pass", Aes.Ctr.encrypt(document.querySelector("#password").value, '12345678910111213', 128));
}
The addon manipulates the DOM of a website and generates links to a REST Webservice within HTML elements which are containing a string that matches a specific Regex pattern. When one clicks on that link, the extension should load the user credentials from localStorage
var username= localStorage.getItem("hbz_username");
var passwd = localStorage.getItem("hbz_pass");
But this doesn't work. When I'm trying it the way described in the above listed documentation, I get a "browser is not defined" Error in the F12 Console.
function restoreOptions() {
function setCurrentChoice(result) {
username = result.username;
}
function onError(error) {
console.log(`Error: ${error}`);
}
var getting = browser.storage.local.get("hbz_username");
getting.then(setCurrentChoice, onError);
}
So my question is: How do I access the localStorage from my extension?
Edit: Added the contents of my previous answer on the post to the question.
Sorry for the wait but I had to prepare a minified version of the plugin without any cryptography or such.
package.json
{
"title": "Addon From Scratch",
"name": "addon-from-scratch",
"version": "0.0.1",
"description": "A basic add-on",
"main": "index.js",
"author": "Chris",
"engines": {
"firefox": ">=38.0a1",
"fennec": ">=38.0a1"
},
"license": "MIT",
"keywords": [
"jetpack"
]
}
index.js
var self = require("sdk/self");
// a dummy function, to show how tests work.
// to see how to test this function, look at test/test-index.js
function dummy(text, callback) {
callback(text);
}
exports.dummy = dummy;
var tag = "body"
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
var pageMod = require("sdk/page-mod");
var data = require("sdk/self").data;
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
function handleClick(state) {
tabs.open("login.html")
}
function onOpened() {
console.log(`Options page opened`);
}
function onError(error) {
console.log(`Error: ${error}`);
}
pageMod.PageMod({
include: "*",
contentScriptFile: data.url("modify-content.js"),
onAttach: function(worker) {
worker.port.emit("getElements", tag);
worker.port.on("gotElement", function(elementContent) {
console.log(elementContent);
});
}
});
modify-content.js
self.port.on("getElements", function() {
var username= localStorage.getItem("hbz_username");
var passwd = localStorage.getItem("hbz_pass");
//here is where usually the DOM Manipulation takes place and the link to the REST Service is created. The link contains the username and the encrypted password as param
alert(username + passwd);
});
options.js
function saveOptions() {
localStorage.setItem("hbz_username", document.querySelector("#username").value);
localStorage.setItem("hbz_pass", document.querySelector("#password").value);
}
function restoreOptions() {
function setCurrentChoice() {
document.querySelector("#username").value = localStorage.getItem("hbz_username");
document.querySelector("#password").value = localStorage.getItem("hbz_pass");
}
function onError(error) {
console.log(`Error: ${error}`);
}
setCurrentChoice();
}
document.addEventListener("DOMContentLoaded", restoreOptions);
document.querySelector("form").addEventListener("submit", saveOptions);
login.html
<form>
Username:<input type="text" id="username" />
Password:<input type="password" id="password" />
<input type="submit" Value="Submit" />
</form>
<script src="options.js"></script>
As I mentioned, I already tried the solution from the above mentioned documentation. This is the code as it is actually. I hope this helps.

#me called by anonymous error when using google plus api inside a gwt project

I'm using the google api javascript client to get information about the user profile inside a gwt project hosted in google app engine.
In localhost, the data is being retrieved correctly. I get a json with the google plus profile. When I deploy to appengine, the response is 401, "#me called by anonymous".
Here is my Code:
<script src="https://apis.google.com/js/client.js"></script>
<script type="text/javascript">
$(function() {
auth();
});
var API_KEY = "***************************************";
var CLIENT_ID = "************.apps.googleusercontent.com";
var scopes = 'https://www.googleapis.com/auth/plus.me';
function auth() {
var config = {
'client_id' : CLIENT_ID,
'scope' : scopes,
'key' : API_KEY,
};
gapi.client.load('plus', 'v1', function() {
api.client.setApiKey(API_KEY);
gapi.auth.authorize(config, function() {
var request = gapi.client.plus.people.get({
'userId' : 'me',
});
request.execute(function(resp) {
console.log(resp);
});
});
});
}
</script>
What i tried:
call to api.client.setApiKey at the begining.
create a new google api access with the google api console
update:
This is the complete response error message:
{
"error": {
"code": 401,
"message": "me called by anonymous",
"data": [
{
"domain": "global",
"reason": "authError",
"message": "me called by anonymous",
"locationType": "header",
"location": "Authorization"
}
]
},
"id": "gapiRpc"
}
There are other messages that may be related:
This is one of them:
Skipping duplicate osapi method definition chili.people.list on transport googleapis; others may exist, but suppressing warnings cb=gapi.loaded1 (línea 119)
Skipping duplicate osapi method definition pos.plusones.list on transport googleapis; others may exist, but suppressing warnings cb=gapi.loaded1 (línea 119)
Skipping duplicate osapi method definition chili.activities.list on transport googleapis; others may exist, but suppressing warnings cb=gapi.loaded1 (línea 119)
Skipping duplicate osapi method definition googleapis.newHttpRequest on transport googleapis; others may exist, but suppressing warnings
this is the other:
Invalid auth token. 1025***** vs 140186****
I could finally resolve the issue with the following settings or steps:
1) In the google apis console, I left the Redirect URIs section empty and completed the JavaScript origins section with the url of my site, repeating it with the https protocol:
JavaScript origins:
http://example.com
https://example.com
I put the script that loads the api before the end body tag:
<script src="https://apis.google.com/js/client.js"></script>
This script comes inside the body, before the api script:
<script type="text/javascript">
var API_KEY = "***************************************";
var CLIENT_ID = "************.apps.googleusercontent.com";
var scopes = 'https://www.googleapis.com/auth/plus.me';
function auth() {
var scopes = 'https://www.googleapis.com/auth/plus.me';
gapi.client.setApiKey(API_KEY);
window.setTimeout(checkAuth, 1000);
function checkAuth() {
gapi.auth.authorize({
client_id : CLIENT_ID,
scope : scopes,
immediate : false
}, handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult) {
makeApiCall();
} else {
checkAuth();
}
}
function makeApiCall() {
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get({
'userId' : 'me'
});
request.execute(function(resp) {
$("#image").attr("src", resp.image.url);
});
});
}
}
</script>
Then I call the function auth() when the user clicks to see his picture.

Categories

Resources