Skip to content

Instantly share code, notes, and snippets.

@devluis
Forked from JuanCanham/gist:2917132
Last active August 29, 2015 14:18
Show Gist options
  • Save devluis/60d0ef940a9b42323e23 to your computer and use it in GitHub Desktop.
Save devluis/60d0ef940a9b42323e23 to your computer and use it in GitHub Desktop.
function getSignature() {
//pretty basic function for testing
if ( startupChecks()) { return; }
var email = SpreadsheetApp.getActiveSpreadsheet().getActiveCell().getValue().toString();
if ( email == "" ) {
Browser.msgBox("No email selected", "Please select a cell containing a user's email" , Browser.Buttons.OK);
return;
}
var result = authorisedUrlFetch(email, {});
Browser.msgBox(result.getContentText());
}
function setIndividualSignature() {
if ( startupChecks()) { return; }
var userData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Summary');
var template = getTemplate();
var row = SpreadsheetApp.getActiveSpreadsheet().getActiveCell().getRow();
if (userData.getRange(row, 1).isBlank() === true) {
var msg = "Please select a cell on a row containing the user who's signature you wish to update";
Browser.msgBox('No user selected', msg, Browser.Buttons.OK);
} else {
setSignature(template, userData, row);
}
}
function setAllSignatures() {
if ( startupChecks()) { return; }
var userData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Summary');
var template = getTemplate();
//Go through each user listing
for ( row = 2; (userData.getRange(row, 1).isBlank() === false); row++) {
setSignature(template, userData, row);
}
}
function getTemplate(){
var settings = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Signature Settings');
var template = settings.getRange(2, 1).getValue().toString();
//Substitute the company wide variables into the template
template = substituteVariablesFromRow(template, settings, 2);
return template;
}
function setSignature(template, userData, row){
var groupData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Signature Group Settings');
//Google Apps Scripts always deals in ranges even if you just want one cell
//getValue returns an object, so convert it to a string
var email = userData.getRange(row, 1).getValue().toString();
//Substitute in group variables, e.g those for groups of users
//this must be done before filling out user specific data as it was added after initial design
var signature = substituteGroupVariables(template, userData, groupData, row);
//Fill out the template with the data from the user's row to form the signatures
signature = substituteVariablesFromRow(signature, userData, row);
//The API docs say there is a 10,000 character limit
//https://developers.google.com/google-apps/email-settings/#updating_a_signature
if (signature.length > 10000) { Browser.msgBox('signature over 10000 characters for:' + email); }
sendSignature(email, signature);
}
function substituteVariablesFromRow(text, sheet, row) {
for ( col = 1; sheet.getRange(1, col).isBlank() === false; col++) {
var tag = sheet.getRange(1, col).getValue().toString();
var value = sheet.getRange(row, col).getValue().toString();
text = tagReplace(tag,value,text);
}
return text;
}
function substituteGroupVariables(text, dataSheet, lookupSheet, row) {
//this function isn't great but for now it will have to do
//iterate over the column names in the datasheet (e.g Primary Office, Certificates)
for ( dataCol = 1; dataSheet.getRange(1, dataCol).isBlank() === false; dataCol++) {
//the tag is the column heading as that is what will appear in the template (e.g "{Primary Office}")
var tag = dataSheet.getRange(1, dataCol).getValue().toString() ;
//iterate over the rows in the group settings sheet to find if a column has a set of values to lookup
for ( lookupRow = 1; lookupSheet.getRange(lookupRow, 1).isBlank() === false; lookupRow += 3) {
if ( tag === lookupSheet.getRange(lookupRow, 1).getValue().toString() ) {
var propertyValue = dataSheet.getRange(row, dataCol).getValue().toString();
//iterate over the possible settings (e.g London, Edinburg, Madrid)
for ( lookupCol = 2; lookupSheet.getRange(lookupRow, lookupCol).isBlank() === false; lookupCol++) {
//search for substring matches as this allows for lists (e.g a list of certifications)
if ( lookupSheet.getRange(lookupRow, lookupCol).getValue().toString().indexOf(propertyValue) !== -1 ) {
var value = lookupSheet.getRange(lookupRow+1, lookupCol).getValue().toString();
text = tagReplace(tag, value, text);
}
}
}
}
}
return text;
}
function tagReplace(tag, value, text){
var escapeChar = ScriptProperties.getProperty('escapeChar');
var unEscapeChar = ScriptProperties.getProperty('unEscapeChar');
tag = escapeChar + tag + unEscapeChar;
//could be done as a regex to avoid this loop but then I'd have two problems
while (text.indexOf(tag) !== -1) {
text = text.replace(tag, value);
}
return text;
}
function sendSignature(email, signature) {
// https://developers.google.com/google-apps/email-settings/#updating_a_signature
var requestData = {
'method': 'PUT',
'contentType': 'application/atom+xml',
'payload': getPayload(signature)
};
var result = authorisedUrlFetch(email, requestData);
if (result.getResponseCode() != 200) {
var msg = 'There was an error sending ' + email + "'s signature to Google";
Browser.msgBox('Error settings signature', msg, Browser.Buttons.OK);
}
}
function getPayload(signature) {
//First line is needed for XML, second isn't but we might as well do it for consistency
signature = signature.replace(/&/g, '&amp;').replace(/</g, '&lt;');
signature = signature.replace(/>/g, '&gt;').replace(/'/g, '&apos;').replace(/"/g, '&quot;');
//Unfortunately when inside app script document.createElement doesn't work so lets just hardcode the XML for now
var xml = '<?xml version="1.0" encoding="utf-8"?>' +
'<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:apps="http://schemas.google.com/apps/2006" >' +
'<apps:property name="signature" value="'+signature+'" /></atom:entry>';
return xml;
}
function authorisedUrlFetch(email, requestData) {
//takes request data and wraps oauth authentication around it before sending out the request
// https://developers.google.com/apps-script/class_oauthconfig
// http://support.google.com/a/bin/answer.py?hl=en&hlrm=en&answer=162105
// https://developers.google.com/apps-script/articles/picasa_google_apis#section2a
// The scope from https://developers.google.com/google-apps/email-settings/ has to be URIcomponent encoded
var oAuthConfig = UrlFetchApp.addOAuthService('google');
oAuthConfig.setConsumerSecret(ScriptProperties.getProperty('oAuthConsumerSecret'));
oAuthConfig.setConsumerKey(ScriptProperties.getProperty('oAuthClientID'));
oAuthConfig.setRequestTokenUrl('https://www.google.com/accounts/OAuthGetRequestToken?scope=https%3A%2F%2Fapps-apis.google.com%2Fa%2Ffeeds%2Femailsettings%2F');
oAuthConfig.setAuthorizationUrl('https://www.google.com/accounts/OAuthAuthorizeToken');
oAuthConfig.setAccessTokenUrl('https://www.google.com/accounts/OAuthGetAccessToken');
UrlFetchApp.addOAuthService(oAuthConfig);
requestData['oAuthServiceName'] = 'google';
requestData['oAuthUseToken'] = 'always';
var emailParts = email.split('@');
var url = 'https://apps-apis.google.com/a/feeds/emailsettings/2.0/' + emailParts[1] + '/' + emailParts[0] + '/signature';
var result = UrlFetchApp.fetch(url, requestData);
if ( result.getResponseCode() != 200 ) {
//Do some logging if something goes wrong
//Too deep to give the user a meaningful error though so pass the result back up anyway
Logger.log('Error on fetch on' + url);
Logger.log(requestData);
Logger.log(result.getResponseCode());
Logger.log(result.getHeaders());
Logger.log(result.getContentText());
}
return result;
}
function onOpen() {
//add a toolbar and list the functions you want to call externally
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];
menuEntries.push({name: 'Set All Signatures', functionName: 'setAllSignatures'});
menuEntries.push({name: 'Set Individual Signature', functionName: 'setIndividualSignature'});
menuEntries.push({name: 'Get Signature', functionName: 'getSignature'});
ss.addMenu('Signatures', menuEntries);
startupChecks();
}
function startupChecks() {
//Check that everything that is needed to run is there
//I don't check that any of it makes sense, just that it exists.
var requiredProperties = [];
//the help text looks pretty terrible but it is better than nothing
var oAuthHelp = 'Goto https://code.google.com/apis/console#:access and register as an "Installed application" \n'+
'Then add the ClientID to authorised 3rd party clients \n'+
'With scope https://apps-apis.google.com/a/feeds/emailsettings/ \n'+
'The script may then need authorising, this can be done by running one of the scripts from the script editor';
requiredProperties.push({name: 'oAuthClientID', help: oAuthHelp});
requiredProperties.push({name: 'oAuthConsumerSecret', help: oAuthHelp});
requiredProperties.push({name: 'escapeChar', help: 'A character or sequence to go before variables that will be substituted, e.g { ` ${'});
requiredProperties.push({name: 'unEscapeChar', help: 'A character or sequence to go after variables that will be substituted, e.g } ` }$'});
var requiredSheets = [];
requiredSheets.push({name: 'Summary', help: 'A "Summary" sheet must exist that contains a 1 header row and 1 row per user, with no gaps in either the 1st column or row, the 1st row must be the users usernames'});
requiredSheets.push({name: 'Signature Settings', help: 'A "Signature Settings" sheet must exist that contains a the template in cell 2A and then has 1 header row and 1 row per company wide variable, with no empty header cells'});
requiredSheets.push({name: 'Signature Group Settings', help: 'A "Signature Group Settings" sheet must exist that contains 3 Rows (setting values, what to substitute, comments) with every third row containing a column header'});
var fail = false;
for ( i = 0; i < requiredProperties.length; i++) {
var property = ScriptProperties.getProperty(requiredProperties[i].name);
if (property == null) {
var title = 'Script Property ' + requiredProperties[i].name + ' is required';
var prompt = requiredProperties[i].help;
var newValue = Browser.inputBox(title, prompt, Browser.Buttons.OK_CANCEL);
if ((newValue === '') || (newValue === 'cancel')) {
fail = true;
} else {
ScriptProperties.setProperty(requiredProperties[i].name, newValue);
}
}
}
for ( i = 0; i < requiredSheets.length; i++) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(requiredSheets[i].name);
if (sheet == null) {
fail = true;
var title = 'Sheet ' + requiredSheets[i].name + ' is required';
var prompt = requiredSheets[i].help;
Browser.msgBox(title, prompt, Browser.Buttons.OK);
}
}
return fail;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment