Javascript Send mail on Gmail with signature and BCC with Google Spreadsheet - javascript

Is there a way with Javascript (or other) from Google Spreadsheet to get the Gmail account signature?
Details:
I have a Google spreadsheet with information in it.
When clicking on a button, it identifies who has to receive the information, prepare a mail, and send it to the person.
However, I want to add the sender's signature at the end of the mail (it includes name, phone, logo, etc.).
I'm open if I get the signature from an other place, as long as it can change according to who's sending the mail.
This is for my volunteering association, not a job.
In case my code may help (I look to fill the var Signature):
var Signature;
var Receivers;
var Subject;
var Location ;
var MailtoSend= "Hello, \n\n The next meeting will be at " + Location + ". \n" + Signature;
MailApp.sendEmail(Receivers, Subject, MailtoSend);
SOLUTION:
I've find a way from another site (can't find it):
Create a template in your gmail draft with your signature, and put in the subject "Template" and in the body {Body}.
Then use the following code to create a copy of the mail, and fill it with all the information:
In the function to send the mail add:
sendGmailTemplate(RecipientTo, RecipientCC, RecipientBCC, SujetAEnvoyer, CourrielAEnvoyer);
Referring to the following functions:
function sendGmailTemplate(RecipientTo, RecipientCC, RecipientBCC, subject, body, options) { //mettre à jour la quantité de recipeien cc bcc to
options = options || {}; // default is no options
var drafts = GmailApp.getDraftMessages();
var found = false;
for (var i=0; i<drafts.length && !found; i++) {
if (drafts[i].getSubject() == "Template") {
found = true;
var template = drafts[i];
}
}
if (!found) throw new Error( "Impossible de trouver le brouillon 'Template' sur le gmail" );
// Generate htmlBody from template, with provided text body
var imgUpdates = updateInlineImages(template);
options.htmlBody = imgUpdates.templateBody.replace('{BODY}', body);
options.attachments = imgUpdates.attachments;
options.inlineImages = imgUpdates.inlineImages;
options.cc = RecipientCC;
options.bcc = RecipientBCC;
options.replyTo = "";
return GmailApp.sendEmail(RecipientTo, subject, body, options);
}
function updateInlineImages(template) {
//////////////////////////////////////////////////////////////////////////////
// Get inline images and make sure they stay as inline images
//////////////////////////////////////////////////////////////////////////////
var templateBody = template.getBody();
var rawContent = template.getRawContent();
var attachments = template.getAttachments();
var regMessageId = new RegExp(template.getId(), "g");
if (templateBody.match(regMessageId) != null) {
var inlineImages = {};
var nbrOfImg = templateBody.match(regMessageId).length;
var imgVars = templateBody.match(/<img[^>]+>/g);
var imgToReplace = [];
if(imgVars != null){
for (var i = 0; i < imgVars.length; i++) {
if (imgVars[i].search(regMessageId) != -1) {
var id = imgVars[i].match(/realattid=([^&]+)&/);
if (id != null) {
var temp = rawContent.split(id[1])[1];
temp = temp.substr(temp.lastIndexOf('Content-Type'));
var imgTitle = temp.match(/name="([^"]+)"/);
if (imgTitle != null) imgToReplace.push([imgTitle[1], imgVars[i], id[1]]);
}
}
}
}
for (var i = 0; i < imgToReplace.length; i++) {
for (var j = 0; j < attachments.length; j++) {
if(attachments[j].getName() == imgToReplace[i][0]) {
inlineImages[imgToReplace[i][2]] = attachments[j].copyBlob();
attachments.splice(j, 1);
var newImg = imgToReplace[i][1].replace(/src="[^\"]+\"/, "src=\"cid:" + imgToReplace[i][2] + "\"");
templateBody = templateBody.replace(imgToReplace[i][1], newImg);
}
}
}
}
var updatedTemplate = {
templateBody: templateBody,
attachments: attachments,
inlineImages: inlineImages
}
return updatedTemplate;
}

There is not a method to request a signature that you have already created. It is possible to create one yourself with inlineImages. The documentation for that can be found here: https://developers.google.com/apps-script/reference/gmail/gmail-app#sendemailrecipient-subject-body-options

Related

App Script create array from email string

Using app scripts I'm trying to extract all the email addresses from email messages and put them in an array. From my console.log messages, I'm getting stuck because it looks like instead of an array I just get a string. i'm not too familiar with javascript. Any help would be great. I'm looking for an array of email address. The methods of message.get() return a string. I want to split out the email address and create a single, unified array.
var ui = SpreadsheetApp.getUi();
function onOpen(e){
ui.createMenu("Gmail Manager").addItem("Get Emails by Label", "getGmailEmails").addToUi();
}
function getGmailEmails(){
var label = GmailApp.getUserLabelByName("MyLabel");
var threads = label.getThreads();
var fullArray = [];
for(var i = threads.length - 1; i >=0; i--){
var messages = threads[i].getMessages();
for (var j = 0; j <messages.length; j++){
var message = messages[j];
if (message.isUnread()){
fullArray.push(extractDetails(message));
}
}
}
console.log("FullArray:"+fullArray);
for(var i=0; i<fullArray.length; i++){
console.log("printing array " + i + ": "+fullArray[i])
}
}
function extractDetails(message){
var dateTime = message.getDate();
var subjectText = message.getSubject();
var senderDetails = message.getFrom();
var ccEmails = message.getCc();
var replyEmail = message.getReplyTo();
var toEmail = message.getTo();
var emailArray = []
var senderArray = senderDetails.split(',');
var ccArray = ccEmails.split(',');
var replyArray = replyEmail.split(',');
var toArray = toEmail.split(',');
for (var i =0 ; i<toArray.length; i++){
console.log("toArray Loop"+ i + " : "+ toArray[i]);
emailArray.push([toArray[i]]);
}
for (var i =0 ; i<ccArray.length; i++){
console.log("ccArray Loop"+ i + " : "+ ccArray[i]);
emailArray.push([ccArray[i]]);
}
console.log("Email Array: "+ emailArray);
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
activeSheet.appendRow([dateTime, senderDetails, subjectText, ccEmails,replyEmail,toEmail,emailArray]);
return emailArray;
}
I think the problem is just the console output. If you change console.log("Email Array: "+ emailArray); to console.log("Email Array: ", emailArray);, then it shows an array of arrays. You could simplify your extract method as follows:
function extractDetails(message) {
/* ... */
var senderDetails = message.getFrom();
var ccEmails = message.getCc();
var replyEmails = message.getReplyTo();
var toEmails = message.getTo();
let emailArray = [senderDetails, ccEmails, replyEmails, toEmails].reduce(
(array, string) => {
//remove names (like "Name <name#company.com>") and filter empty values
let emails = string
.split(/\s*,\s*/)
.map(e => e.replace(/.*?([^#<\s]+#[^#\s>]+).*?/g, "$1"))
.filter(Boolean);
if(emails.length > 0)
return array.concat(emails)
return array
}, []);
/* ... */
}

Email is not sending

Code Runs No Errors But I Do Not Receive and email when I check my gmail
This line is the issue I believe I am writing this incorrectly so its not sending the email help please
if(values.getDisplayValues() === "On-going" || values.getDisplayValues() === "" && values1.getDisplayValues() >= 80)
Rest of the code:
function sendEmail(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var SpreadsheetID = ss.getSheetId();
var spreadsheetURL = ss.getUrl();
var SpreadsheetID = spreadsheetURL.split("/")[5];
var filterViewName = 'PO_Log Precentage';
var filterViewID = filterId(SpreadsheetID, filterViewName);
var url = createURL(spreadsheetURL, filterViewID);
Logger.log(url);// Testing to see the correct url is created
var po_numID = ss.getSheetByName("Purchase Orders List").getRange("A2").getDisplayValue().substr(0,3);
Logger.log(po_numID);
var email_va = ss.getSheetByName("Purchase Orders List");
var values = email_va.getRange("Q2:Q");
var values1 = email_va.getRange("T2:T");
Logger.log(values)
var emailDataSheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/17G0QohHxjuAcZzwRtQ6AUW3aMTEvLnmTPs_USGcwvDA/edit#gid=1242890521").getSheetByName("TestA");
Logger.log(emailDataSheet.getSheetName());
var emailData = emailDataSheet.getRange("A2:A").getDisplayValues().flat().map(po => po.substr(0,3));
Logger.log(emailData)
var subject = po_numID + " Po Log Daily Notification ";
var options = {}
options.htmlBody = "Hi All, " + "The following" + '<a href=\"' +url+ '" > Purchase Orders </a>' + "are over 80% spent" + "";
// I believe this if statement is the problem I am writing it in correctly
if(values.getDisplayValues() === "On-going" || values.getDisplayValues() === "" && values1.getDisplayValues() >= "80%"){
emailData.every((po, index) => {
if (po == po_numID){
const email = emailDataSheet.getRange(index + 2,7).getValue();
console.log(email);
MailApp.sendEmail(email, subject, '', options);
return false;
} else {
return true;
}
});
}
}
https://docs.google.com/spreadsheets/d/1QW5PIGzy_NSh4MT3j_7PggxXq4XcW4dCKr4wKqIAp0E/edit#gid=611584429
Here is the spread sheet
values.getDisplayValues() gives you an array.
Basically it makes no sense to compare an array and a string with === and >= etc.
You need to decide which element of the array there should be. May be something like values.getDisplayValues()[0][0] (because it's likely an 2D-array). I can't check your code, so just a guess.

Google forms not asking for permissions Google Scripts

So I already looked at all the articles that I could find here, yet I still couldn't solve the problem. My question is how do I make Google App Scripts ask for permission to view certain files. Here's the problem in Google App Scripts:
Script:
var NamesToDelete = [""];
function moveFiles(sourceFileId, targetFolderId) {
var file = DriveApp.getFileById(sourceFileId);
var folder = DriveApp.getFolderById(targetFolderId);
file.moveTo(folder);
}
function createFolder(folderName){
var parentFolder = DriveApp.getRootFolder();
var subFolders = parentFolder.getFolders();
var doesntExists = true;
var newFolder = '';
// Check if folder already exists.
while(subFolders.hasNext()){
var folder = subFolders.next();
//If the name exists return the id of the folder
if(folder.getName() === folderName){
doesntExists = false;
newFolder = folder;
return newFolder.getId();
};
};
//If the name doesn't exists, then create a new folder
if(doesntExists == true){
//If the file doesn't exists
newFolder = parentFolder.createFolder(folderName);
return newFolder.getId();
};
};
function start(user){
var body = "Hello " + user + ",<br> Your google drive cleanup has been successfully initiated. <br> Please go to your drive and click the file named 'Delete Me.' Select everything inside (shift+select) and right click it. In the options that come up, click 'Remove.' <br> Thank you, <br> SS Tools"
body = body + "<br> Words you chose: <br>";
for (var i = 0; i<NamesToDelete.length; i++) {
body = body + NamesToDelete[i] + "<br>";
}
var NEW_FOLDER_NAME = "DELETE ME";
var myFolderID = createFolder(NEW_FOLDER_NAME);
var tutorialPicture = UrlFetchApp
.fetch("https://i.gyazo.com/f8716f9d9b0539506a78fba52e12788b.png")
.getBlob()
.setName("How to Delete");
Logger.log(myFolderID);
listFolders(myFolderID)
MailApp.sendEmail({
to: user,
subject: "SS Tools: Drive Cleanup Successfully Initiated",
htmlBody: body,
inlineImages:
{
Tutorial: tutorialPicture,
}
});
};
function FirstWord(word) {
var wordLength = word.length
var firstWord = ""
for (var i = 0; i < wordLength; i++) {
var c = word[i]
if (c == " ") {
break;
}
else if(c >= '0' && c <= '9') {
continue;
}
firstWord = firstWord + word[i]
}
return firstWord
}
function checkIfDeleteAB(One, Two) {
var theFirstWord = FirstWord(One)
if (theFirstWord == Two || theFirstWord == "") {
return true
}
return false
}
function listFolders(parentID, folder) {
folder = folder || DriveApp.getRootFolder();
var name = folder.getName();
var files = folder.getFiles();
while ( files.hasNext() ) {
var nextFile = files.next();
var nextName = nextFile.getName();
var arrayLength = NamesToDelete.length
for (var i = 0; i < arrayLength; i++) {
if (checkIfDeleteAB(nextName, NamesToDelete[i])) {
Logger.log(name + " :: " + nextName);
//nextFile.setTrashed(true);
moveFiles(nextFile.getId(), parentID);
break;
}
}
}
var subfolders = folder.getFolders();
while (subfolders.hasNext()) {
listFolders(parentID, subfolders.next());
}
}
function GetResponsesArray(form) {
var response = form.response;
var user = response.getRespondentEmail();
var item = response.getItemResponses();
var array = item[0].getResponse();
var word = "";
var c = 0;
var done = false;
Logger.log(array)
for (var i = 0; i<array.length; i++){
if(array[i] == " "){
c = 0;
NamesToDelete.push(word);
Logger.log(word)
word = "";
done = true;
}else{
word += array[i];
Logger.log("Shaping word :: " + word)
c++;
done = false;
}
}
if (done == false){
NamesToDelete.push(word);
}
Logger.log(user)
start(user);
}
If you can find the answer to my question I would be grateful!
Google gives the following explanation for the Access denied: DriveApp exception:
Administrators of G Suite domains have the ability to disable the Drive SDK for their domain, which prevents their users from installing and using Google Drive apps. This setting also prevents the users from being able to use Apps Script add-ons that use the Drive service or Advanced Drive Service (even if the script was authorized prior to the admin disabling Drive SDK).
In other words, it seems like you are not the admin of your domain and the admin had disabled the usage of Drive SDK for you / for the whole doman.
You need to contact the admin and ask him to change permissions.

Docusign Signer Name

At my current company we have DocuSign integrated with Salesforce for sending out contracts to our potential clients.
Each contract is required to be signed by our potential client but also from our VP of sales/services.
I created a custom button on the quote object to submit the quote to DocuSign passing the information required (Signer Role, name, email etc) The problem I am having is that for some reason the full name of the second signer (the internal signer) does not get passed on to DocuSign so the sales rep has to manually go and edit the recipients each time and add the name.
Button code:
var quoteApproved = {!Quote.Quote_Approved__c};
//********* Option Declarations (Do not modify )*********//
var RC = '';
var RSL = '';
var RSRO = '';
var RROS = '';
var CCRM = '';
var CCTM = '';
var CCNM = '';
var CRCL = '';
var CRL = '';
var OCO = '';
var DST = '';
var LA = '';
var CEM = '';
var CES = '';
var STB = '';
var SSB = '';
var SES = '';
var SEM = '';
var SRS = '';
var SCS = '';
var RES = '';
//*************************************************//
switch ("{!Quote.Signed_by__c}") {
case "John Cash":
CRL = "Email~john.cash#company.com; FirstName~John; LastName~Cash; Role~Signer 2; RoutingOrder~1";
CCTM = "Signer 2~Signer";
break;
case "Mark Cash":
CRL = "Email~mark.cash#company.com; FirstName~Mark; LastName~Cash; Role~Signer 2; RoutingOrder~1";
CCTM = "Signer 2~Signer";
}
if (quoteApproved) {
{
!REQUIRESCRIPT("/apex/dsfs__DocuSign_JavaScript")
}
var sourceId = DSGetPageIDFromHref();
var RQD = DSGetPageIDFromHref();
window.location.href = "/apex/dsfs__DocuSign_CreateEnvelope?DSEID=0&SourceID=" + sourceId + "&CCTM=" + CCTM + "&CRL=" + CRL + "&RQD=" + RQD;
} else {
alert("Your quote has not been approved yet. \nPlease submit for approval before sending the contract.");
}
I have resolved the issue. The RQD variable was adding a # at the end of the URL preventing the completion of the field mapping.

class inheritance in javascript/angular

I am working on my hello world project. I have two pages let's call them "configuration" and "add configuration" *.html. Each one has its own controller like this:
angular.module('MissionControlApp').controller('ConfigController', ConfigController);
angular.module('MissionControlApp').controller('AddConfigController', AddConfigController);
Now, each controller has some properties that very much overlap:
function ConfigController($routeParams, ConfigFactory, $window){
var vm = this;
vm.status;
vm.projectId = $routeParams.projectId;
vm.selectedProject;
vm.configurations;
vm.selectedConfig;
vm.selectedRecords;
vm.filteredConfig;
vm.newFile;
vm.fileWarningMsg = '';
vm.addFile = function(){
var filePath = vm.newFile;
var encodedUri = encodeURIComponent(filePath);
vm.fileWarningMsg='';
ConfigFactory
.getByEncodedUri(encodedUri).then(function(response){
var configFound = response.data;
var configNames = '';
var configMatched = false;
if(response.status === 200 && configFound.length > 0){
//find an exact match from text search result
for(var i = 0; i < configFound.length; i++) {
var config = configFound[i];
for(var j=0; j<config.files.length; j++){
var file = config.files[j];
if(file.centralPath.toLowerCase() === filePath.toLowerCase()){
configMatched = true;
configNames += ' [' + config.name + '] ';
break;
}
}
}
}
if(configMatched){
vm.fileWarningMsg = 'Warning! File already exists in other configurations.\n' + configNames;
} else if(filePath.length > 0 && filePath.includes('.rvt')){
var file1 = { centralPath: filePath };
vm.selectedConfig.files.push(file1);
vm.newFile = '';
} else{
vm.fileWarningMsg = 'Warning! Please enter a valid file.';
}
}, function(error){
vm.status = 'Unable to get configuration data: ' + error.message;
});
};
My AddConfigController also wants to have the same functionality for addFile() so I just copy pasted the same code, but coming from C# i am sure i can do some class inheritance here, and just inherit from ConfigController and extend...right?
If this is super noob question. then apologies. js is a bit of a mystery to me.
function AddConfigController($routeParams, ConfigFactory, $window){
var vm = this;
vm.status;
vm.projectId = $routeParams.projectId;
vm.selectedProject = {};
vm.newConfig = {};
vm.newFile;
vm.fileWarningMsg = '';
vm.addFile = function(){
var filePath = vm.newFile;
var encodedUri = encodeURIComponent(filePath);
vm.fileWarningMsg='';
ConfigFactory
.getByEncodedUri(encodedUri).then(function(response){
var configFound = response.data;
var configNames = '';
var configMatched = false;
if(response.status === 200 && configFound.length > 0){
//find an exact match from text search result
for(var i = 0; i < configFound.length; i++) {
var config = configFound[i];
for(var j=0; j<config.files.length; j++){
var file = config.files[j];
if(file.centralPath.toLowerCase() === filePath.toLowerCase()){
configMatched = true;
configNames += ' [' + config.name + '] ';
break;
}
}
}
}
if(configMatched){
vm.fileWarningMsg = 'Warning! File already exists in other configurations.\n' + configNames;
} else if(filePath.length > 0 && filePath.includes('.rvt')){
var file1 = { centralPath: filePath };
vm.selectedConfig.files.push(file1);
vm.newFile = '';
} else{
vm.fileWarningMsg = 'Warning! Please enter a valid file.';
}
}, function(error){
vm.status = 'Unable to get configuration data: ' + error.message;
});
};
Since you asked about inheritance and you appear to be using ECMAScript 5, let me suggest taking a look at Object.create(). Specifically, the classical inheritance example.
That said, in AngularJS, a better solution would be to create a Service that manages files or configurations and put the addFile function in there. That way, both controllers could inject the service and call the same function when it is time to add a file. Likewise, other services and controllers that may need access to this functionality could inject it as well.

Categories

Resources