Learning Google Apps Script - Sample Chapter
Learning Google Apps Script - Sample Chapter
$ 39.99 US
25.99 UK
P U B L I S H I N G
Ramalingam Ganapathy
Learning Google
Apps Script
ee
pl
C o m m u n i t y
E x p e r i e n c e
D i s t i l l e d
Learning Google
Apps Script
Customize and automate Google Applications using Apps Script
Sa
m
Ramalingam Ganapathy
more than 15 years of working experience of JavaScript and Google Apps Script. In
1985, he started his career as a digital electronic circuit designer and service engineer.
Highly interested in reading technical books and building electronic projects, he is
a detail-oriented and logical person. Since 2001, he has been freelancing with Elance
and Upwork (formerly oDesk). He earned a good reputation on the Upwork portal,
and most of his clients are satisfied.
Preface
Google Apps is a collection of applications, namely, Gmail, Calendar, Drive, Docs,
Sheets, and Forms. You can customize or automate Google Apps using the scripting
language JavaScript with Google's defined classes. Google implements Google Apps
Script (GAS) based on JavaScript.
Almost all Google Apps provide one or more services. GAS services and APIs
provide easy access to automate tasks across Google products and third-party
services. You can use these service classes in your GAS code to customize or
automate Google Apps.
This book introduces basic things first before moving to advanced concepts
step by step with practical code and examples. By reading this book, you'll
gather expertise in Google Apps Script. Happy reading!
Preface
Chapter 5, Creating Google Calendar and Drive Applications, teaches the reader to create
Calendar events and sync events from one Calendar to another Calendar. This
chapter also teaches how to enable GAS advanced services.
Chapter 6, Creating Feed Reader and Translator Applications, is about learning and
creating many useful applications, including RSS/Atom reader and language
translator applications.
Chapter 7, Creating Interactive Webpages, tells how to create an RSS feed/publisher, a
file uploading application, and a full-blown timesheet application using HtmlService.
Chapter 8, Building a Workflow Application, explains how to create a workflow
application and proceeds create a useful real-world order processing application.
Chapter 9, More Tips and Tricks and Creating an Add-on, is all about using external
libraries including OAuth2, and Apps Script add-ons.
[ 33 ]
To know more available methods of the ContactsApp class, in the code editor, type
ContactsApp and . (a dot) next to it. Then, you can view all the available methods
with parameter details in code hint as shown in the following screenshot:
You can see deprecated methods struck out in the preceding screenshot; you are
advised not to use those methods.
The getSheets() method returns an array of Sheet objects. From the array,
we can refer to an individual Sheet by its index.
In Google Sheets, column label starts from the letter A, and is counted in a
programmatic point of view, from left to right starting with the number 1. For
example, column A is 1, B is 2, and so on. Rows are identified by their respective
label numbers. In GAS, we can reference a cell or a range of cells by A1 notation or
by separate row and column numbers.
[ 34 ]
Chapter 3
For example:
[ 35 ]
Here, clear is the Range object's method to clear everything including format and
formula, in a range of cells. You can use the clear method of the Sheet object to
clear the entire Sheet. Alternatively, you can use the clearContent method to clear
content only.
// Returns an array of contacts where
// contacts name matches with search text.
var contacts = ContactsApp.getContactsByName(searchCriteria);
// Limit number of contacts.
if(contacts.length > numOfContacts) contacts.length =
numOfContacts;
var cell = SheetContacts.getRange("A7");
for(var i in contacts){
var name = contacts[i].getFullName();
var email = contacts[i].getEmails()[0];
if(email) email = email.getAddress();
else email = "";
// For simplicity get the first phone number
var phone = contacts[i].getPhones()[0];
if (phone) phone = phone.getPhoneNumber();
else phone = "";
[ 36 ]
Chapter 3
// For simplicity get the first address
var address = contacts[i].getAddresses()[0];
if(address) address = address.getAddress();
else address = "";
// cell.offset(rowOffset, columnOffset)
cell.offset(i,0).setValue(name);
cell.offset(i,1).setValue(email);
cell.offset(i,2).setValue(phone);
cell.offset(i,3).setValue(address);
}
};
Do not copy paste the preceding code, but edit it yourself. By doing
so, you'll be aware of available method signatures (method names and
parameters) of classes such as SpreadsheetApp, ContactApp, and
Contact with the help of the script editor's code hint feature.
After you have edited and saved code without error, turn to the spreadsheet
window. If you enter a search term in the A3 cell (search box) and click on Search,
then the first 10 contacts will be listed as shown in the following screenshot (the
listed contacts details vary as per your Gmail username and contacts):
[ 37 ]
What if you want to update the listed contacts by the searchContacts function? For
example, you may want to update the phone number and/or address of a contact. To
update contact fields, we will create another function called updateContacts. Before
creating that, in the Contacts Sheet, add a button next to Search named Update and
assign function name updateContacts as shown in the following screenshot:
Update those field values you would like to update. Now create the function
listed here:
function updateContacts(){
var SheetContacts = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Contacts");
var cell = SheetContacts.getRange("A7");
var numOfContacts = 10;
for(var i = 0; i < numOfContacts; i++){
var email = cell.offset(0, 1).getValue();
// Skip if email field is null
if(!email) continue;
var contact = ContactsApp.getContact(email);
// Skip if contact is null or undefined
if(!contact) continue;
var name = cell.offset(i, 0).getValue();
[ 38 ]
Chapter 3
// Skip if name field is null
if(!name) continue;
contact.setFullName(name);
[ 39 ]
The preceding function retrieves contacts by the given e-mail ID; and, for each
contact, it also retrieves field values and updates/adds those field values with the
Sheet values. This function can update/add full name, phone, and address fields
but not the e-mail ID.
You can use RegExp to extract only the required data from the message body text.
[ 40 ]
Chapter 3
Properties service
GAS provides the properties service to store and/or to retrieve project-related data.
The data organized as key/value pairs, can be set manually or by script codes. The
following screenshot shows how you can set properties manually. To see this dialog,
click on the File menu and select Project properties:
You can use manually created project properties in script codes, but
the properties created by code sometimes may not be visible in the
Project properties dialog. You can create, update, or delete project
properties in codes.
[ 41 ]
If any function name is appended with _, then it will not be listed under
the Run menu. You cannot run these functions directly, but they can be
called from the other functions. These are called private functions.
You can create the createFolder_ function in the same script file along with
the saveEmailAttachmentsToDrive function or in a separate script file such
as Library.gs:
/**
* Checks latest 100 inbox threads,
*
saves attachments in 'Gmail attachments' folder,
*
*/
function saveEmailAttachmentsToDrive(){
// Create 'Gmail Attachments' folder if not exists.
createFolder_('Gmail attachments');
// Get inbox threads starting from the latest one to 100.
var threads = GmailApp.getInboxThreads(0, 100);
var messages = GmailApp.getMessagesForThreads(threads);
var folderID = PropertiesService.getUserProperties()
.getProperty("FOLDER");
var file, folder = DriveApp.getFolderById(folderID);
for (var i = 0 ; i < messages.length; i++) {
for (var j = 0; j < messages[i].length; j++) {
if(!messages[i][j].isUnread()){
var msgId = messages[i][j].getId();
// Assign '' if MSG_ID is undefined.
var oldMsgId = PropertiesService.getUserProperties()
.getProperty('MSG_ID') || '';
if(msgId > oldMsgId){
var attachments = messages[i][j].getAttachments();
[ 42 ]
Chapter 3
for (var k = 0; k < attachments.length; k++) {
PropertiesService.getUserProperties()
.setProperty('MSG_ID', messages[i][j].getId());
try {
file = folder.createFile(attachments[k]);
Utilities.sleep(1000);// Wait before next iteration.
} catch (e) {
Logger.log(e);
}
}
}
else return;
}
}
}
};
The preceding function calls the following createFolder_ function with the folder
name as an argument. The function createFolder_ looks for the given folder,
creates if it does not exist, and returns its unique ID:
function createFolder_(name) {
var folder, folderID, found = false;
/*
* Returns collection of all user folders as an iterator.
* That means it do not return all folder names at once,
* but you should get them one by one.
*
*/
var folders = DriveApp.getFolders();
while (folders.hasNext()) {
folder = folders.next();
if (folder.getName() == name) {
folderID = folder.getId();
found = true;
break;
}
};
[ 43 ]
[ 44 ]
Chapter 3
For this task, create a Form with three fields as shown in the following screenshot:
Submit the test data from a live form. Your submitted data will be saved in a
response Sheet named something like Form Responses 1. The column headers will
be as per your Form fields as shown in the following screenshot. Data may vary as
per your input.
4. Copy the Sheet name from the Rename dialog and paste it in the
following code:
function sendEmail(){
var sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Form Responses 1");
var lastRow = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var data = sheet.getRange(lastRow,1,1,lastCol)
.getValues()[0];
var to = "[[ receiver email id]]";
var message = "Name: " + data[1] + "\n";
message += "Phone: " + data[2] + "\n";
message += "Question: " + data[3] + "\n";
// MailApp.sendEmail(recipient, subject, body);
MailApp.sendEmail(to, "Chapter 3", message);
}
You created a Form and a function to send response data to an e-mail ID. Creating
a trigger so as to run the sendEmail function as soon as a Form is submitted will
complete this task.
[ 46 ]
Chapter 3
Under the Run heading, select the sendEmail function for which you want to create
the trigger. Select From spreadsheet and On form submit under the Events heading
as shown in the preceding screenshot.
If a Form user submits data to the spreadsheet, the trigger will run the sendEmail
function.
For more info on triggers, please go to https://developers.google.com/appsscript/guides/triggers/.
[ 47 ]
[ 48 ]
Chapter 3
var msgLen = messages.length;
var isAllMarkedRead = true;
// iterate over each message
// CAUTION: limit loop iterations for initial testing.
for (var j = 0; j < 5 /* msgLen */; j++) {
var message = messages[j];
if(message.isUnread()){
var bodyText = message.getPlainBody();
var test = regExp.exec(bodyText);
message.forward(recipient);
isAllMarkedRead = false;
message.markRead();
}
};
if(isAllMarkedRead) len++;
Utilities.sleep(1000);
}
};
Chapter 3
The first step is creating a draft in your Gmail as shown in the following screenshot.
The draft is used as a template. You can use any special character to enclose the text
to be replaced. In the draft, the code shown in the following screenshot uses left (<<)
and right (>>) angled brackets to replace the first name with the First Name column
data in an EmailList Sheet. You can include any other placeholder or field as per
your requirement. Set up the draft, but don't send it now:
Create a Sheet with the name as EmailList in a new Sheet or existing Sheet. Create
the column headers as shown here:
[ 51 ]
Create functions as shown in the following code, in the script editor. Replace the
draft and sender name with actual values. Set maxEmails (this code uses 50) by
considering your daily e-mail sending quota:
// Returns your draft text.
function getDraftBody(draftName){
var drafts = GmailApp.getDraftMessages();
for(var i in drafts)
if( drafts[i].getSubject() == draftName )
return drafts[i].getPlainBody();
}
function sendEmails(){
// EmailList sheet column numbers, 0 based.
const FIRST_NAME_COL = 0;
const EMAIL_IDS_COL = 1;
const SUB_COL = 2;
const DATE_COL = 3;
var maxEmails = 50;
var draftName = "Chapter 3";// Draft's subject name
var draftBody = getDraftBody(draftName);
var quotaLeft = MailApp.getRemainingDailyQuota();
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("EmailList");
// Gets all sheet data as a 2-dimensional array.
var data = sheet.getDataRange().getValues();
var header = data.shift();
for(var i=0,count=0; count < maxEmails && count < quotaLeft
&& i < data.length; ++i){
var firstName = data[i][FIRST_NAME_COL];
var recipient = data[i][EMAIL_IDS_COL];
var subject = data[i][SUB_COL];
var htmlBody = draftBody.replace("<<FirstName>>", firstName);
if(recipient){
GmailApp.sendEmail(
recipient,
[ 52 ]
Chapter 3
subject,
"",
{
name:"[[ Sender Name ]]",
htmlBody:htmlBody
}
);
data[i][DATE_COL] = new Date();
++count;
}
};
// Inserts header at top of the array.
data.unshift(header);
// Stores values of array in sheet.
sheet.getRange(1, 1, data.length, header.length)
.setValues(data);
}
Populate data in the EmailList Sheet. To send e-mails, run the sendEmails function.
The <<FirstName>> field in your draft will be replaced as per your First Name
column data in the EmailList Sheet. That's it!
Congratulations! You have created a working e-mail merger application.
Summary
In this chapter, you learned about ContactsApp, MailApp, and GmailApp classes and
their methods. Using these classes, you created many useful real-world applications
including an e-mail merger application. In the next chapter, you will learn how to
create Forms programmatically using FormApp and HtmlService classes. Also you
will learn about doGet and doPost simple trigger functions.
[ 53 ]
www.PacktPub.com
Stay Connected: