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
Related
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!
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.
I'm testing a sample code. It has always worked but suddenly i get:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
Again, it has ALWAYS worked. Nothing changed. I know to set console dev stuff and blablabla. I would like to know the cause of this issue.
This is my script:
gapi.client.init({
'apiKey': 'xxxxxxxx',
'discoveryDocs': ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"],
'clientId': 'xxxx.apps.googleusercontent.com',
'scope': 'https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar',
}).then(function() {
gapi.client.calendar.events.list({
'calendarId': 'primary',
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 10,
'orderBy': 'startTime' //from input
}).then(function(response) {
var events = response.result.items;
if (events.length > 0) {
for (var i = 0; i < events.length; i++) {
var event = events[i];
var when = event.start.dateTime;
if (!when) {
when = event.start.date;
}
appendPre(event.summary + ' (' + when + ')created at '+ event.created);
}
} else {
appendPre('No upcoming events found.');
}
});
});
function appendPre(message) {
var pre = document.getElementById('content');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
Even if you are not authenticating to Calendar as a user, you should create a client project and attach your key to requests so that Google has a project to "bill" the quota usage against. This will prevent these kind of issues in the future. See Google's help article but the general steps would be:
1) Create a Google API Project at https://console.developers.google.com.
2) Enable Calendar API for the project.
3) Get the API key under API Manager > Credentials.
4) Include the key as a parameter for all your Calendar API requests. E.g.
GET https://www.googleapis.com/calendar/v3/calendars/calendarId/events?key={your_key}
Solved with "https://www.googleapis.com/auth/calendar.readonly" scope! It works again without any changes. Maybe it needs some time, but "https://www.googleapis.com/auth/calendar" still not working.
I am running on a virtual host (as well as a Python SimpleHTTPServer) and I have been following Google's instructions, but I can't get the Google drive authorization to pop up on page refresh.
https://developers.google.com/drive/web/auth/web-client
Ive placed the first block of code in my index.html file, and Ive placed the following 2 snippets in <script> tags within <body>. I filled in the CLIENT_ID with the ID I created in the Google developer console...what am I missing?
<html>
<head>
<script type="text/javascript">
var CLIENT_ID = '1052173400541-355uhjrflurk7fmlon0r5umnn12i9ag3.apps.googleusercontent.com';
var SCOPES = [
'https://www.googleapis.com/auth/drive.file',
'email',
'profile',
// Add other scopes needed by your application.
];
/**
* Called when the client library is loaded.
*/
function handleClientLoad() {
checkAuth();
}
/**
* Check if the current user has authorized the application.
*/
function checkAuth() {
gapi.auth.authorize(
{'client_id': CLIENT_ID, 'scope': SCOPES, 'immediate': true},
handleAuthResult);
}
/**
* Called when authorization server replies.
*
* #param {Object} authResult Authorization result.
*/
function handleAuthResult(authResult) {
if (authResult) {
// Access token has been successfully retrieved, requests can be sent to the API
} else {
// No access token could be retrieved, force the authorization flow.
gapi.auth.authorize(
{'client_id': CLIENT_ID, 'scope': SCOPES, 'immediate': false},
handleAuthResult);
}
}
</script>
<script type="text/javascript" src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>
</head>
<body>
<h1>welcome to google_drive.local</h1>
<script type='text/javascript'>
/**
* Load the Drive API client.
* #param {Function} callback Function to call when the client is loaded.
*/
function loadClient(callback) {
gapi.client.load('drive', 'v2', callback);
}
/**
* Print a file's metadata.
*
* #param {String} fileId ID of the file to print metadata for.
*/
function printFile(fileId) {
var request = gapi.client.drive.files.get({
'fileId': fileId
});
request.execute(function(resp) {
if (!resp.error) {
console.log('Title: ' + resp.title);
console.log('Description: ' + resp.description);
console.log('MIME type: ' + resp.mimeType);
} else if (resp.error.code == 401) {
// Access token might have expired.
checkAuth();
} else {
console.log('An error occured: ' + resp.error.message);
}
});
}
</script>
</body>
</html>
By setting immediate=true you are suppressing the auth popup.
It should be false the first time through and is generally set to true thereafter for each hourly refresh.
I am working with Google drive picker, where once an item is selected from Google drive a url is produced. The problem is that that url is only accessible by the owner, and hence not public. I want the URL to be publicly accessible.
Hence, i've looked into the following guide:
https://developers.google.com/picker/docs/reference#Response.Documents
and feel that the Document.AUDIENCE class would be best applicable, however I do not know how to add that criteria into the below google drive picker example code.
Any help would be greatly appreciated.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Picker Example</title>
<script type="text/javascript">
// The Browser API key obtained from the Google Developers Console.
var developerKey = 'xxxxxxxYYYYYYYY-12345678';
// The Client ID obtained from the Google Developers Console. Replace with your own Client ID.
var clientId = "1234567890-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com"
// Scope to use to access user's photos.
var scope = ['https://www.googleapis.com/auth/photos'];
var pickerApiLoaded = false;
var oauthToken;
// Use the API Loader script to load google.picker and gapi.auth.
function onApiLoad() {
gapi.load('auth', {'callback': onAuthApiLoad});
gapi.load('picker', {'callback': onPickerApiLoad});
}
function onAuthApiLoad() {
window.gapi.auth.authorize(
{
'client_id': clientId,
'scope': scope,
'immediate': false
},
handleAuthResult);
}
function onPickerApiLoad() {
pickerApiLoaded = true;
createPicker();
}
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
oauthToken = authResult.access_token;
createPicker();
}
}
// Create and render a Picker object for picking user Photos.
function createPicker() {
if (pickerApiLoaded && oauthToken) {
var picker = new google.picker.PickerBuilder().
addView(google.picker.ViewId.PHOTOS).
setOAuthToken(oauthToken).
setDeveloperKey(developerKey).
setCallback(pickerCallback).
build();
picker.setVisible(true);
}
}
// A simple callback implementation.
function pickerCallback(data) {
var url = 'nothing';
if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
url = doc[google.picker.Document.URL];
}
var message = 'You picked: ' + url;
document.getElementById('result').innerHTML = message;
}
</script>
</head>
<body>
<div id="result"></div>
<!-- The Google API Loader script. -->
<script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
To add public permissions to the selected file. You will need to use the drive api to add file permissions
Please see https://developers.google.com/drive/v2/reference/permissions/insert
You will need to insert a permission to the file with role set to 'reader' and the type to 'anyone'
You can refer to the javascript implementation in the example
/**
* Insert a new permission.
*
* #param {String} fileId ID of the file to insert permission for.
* #param {String} value User or group e-mail address, domain name or
* {#code null} "default" type.
* #param {String} type The value "user", "group", "domain" or "default".
* #param {String} role The value "owner", "writer" or "reader".
*/
function insertPermission(fileId, value, type, role) {
var body = {
'value': value,
'type': type,
'role': role
};
var request = gapi.client.drive.permissions.insert({
'fileId': fileId,
'resource': body
});
request.execute(function(resp) { });
}
To elaborate on #Sam's answer, here is how you would do it without using the gapi.client.drive Javascript API.
Let's say that some_id is your document id:
request = gapi.client.request({
path: '/drive/v2/files/some_id/permissions',
method: 'POST',
body: {
role: 'reader'
type: 'anyone'
}
});
request.execute(function (res) {
console.log(res);
})
Which generates a request to https://content.googleapis.com/drive/v2/files/some_id/permissions?alt=json with this body: {role: "reader", type: "anyone"}
Here is the result you will get:
{
"kind": "drive#permission",
"etag": "some etage",
"id": "anyone",
"selfLink": "https://www.googleapis.com/drive/v2/files/some_id/permissions/anyone",
"role": "reader",
"type": "anyone"
}