the eSignature dilemma - google-apps-script

I have a form that needs an eSignature collected via mobile. I need it to be free with unlimited responses. I have a working jSignature HTML code for Google Sheets, but I cannot get it to work on mobile.
Ideally, I'd like to stick with Google, though I've been experimenting outside of it with things like Wix (doesn't do exactly what I need it to) or Android Studio (beyond my ability). My ability beyond that isn't great by any means so I'm not sure where to begin, though I have a suspicion it isn't about what I can do so much as what google sheets can do, as in, I've read a bit about how scripting doesn't work well on mobile.
HTML
<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/></head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/willowsystems/jSignature/master/libs/jSignature.min.js"></script>
<body>
<form id="customerForm">
Please sign your name in the pad below: <br>
Full Name: <input type="text" name="username"><br>
Employee Number: <input type="employeenumber" name="useremployeenumber"><br><br>
Signature:
<div id="signature"></div><br>
<img id="rendered" src="" style="display:none">
<input type="button" value="Save" onclick="renderSignature();saveImage();"/>
</form>
</body>
<script>
document.getElementById("signature").style.border = "1px solid black";
$("#signature").jSignature({
'background-color': 'transparent',
'decor-color': 'transparent'
});
function renderSignature(){
$("img#rendered").attr("src",$('#signature').jSignature('getData','default'));
}
function saveImage(e){ //This sends the image src to saveImages function
var bytes = document.getElementById('rendered').src;
console.log(bytes);
var sign = {
username: document.getElementsByName('username')[0].value,
useremployeenumber: document.getElementsByName('useremployeenumber')[0].value
};
google.script.run.saveImage(bytes, sign);
return
}
window.onload=function(){
google.script.run
.withSuccessHandler(function(){google.script.host.close();})
.saveImage(bytes, sign);
}
</script>
</html>
CODE.GS
function showDialog() {
var html = HtmlService.createHtmlOutputFromFile('jSignature')
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Your Signature is Required');
}
function doGet() {
return HtmlService
.createTemplateFromFile('jSignature')
.evaluate();
}
function saveImage(bytes, sign){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('my page name');
var dateObj = Date.now();
var bytes = bytes.split(",")
var blob = Utilities.newBlob(Utilities.base64Decode(bytes[1]), 'image/png');
var fileName = blob.setName("Signature "+dateObj).getName();
var sigFolder = DriveApp.getFolderById("my folder id");
var url = sigFolder.createFile(blob).getId();
Logger.log(url)
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Register');
var name = sign.username;
var employeenumber = sign.useremployeenumber;
var signature = ss.insertImage(blob,4,ss.getLastRow()+1);
signature.setWidth(500);
signature.setHeight(20);
signature
var imageCell = ss.getRange(ss.getLastRow()+1, 1, 1, 3).setValues([[Date(), name,employeenumber]]);
}
What I've been thinking about is having the user open the form responses google sheet first, to a page with a link to the form. They would follow that link, fill out the form then be taken back to the google sheets form where an onFormSubmit trigger would bring up the jSignature pad. They would sign and all the information would be collected on one line in the form responses.
How do I make jSignature work for Google Sheets mobile? Is it even possible?

Related

Upload Image in google sheet cell using google apps script?

I am looking to upload an image into google sheet cell using a google apps script, I found a script that uploads image into Google Drive folder and then gets the image url into sheet that can be manipulated to get the image:
Here is the first function:
Code.gs
function addImage() {
var filename = 'Row';
var htmlTemp = HtmlService.createTemplateFromFile('Index');
htmlTemp.fName = filename;
htmlTemp.position = 2;
var html = htmlTemp.evaluate().setHeight(96).setWidth(415);
var ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, 'Upload');
}
Following is the return function:
Code.gs
function upload(obj) {
//Retrieve the input data of the Form object.
var newFileName = obj.fname;
var rowNum = obj.position;
var blob = obj.file;
var upFile = DriveApp.getFolderById('[folderid]').createFile(blob).setName(newFileName);
var fileUrl = upFile.getUrl();
var urlCell = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getRange(rowNum,5);
urlCell.setValue('=HYPERLINK("' + fileUrl + '","View image")');
}
This is the html part:
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_center">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
</head>
<body>
<form id="myForm">
Please upload image below.<br /><br />
<input type="hidden" name="fname" id="fname" value="<?= fName ?>"/>
<input type="hidden" name="position" id="position" value="<?= position ?>"/>
<input type="file" name="file" id="file" accept="image/jpeg,.pdf" />
<input type="button" value="Submit" class="action" onclick="formData(this.parentNode)" />
<input type="button" value="Close" onclick="google.script.host.close()" />
</form>
<script>
//Disable the default submit action using “func1”
window.onload=func1;
function func1() {
document.getElementById('myForm').addEventListener('submit', function(event) {
event.preventDefault();
});
}
function formData(obj){
google.script.run.withSuccessHandler(closeIt).upload(obj);
}
function closeIt(e){
console.log(e);
google.script.host.close();
};
</script>
</body>
</html>
When I ran the addImage() function, a dialog box popped up in which I uploaded a jpeg image, but when I clicked on submit button, it did not do anything and stuck there, any help would be much appreciated. Thanks
Issue and workaround:
From [Fixed] Google Apps Script Web App HTML form file-input fields not in blob compatible format, in the current stage, when Web Apps is used, the file object in the form object can be parsed by google.script.run. But, unfortunately, it seems that when a dialog and sidebar are used, this cannot be parsed. So, in the current stage, as the current workaround, it is required to parse the file object on the Javascript side. When this is reflected in your script, how about the following modification?
Google Apps Script side: Code.gs
Please `upload as follows.
function upload(obj, rowNum) {
var newFileName = obj[2];
var blob = Utilities.newBlob(...obj);
var upFile = DriveApp.getFolderById('[folderid]').createFile(blob).setName(newFileName);
var fileUrl = upFile.getUrl();
var urlCell = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getRange(rowNum, 5);
urlCell.setValue('=HYPERLINK("' + fileUrl + '","View image")');
return "Done.";
}
HTML & Javascript side: Index.html
Please formData as follows.
function formData(obj) {
const file = obj.file.files[0];
const fr = new FileReader();
fr.readAsArrayBuffer(file);
fr.onload = f =>
google.script.run.withSuccessHandler(closeIt).upload([[...new Int8Array(f.target.result)], file.type, obj.fname.value], obj.position.value);
}

How can I create a web app where I can read text from a sheet based on dropdown selections?

I am trying to create a role description generator which reads pre-written text from a Google Sheet and assembles it in blocks in a web app through selections (team, role, seniority level, etc.) in dropdown menus.
This is an example of what the data in the sheet looks like:
Team name
Team description
A-team
Description
B-team
Description
...
...
So far, for the team selection, I have created the dropdown menu which reads the data from the sheet, and pulls the names of each team into a dropdown list. But my problem is loading the corresponding team description text into the HTML page. I just can't seem to get it to work.
When pressing the generate button, what should happen is that the description for A-team is loaded, but instead I get [object MouseEvent].
Any suggestions? Thanks in advance! :)
Here's my code:
Code.gs
var url = "*spreadsheet URL*";
function doGet(e) {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
//get the data for the dropdown list
function valuesForList(list) {
//define the data
var ss = SpreadsheetApp.openByUrl(url)
var teamsSheet = ss.getSheetByName('Data');
var lastRow = teamsSheet.getLastRow();
var teamsRange = teamsSheet.getRange(1, 3, lastRow, 1);
//create a named range
ss.setNamedRange('teamsList', teamsRange);
//get the values from the range
var listValues = ss.getRangeByName(list).getValues();
return listValues;
}
//the function to show the data on the index.html
function PostInfo (userInfo){
//load the data
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues();
var teamList = data.map(function(r){ return r[0]});
var teamDesc = data.map(function(r){ return r[1]});
var position = teamList.indexOf(userInfo.teams);
if(position > -1){
return teamDesc[position];
} else {
return "Unavailable";
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
<script>
function onListSuccess(list) {
var listLength = list.length;
for (i=0; i<listLength;i++) {
var dropdown = document.getElementById("teams");
var opt = document.createElement("option");
dropdown.options.add(opt);
opt.text = list[i][0];
opt.value = list[i][0];
}
}
function onListSelect(teamDesc){
var text = teamDesc.toString().split(",");
document.getElementById('est').innerHTML = text;
}
</script>
</head>
<body>
<div id="main">
<h1>Role Description Generator</h1>
<p>
<label for="teams">Team:</label>
</p>
<p>
<select name="teams" id="teams" tabindex="2"></select>
</p>
<button id="btn">Generate</button>
<div>
<label for="est">Team description:</label>
<p id="est" name="est"></p>
</div>
</div>
</body>
<script>
function populateList(){
google.script.run.withSuccessHandler(onListSuccess).valuesForList('teamsList');
}
document.getElementById("teams").addEventListener("change", doStuff);
document.getElementById("btn").addEventListener("click", onListSelect);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.PostInfo(userInfo);
}
window.addEventListener('load', populateList);
</script>
</html>
Modification points:
In your script, when the dropdown list is changed, doStuff() is run. But in this case, google.script.run.PostInfo(userInfo) runs only the function of PostInfo at Google Apps Script. By this, the returned value is not used.
And, when the button is clicked, onListSelect is run. But in this case, teamDesc of onListSelect(teamDesc) is the event object. By this, such value of [object MouseEvent] is shown. I thought that this might be the reason of your issue.
By the way, when I saw your Google Apps Script, I noticed that PostInfo has a modification point. When var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues(); is run, I think that an error occurs. Because ws2 is not declared. In your case, is that ws? I thought that this might be due to your miscopy.
When you want to show the value from PostInfo when the button is clicked, how about the following modification?
Modified script:
HTML&Javascript side:
From:
document.getElementById("teams").addEventListener("change", doStuff);
document.getElementById("btn").addEventListener("click", onListSelect);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.PostInfo(userInfo);
}
To:
document.getElementById("btn").addEventListener("click", doStuff);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.withSuccessHandler(onListSelect).PostInfo(userInfo);
}
Google Apps Script side:
From:
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues();
To:
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws.getLastRow(),2).getValues();
Note:
In this modidication, it supposes that the Google Apps Script works fine and returns the correct values. Please be careful this.
Reference:
Class google.script.run

How to implement DatePicker in my Google Apps Scripts

I would like to make the two dates start_date_cal and end_date_cal chosen by the user who is running the script below.
By reading a lot about how to do that, I have found plenty about Html Services, but I couldn't successed in implementing it in my code.
function main_cotisations() {
var cell_col_number = "";
var cell_row_number = "";
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('2019-2020');
//ss.setActiveSelection('A3');
ss.getRange('A3').activate();
// Get active cell and get value of it
var cell = ss.getCurrentCell();
var cellValue = cell.getValue();
var start_date_cal = new Date('December 12, 2019 19:00:00 +1100');
var end_date_cal = new Date('December 12, 2019 22:30:00 +1100');
// Start the loop
while (cellValue != ""){
cellValue = cell.getValue();
if (cellValue != "") {
var infos_user = {};
infos_user.prenom = sheet.getRange(cell.getRow(),cell.getColumn()+2).getValue();
infos_user.mail = sheet.getRange(cell.getRow(),cell.getColumn()+3).getValue();
infos_user.solde_a_payer = sheet.getRange(cell.getRow(),cell.getColumn()+8).getValue();
infos_user.url_calendar = 'https://www.google.com/calendar/render?action=TEMPLATE&text=Penser+aux+capitations+: '+infos_user.solde_a_payer+'F&location=Ducos&dates='+getRelativeDate(start_date_cal)+'%2F'+getRelativeDate(end_date_cal);
if (sheet.getRange(cell.getRow(),cell.getColumn()+9).isChecked() == false) {
sendmail_paiement(infos_user);
}
};
cell = cell.offset(1, 0);
};
var infos_globales = {};
infos_globales.solde_global = sheet.getRange(cell.getRow()-1,cell.getColumn()+8).getValue();
infos_globales.nb_user_hospitalier = sheet.getRange(cell.getRow()-1,cell.getColumn()+9).getValue();
infos_globales.nb_user_retard =sheet.getRange(cell.getRow()-1,cell.getColumn()+10).getValue();
}
//***********************************************************************************************************************
function getRelativeDate(given_date) {
//{given_date = Utilities.formatDate(temp_date, "GMT", "yyyyMMdd'T'HHmmss'Z'");}
temp = Utilities.formatDate(given_date, "GMT", "yyyyMMdd'T'HHmmss'Z'");
return temp.toString();
}
function sendmail_paiement(infos_user) {
var templ = HtmlService
.createTemplateFromFile('tpl_mail_exceptionnel');
templ.infos_user = infos_user;
var message = templ.evaluate().getContent();
if (infos_user.solde_a_payer>0) {
MailApp.sendEmail(
infos_user.mail,
"Rappel de capitations",
'',
{htmlBody: message,}
);
}
}
I would appreciate some help here.
It could be implemented by developing an WebApp from there you could create a form with a date picker and a submit button to send the date back to the apps script and execute your code.
I've done a little demo:
Go apps script and make a new project:
function doPost(e) {
Logger.log(e.parameter.start);
Logger.log(e.parameter.end);
return HtmlService.createHtmlOutput("Hi there, <br> When you click submit doPost() function is called. <br> By calling e.parameter.start you'll get the desired dates <br> Like:" +e.parameter.start+ "<br>Now check the logs. <br> Good luck");
}
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('index');
}
Now, on apps script, press File --> New --> Html file --> Name it 'index'
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form action="PASTE_YOUR_WEBAPP_LINK_HERE" method="post">
<label for="start">Start date:</label>
<input type="date" id="start" name="start"
value="2019-10-21"
min="2019-01-01" max="2020-12-31">
<input type="date" id="end" name="end"
value="2019-10-22"
min="2019-01-01" max="2020-12-31">
<input type="submit" value="Submit">
</form>
</body>
</html>
Go to Publish --> Deploy as Web App
Copy the link and post it inside the index HTML file, were it says PASTE_YOUR_WEBAPP_LINK_HERE
Now you can visit your deployed WebApp:
Anytime you click submit you'll call doPost() function.
So, now you can paste your code inside doPost() function, you can use the dates from the datepicker and execute any apps script you wish.
Hope this helps.

Google Sheets web app register several options

I've taken an example from a book to adapt it. the thing is that the original code has radio check (to enable only one option) and I'd like to have them as checkbocxes (to register several choices). I've made the changes in the HTML code and it looks fine but the spreadsheet still registers one of the choices instead of all of them.
Could you please let me know what I'm missing?
Here's the HTML of the project
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form method="post" action="<?= pubUrl ?>">
<h4>Where will you go for vacation?</h4>
<? for (var i in places) { ?>
<input type="checkbox" name="places"
value="<?= places[i] ?>" /><?= places[i] ?><br />
<? } ?>
<br />
<input type="submit" value="SUBMIT" />
</form>
</body>
</html>
And here's the GAS (google Script)
function doGet() {
// Replace with your spreadsheet's ID.
var ss = SpreadsheetApp.openById("1hpYbBbpVfxsciVCpZGBcqHPeBo2Wuj7U1Y9CaxsI9go");
var SheetPlaces = ss.getSheetByName("Places");
var data = SheetPlaces.getDataRange().getValues();
// Remove header row.
data.shift();
var places = [];
// Populate the places array with the first column's data.
data.forEach(function(row){
places.push(row[0]);
});
var template = HtmlService.createTemplateFromFile("form.html");
//Assign the published URL to the template object in the doGet
template.pubUrl = "https://script.google.com/macros/s/AKfycbx90heH12wfP4-kVZCOkEpI7Bi5wqpDAf-ndLrf3bPPYSwwEp5Q/exec";
//o
//template.pubUrl = ScriptApp.getService().getUrl();
// Assign the places array to the template object.
template.places = places;
var html = template.evaluate();
return HtmlService.createHtmlOutput(html);
}
function doPost(e){
// Replace with your spreadsheet's ID.
var ss = SpreadsheetApp.openById("1hpYbBbpVfxsciVCpZGBcqHPeBo2Wuj7U1Y9CaxsI9go");
var SheetResponses = ss.getSheetByName("Responses");
// Create a 'Responses' sheet if it does not exist.
if(!SheetResponses){ SheetResponses = ss.insertSheet("Responses");
}; SheetResponses.appendRow([e.parameter.places]); return ContentService.createTextOutput( "Your response submitted successfully. Thank you!" );
}
function createForm() {
var ThisSpreadsheet = SpreadsheetApp.getActive();
var SheetPlaces = ThisSpreadsheet.getSheetByName("Places");
// Load 'Places' sheet data as a 2-dimensional array.
var data = SheetPlaces.getDataRange().getValues();
// remove the header row
data.shift();
var places = [];
// Populate the places array with the first column's data
data.forEach(function(row){
places.push(row[0]);
});
// Create a new form
var form = FormApp.create("Vacation Form");
form.addMultipleChoiceItem()
.setTitle('Where will you go for a vacation?')
.setChoiceValues(places)
.showOtherOption(true)
}
Also, here's the web app url
https://script.google.com/macros/s/AKfycbx90heH12wfP4-kVZCOkEpI7Bi5wqpDAf-ndLrf3bPPYSwwEp5Q/exec
Thanks for your help!

How can I show a google document automatically after onclick in google script?

I'm trying to create a script that has a datepicker on the right side. The code below will allow the user to create a file named by the date when he click a certain date in the date picker.. My problem is how do I open/show the file in the browser?
This is my google script code....
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'showSidebar')
.addToUi();
}
function creatediary(x) {
//var doc = DocumentApp.create(x);
//var retrievedID = DocumentApp.getID(doc);
//var doc = DocumentApp.openById(doc);
}
function listFiles(z) {
var files = DriveApp.getFiles();
var doc = DocumentApp.create(z);
while ( files.hasNext() ) {
var file = files.next();
if(file.getName() === z){
var retrievedID = file.getId();
var retrievedurl = file.getUrl();
//var doc = DocumentApp.create(retrievedurl);
//var doc = DocumentApp.create(retrievedID);
var doc = DocumentApp.openById(retrievedID);
//showURL('Open document named "'+z+'"',retrievedurl);
}
;
}
}
function onInstall(e) {
onOpen(e);
}
function showSidebar() {
var ui = HtmlService.createHtmlOutputFromFile('Sidebar')
.setTitle('Diary');
DocumentApp.getUi().showSidebar(ui);
}
This is my .html code...
<html lang="en">
<head>
</head>
<style>
div.ui-datepicker{
font-size:10px;
}
</style>
<div>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Default functionality</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.1/jquery-ui.js"></script>
<link rel="stylesheet" href="/resources/demos/style.css">
</div>
<script>
$(function() {
$("#datepicker").datepicker(
{
onSelect: function()
{
var dateObject = $(this).datepicker('getDate');
document.getElementById("demo").innerHTML = dateObject;
//var doc = DocumentApp.create("sample");
//DriveApp.createFile(dateObject, 'Diary');
google.script.run.creatediary(document.getElementById("demo").innerHTML);
google.script.run.listFiles(document.getElementById("demo").innerHTML);
}
});
});
</script>
<body>
<p id="demo"></p>
<p>Date: <input type="text" id="datepicker"></p>
<iframe src="https://www.google.com/calendar/embed?
src=gttvlnnqitf8atif2rreplcdl0%40group.calendar.google.com&ctz=Asia/Manila" style="border: 0" width="800" height="600" frameborder="0" scrolling="no"></iframe>
</body>
</html>
As stated elsewhere (Stack Overflow Open a doc from Google Apps Script), Apps Script does not allow script to automatically navigate to other URLs or open other files.
You can, however, use a script to acquire the URL for the file and present it to the user as a link so they can open it:
var doc = DocumentApp.create(fileName);
var url = doc.getUrl();
// Give the url to the user via a dialog, link in a doc, link in an email or
// another method