I have a project to create PDF out from google form and send multiple PDF through email in Google Script,
The relevant CODE segment is as follows ,
if(TF_Samples!= null){
//Generating Lab Reports for TAPE LIFT
for (var Sample_Number in TF_Samples) {
//Creating Report File - Word Version
Template_Doc = Tape_Template_Doc ;
Report_File_Word = Template_Doc.makeCopy(Report_Folder_Word);
Report_File_Word.setName(data[0]+"-TAPE LIFT");
Opened_Report_Doc = DocumentApp.openById(Report_File_Word.getId());
//Getting the Content of the Report & Replacing the Fields
Report_Body = Opened_Report_Doc.getBody();
Report_Body.replaceText("{{ Client Address }}",data[2]);
Report_Body.replaceText("{{ Sample Date }}",data[1]);
Report_Body.replaceText("{{ Sample Number }}",TF_Samples[Sample_Number]);
//Saving & Closing the Report
Opened_Report_Doc.saveAndClose();
//Creating PDF version of Report
Blob_PDF = Report_File_Word.getAs(MimeType.PDF);
Report_PDF_Version = Report_Folder_PDF.createFile(Blob_PDF).setName(data[0]+"-SPORE TRAPE");
}
}
I managed to send email with single attachment but since here I am looping through a for loop I don't know how to do that.
Create a global array and push within each loop iteration the file into the array
Sample modification:
...
var files = [];
for (var Sample_Number in TF_Samples) {
...
Report_PDF_Version = Report_Folder_PDF.createFile(Blob_PDF).setName(data[0]+"-SPORE TRAPE");
files.push(Report_PDF_Version);
}
GmailApp.sendEmail(recipient, subject, body, {attachments:files});
...
as specified in the documentation, the attachment argument must be an array of blobs.
So, instead of converting the blob to a drive file just send the blobs like this :
Blob_PDF = Report_File_Word.getAs(MimeType.PDF);
and push these blobs in an array one by one.
The argument will then be valid.
Related
I wonder if someone can help me with this as I'm new to JavaScript.
I have a form, and within it I have a file input. However, when a file is uploaded I need to manipulate it and add new properties. So I extract the file from the input like so:
let uploadedFile = [];
for (let val of this.files) {
uploadedFile.push(val)
}
for (val of uploadedFile){
//Check file extension to classify the file type:
let extension = val.name.split('.')
extension = String((extension[extension.length-1]).toLowerCase());
let fileType = val.type;
//Arrays to hold extensions types we want to identify
const imageExtensions = ['jpeg','jpg','png']
const documentExtensions = ['xls','xlsx','csv','pdf']
//Classify files and push to global files array
if (imageExtensions.includes(extension)){
let newFile = new File([val], `${crname}-nr-${FileCount}.${extension}`, {type: `${fileType}`});
newFile.originalName = `${val.name}`
globalFilesArray.push(newFile);
}
}
So as you can see the new file objects are pushed to an array named 'globalFilesArray'
Now in Flask i'm typically accessing the form data using the request module.
Must I now pass the array into a separate hidden file input so I can access it when the entire form is submitted?
I'm a bit stumped with this because I need this to happen only when the form is submitted so I can also access the rest of the input fields in the form.
I need to parse a PDF file with a lot of Acroform fields and extract the field name (/T in PDF markup) and value (/V in PDF markup) for each field. I'd like to do this client side in JavaScript but I'm not finding existing examples or libraries that seem to do this. Does any one have any suggestions on how to approach it?
Edit: I want to do this client side for performance reasons. I will be taking the field values from the PDF and saving them to a database. But for some of the fields I will need to prompt for additional input before saving. I'd like to do that all on the client and then send the values to the server for saving instead of uploading the PDF to the server, parsing it on the server, sending the fields that need additional input back to the client, and then sending those results to be saved.
The company I work for has PDFTron WebViewer SDK which can you read through all the form fields, read/edit the values, all client side in the browser, and you can also allow the user to view the entire PDF and manually fill in any fields.
Sample: https://www.pdftron.com/webviewer/demo/pdf-forms
I need to parse a PDF file with a lot of Acroform fields and extract the field name (/T in PDF markup) and value (/V in PDF markup) for each field.
You can see live code in the pdf-forms sample linked above, but this code below would iterate all fields and print the value, if any, to the console.
viewerElement.addEventListener('ready', function() {
var viewerInstance = viewer.getInstance();
const docViewer = viewerInstance.docViewer;
const annotManager = docViewer.getAnnotationManager();
const fieldManager = annotManager.getFieldManager();
const checkField = (field) => {
// Do something with data
const { name, value } = field;
console.log(name + ' ' + value);
// Check children fields
field.children.forEach(checkField);
}
docViewer.on('annotationsLoaded', function() {
fieldManager.forEachField(checkField);
});
});
I will be taking the field values from the PDF and saving them to a
database.
If you just want the field values, you can get that easily from WebViewer, in PDF ISO XFDF XML format, which you can send to your server and merge to the PDF using any PDF SDK that supports XFDF. This way, you can avoid transferring the entire PDF back and forth, and just send the much smaller form field values as XML.
var xmlXfdfFieldData = annotManager.exportAnnotations({fields:true, widgets:false, links:false});
for some of the fields I will need to prompt for additional input
before saving.
The WebViewer UI/UX is completely customizable so you can interact/prompt the user on which fields to update.
This sample below shows how to change color and opacity of form fields, which would be a starting point you can use to apply your own business logic.
https://www.pdftron.com/documentation/web/guides/form-samples#customizing-form-fields
It turns out not to be that hard to parse what I need out of the PDF and a library would probably be overkill. Here's the code I'm using:
function importPdf(event) {
const reader = new FileReader();
reader.onload = function () {
const matches = reader.result.match(/((obj)[\s\S]*?(endobj))/g);
matches.forEach(match => {
const tIndex = match.indexOf('/T (');
const vIndex = match.indexOf('/V (');
if(tIndex > -1 && vIndex > -1) {
const fieldNameSegment = match.substring(tIndex + 4);
const fieldNameValue = fieldNameSegment.substring(0,fieldNameSegment.indexOf(')'));
const valueSegment = match.substring(vIndex + 4);
const dataValue = valueSegment.substring(0, valueSegment.indexOf(')'));
console.log(fieldNameValue, dataValue);
}
});
}
reader.readAsText(event.target.files[0]);
}
<input type="file" id="file-input" onchange="importPdf(event)">
Could you help me do the task using JavaScript?
I have a task and if i do it manually it looks like this:
i create Saved Search in NetSuite.
Download the result of created saved search in csv.
The i put this file on ftp server, using FileZilla. (i had a connection with server previously: write a domain, username and password - that's all)
Now, a need it solve through sutlet script.
1. Create Saved Search - done
2. Create csv with result of saved search in content and put it in file cabinet in the NetSuite - done
3. Ok, now i have a needs me file but i do not understand how to pass it on ftp.
*i tried to study several articles, but frankly speaking could not to solve my problem. Moreover, their article seems about manually method not automative
this aritcle - https://ursuscode.com/netsuite-tips/suitescript-2-0-sftp-tool/*
var searchResult = Contacts.run().getRange(0,999);
log.debug('Contacts', searchResult);
var Header = 'INTERNAL ID' + ';' + 'FIRST NAME' + ';' + 'LAST NAME';
var Content = "";
for (var i = 0; i < searchResult.length; i++) {
var internalid = searchResult[i].getValue('internalid');
var FirstName = searchResult[i].getValue('firstname');
var LastName = searchResult[i].getValue('lastname');
Content = Content + internalid + ';'
+ FirstName + ';'
+ LastName;
Content = Content + '\n';
}
var fileObj = file.create({
name: 'test.csv',
fileType: file.Type.CSV,
contents: Header + '\n' + Content
});
fileObj.folder = 45434;
var fileId = fileObj.save();
var savedFileObj = file.load({
id: fileId
});
var myPwGuid = '';
var myHostKey = ''
var objConnection = sftp.createConnection({
username: '',
passwordGuid: myPwGuid,
url: 'ftp.expertsender.com',
hostKey: myHostKey
});
NetSuite does not support ftp, it only supports sftp.
NetSuite has the support for SFTP, FTP and SFTP runs in different port numbers, However FTP transfers data in plain text format which will compromise your security, using SFTP is the better option as it will transfer your data in encrypted format and security is assured.
In your example, I believe you're calling FTP request which will not work in this case.
Oki,
Now, the article you did mention is the right one : why ? Because the first required step to be able to use SFTP is to generate a GUID. You are talking about manual methods, well yes, including the one in that Article, but it is not a problem, because once you have generated the GUID, you don't need to change it, so it is a one time action, unless your ftp credential change.
So, first step : use "ursuscode" to create a Suitelet. Deploy that suitelet and use it to generate the GUID (it is a form where you need to set the ftp password, host...). Using the same form, you can then generate the HOST key (check the video).
Second step, use the Generated GUID and HOST Key in your code.
Third step, add the code to upload the file : from netsuite help page, here is an example:
connection.upload({
directory: 'relative/path/to/remote/dir',
filename: 'newFileNameOnServer.js',
file: myFileToUpload,
replaceExisting: true
});
By the way, you can upload the file without the need to Save and Reload it again (https://system.na2.netsuite.com/app/help/helpcenter.nl?fid=section_4617004932.html).
Note: remember that this is an SFTP, so probably support only SFTP not FTP.
Suggestion: About the GUID (and the other data needed for the connection), I suggest that you use a Script Parameter to provide the GUID to your script code, so if your password change, you can regenerate the GUID and update the script parameter value without the need to touch your code.
Hope this helps!
I am trying to validate a webhook with Podio (https://developers.podio.com/doc/hooks/validate-hook-verificated-215241) using google apps script.
Currently I have the following script successfully writing data to a document (after the Podio Post is activated):
function doPost(l) {
var doc = DocumentApp.openById('1to3-JzhE27-LK0Zw7hEsdYgiSd7xQq7jjp13m6YwRh0');
var jstring = Utilities.jsonStringify(l);
doc.appendParagraph(jstring);
}
With the data appearing as follows:
{"queryString":null,"parameter":{"hook_id":"38035","code":"a92e06a2","type":"hook.verify"},"contextPath":"","parameters":{"hook_id":["38035"],"code":["a92e06a2"],"type":["hook.verify"]},"contentLength":44}
For some reason, google apps script won't let me take this data and access the properties like this:
jstring.parameter.code;
If I copy the (seemingly) JSON string into a separate script under a new variable, I can then access the data within the JSON.
What am I doing wrong here?
It looks like you have a JavaScript object that you convert to a JSON string, jstring. It is just a string. If you want to access the properties represented in the string, use the original object, l. Ie, l.parameter.code
function doPost(l) {
var doc = DocumentApp.openById('1to3-JzhE27-LK0Zw7hEsdYgiSd7xQq7jjp13m6YwRh0');
var jstring = Utilities.jsonStringify(l);
doc.appendParagraph(jstring);
dosomething(l.parameter.code);
}
This is a followup to a question I asked yesterday on the Google Apps Script Office Hours Hangout.
The goal of my final script is to create an election process for student elections at the high school where I work using Google Forms. The script has three parts: 1) Create Unique "Voting IDs" (a random 6-digit code) 2) Merge the student data (Name, Homeroom, & Voting ID) on with a template document that will create specific voting instruction for each student. (i.e. an old-fashioned mail merge) 3) Verify the results by checking Voting ID's and removing duplicate votes.
The part of the script that I am having trouble with is the student data merge (step 2). The first dataset is the only one that works. The rest show up as "DocumentBodySection". I have a feeling it is either how I am copying the text from the Document Template or how I am adding the text to the new document.
Spreadsheet w/ Data: https://docs.google.com/spreadsheet/ccc?key=0AierVcXWELCudFI1LU10RnlIVHNsUm11a0dDWEV6M1E
Document Template: (see followup comment for url)
Document Created by Script: https://docs.google.com/document/d/12r2D9SpIVmQYVaasMyMWKjHz6q-ZZyIMEBGHTwlQct8/edit
//Get Settings & Data
ss = SpreadsheetApp.getActiveSpreadsheet();
source_sheet = ss.getSheetByName("Student Data");
settings_sheet = ss.getSheetByName("SETTINGS");
results_column = settings_sheet.getRange("B19").getValue();
source_column = settings_sheet.getRange("B18").getValue();
source_lastrow = source_sheet.getLastRow();
docTemplateID = settings_sheet.getRange("B13").getValue();
docCopyName = settings_sheet.getRange("B14").getValue();
//Merge Student Data with Document
function SendDataMerge () {
// Open docTemplate and Copy Contents to entryTemplate
var docTemplate = DocumentApp.openById(docTemplateID);
var entryTemplate = docTemplate.getActiveSection();
docTemplate.saveAndClose();
// Make a NEW copy of docTemplate
var docTemplate = DocsList.getFileById(docTemplateID);
var docCopy = DocsList.copy(docTemplate, docCopyName);
var docCopyID = docCopy.getId();
// Create Array of Student Data (First, Last, Grouping, VID)
var data = source_sheet.getRange("A2:D"+source_lastrow).getValues();
// Open docCopy for Editing & Clear Contents
var doc = DocumentApp.openById(docCopyID);
var docText = doc.editAsText();
// Run through Student Data
for(var i=0; i<5 /*data.length*/; i++) { //For testing, limit this to 5 entries
var lastName = data[i][0];
var firstName = data[i][1];
var grouping = data[i][2];
var vid = data[i][3];
docText.replaceText('keyLastName', lastName);
docText.replaceText('keyFirstName', firstName);
docText.replaceText('keyGrouping', grouping);
docText.replaceText('keyVID', vid);
docText.appendText('\n*** Appended Text (End of entry) ***');
docText.appendText(entryTemplate);
}
// Save and Close
doc.saveAndClose();
}
I worked around this issue by creating a copy of the template, doing the text replacement and then appending the template elements from the original document into the copy. In particular, I used: var copyTables = templateDoc.getTables(); to fetch and store the tables (as all of my template data was contained in a table) and copyDoc.appendTable(copyTables[0].copy() ); to append the copy (the .copy() at the end seems to work the real magic). This provides the flexibility of updating the template in the friendly Documents interface without having to see a programmer.
I think the problem is with this line:
docText.appendText(entryTemplate);
The variable entryTemplate holds a DocumentBodySection, which is why you are seeing that in the output. If you are trying to append another copy of the original template text you'll need to store that before you enter the loop.
I agree with Eric that appendText(entryTemplate) isn't going to do what you want it to do.
Since you're trying to create one large document with all the students, using a "template" and replacing the text isn't going to work well. I'd suggest instead, creating the "template" in code using the api calls that produce the formatting you want. Then it makes it simple to keep appending new pages of student instructions. Although I think you may run into slowness when the document gets large... I don't know how many students you have.