【Google App Script GAS】 Multiple Upload and e signature

 code.gs



//  1. Enter sheet name where data is to be written below
var SHEET_NAME = "Sheet1";
//  2. Run > setup
//
//  3. Publish > Deploy as web app
//    - enter Project Version name and click 'Save New Version'
//    - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously)
//
//  4. Copy the 'Current web app URL' and post this in your form/script action
//
//  5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
function doGet(e) {
  return HtmlService.createHtmlOutputFromFile('forms.html').setTitle("Registration With Document");
}

function read_value(request){  

  var ss = SpreadsheetApp.openById("1vSGBziTX1hksq98rKws_EdyDNXTiXkYyrXtnbz29z1Q");
  var sheet = ss.getSheetByName(SHEET_NAME);

  var rows = sheet.getDataRange();
  var numRows = rows.getNumRows();
  var values = rows.getValues();

  var json =JSON.stringify(values);

  return json;



}







function uploadFileToGoogleDrive(dataname,  emailtelpositionimgData) {
  try {

    var dropbox = "Received Files";
    //var folder, folders = DriveApp.getFoldersByName(dropbox);
    var folder=DriveApp.getFolderById('13fh8Q3tC0Vz43WqB2LsW6exLlR1zU_uI');
    var file = folder.createFolder([nameemail].join(" "))
    var filelink = [];
    for (var index = 0index < data.lengthindex++) {
      var contentType = data[index].image.substring(5,data[index].image.indexOf(';')),
      bytes = Utilities.base64Decodedata[index].image.substrdata[index].image.indexOf('base64,')+7)),
      blob = Utilities.newBlob(bytescontentType,  data[index].name);
      file.createFile(blob)
      filelink.push(file.getUrl()) ;
    }
    var lock = LockService.getPublicLock();
    lock.waitLock(30000);  // wait 30 seconds before conceding defeat.    

    // next set where we write the data - you could write to multiple/alternate destinations
    var doc = SpreadsheetApp.openById("1vSGBziTX1hksq98rKws_EdyDNXTiXkYyrXtnbz29z1Q");
    var sheet = doc.getSheetByName(SHEET_NAME);
    // we'll assume header is in row 1 but you can override with header_row in GET/POST data
    var headRow =  1;
    var headers = sheet.getRange(111sheet.getLastColumn()).getValues()[0];
    var nextRow = sheet.getLastRow()+1// get next row

    var row = [];
    
    var getLastRow = sheet.getRange(sheet.getLastRow(), 3).getValue();

    var rid = (getLastRow == 'pid' ? 0 : getLastRow) + 1;
    // 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 if (headers[i] == "name"){
      row.push(name);
    } else if (headers[i] == "pid"){
      row.push(rid);
    } else if (headers[i] == "email"){
      row.push(email);
    } else if (headers[i] == "tel"){
      row.push(tel);
    } else if (headers[i] == "position"){
      row.push(position);
    } else if (headers[i] == "filelink"){
      row.push(filelink);
    }else if (headers[i] == "imgData" && imgData){
      row.push(imgData);
    }

    }
    // more efficient to set values as [][] array than individually
    sheet.getRange(nextRow11row.length).setValues([row]);

    // return json success results
    //return ContentService
    //      .createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
    //      .setMimeType(ContentService.MimeType.JSON);    


    return "OK";

  } catch (f) {
    return f.toString();
  } finally { //release lock
  lock.releaseLock();
}







}


function update_value(data,pidnameemailtelpositionimgData){
  try {

    var dropbox = "Received Files";
    //var folder, folders = DriveApp.getFoldersByName(dropbox);
    var folder=DriveApp.getFolderById('13fh8Q3tC0Vz43WqB2LsW6exLlR1zU_uI');
    /*
    if (folders.hasNext()) {
      folder = folders.next();
      } else {
        folder = DriveApp.createFolder(dropbox);
        }
        */
        /* Credit: www.labnol.org/awesome */

        var file = folder.getFoldersByName([nameemail].join(" "))
        var filelink = [];
        for (var index = 0index < data.lengthindex++) {
          var contentType = data[index].image.substring(5,data[index].image.indexOf(';')),
          bytes = Utilities.base64Decodedata[index].image.substrdata[index].image.indexOf('base64,')+7)),
          blob = Utilities.newBlob(bytescontentType,  data[index].name);
          file.createFile(blob)
          filelink.push(file.getUrl()) ;
        }
        var lock = LockService.getPublicLock();
        lock.waitLock(30000);  // wait 30 seconds before conceding defeat.    

        // next set where we write the data - you could write to multiple/alternate destinations
        var doc = SpreadsheetApp.openById("1vSGBziTX1hksq98rKws_EdyDNXTiXkYyrXtnbz29z1Q");
        var sheet = doc.getSheetByName(SHEET_NAME);
        // we'll assume header is in row 1 but you can override with header_row in GET/POST data
        var headRow =  1;
        var headers = sheet.getRange(111sheet.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 if (headers[i] == "name"){
          row.push(name);
        } else if (headers[i] == "email"){
          row.push(email);
        } else if (headers[i] == "pid"){
          row.push(pid);
        }else if (headers[i] == "tel"){
          row.push(tel);
        } else if (headers[i] == "position"){
          row.push(position);
        } else if (headers[i] == "filelink" && filelink){
          row.push(filelink);
        }else if (headers[i] == "imgData" && imgData){
          row.push(imgData);
        }

        }

        var lr = sheet.getLastRow();

        for(var i = 1;i <= lr;i++){
          var rid = sheet.getRange(i3).getValue();
          if(rid == pid){
            sheet.getRange(i11row.length).setValues([row]);
            break;
            var result = "value updated successfully";
          }
        }



        return "OK";

  } catch (f) {
    return f.toString();
  } finally { //release lock
  lock.releaseLock();
}

}


function delete_value(pidnameemailtelposition){
  try {


    var lock = LockService.getPublicLock();
    lock.waitLock(30000);  // wait 30 seconds before conceding defeat.    

    // next set where we write the data - you could write to multiple/alternate destinations
    var doc = SpreadsheetApp.openById("1vSGBziTX1hksq98rKws_EdyDNXTiXkYyrXtnbz29z1Q");
    var sheet = doc.getSheetByName(SHEET_NAME);
    // we'll assume header is in row 1 but you can override with header_row in GET/POST data
    var headRow =  1;
    var headers = sheet.getRange(111sheet.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("");
    } else if (headers[i] == "name"){
      row.push("");
    } else if (headers[i] == "email"){
      row.push("");
    } else if (headers[i] == "pid"){
      row.push("");
    }else if (headers[i] == "tel"){
      row.push("");
    } else if (headers[i] == "position"){
      row.push("");
    } 

    }

    var lr = sheet.getLastRow();

    for(var i = 1;i <= lr;i++){
      var rid = sheet.getRange(i3).getValue();
      if(rid == pid){
        sheet.deleteRow(i);
        break;
        var result = "value delete successfully";
      }
    }



    // more efficient to set values as [][] array than individually
    //  sheet.getRange(2, 1, 1, row.length).setValues([row]);

    // return json success results
    //return ContentService
    //      .createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
    //      .setMimeType(ContentService.MimeType.JSON);    


    return "OK";

  } catch (f) {
    return f.toString();
  } finally { //release lock
  
  lock.releaseLock();
}

}

function setup() {
  var doc = SpreadsheetApp.getActiveSpreadsheet();
  SCRIPT_PROP.setProperty("1vSGBziTX1hksq98rKws_EdyDNXTiXkYyrXtnbz29z1Q"doc.getId());
}


forms.html



<!DOCTYPE html>
<html>

<head>
  <base target="_blank">
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Registration</title>
  <link rel="stylesheet" href="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />

  <link rel="stylesheet"
    href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic|Material+Icons" />
  <link rel="stylesheet" href="https://unpkg.com/vue-material/dist/vue-material.min.css" />
  <link rel="stylesheet" href="https://unpkg.com/vue-material/dist/theme/default.css" />

  <style>
    .disclaimer {
      width: 480px;
      color: #646464;
      margin: 20px auto;
      padding: 0 16px;
      text-align: center;
      font: 400 12px RobotoHelveticaArialsans-serif
    }

    .disclaimer a {
      color: #009688
    }

    #credit {
      display: none
    }
  </style>
</head>

<body>
  <template id="NotFoundPage">
    <div>
      <h1>404 Not Found</h1>
    </div>
  </template>
  <template id="HomePage">


    <md-table v-model="list" md-card>
      <md-table-toolbar>
        <h1 class="md-title">Users</h1>
      </md-table-toolbar>
      <md-progress-spinner v-if="loading" :md-diameter="30" :md-stroke="3" md-mode="indeterminate">
      </md-progress-spinner>
      <md-table-row v-if="!loading" slot="md-table-row" slot-scope="{ item }">
        <md-table-cell md-label="Name" md-sort-by="name">{{ item.name }}</md-table-cell>
        <md-table-cell md-label="Email" md-sort-by="email">{{ item.email }}</md-table-cell>
        <md-table-cell md-label="Job Title" md-sort-by="title">{{ item.position }}</md-table-cell>
        <md-table-cell md-label="Action">
          <md-button class="md-primary" @click="onEdit(item)">Edit</md-button>
          <md-button class="md-accent" @click="onDelete(item, index)">Delete</md-button>
        </md-table-cell>

      </md-table-row>
    </md-table>


  </template>
  <template id="registerpage">
    <div>
      <form class="main" id="form" novalidate="novalidate" style="max-width: 480px;margin: 40px auto;">

        <md-card class="md-layout-item ">
          <md-card-header>
            <div class="md-title">Register</div>
          </md-card-header>

          <md-card-content>


            <md-field>
              <label>Name</label>
              <md-input v-model="register.name"></md-input>
            </md-field>

            <md-field>
              <label>Email</label>
              <md-input type="email" v-model="register.email"></md-input>
            </md-field>
            <md-field>
              <label>Telephone</label>
              <md-input v-model="register.tel"></md-input>
            </md-field>
            <md-field>
              <label>Position</label>
              <md-select v-model="register.position" name="movie" id="movie">
                <md-option value="Assistance">Assistance</md-option>
                <md-option value="Secretary">Secretary</md-option>
                <md-option value="Head">Head</md-option>
                <md-option value="Other">Other</md-option>
              </md-select>
            </md-field>
            <md-field>
              <label>File</label>
              <md-file @change="onFileChange" />
            </md-field>
            <md-field>

              <div :style="{'display': isClose ? 'block' : 'none'}">
                <div ref="page" id="page">
                  <img :src="imgData">
</div>
                </div>
                <div :style="{'display': !isClose ? 'block' : 'none'}" data-role="popup"
                  ref="divPopUpSignContract" id="divPopUpSignContract">
                  <div class="ui-btn-right" @click="closePopUp(false)"> Close </a>
                    <p class="popupHeader">Sign Pad</p>
                  </div>
                  <div class="ui-content popUpHeight">
                    <div ref="div_signcontract" id="div_signcontract">
                      <canvas ref="canvas" id="canvas">Canvas is not supported</canvas>
                      <div>
                        <!-- <input id="btnSubmitSign" type="button" data-inline="true" data-mini="true" data-theme="b" value="Submit Sign" @click="fun_submit()" /> -->
                        <input id="btnClearSign" type="button" data-inline="true" data-mini="true" data-theme="b" value="Clear" @click="init_Sign_Canvas()" />
</div>
                      </div>
                    </div>
                  </div>
            </md-field>
            <md-button v-if="!loading" @click="submitForm" class="md-raised md-primary">Submit</md-button>
            <md-progress-spinner v-else :md-diameter="30" :md-stroke="3" md-mode="indeterminate">
            </md-progress-spinner>

          </md-card-content>


        </md-card>

      </form>
    </div>
  </template>

  <div id="app">
    <div class="page-container">
      <md-app>
        <md-app-toolbar class="md-primary">
          <span class="md-title">Register</span>
          <div class="md-toolbar-section-end">
            <md-button @click="$router.push({path: '/'})">Home</md-button>
            <md-button @click="$router.push({path: '/register'})">Register</md-button>
          </div>
        </md-app-toolbar>
        <nav>
          <div class="nav-wrapper">
            <ul id="nav-mobile" class="left hide-on-med-and-down">
              <li>
                <router-link to="/" exact>Home</router-link>
              </li>
              <li>
                <router-link to="/register">Register</router-link>
              </li>
            </ul>
          </div>
        </nav>
        <md-app-content>
          <router-view></router-view>
        </md-app-content>
      </md-app>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.4.9/vue-router.min.js"></script>
  <script src="https://unpkg.com/vue-material"></script>



  <script>
    Vue.use(VueMaterial.default)
var item = {}
var RegisterPage = Vue.component'registerpage', {
template'#registerpage',
data(){
return{
isClosefalse,
imgData''
isSignfalse,
leftMButtonDownfalse,
loadingfalse,
redTextfalse,
process'',
successfalse,
forminnertrue,
register:{
imgData''
name'' ,
pid'',
email'',
tel'',
position'',
images:[]
}
}
},
mounted(){
this.init_Sign_Canvas();
if(this.$route.params.id){
this.register =  this.$route.params}
},
beforeRouteEnter (tofromnext) {

next(vm => vm.setData(vm.$route.params))

},
methods:{
fun_submit() {
var vm = this;
if(vm.isSign) {
var canvas = $(vm.$refs.canvas).get(0);
vm.imgData = canvas.toDataURL();
$(vm.$refs.page).find('p').remove();
$(vm.$refs.page).find('img').remove();
$(vm.$refs.page).append($('<p>Your Sign:</p>'));
$(vm.$refs.page).append($('<img/>').attr('src',vm.imgData));

vm.closePopUp(false);
else {
alert('Please sign');
}
},

init_Sign_Canvas() {
var vm = this;
vm.isSign = false;
vm.leftMButtonDown = false;

//Set Canvas width
var sizedWindowWidth =$(vm.$refs.div_signcontract).width();
if(sizedWindowWidth > 700){
sizedWindowWidth = $(window).width() / 2;
}                                                                                                                                                                                                           else if(sizedWindowWidth > 400){
sizedWindowWidth = sizedWindowWidth - 50;
}                                                                                                                                                                                                      else{

sizedWindowWidth = sizedWindowWidth - 20;                                                                                                                                                                                                                                      }


$(vm.$refs.canvas).width(sizedWindowWidth);
$(vm.$refs.canvas).height(200);
$(vm.$refs.canvas).css("border","1px solid #000");

var canvas = $(vm.$refs.canvas).get(0);

canvasContext = canvas.getContext('2d');

if(canvasContext)
{
canvasContext.canvas.width  = sizedWindowWidth;
canvasContext.canvas.height = 200;

canvasContext.fillStyle = "#fff";
canvasContext.fillRect(0,0,sizedWindowWidth,200);

canvasContext.moveTo(50,150);
canvasContext.lineTo(sizedWindowWidth-50,150);
canvasContext.stroke();

canvasContext.fillStyle = "#000";
canvasContext.font="20px Arial";
canvasContext.fillText("x",40,155);
}

$(canvas).on('mousedown'function (e) {
if(e.which === 1) { 
vm.leftMButtonDown = true;
canvasContext.fillStyle = "#000";
var x = e.pageX - $(e.target).offset().left;
var y = e.pageY - $(e.target).offset().top;
canvasContext.moveTo(xy);
}
e.preventDefault();
return false;
});

$(canvas).on('mouseup'function (e) {
if(vm.leftMButtonDown && e.which === 1) {
vm.leftMButtonDown = false;
vm.isSign = true;
}
e.preventDefault();
return false;
});

// draw a line from the last point to this one
$(canvas).bind('mousemove'function (e) {
if(vm.leftMButtonDown == true) {
canvasContext.fillStyle = "#000";
var x = e.pageX - $(e.target).offset().left;
var y = e.pageY - $(e.target).offset().top;
canvasContext.lineTo(x,y);
canvasContext.stroke();
}
e.preventDefault();
return false;
});
},
closePopUp(arg = null) {
this.isClose =  !this.isClose;
console.log(this.isClose)
},
changeValue(e){
console.log("select");
},
setData(params){
if(params.pid){
console.logparams.position)
this.register.name = params.name;
this.register.pid = params.pid;
this.register.email = params.email;
this.register.tel = params.tel;
this.register.position = params.position;
this.register.imgData = params.imgData;
this.imgData = params.imgData}

},
onFileChange(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.createImage(files);
},
showSuccess(e){
this.loading = false;
if (e === "OK") {
this.forminner = false;
this.success = trueelse {
this.showError(e);
}
},
showError(e) {
alert(e);
this.process = e;
this.redText = true},

restartForm() {
this.forminner = true;
this.success = false//         $('#success')    .hide();
this.progress = "";
this.redText = false},
createImage(files) {

var vm = this;
for (var index = 0index < files.lengthindex++) {
var name = files[index].name
var reader = new FileReader();
reader.onload = function(event) {
var imageUrl = event.target.result
vm.register.images.push({namenameimageimageUrl});
}
reader.readAsDataURL(files[index]);
}



},
submitForm(){
var vm = this;
vm.fun_submit()
vm.loading = true;
if(vm.register.pid){
google.script.run.withSuccessHandler(vm.showSuccess)
.update_value(vm.register.imagesvm.register.pidvm.register.name,
vm.register.emailvm.register.tel,
vm.register.positionvm.imgData);

}else{
google.script.run.withSuccessHandler(vm.showSuccess)
.uploadFileToGoogleDrive(vm.register.imagesvm.register.name,
vm.register.emailvm.register.tel,
vm.register.positionvm.imgData);
}

},

}

})
const NotFoundPage = {
name'NotFoundPage',
template'#NotFoundPage'
}
const HomePage = {
name'HomePage',
template'#HomePage',
data(){
return{
list:[],
loadingfalse,
}
},
mounted(){
this.loading = true
google.script.run.withSuccessHandler(this.getData).read_value()
},
methods:{
onDelete(itemindex){
console.log(itemindex)
google.script.run.withSuccessHandler((e) => { console.log(e)})
.delete_value(item.piditem.name,
item.emailitem.tel,
item.position);
},
onEdit(ev){
var vm = this;
new Promise( (resolve,reject) => {
item = ev
resolve(ev);
}).then( (val) => {
this.$router.push({ name'register'paramsev})
});

},
getData(value){
this.loading = false;
var sheet = JSON.parse(value)
var rows = sheet[0]
for (var r = 1l = sheet.lengthr < lr++) {

var record  = {};
for (var p in rows) {
record[rows[p]] = sheet[r][p] ;
}
this.list.push(record);
}
}
}
}
const routes = [
path'/'componentHomePage },
path'/register/:id?'name'register'componentRegisterPage },
path'*'componentNotFoundPage },
]

const router = new VueRouter({
routes})






new Vue({
router,
data(){
return{
}
},
mounted(){

},

}).$mount("#app")
  </script>

</body>

</html>

1 comment:

Lessons from 1,000+ YC startups: Pivoting, resilience, avoiding tar pit ideas, more | Dalton Caldwell (Y Combinato…

Dalton is Managing Director and Group Partner at Y Combinator. He’s advised more than 35 YC unicorns, including DoorDash, Amplitude, Webflow...

Contact Form

Name

Email *

Message *