| Issue 2332: | Google Calendar API V3 - Sample Implementation. | |
| 1 person starred this issue and may be notified of changes. | Back to list |
This is my best stab at implementing GCal API V3. The implementation I am using on my site is slightly different than this. I have removed the old 'gcal' references and I have a custom url in the events.push function in place of the the actual entry.htmlLink value so that the edit link points back to my server. I also have my fullcalendar sources pointing to my server and my server handles all the google oauth v3 authentication and google calendar API v3 calls for events list/create/update/delete for secure communications. Because my implementation is slightly different, I have not tested this exact code below in a production environment, but the processing logic for the GCal API V3 JSON is the same. In this sample code you will also see that I tried to retain backward compatibility with GCal API V1 (even though Gcal API V1 is being deprecated in one month). In searching for upgrading my own system, all my googling finds only request for V3 implenentation. This is really needed! Thanks for the amazing FullCalendar app! I hope I can give back a little by contributing this. Best of luck! // gcal.js Modified to implement Google Calendar API V3 /*! * FullCalendar v2.1.1 Google Calendar Plugin * Docs & License: http://arshaw.com/fullcalendar/ * (c) 2013 Adam Shaw * Modified by Garrett Bach 2014-10-28 to process GCal API V3 JSON events list */ (function(factory) { if (typeof define === 'function' && define.amd) { define([ 'jquery' ], factory); } else { factory(jQuery); } })(function($) { var fc = $.fullCalendar; var applyAll = fc.applyAll; fc.sourceNormalizers.push(function(sourceOptions) { if (sourceOptions.dataType == 'gcal' || (sourceOptions.dataType === undefined && (sourceOptions.url || '').match(/^(http|https):\/\/www.google.com\/calendar\/feeds\//))) { sourceOptions.dataType = 'gcal'; if (sourceOptions.editable === undefined) { sourceOptions.editable = false; } } else if (sourceOptions.dataType == 'gcalv3' || (sourceOptions.dataType === undefined && (sourceOptions.url || '').match(/^(http|https):\/\/www.googleapis.com\/calendar\/v3\/calendars\//))) { sourceOptions.dataType = 'gcalv3'; if (sourceOptions.editable === undefined) { sourceOptions.editable = false; } } }); fc.sourceFetchers.push(function(sourceOptions, start, end, timezone) { if (sourceOptions.dataType == 'gcal') { return transformOptions(sourceOptions, start, end, timezone); } else if (sourceOptions.dataType == 'gcalv3') { return transformOptionsV3(sourceOptions, start, end, timezone); } }); function transformOptions(sourceOptions, start, end, timezone) { var success = sourceOptions.success; var data = $.extend({}, sourceOptions.data || {}, { singleevents: true, 'max-results': 9999 }); return $.extend({}, sourceOptions, { url: sourceOptions.url.replace(/\/basic$/, '/full') + '?alt=json-in-script&callback=?', dataType: 'jsonp', data: data, timezoneParam: 'ctz', startParam: 'start-min', endParam: 'start-max', success: function(data) { var events = []; if (data.feed.entry) { $.each(data.feed.entry, function(i, entry) { var url; $.each(entry.link, function(i, link) { if (link.type == 'text/html') { url = link.href; if (timezone && timezone != 'local') { url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + encodeURIComponent(timezone); } } }); events.push({ id: entry.gCal$uid.value, title: entry.title.$t, start: entry.gd$when[0].startTime, end: entry.gd$when[0].endTime, url: url, location: entry.gd$where[0].valueString, description: entry.content.$t }); }); } var args = [events].concat(Array.prototype.slice.call(arguments, 1)); var res = applyAll(success, this, args); if ($.isArray(res)) { return res; } return events; } }); function transformOptionsV3(sourceOptions, start, end, timezone) { var success = sourceOptions.success; var data = $.extend({}, sourceOptions.data || {}, { singleevents: true, 'max-results': 9999 }); return $.extend({}, sourceOptions, { url: sourceOptions.url, dataType: 'json', data: data, startParam: 'start-min', endParam: 'start-max', success: function(data) { var events = []; if (data.feed.entry) { $.each(data.feed.entry, function(i, entry) { events.push({ id: entry.iCalUID, title: entry.summary || '', // must allow default to blank, if it's not set it doesn't exist in the json and will error here start: entry.start.dateTime || entry.start.date, end: entry.end.dateTime || entry.start.date, // because end.date may be the next day, cause a '2-all-day' event, we use start.date here. url: entry.htmlLink, location: entry.location || '', // must allow default to blank, if it's not set it doesn't exist in the json and will error here description: entry.description || '' // must allow default to blank, if it's not set it doesn't exist in the json and will error here }); }); } var args = [events].concat(Array.prototype.slice.call(arguments, 1)); var res = applyAll(success, this, args); if ($.isArray(res)) { return res; } return events; } }); } // legacy fc.gcalFeed = function(url, sourceOptions) { return $.extend({}, sourceOptions, { url: url, dataType: 'gcal' }); }; });
Oct 30, 2014
I noticed one change needed in my code. For the V3 events.push change the following: from "id: entry.iCalUID" to "id: entry.id" I didn't realize I was grabbing the wrong ID until after I got deeper into the implementation.
Feb 3, 2015
Hi Garret, I have looked and looked on the internet for a solution, yours is the closest solution I found. I am just wondering if you could give me a hint or point me in the right direction. You see I have created a web form which will be open to public and I want the people to submit the form to a public google calendar without authentication/authorization, because I have to consider the possibility that they might not have a gmail account. Now from my understanding in order to do this I just need the API key from the google developer console and they should be able to insert the event, which is then monitored by an admin who either approves or disapprove the form and then they copy it to the master calendar which available to public for viewing. I would really appreciate any help in this matter. Thanks so much for your time. Regards, Adnan Abbasi
Feb 3, 2015
Hi Adnan, It's difficult to help without diving fully into your project, but I can try to offer some workflow ideas. I hope my suggestions below are not completely out of scope, and that these ideas are helpful. I'm sorry I cannot spare more time advising other than this one email. First off, from your description, I'm not seeing where the user's calendar is used, it sounds like you are dealing with events related to your own company/organization, therefore only one google account calendar, so no other api keys should be needed for users, and you can create a second calendar within the one company Google account to separate proposed/unapproved events from approved/real events so that you are only dealing with calendars you have full control over with your api keys. So I will continue with the idea that are dealing with two calendars really, or that you need to be dealing with two, one for proposed unapproved events and a second for approved events. I don't see how you could differentiate unapproved and approved Events within a single google calendar. And for these two categories of events I wouldn't think you would need access to the users calendar for proposed events. It's better to have fewer calendars involved so that you are only spring the API keys for one or two gcals. You could then display these approved and unapproved events on the same FullCalendar view in different colors. The first workflow idea is to then add all proposed events to calendar A, and then when an event is approved, use the API calls to move the event to the other calendar. (I found the move command tricky, at least in my situation, I reverted to deleting the existing event from cal A and then creating a new event with the same information in cal B. Another workflow option might be to use only one calendar (B) and skip the google calendar (A) for the initial proposed events (prior to approval). This form submission could simply store the proposed event information, say in a database, and you generate your own basic event feed for the unapproved proposed events to FullCalendar. That is actually quite easy to do reading the FullCalendar docs on custom event sources. Then when the events are approved, rather than moving the events from cal A to cal B you are simply adding to cal B, the official approved events. If there are no recurring events it's really easy to store basic event data in your own database and generate your own custom feed for for FullCalendar view. You could do that for both approved and unapproved events and skip the GCal and APIs all together. Best of luck in your endeavors. I hope you get it all working. -Garrett (From my mobile) |
|
| ► Sign in to add a comment |
Mergedinto: 1526