Skip to content

Instantly share code, notes, and snippets.

@mogsdad
Last active June 21, 2016 17:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mogsdad/5960749 to your computer and use it in GitHub Desktop.
Save mogsdad/5960749 to your computer and use it in GitHub Desktop.
/**
* calCopier.gs - Google Apps Script to copy events between two google calendars.
*
* Copyright 2013 by David Bingham (akd Mogsdad)
* Licensed under Creative Commons 0 / CC0 / http://directory.fsf.org/wiki/License:CC0
*/
/**
* Adds a custom menu to the active spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Copy Calendar",
functionName : "copyCalendar"
}];
sheet.addMenu("Calendars", entries);
};
/**
* Use UiApp to present a form to user.
*/
function copyCalendar() {
var app = UiApp.createApplication();
var form = app.createFormPanel().setId("emailCopyForm");
var panel = app.createVerticalPanel();
var flow = app.createFlowPanel();
var flowFrom = app.createFlowPanel();
flowFrom.add(app.createLabel("From Calendar:"));
flowFrom.add(app.createListBox().setName("fromType")
.addItem("Use Default Calendar","useDefault")
.addItem("Calendar ID","calId")
.addItem("Calendar Name","calName"));
flowFrom.add(app.createTextBox().setName("fromValue"));
panel.add(flowFrom);
var flowTo = app.createFlowPanel();
flowTo.add(app.createLabel("To Calendar:"));
flowTo.add(app.createListBox().setName("toType")
.addItem("Use Default Calendar","useDefault")
.addItem("Calendar ID","calId")
.addItem("Calendar Name","calName"));
flowTo.add(app.createTextBox().setName("toValue"));
panel.add(flowTo);
var flowDates = app.createFlowPanel();
flowDates.add(app.createLabel("Date range for copy:"));
flowDates.add(app.createInlineLabel("From:"));
flowDates.add(app.createDateBox().setName("startDate").setValue(new Date()).setFormat(UiApp.DateTimeFormat.DATE_TIME_FULL));
flowDates.add(app.createInlineLabel(" To:"));
flowDates.add(app.createDateBox().setName("endDate").setValue(new Date()).setFormat(UiApp.DateTimeFormat.DATE_TIME_FULL));
panel.add(flowDates);
panel.add(app.createSubmitButton("Submit"));
form.add(panel);
app.add(form);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
/**
* doPost function with multi-form handling. Individual form handlers must
* return UiApp instances.
*/
function doPost(eventInfo) {
var app;
Logger.log("Form ID = %s", eventInfo.parameter.formId);
// Call appropriate handler for the posted form
switch (eventInfo.parameter.formId) {
case 'emailCopyForm':
app = postEmailCopyForm(eventInfo);
break;
default:
app = reportFormParameters (eventInfo);
break;
}
return app;
}
function postEmailCopyForm(eventInfo) {
var error = false;
var app = UiApp.getActiveApplication();
var panel = app.createVerticalPanel();
// Validate input
var input = eventInfo.parameter;
var fromCal = getCalId(input.fromType, input.fromValue);
if (fromCal == null) {
panel.add(app.createLabel("Calendar <" + input.fromValue + "> does not exist, or you do not have permission to access it."));
error = true;
}
var toCal = getCalId(input.toType, input.toValue);
if (toCal == null) {
panel.add(app.createLabel("Calendar <" + input.toValue + "> does not exist, or you do not have permission to access it."));
error = true;
}
if (!error && (fromCal.getId() == toCal.getId())) {
panel.add(app.createLabel("Cannot copy to & from same calendar."));
error = true;
}
var startDate = input.startDate;
var endDate = input.endDate;
if (Date(startDate) > Date(endDate)) {
panel.add(app.createLabel("Invalid dates."));
error = true;
}
// Ok, all validation done - do the copying.
if (!error) {
Logger.log("From <%s> id = <%s> name = <%s>.", input.fromValue,fromCal,fromCal.getName());
Logger.log("To <%s> id = <%s> name = <%s>.", input.toValue,toCal,toCal.getName() );
var numCopied = copyEvents(fromCal,toCal,startDate,endDate);
panel.add(app.createLabel("Copied " + numCopied + " events from " + fromCal.getName() + " to " +toCal.getName()));
}
app.add(panel);
return app;
}
/**
* Debug function - returns a UiInstance containing all parameters from the
* provided form Event.
*
* Example of use:
* <pre>
* function doPost(eventInfo) {
* return reportFormParameters(eventInfo);
* }
* </pre>
*
* @param {Event} eventInfo Event from UiApp Form submission
*
* @return {UiInstance}
*/
function reportFormParameters (eventInfo) {
var app = UiApp.getActiveApplication();
var panel = app.createVerticalPanel();
panel.add(app.createLabel("Form submitted"));
for (var param in eventInfo.parameter) {
switch (param) {
// Skip the noise; these keys are used internally by UiApp
case 'lib':
case 'appId':
case 'formId':
case 'token':
case 'csid':
case 'mid':
break;
// Report parameters named in form
default:
panel.add(app.createLabel(" - " + param + " = " + eventInfo.parameter[param]));
break;
}
}
app.add(panel);
return app;
}
/**
* Process Calendar Type & Value parameters to return a calendar object.
* Returns null if no matching calendar.
*/
function getCalId(type,value) {
var cal = null;
Logger.log("type == %s, value == %s", type, value);
if (type == "useDefault") {
cal = CalendarApp.getDefaultCalendar();
}
else if (value != "") {
if (type == "calName") {
var cals = CalendarApp.getCalendarsByName(value);
if (cals.length > 0) {
cal = cals[0];
}
}
if (type == "calId") {
//var value = 'f4ivbet3sro7smqkdm9613h7eg@group.calendar.google.com';
cal = CalendarApp.getCalendarById(value);
}
}
return cal;
}
/**
* test_copyEvents() - test driver for use in debugger.
*/
function test_copyEvents() {
var fromCal=getCalId("calName","Source");
var toCal=getCalId("calName","Destination");
var startDate = "Thursday, 2013 July 11 12:00:00 UTC-4";
var endDate= "Tuesday, 2013 July 16 12:00:00 UTC-4";
var numCopied = copyEvents(fromCal, toCal, startDate, endDate);
debugger;
}
/**
* Copy all events in given date range. Returns number of unique events
* that were copied.
*/
function copyEvents(fromCal, toCal, startDate, endDate) {
var copiedEvents = [];
var date = new Date(startDate);
var endD = new Date(endDate);
Logger.log("startDate="+startDate+", date="+date+", endDate="+endDate);
while (date <= endD) {
Logger.log(date);
var events = fromCal.getEventsForDay(date);
for (var event=0; event < events.length; event++) {
var src = events[event];
var srcId = src.getId();
if (copiedEvents.indexOf(srcId) >= 0) continue; // Multi-day events only need to be copied once
copiedEvents.push(srcId);
var newEvent;
if (src.isAllDayEvent()) {
newEvent = toCal.createAllDayEvent(src.getTitle(), src.getAllDayStartDate())
.setTime(src.getAllDayStartDate(), src.getAllDayEndDate());
}
else {
newEvent = toCal.createEvent(src.getTitle(), src.getStartTime(), src.getEndTime());
}
// Set additional attributes, common for all day & single events
var desc = src.getDescription();
if (!src.isOwnedByMe()) { // Prepend Email address of creator to description, if not "me"
var creators = src.getCreators();
if (creators.length > 0)
desc = creators[0].concat(desc);
}
try {
newEvent.setDescription(desc);
} catch (e) {
// Work around http://code.google.com/a/google.com/p/apps-api-issues/issues/detail?id=3425
};
newEvent.setLocation(src.getLocation());
// Email Reminders
var reminders = src.getEmailReminders();
for (var rem=0; rem < reminders.length; rem++) {
src.addEmailReminder(reminders[rem]);
}
// Popup Reminders
reminders = src.getPopupReminders();
for (var rem=0; rem < reminders.length; rem++) {
src.addPopupReminder(reminders[rem]);
}
// SMS Reminders
reminders = src.getSmsReminders();
for (var rem=0; rem < reminders.length; rem++) {
src.addSmsReminder(reminders[rem]);
}
}
date.setDate(date.getDate() + 1);
}
return copiedEvents.length;
}

Calendar Copier

This Google Apps Script runs in a spreadsheet, where it prompts the user for the identity of two calendars, and a range of dates, then copies the corresponding events from one calendar to the other.

Caveats

This script is incomplete, ignoring a number of event attributes. (Feel free to enhance it, if you wish!) There are also some known issues:

  • All Day Events aren't really all day events. The createAllDayEvent() method should take care of this, but doesn't. Google Bug.
  • Recurrence isn't supported. (You can't find out the recurrence of existing events, so cannot recreate it.)

To use

This script is designed as a spreadsheet-contained script. From Google Drive, open a Google Spreadsheet, open the script editor, paste in all the content of calCopier.js, and save. Authorization is required for operation, so attempt to run the onOpen() function from the editor, which will trigger the authorization mechanism.

You may copy events between any two existing calendars. You must have read privileges on the source calendar, and must have edit privileges on the destination. You do not need to own either calendar, however you can only refer to non-owned calendars by ID. (Owned calendars add the option of using the calendar name or the account's default calendar.)

Test Harness

That sounds grand... but it's just one function, test_copyEvents() will drive a test of the main copy routine. As written, it assumes your account has a "Source" and a "Destination" calendar, and that "Source" has events in the given date range.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment