I'm using google sheets and I'm creating a document that will pull through employees that are out of the office. I have a menu option to add new employee data, and it opens the sidebar where I have an HTML form (Image of my project). It then posts the data on the next available line.
Issue: The checkboxes keep pulling through as undefined, and I can't figure out how to retrieve their true/false value.
Here is my html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
function submitForm(formObject) {
google.script.run
.submitAddEmployee(formObject);
}
</script>
</head>
<body>
<form id="AddEmployee" onsubmit="submitForm(this)">
Employee Name:<br />
<input type="text" name="employeename" />
<hr /><br /> Check boxes that Employee Works:<br />
<input type="checkbox" name="Workdays" value="sunday"> Sunday<br />
<input type="checkbox" name="Workdays" value="monday" /> Monday<br />
<input type="checkbox" name="Workdays" value="tuesday" /> Tuesday<br />
<input type="checkbox" name="Workdays" value="wednesday" /> Wednesday<br />
<input type="checkbox" name="Workdays" value="thursday" /> Thursday<br />
<input type="checkbox" name="Workdays" value="friday" /> Friday<br />
<input type="checkbox" name="Workdays" value="saturday" /> Saturday
<hr /><br />
<input type="checkbox" name="ooo" value="isOOO" />OOO Employee?
<br /><br /><input type="submit" value="Submit" />
</form>
<div id="output"></div>
</body>
</html>
Here is my script:
function submitAddEmployee(object) {
var emName = object.employeename;
var sunday = object.sunday;
var monday = object.monday;
var tuesday = object.tuesday;
var wednesday = object.wednesday;
var thursday = object.thursday;
var friday = object.friday;
var saturday = object.saturday;
var isOOO = object.isOOO;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");
if (sheet != null) {
var range = sheet.getDataRange();
var nextRow = range.getLastRow() + 1;
sheet.getRange(nextRow, 1).setValue(emName);
sheet.getRange(nextRow, 2).setValue(sunday);
sheet.getRange(nextRow, 3).setValue(monday);
sheet.getRange(nextRow, 4).setValue(tuesday);
sheet.getRange(nextRow, 5).setValue(wednesday);
sheet.getRange(nextRow, 6).setValue(thursday);
sheet.getRange(nextRow, 7).setValue(friday);
sheet.getRange(nextRow, 8).setValue(saturday);
sheet.getRange(nextRow, 9).setValue(isOOO);
sheet.getRange(nextRow, 10).setValue("False");
} else {
Browser.msgBox('Sheet "Tasks" was not found.');
}
}
Any help you can give me would be appreciated.
Thank you!
The submitted checkbox values are accessed by the field Name rather than the Value.
So in your case, you have a series of fields with the same name, "Workdays", which would be accessed as object.Workdays.
The simplest fix is to rename your checkboxes to match your Apps Script:
<input type="checkbox" name="sunday" value="sunday"> Sunday<br />
etc.
Related
I have in a spreadsheet, a MENU with buttons linked to other sheets.
Since I have many sheets, I want that when selecting a button it is the same for a group of those sheets.
Ex: btn "Enter approved teachers", there are 4 sheets that are to enter that data, each one corresponds to a different area.
That when I click on this button, I skip a "popup" that shows me a list of that sheets and lets me select it with an "OK".
The part of selecting the sheet with a script is easy to do with a youtube tutorial, but the control of sheets and selection I can not find anywhere.
I haven't been able to try anything, as I don't have experience coding in Apps Script or Js.
Hopefully this is close to what you are looking for:
This a dialog I use for selecting which files I wish to backup from my spreadsheets.
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
input {margin: 2px 5px 2px 0;}
#btn3,#btn4{display:none}
</style>
</head>
<body>
<form>
<input type="text" id="scr" name="script" size="60" placeholder="Enter Apps Script Id" onchange="getFileNames();" />
<br /><input type="text" id="fldr" name="folder" size="60" placeholder="Enter Backup Folder Id" />
<div id="shts"></div>
<br /><input type="button" value="0" onClick="unCheckAll();" size="15" id="btn3" />
<input type="button" value="1" onClick="checkAll();"size="15" id="btn4"/>
<br /><input type="checkbox" id="files" name="saveFiles" checked /><label for="files">Save Files</label>
<br /><input type="checkbox" id="json" name="saveJson" checked /><label for="json">Save JSON</label>
<br /><input type="button" value="Submit" onClick="backupFiles();" />
</form>
<script>
function getFileNames() {
const scriptid = document.getElementById("scr").value;
google.script.run
.withSuccessHandler((names) => {
document.getElementById('btn3').style.display = "inline";
document.getElementById('btn4').style.display = "inline";
names.forEach((name,i) => {
let br = document.createElement("br");
let cb = document.createElement("input");
cb.type = "checkbox";
cb.id = `cb${i}`;
cb.name = `cb${i}`;
cb.className = "cbx";
cb.value = `${name}`;
cb.checked = true;
let lbl = document.createElement("label");
lbl.htmlFor = `cb${i}`;
lbl.appendChild(document.createTextNode(`${name}`));
document.getElementById("shts").appendChild(cb);
document.getElementById("shts").appendChild(lbl);
document.getElementById("shts").appendChild(br);
});
})
.getAllFileNames({scriptId:scriptid});
}
function unCheckAll() {
let btns = document.getElementsByClassName("cbx");
console.log(btns.length);
for(let i =0;i<btns.length;i++) {
btns[i].checked = false;
}
}
function checkAll() {
let btns = document.getElementsByClassName("cbx");
console.log(btns.length)
for(let i = 0;i<btns.length;i++) {
btns[i].checked = true;
}
}
function backupFiles() {
console.log('backupFiles');
sObj = {};
sObj.script = document.getElementById('scr').value;
sObj.folder = document.getElementById('fldr').value;
sObj.saveJson = document.getElementById('json').checked?'on':'';
sObj.saveFiles = document.getElementById('files').checked?'on':'';
sObj.selected = [];
console.log("1");
const cbs = document.getElementsByClassName("cbx");
let selected = [];
for(let i = 0;i<cbs.length; i++) {
let cb = cbs[i];
if(cb.checked) {
sObj.selected.push(cb.value)
}
}
console.log("2");
google.script.run
.withSuccessHandler(function(obj){google.script.host.close();})
.scriptFilesBackup(sObj);
console.log(JSON.stringify(sObj));
}
</script>
</body>
</html>
This is what the dialog looks like:
I would like to create a script to receive expense receipts in a Google drive, and log details provided by a Google Form (date, vendor, amount, and picture of the receipt...)
I've tried to replicate the script and html from How do I rename files uploaded to an apps script web app form?, and end up with error 400 with no details...
Also tried to merge Amit Agarwal's script
https://www.labnol.org/internet/receive-files-in-google-drive/19697/
with https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server
Second example logs entries into google sheets, but also sends email : Perfect! Amit's example allows to create named folders and I can rename the file with some additionnal code, love it!
But in my try to merge both, I end up with two buttons at the bottom of the form... one sends the rows, the other sends the file! :D
Here's my actual script.gs
// if you want to store your email server-side (hidden), uncomment the next line
//var TO_ADDRESS = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// spit out all the keys/values from the form in HTML for email
// uses an array of keys if provided or the object to determine field order
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('index.html').setTitle("envoyez vos pièces jointes");
}
// this is from Amit Agarwal's example
function uploadFileToGoogleDrive(data, file, prenom, nom) {
try {
var dropbox = "Justificatifs reçus";
var folder, folders = DriveApp.getFoldersByName(dropbox);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropbox);
}
var contentType = data.substring(5,data.indexOf(';')),
bytes = Utilities.base64Decode(data.substr(data.indexOf('base64,')+7)),
blob = Utilities.newBlob(bytes, contentType, file),
file = folder.createFolder([prenom, nom].join(" ")).createFile(blob);
//this is an addition i've made to rename the files upon submission, used to work in previous tries, but now gives "null null.pdf'
//var newFileName = [prenom +'_'+ nom +".pdf"];
//file.setName(newFileName);
return "OK";
} catch (f) {
return f.toString();
}
}
// Here stops Amit Agarwal's script, below is the following of
//https://github.com/dwyl/learn-to-send-email-via-google-script-html-no-server
function formatMailBody(obj, order) {
var result = "";
if (!order) {
order = Object.keys(obj);
}
// loop over all keys in the ordered form data
for (var idx in order) {
var key = order[idx];
result += "<h4 style='text-transform: capitalize; margin-bottom: 0'>" + key + "</h4><div>" + sanitizeInput(obj[key]) + "</div>";
// for every key, concatenate an `<h4 />`/`<div />` pairing of the key name and its value,
// and append it to the `result` string created at the start.
}
return result; // once the looping is done, `result` will be one long string to put in the email body
}
// sanitize content from the user - trust no one
// ref: https://developers.google.com/apps-script/reference/html/html-output#appendUntrusted(String)
function sanitizeInput(rawInput) {
var placeholder = HtmlService.createHtmlOutput(" ");
placeholder.appendUntrusted(rawInput);
return placeholder.getContent();
}
function doPost(e) {
try {
Logger.log(e); // the Google Script version of console.log see: Class Logger
record_data(e);
// shorter name for form data
var mailData = e.parameters;
// names and order of form elements (if set)
var orderParameter = e.parameters.formDataNameOrder;
var dataOrder;
if (orderParameter) {
dataOrder = JSON.parse(orderParameter);
}
// determine recepient of the email
// if you have your email uncommented above, it uses that `TO_ADDRESS`
// otherwise, it defaults to the email provided by the form's data attribute
var sendEmailTo = (typeof TO_ADDRESS !== "undefined") ? TO_ADDRESS : mailData.formGoogleSendEmail;
// send email if to address is set
if (sendEmailTo) {
MailApp.sendEmail({
to: String(sendEmailTo),
subject: "Contact form submitted",
// replyTo: String(mailData.email), // This is optional and reliant on your form actually collecting a field named `email`
htmlBody: formatMailBody(mailData, dataOrder)
});
}
return ContentService // return json success results
.createTextOutput(
JSON.stringify({"result":"success",
"data": JSON.stringify(e.parameters) }))
.setMimeType(ContentService.MimeType.JSON);
} catch(error) { // if error return this
Logger.log(error);
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": error}))
.setMimeType(ContentService.MimeType.JSON);
}
}
/**
* record_data inserts the data received from the html form submission
* e is the data received from the POST
*/
function record_data(e) {
var lock = LockService.getDocumentLock();
lock.waitLock(30000); // hold off up to 30 sec to avoid concurrent writing
try {
Logger.log(JSON.stringify(e)); // log the POST data in case we need to debug it
// select the 'responses' sheet by default
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sheetName = e.parameters.formGoogleSheetName || "responses";
var sheet = doc.getSheetByName(sheetName);
var oldHeader = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var newHeader = oldHeader.slice();
var fieldsFromForm = getDataColumns(e.parameters);
var row = [new Date()]; // first element in the row should always be a timestamp
// loop through the header columns
for (var i = 1; i < oldHeader.length; i++) { // start at 1 to avoid Timestamp column
var field = oldHeader[i];
var output = getFieldFromData(field, e.parameters);
row.push(output);
// mark as stored by removing from form fields
var formIndex = fieldsFromForm.indexOf(field);
if (formIndex > -1) {
fieldsFromForm.splice(formIndex, 1);
}
}
// set any new fields in our form
for (var i = 0; i < fieldsFromForm.length; i++) {
var field = fieldsFromForm[i];
var output = getFieldFromData(field, e.parameters);
row.push(output);
newHeader.push(field);
}
// more efficient to set values as [][] array than individually
var nextRow = sheet.getLastRow() + 1; // get next row
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// update header row with any new data
if (newHeader.length > oldHeader.length) {
sheet.getRange(1, 1, 1, newHeader.length).setValues([newHeader]);
}
}
catch(error) {
Logger.log(error);
}
finally {
lock.releaseLock();
return;
}
}
function getDataColumns(data) {
return Object.keys(data).filter(function(column) {
return !(column === 'formDataNameOrder' || column === 'formGoogleSheetName' || column === 'formGoogleSendEmail' || column === 'honeypot');
});
}
function getFieldFromData(field, data) {
var values = data[field] || '';
var output = values.join ? values.join(', ') : values;
return output;
}
And here's the index.html
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Envoyez vos justificatifs</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/css/materialize.min.css">
<style>
.disclaimer{width: 480px; color:#646464;margin:20px auto;padding:0 16px;text-align:center;font:400 12px Roboto,Helvetica,Arial,sans-serif}.disclaimer a{color:#009688}#credit{display:none}
</style>
</head>
<!-- START HERE -->
<link rel="stylesheet" href="https://unpkg.com/purecss#1.0.0/build/pure-min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<!-- Style The Contact Form How Ever You Prefer -->
<link rel="stylesheet" href="style.css">
<form class="gform pure-form pure-form-stacked" method="POST" data-email="example#email.net"
action="https://script.google.com/macros/s/AKfycbzSlohhLp27NcnG8lBt13GUm4PblUMTL9uU1CTgcOBohz1iH0k/exec"
id="form" novalidate="novalidate" style="max-width: 480px;margin: 40px auto;">
<div id="forminner">
<div class="row">
<div class="col s12">
<h5 class="center-align teal-text">Envoyez vos justificatifs</h5>
<p class="disclaimer">This File Upload Form (tutorial) is powered by Google Scripts</p>
</div>
</div>
<fieldset class="pure-group">
<legend><H5>Vous êtes</H5></legend>
<input id="radio-group--madame" type="radio" name="radio-group" value="madame"> <label for="radio-group--madame">Madame</label>
<input id="radio-group--monsieur" type="radio" name="radio-group" value="monsieur"> <label for="radio-group--monsieur">Monsieur</label>
</fieldset>
<div class="form-elements">
<fieldset class="pure-group">
<label for="firstname">Votre prénom</label>
<input id="firstname" name="Prénom" placeholder="indiquez votre prénom" />
</fieldset>
<div class="form-elements">
<fieldset class="pure-group">
<label for="name">Votre nom</label>
<input id="name" name="Nom" placeholder="indiquez votre nom" />
</fieldset>
<fieldset class="pure-group">
<label for="email"><em>Votre</em> Adresse email</label>
<input id="email" name="Votre adresse email" type="email" value=""
required placeholder="Alice#paydesmerv.... ou votre vrai adresse pour recevoir la confirmation"/>
</fieldset>
<fieldset class="pure-group">
<legend><H5>Votre dépense</H5></legend>
<label for="date">Date</label>
<input id="date" type="date" name="date de la dépense" value="">
</fieldset>
<fieldset class="pure-group">
<label for="time">Heure de la dépense</label>
<input id="time" type="time" name="Heure de la dépense" value="">
</fieldset>
<fieldset class="pure-group">
<label for="menu">Type de dépense</label>
<select id="menu" name="Type de dépense">
<option selected="">Je ne sais pas quel type de dépense choisir</option>
<option>Carburant (essence, diesel, gasoil)</option>
<option>Location de matériel (voiture, informatique, photocopieur)</option>
<option>Voyages et déplacements (train, transports, taxi, VTC, péages, parking, avion)</option>
<option>Frais postaux (La Poste, timbres, colis, lettre recommandée)</option>
<option>Frais de mission (repas, restaurants)</option>
<option>Frais de missions (logement, hôtel)</option>
<option>Divers</option>
</select>
</fieldset>
<fieldset class="pure-group">
<label for="number">Montant de la dépense en €</label>
<input id="number" type="number" name="Montant de la dépense" min="0" step="0.01" value="0.00">
</fieldset>
<fieldset class="pure-group">
<label for="message">Message: </label>
<textarea id="message" name="Message Facultatif" rows="10"
placeholder="Vous pouvez apporter des précisions sur la dépense ici..."></textarea>
</fieldset>
<fieldset class="pure-group honeypot-field">
<label for="honeypot">To help avoid spam, utilize a Honeypot technique with a hidden text field; must be empty to submit the form! Otherwise, we assume the user is a spam bot.</label>
<input id="honeypot" type="text" name="honeypot" value="" />
</fieldset>
<legend><H5>Ajoutez le justificatif</H5></legend>
<p> Pas de remboursement possible sans justificatif</p>
<!-- Here is the issue: code below sends the file to my drive but no new row is added to the spreadsheet. -->
<div class="row">
<div class="file-field input-field col s12">
<div class="btn">
<span>Fichier</span>
<input id="files" type="file" name="Fichier reçu" multiple>
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="choisissez un fichier sur votre ordinateur">
</div>
</div>
</div>
<!-- code below creates new row with filename but does not upload the file to drive...
exemple from : https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_file -->
<form action="/action_page.php">
Select files: <input type="file" name="Fichier reçu"><br><br>
<input type="submit">
</form>
<button class="waves-effect waves-light btn submit-btn" type="submit" onclick="submitForm(); return false;">Submit</button>
</div>
<!-- Customise the Thankyou Message People See when they submit the form: -->
<div class="thankyou_message" style="display:none;">
<h2><em>Thanks</em> for contacting us!
We will get back to you soon!</h2>
</div>
</form>
<!-- Submit the Form to Google Using "AJAX" -->
<script data-cfasync="false" src="form-submission-handler.js"></script>
<!-- END -->
<div class="row">
<div class="input-field col s12" id = "progress">
</div>
</div>
<div id="success" style="display:none">
<h5 class="left-align teal-text">File Uploaded</h5>
<p>Your file has been successfully uploaded.</p>
<p>The pro version (see demo form) includes a visual drag-n-drop form builder, CAPTCHAs, the form responses are saved in a Google Spreadsheet and respondents can upload multiple files of any size.</p>
<p class="center-align"><a class="btn btn-large" href="https://gum.co/GA14?wanted=true" target="_blank">Upgrade to Pro</a></p>
</div>
</form>
<div class="fixed-action-btn horizontal" style="bottom: 45px; right: 24px;">
<a class="btn-floating btn-large red">
<i class="large material-icons">menu</i>
</a>
<ul>
<li><a class="btn-floating red" href="shorturl" target="_blank" title="Buy License - File Upload Form"><i class="material-icons">monetization_on</i></a></li>
<li><a class="btn-floating blue" href="shorturl" target="_blank" title="Video Tutorial"><i class="material-icons">video_library</i></a></li>
<li><a class="btn-floating green" href="http://www.labnol.org/internet/file-upload-google-forms/29170/" target="_blank" title="How to Create File Upload Forms"><i class="material-icons">help</i></a></li>
</ul>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/js/materialize.min.js"></script>
<script src="https://gumroad.com/js/gumroad.js"></script>
<script>
var file,
reader = new FileReader();
reader.onloadend = function(e) {
if (e.target.error != null) {
showError("File " + file.name + " could not be read.");
return;
} else {
google.script.run
.withSuccessHandler(showSuccess)
.uploadFileToGoogleDrive(e.target.result, file.name, $('input#nom').val(), $('input#prenom').val());
}
};
function showSuccess(e) {
if (e === "OK") {
$('#forminner').hide();
$('#success').show();
} else {
showError(e);
}
}
function submitForm() {
var files = $('#files')[0].files;
if (files.length === 0) {
showError("Choisissez un fichier a télécharger");
return;
}
file = files[0];
if (file.size > 1024 * 1024 * 5) {
showError("The file size should be < 5 MB. Please <a href='http://www.labnol.org/internet/file-upload-google-forms/29170/' target='_blank'>upgrade to premium</a> for receiving larger files in Google Drive");
return;
}
showMessage("Téléchargement du fichier");
reader.readAsDataURL(file);
}
function showError(e) {
$('#progress').addClass('red-text').html(e);
}
function showMessage(e) {
$('#progress').removeClass('red-text').html(e);
}
</script>
So, I'd like to have a unique form to send emails, log into sheets and save file in drive.
Additionally, that would be perfect is the file link could appear inside sheets and if the uploaded file could also be sent as attachment with the email.
Thanks a lot for proof reading my codes and putting me on the right direction!
Collecting Receipt Information
This function collects Date,Vendor,Amount and Uploads an image. It runs as both a dialog and/or a webapp. The spreadsheet displays the images url which allows you to hover over in order to get a link to the image which can be view in a default viewer.
This is the form:
Here's the code:
Code.gs:
function onOpen() {
SpreadsheetApp.getUi().createMenu('Receipt Collection')
.addItem('Get Receipt', 'showAsDialog')
.addToUi();
}
function uploadTheForm(theForm) {
Logger.log(JSON.stringify(theForm));
var rObj={};
rObj['vendor']=theForm.vendor;
rObj['amount']=theForm.amount;
rObj['date']=theForm.date;
rObj['notes']=theForm.notes;
var fileBlob=Utilities.newBlob(theForm.bytes, theForm.mimeType, theForm.filename);
var fldr = DriveApp.getFolderById(receiptImageFolderId);
rObj['file']=fldr.createFile(fileBlob);
rObj['filetype']=fileBlob.getContentType();
Logger.log(JSON.stringify(rObj));
var cObj=formatFileName(rObj);
Logger.log(JSON.stringify(cObj));
var ss=SpreadsheetApp.openById(SSID);
ss.getSheetByName('Receipt Information').appendRow([cObj.date,cObj.vendor,cObj.amount,cObj.notes,cObj.file.getUrl()]);
var html=Utilities.formatString('<br />FileName: %s',cObj.file.getName());
return html;
}
function formatFileName(rObj) {
if(rObj) {
Logger.log(JSON.stringify(rObj));
var mA=rObj.date.split('-');
var name=Utilities.formatString('%s_%s_%s.%s',Utilities.formatDate(new Date(mA[0],mA[1]-1,mA[2]),Session.getScriptTimeZone(),"yyyyMMdd"),rObj.vendor,rObj.amount,rObj.filetype.split('/')[1]);
rObj.file.setName(name);
}else{
throw('Invalid or No File in formatFileName() upload.gs');
}
return rObj;
}
function doGet() {
var output=HtmlService.createHtmlOutputFromFile('receipts').setTitle('Receipt Information');
return output.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL).addMetaTag('viewport', 'width=360, initial-scale=1');
}
function showAsDialog() {
var ui=HtmlService.createHtmlOutputFromFile('receipts');
SpreadsheetApp.getUi().showModelessDialog(ui, 'Receipts')
}
receipts.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(function(){
google.script.run
.withSuccessHandler(function(rObj){
$('#dt').val(rObj.date);
//$('#vndr').val(rObj.vendor);
//$('#amt').val(rObj.amount);
//$('#notes').val(rObj.notes);
})
.initForm();
});
function fileUploadJs(frmData) {
var amt=$('#amt').val();
var vndr=$('#vndr').val();
var img=$('#img').val();
if(!amt){
window.alert('No amount provided');
$('#amt').focus();
return;
}
if(!vndr) {
window.alert('No vendor provided');
$('#vndr').focus();
return;
}
if(!img) {
window.alert('No image chosen');
$('#img').focus();
}
document.getElementById('status').style.display ='inline';
const file = frmData.receipt.files[0];
const fr = new FileReader();
fr.onload = function(e) {
const obj = {vendor:frmData.elements.vendor.value, date:frmData.elements.date.value, amount: frmData.elements.amount.value, notes: frmData.elements.notes.value, filename: file.name, mimeType:file.type, bytes:[...new Int8Array(e.target.result)]};
google.script.run
.withSuccessHandler(function(hl){
document.getElementById('status').innerHTML=hl;
})
.uploadTheForm(obj);
};
fr.readAsArrayBuffer(file);
}
console.log('My Code');
</script>
<style>
input,textarea{margin:5px 5px 5px 0;}
</style>
</head>
<body>
<h3 id="main-heading">Receipt Information</h3>
<div id="formDiv">
<form id="myForm">
<br /><input type="date" name="date" id="dt"/>
<br /><input type="number" name="amount" placeholder="Amount" id="amt" />
<br /><input type="text" name="vendor" placeholder="Vendor" id="vndr"/>
<br /><textarea name="notes" cols="40" rows="2" placeholder="NOTES" id="notes"></textarea>
<br/>Receipt Image
<br /><input type="file" name="receipt" id="img" />
<br /><input type="button" value="Submit" onclick="fileUploadJs(this.parentNode)" />
</form>
</div>
<div id="status" style="display: none">
<!-- div will be filled with innerHTML after form submission. -->
Uploading. Please wait...
</div>
</body>
</html>
global.gs
var receiptImageFolderId='your receipt image folder id';
var SSID='your spreadsheet id';
The Spreadsheet Looks Like this:
Note I changed image Id to image Url so that you can just click on the url to get a viewer to look at the image. I think this is much cleaner than trying to put images of varying sizes into the spreadsheet.
I have a form response sheet (https://docs.google.com/spreadsheets/d/1YCneMRUC6ZKK0V3qs0mROhr6j62mdNIWAxcW71aAQIg/edit#gid=0) which saves all the requests from our stakeholders, and our workflow lead has to manually assign these requests to the members of our team in a round-robin fashion while ensuring each of the team members has an equal distribution of requests.
However, if a duplicate task is submitted (which is very much possible), it should be assigned to the same person that handled it earlier.
Is it possible to employ Google scripts solution to get this type of random yet equal distribution of tasks among the assignee group? The agent availability on any given day is also important, as they could be out of office, therefore the workflow lead keeps revising the agent list almost on a daily basis. Hence, it's all the more useful to have a Google AppScript solution to this problem (assigning one task at a time to the next available agent in queue). If the script can email the agent that would be ideal, but not necessary. Kindly advise! Thanks.
Round Robin Assignment
This script provides the following assignments:
If task title is repeated it assigns that task to original assignee.
If task title is new then it assigns that task to the assignee that has the least tasks.
If the title is new and all assignees have the same number of tasks then it makes a random selection with Math.floor(Math.random() * assigneeArray.length);
Here's the code:
Code.gs:
function onOpen() {
SpreadsheetApp.getUi().createMenu('My Tools')
.addItem('Add Task', 'addTask')
.addItem('Add Assignee', 'addAssignee')
.addSubMenu(SpreadsheetApp.getUi().createMenu('Utility')
.addItem('Select Columns Skip Header', 'jjeSUS1.selectColumnsSkipHeader')
.addItem('Create Named Range', 'jjeSUS1.createNamedRange'))
.addToUi();
}
function addAssignee() {
showFormDialog({filename:'addAssignee',title:'Add Assignee'});
}
function postAssigneeData() {
}
function addTask() {
showFormDialog({filename:'addTask',title:'Add Task'});
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function showFormDialog(dObj){
var form=HtmlService.createHtmlOutputFromFile(dObj.filename).getContent();
var ui=HtmlService.createTemplateFromFile('html').evaluate();
ui.append(form);
ui.append("</body></html>");
SpreadsheetApp.getUi().showModelessDialog(ui, dObj.title);
}
function saveData(dObj) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(dObj.sheetName);
var hrg=sh.getRange(1,1,1,sh.getLastColumn());
var hA=hrg.getValues()[0];
var vA=[];
for(var i=0;i<hA.length;i++) {
vA.push((dObj[hA[i]])?dObj[hA[i]]:'');//Column headers must agree with form names
}
dObj['row']=sh.getLastRow()+1;
var cA=Object.keys(dObj).filter(function(el){return (el!=='row' && el !='sheetName')});
for(var i=0;i<cA.length;i++) {
saveValue(dObj.row,cA[i],dObj[cA[i]],dObj.sheetName,1);
}
return dObj;
}
function makeAssignment(aObj) {
Logger.log(aObj);
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Assignments')
var title=aObj.Title;
var taskObj=getTasks();
//Check to see if someone has already done this once
if(taskObj.taskA.indexOf(title)>-1) {
saveValue(aObj.row,'Assignment',taskObj[aObj.Title],'Assignments',1);
saveValue(aObj.row,'Date',Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "E MM d, yyyy HH:mm"),'Assignments',1);
postTaskData(aObj.Title,taskObj[aObj.Title]);
}else{
var assA=getAssigneeTasks();
if(assA[0].allCountsEqual=='false') {
//they don't have the same number of tasks so take the lowest one
saveValue(aObj.row,'Assignment',assA[0].email,'Assignments',1);
saveValue(aObj.row,'Date',Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "E MM d, yyyy HH:mm"),'Assignments',1);
postTaskData(aObj.Title,assA[0].email);
}else{
//they all have the same number of task so take a random one
var n=Math.floor(Math.random()*assA.length);
saveValue(aObj.row,'Assignment',assA[n].email,'Assignments',1);
saveValue(aObj.row,'Date',Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "E MM d, yyyy HH:mm"),'Assignments',1);
postTaskData(aObj.Title,assA[n].email);
}
}
return true;
}
function getTasks() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Data');
var taskObj={'taskA':[]};
var h=jjeSUS1.getColumnHeight(1, sh, ss);
if(h>2) {
var rg=sh.getRange(3,1,h-2,2);
var vA=rg.getValues();
for(var i=0;i<vA.length;i++) {
taskObj[vA[i][0]]=vA[i][1];
if(taskObj.taskA.indexOf(vA[i][0])==-1) {
taskObj.taskA.push(vA[i][0]);//Unique Task Array
}
}
}
return taskObj
}
function postTaskData(key,value) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Data');
sh.appendRow([key,value]);
}
function getAssigneeTasks() {
var taskObj=getTasks();
var aeqA=getAssignees().map(function(el){return {email:el,count:0,allCountsEqual:'false'}});
var keysA=Object.keys(taskObj).filter(function(el){return (el != 'taskA')});
for(var i=0;i<aeqA.length;i++) {
for(var j=0;j<keysA.length;j++) {
if(taskObj[keysA[j]]==aeqA[i].email){
aeqA[i].count+=1;
}
}
}
aeqA.sort(function(a,b){return a.count - b.count;});
var isTrue=true;
var maxCount=aeqA[aeqA.length-1].count;
aeqA.forEach(function(el){if(el.count!=maxCount){isTrue=false;}});
if(isTrue) {
aeqA.map(function(el){return el.allCountsEqual='true';});
}
return aeqA;
}
function saveValue(row,columnName,value,sheetName,headerRow) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(sheetName);
var hA=sh.getRange(headerRow,1,1,sh.getLastColumn()).getValues()[0];
sh.getRange(row,hA.indexOf(columnName)+1).setValue(value);
}
function getAssignees() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Assignees');
var hrg=sh.getRange(1,1,1,sh.getLastColumn());
var hA=hrg.getValues()[0];
return sh.getRange(2, hA.indexOf('Email')+1, sh.getLastRow()-1,1).getValues().map(function(r){return r[0]});
}
function closeDialog() {
var userInterface=HtmlService.createHtmlOutputFromFile('dummy');
SpreadsheetApp.getUi().showModelessDialog(userInterface,'Closing');
}
css.html:
<style>
body {background-color:#ffffff;}
input[type="button"],input[type="text"]{margin:0 0 2px 0;}
</style>
resources.html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
html.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('resources') ?>
<?!= include('css') ?>
<?!= include('script') ?>
</head>
<body>
script.html:
<script>
$(function(){
document.getElementById('txt1').focus();
});
function getInputObject(obj) {
var rObj={};
var length=Object.keys(obj).length;
for(var i=0;i<length;i++){
console.log('Name: %s Type: %s',obj[i].name,obj[i].type);
if(obj[i].type=="text"){
rObj[obj[i].name]=obj[i].value;
}
if(obj[i].type=="select-one"){
rObj[obj[i].name]=obj[i].options[obj[i].selectedIndex].value;
}
if(obj[i].type="hidden"){
if(obj[i].name) {
rObj[obj[i].name]=obj[i].value;
}
}
}
return rObj;
}
function processForm(obj){
var fObj=getInputObject(obj);
console.log(JSON.stringify(fObj));
google.script.run
.withSuccessHandler(function(rObj){
document.getElementById("btn").disabled=true;
$('#msg').html('<br /><h1>Data Saved.</h1>');
if(rObj.sheetName=='Assignments') {
google.script.run
.withSuccessHandler(function(){
$('#msg').html('<br /><h1>Assignments Complete.</h1>');
google.script.host.close();
})
.makeAssignment(rObj);
}else{
google.script.host.close();
}
})
.saveData(fObj);
}
console.log('My Code');
</script>
addAssignee.html:
<div id="heading"><h1>Add Assignee</h1></div>
<div id="content">
<h3>Please Enter First Name, Last Name, Phone and Email into the text areas adjacent to the text box labels.</h3>
<form id="assigneeForm" onsubmit="event.preventDefault();processForm(this);" >
<br /><input type="text" id="txt1" name="First" /> First
<br /><input type="text" id="txt2" name="Last" /> Last
<br /><input type="text" id="txt3" name="Phone" /> Phone
<br /><input type="text" id="txt3" name="Email" /> Email
<br /><input type="hidden" value="Assignees" name="sheetName" />
<br /><input id="btn" type="submit" value="Submit" />
<br />
</form>
</div>
<div id="msg"></div>
<div id="cntl"><input type="button" id="btn" value="Close" onClick="google.script.host.close();" ></div>
addTask.html:
<div id="heading"><h1>Add Task</h1></div>
<div id="content">
<h3>Please Enter Title and Description into the text areas adjacent to the text box labels.</h3>
<form id="assigneeForm" onsubmit="event.preventDefault();processForm(this);" >
<br /><input type="text" id="txt1" name="Title" /> Title
<br /><input type="text" id="txt2" name="Description" /> Description
<br /><input type="hidden" value="Assignments" name="sheetName" />
<br /><input id="btn" type="submit" value="Submit" />
<br />
</form>
</div>
<div id="msg"></div>
<div id="cntl"><input type="button" id="btn" value="Close" onClick="google.script.host.close();" ></div>
The three pages of my spreadsheet look as follows: (names are on images)
JavaScript Arrays
JavaScript Objects
HtmlService
Templated Html
Public Libraries
I am trying to create a web app whereby a user fills out a form and the data gets populated into a google sheet. My problem is after I fill out the form the sheet gets populated with undefined data.
How do I define this data and append to a new row each time I fill out the form.
Example was taken from here.
Code.gs
function doGet(e){
return handleResponse(e);
}
var SHEET_NAME = "Sheet1";
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
function handleResponse(e) {
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameter[headers[i]]);
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return HtmlService.createHtmlOutputFromFile('index');
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
var $form = $('form#test'),
url = 'https://...';
$('#submit-form').on('click', function(e) {
e.preventDefault();
var jqxhr = $.ajax({
url: url,
method: "GET",
dataType: "json",
data: $form.serializeObject()
}).success(
// do something
);
})
</script>
</head>
<body>
<form id="test">
<p><label>ID</label>
<input type="number" name="ID"/></p>
<p><label>Part Number</label>
<input type="text" name="Part Number"/></p>
<p><label>Description</label>
<input type="text" name="Description"/></p>
<p><label>Item Link</label>
<input type="url" name="Item Link"/></p>
<p><label>Supplier</label>
<input type="text" name="Supplier"/></p>
<p><label>Manufacturer</label>
<input type="text" name="Manufacturer"/></p>
<p><label>Pins</label>
<input type="number" name="Pins"/></p>
<p><label>Size</label>
<input type="text" name="Size"/></p>
<p><label>Order Number</label>
<input type="number" name="Order Number"/></p>
<p><label>Location</label>
<input type="text" name="Location"/></p>
<p><input type="submit" value="submit-form"/></p>
</form>
</body>
</html>
Since you've borrowed code from another site it seems overly complicated for my taste. Here is a simpler solution IMHO. I don't see parameter e used anywhere so i removed. Also the use of html form is not needed but I left it as is
Code.gs
function doGet(){
try {
return HtmlService.createHtmlOutputFromFile("HTML_Test");
}
catch(err) {
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": err}))
.setMimeType(ContentService.MimeType.JSON);
}
}
function submitData(data) {
var shid = "your spreadsheet id here";
var ss = SpreadsheetApp.openById(shid);
var sh = ss.getSheetByName("Sheet1");
try {
sh.getRange(sh.getLastRow()+1,1,1,data.length).setValues([data]);
return "success"
}
catch(err) {
sh.getRange(sh.getLastRow()+1,1).setValue(err);
return err;
}
}
HTML_Test.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form id="test">
<p><label>ID</label>
<input type="number" id="ID"/></p>
<p><label>Part Number</label>
<input type="text" id="Part Number"/></p>
<p><label>Description</label>
<input type="text" id="Description"/></p>
<p><label>Item Link</label>
<input type="url" id="Item Link"/></p>
<p><label>Supplier</label>
<input type="text" id="Supplier"/></p>
<p><label>Manufacturer</label>
<input type="text" id="Manufacturer"/></p>
<p><label>Pins</label>
<input type="number" id="Pins"/></p>
<p><label>Size</label>
<input type="text" id="Size"/></p>
<p><label>Order Number</label>
<input type="number" id="Order Number"/></p>
<p><label>Location</label>
<input type="text" id="Location"/></p>
<p><input type="button" value="Submit" onclick="submitForm()"/></p>
</form>
<script>
function submitForm() {
try {
var data = [];
data.push(document.getElementById("ID").value);
data.push(document.getElementById("Part Number").value);
data.push(document.getElementById("Description").value);
data.push(document.getElementById("Item Link").value);
data.push(document.getElementById("Supplier").value);
data.push(document.getElementById("Manufacturer").value);
data.push(document.getElementById("Pins").value);
data.push(document.getElementById("Size").value);
data.push(document.getElementById("Order Number").value);
data.push(document.getElementById("Location").value);
google.script.run.withSuccessHandler(success).submitData(data);
}
catch(err) {
alert(err);
}
}
function success(msg) {
alert(msg);
}
</script>
</body>
</html>
I have four user input text fields in html created to user input data. I want to pass this four values into Google spreadsheet. This HTML is created using Google Apps Script.
I am not familiar with Google Apps Script but looking badly to develop a tool. Can anyone help me to work on this
This is a simple HTML file communicating with Google Apps Script contained in a Spreadsheet. The HTML file and the Google Apps Script communicate with each other and I pass one array from the HTML file to the Google Script.
The Code.gs file:
function doGet()
{
var html = HtmlService.createHtmlOutputFromFile('index');
return html.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
}
function getData(a)
{
var ts = Utilities.formatDate(new Date(), "GMT-6", "M/d/yyyy' 'HH:mm:ss");
a.splice(0,0,ts);
var ss=SpreadsheetApp.openById('SPREADSHEETID')
ss.getSheetByName('Form Responses 1').appendRow(a);
return true;
}
function getURL()
{
var ss=SpreadsheetApp.openById('SPREADSHEETID');
var sht=ss.getSheetByName('imgURLs');
var rng=sht.getDataRange();
var rngA=rng.getValues();
var urlA=[];
for(var i=1;i<rngA.length;i++)
{
urlA.push(rngA[i][0]);
}
return urlA;
}
The index.html file:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div id="data">
<br />Text 1<input name="t1" type="text" size="15" id="txt1" placeholder="Text 1" />
<br />Text 2<input name="t2" type="text" size="15" id="txt2" placeholder="Text 2" />
<br />Text 3<input name="t3" type="text" size="15" id="txt3" placeholder="Text 3" />
<br />Text 4<input name="t4" type="text" size="15" id="txt4" placeholder="Text 4" />
<br /><input type="radio" name="Type" value="Member" checked />Member
<br /><input type="radio" name="Type" value="Guest" />Guest
<br /><input type="radio" name="Type" value="Intruder" />Intruder
<br /><input type="button" value="submit" id="btn1" />
<br /><img id="img1" src="" alt="img1" width="300" />
</div>
<div id="resp" style="display:none;">
<h1>Response</h1>
<p>Your data has been received.</p>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
$('#btn1').click(validate);
$('#txt4').val('');
$('#txt3').val('');
$('#txt2').val('');
$('#txt1').val('')
google.script.run
.withSuccessHandler(setURL)
.getURL();
});
function setURL(url)
{
$('#img1').attr('src',url[0]);
}
function setResponse(a)
{
if(a)
{
$('#data').css('display','none');
$('#resp').css('display','block');
}
}
function validate()
{
var txt1 = document.getElementById('txt1').value || '';
var txt2 = document.getElementById('txt2').value || '';
var txt3 = document.getElementById('txt3').value || '';
var txt4 = document.getElementById('txt4').value || '';
var type = $('input[name="Type"]:checked').val();
var a = [txt1,txt2,txt3,txt4,type];
if(txt1 && txt2 && txt3 && txt4)
{
google.script.run
.withSuccessHandler(setResponse)
.getData(a);
return true;
}
else
{
alert('All fields must be completed.');
}
}
function loadTxt(from,to)
{
document.getElementById(to).value = document.getElementById(from).value;
}
function radioValue()
{
var radios = document.getElementsByName('genderS');
for (var i = 0, length = radios.length; i < length; i++)
{
if(radios[i].checked)
{
return radios[i].value;
}
}
}
console.log('My Code');
</script>
</body>
</html>