I'm having a really hard time sending an automated email (with Google Apps Script) that includes a URL that contains query parameter.
Expected Behavior
Google Apps Script (specifically, the Gmail service) sends an email, and part of the email body contains a URL with a query parameter. The URL will look something like this:
http://my.app/products?id=Bz9n7PJLg8hufTj11gMF
Observed Behavior
The Gmail service seems to be stripping out the = from my URL. So, the body of the email ends up looking like this:
...
http://my.app/products?idBz9n7PJLg8hufTj11gMF
...
Obviously, that link won't work.
I've checked other questions here on SO, and I've tried working with the base encoding tools from the GAS Utilities service, as well as working with the encodeURI() JavaScript method. No luck so far.
Email-sending Code
//////// GENERATING MESSAGE FROM ID ////////////
// Gets message from ID
var id = Gmail.Users.Drafts.get('me', 'r-1006091711303067868').message.id
var message = GmailApp.getMessageById(id)
var template = message.getRawContent()
// Replaces template variables with custom ones for the user using RegExes
let listingUrl = 'http://my.app/products?id=xyz'
let creatorEmail = 'hello#gmail.com'
let creatorUsername = 'Sam'
template = template.replace(/templates#my.app/g, creatorEmail)
template = template.replace(/firstName/g, creatorUsername)
//** Below is the string that gets modified and broken **//
template = template.replace(/listingUrl/g, listingUrl)
// Creates the new message
var message = Gmail.newMessage()
var encodedMsg = Utilities.base64EncodeWebSafe(template)
message.raw = encodedMsg
// Sends it
Gmail.Users.Messages.send(message, "me", Utilities.newBlob(template, "message/rfc822"))
Regex-based Solution
With the help of Tanaike and Rafa Guillermo, the solution that ended up working for me was to replace = with = by using a little .replace() like this:
listingUrl = listingUrl.replace(/=/, '=')
Related
I have the below function trying to convert a webpage to xml, so I can start extracting out some data from tables etc.
function getWebpageContent() {
var url = "https://training.gov.au/Training/Details/BSBCRT501";
var xml = UrlFetchApp.fetch(url).getContentText();
var document = XmlService.parse(xml);
Logger.log(document);
}
I'm recieving this error:
Exception: Error on line 170: The entity name must immediately follow the '&' in the entity reference.
getWebpageContent # Code.gs:6
When I search that webpage for the "&" symbol, (assuming that XmlService is confusing the 'and' symbol for some sort of html code and throwing an error) I can only find one hidden one. And am not sure how to circumvent it.
Any way to dodge that error and get the webpage info as Xml in Apps Script?
From your following replying,
The output I want from this page (https://training.gov.au/Training/Details/BSBCRT501) is each in the 'Elements and Performance Criteria' table. I want to save it as an array to then reformat into my spreadsheet. I might just use IMPORTXML in a spreadsheet formula instead.
In this case, how about the following formula?
Sample formula:
=IMPORTXML("https://training.gov.au/Training/Details/BSBCRT501","//table[2]//tr")
Result:
Reference:
IMPORTXML
Added:
From your following replying,
That's a great answer thanks and the method I think I'll use. It doesn't link break at each point (2.1, 2.2 etc) unfortunately but it's still good. I don't think I can accept it as the answer though as it doesn't solve the specific Apps Script problem, but thanks a lot for this.
I added a sample script for using Google Apps Script. Could you please confirm it?
Sample script:
Before you use this script, please enable Sheets API at Advanced Google services. When you run this script, the table is put to the active sheet.
function myFunction() {
const url = "https://training.gov.au/Training/Details/BSBCRT501";
const res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
if (res.getResponseCode() != 200) throw new Error(res.getContentText());
const table = [...res.getContentText().matchAll(/<TABLE[\s\S\w]+?<\/TABLE>/g)];
if (table && table[1][0]) {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getActiveSheet();
const resource = {requests: [{pasteData: {html: true, data: table[1][0], coordinate: {sheetId: sheet.getSheetId()}}}]};
Sheets.Spreadsheets.batchUpdate(resource, spreadsheet.getId());
}
}
References:
Class UrlFetchApp
Method: spreadsheets.batchUpdate
This guy here explains the underlying cause, and also gives the solution:
The entity name must immediately follow the '&' in the entity reference
I have a project where i need to bulk send an email with text specific to each user, and with the signature and image from the associated gmail account. I've got this atm:
//get template/draft email & body
const scGmailTemplate = GmailApp.getDraft("r4329894329375089160");
const scGmailTemplateMessage = scGmailTemplate.getMessage();
const scGmailTemplateBody = scGmailTemplateMessage.getBody();
//create new gmail
let scGmailContactMessageBody = scGmailTemplateBody;
//create array for gmail find & replaces [find, replace]
const gmailFindReplace = [["INV_START", scVars.INV_START],
["INV_END", scVars.INV_END],
["DEM_DATE", scVars.DEM_DATE]
];
gmailFindReplace.forEach(x=>{scGmailContactMessageBody=scGmailContactMessageBody.replace(x[0], x[1])});
const scGmailSubject = "Service Charge Invoice ("+scVars.INV_START+"-"+scVars.INV_END+")";
let bodyHtml = HtmlService.createHtmlOutput(scGmailContactMessageBody);//didnt work
GmailApp.sendEmail("me#me.com", "test", scGmailContactMessageBody);
Issue is the resultant email is just the raw html with the image displayed at the bottom
I've tried adding {htmlBody: html} as an option but that throws an error html not defined.
Any help would be much appreciated
The htmlBody expects a string.
GmailApp.sendEmail("me#me.com", "test", scGmailContactMessageBody, {htmlBody: scGmailContactMessageBody});
No need for let bodyHtml = HtmlService.createHtmlOutput(scGmailContactMessageBody); as getBody() already returns an HTML string.
From the question
I've tried adding {htmlBody: html} as an option but that throws an error html not defined.
The error message is straight forward, the code doesn't include an statement declaring html
try replacing html by bodyHtml
I am collecting data from a user on Server A,
I need to send that data in a URL to server B (separate buildings and companies)
On server A it is a CRM system which is pre built and I cannot just simply use PARAMETERs as I cannot HASH the PARAMETERs as the system is pre built by a third party and they would charge to allow for this.
So I have managed to build some JS that replaces certain characters from the PARAMETERs I can collect.
Here is a small snippet of what I have to make my HASH.
<script type="text/javascript">
// Collect USERID
var m = 'XX784188';
// HASH USERID
m = m.replace(/7/g, 'M');
m = m.replace(/4/g, 'S');
// Set up Object n as Location name.
var n = 'Cumbria';
// Rename Location to correct code
n = n.replace(/[Cumbria]/g, '01');
// Test Object m & n
alert(n);
alert(m);
Here is the above in a test.
Now what I cannot seem to find out is how do I insert the results into a url and redirect the user to that URL.
For example:http://google.com/?n=&m=
I can insert this line I know for the redirect:
window.location = "http://google.com/?n=&m="
I just need to know how I make that URL look like this google.com/?n=01&m=XXM8S188
Funny, I just answered the same thing 1min ago :
window.location = "http://google.com/?n="+n+"&m="+m
Your snippet code and JS Fiddle code are different.
For snippet code then you simply insert your values like,
window.location = "http://google.com/?n="+n+"&m="+m;
For JSFiddle code, then,
window.location = "http://google.com/?"+serialiseObject(obj);
Ok, so I'm learning web design as a co-op at a company. However, the department I'm in is lacking in knowledgeable people in web design. So here we go...
Building a site that will allow the department to manage PTO. I want to implement ajax b/c the main page will have a calendar system so the manager can view the PTO week by week. As a precursor to that, I'm attempting to implement ajax with the "add Employee" page for practice.
However, I can't seem to figure out what I'm missing (aka, why it's not doing anything)
This page just needs to add the new employee to the database. No display needed.
The main page just has 4 text fields and I get the information from those fields in javascript like so
var firstName = document.getElementById("firstNameField");
var lastName = document.getElementById("lastNameField");
var manager = document.getElementById("managerField");
var networkID = document.getElementById("networkIDField");
Simple enough so far.
So I set up the ajax code like so, (this is gathered from what I've read.
var url = "addEmpJSP.jsp?firstNameField=" + escape(firstName)+"&lastNameField="+escape(lastName)+"&managerField="+escape(manager)+"&networkIDField="+escape(networkID);
xmlhttp.open("POST",url,true);
xmlhttp.onreadystatechange=dummy;
xmlhttp.send(null);
This is the part where I'm assuming it's correct as I'm still learning ajax and how it works. I don't think I need to handle a response as I simply want the called jsp file to automatically do whats needed. (if that's possible).
The jsp file looks like this
<%
ResultSet rsEmpl;
Connection connection1 = getDBConnection();
Statement statment1=connection1.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
String fName = request.getParameter("firstNameField");
String lName = request.getParameter("lastNameField");
String manager = request.getParameter("managerField");
String networkID = request.getParameter("networkIDField");
Int empId = 0;
String EditEmplSQL = "select * from PTO_employee";
rsEmpl=statment1.executeQuery(EditEmplSQL);
rsEmpl.last();
empId = rsEmpl.getRow() - 1;
statement1.execute("INSERT INTO PTO_employee VALUES ("+empID+","+lName+","+fName+","+0+","+2+","+networkID);
%>
I have a button on the page that executes the javascript function that contains the ajax info. I'm avoiding jquery atm b/c I'm trying to understand this stuff and how it works before I attempt to use "shortcuts" like jquery. I'm working towards a degree in Software Engineering so understanding this stuff is my priority, not getting it done.(that's just a bonus) If you need anymore information I can provide it. Sorry for my lack of knowledge and if this is completely off base then :(
The main page just has 4 text fields and I get the information from those fields in javascript like so
var firstName = document.getElementById("firstNameField");
var lastName = document.getElementById("lastNameField");
var manager = document.getElementById("managerField");
var networkID = document.getElementById("networkIDField");
That gives you whole HTML DOM elements back, not the values of those elements. HTML DOM elements are like Java classes, having properties, methods and so on. Assuming that it are HTML input elements like <input>, then use their value property instead to get the value. So:
var firstName = document.getElementById("firstNameField").value;
var lastName = document.getElementById("lastNameField").value;
var manager = document.getElementById("managerField").value;
var networkID = document.getElementById("networkIDField").value;
So I set up the ajax code like so, (this is gathered from what I've read.
var url = "addEmpJSP.jsp?firstNameField=" + escape(firstName)+"&lastNameField="+escape(lastName)+"&managerField="+escape(manager)+"&networkIDField="+escape(networkID);
xmlhttp.open("POST",url,true);
xmlhttp.onreadystatechange=dummy;
xmlhttp.send(null);
The escape() is the wrong function. It escapes JS syntax, it does not encode URI components. You should be using encodeURIComponent() function instead.
The jsp file looks like this
...
Int empId = 0;
...
This doesn't compile. It should be int instead.
...
String EditEmplSQL = "select * from PTO_employee";
rsEmpl=statment1.executeQuery(EditEmplSQL);
rsEmpl.last();
empId = rsEmpl.getRow() - 1;
...
Unnecessarily overcomplicated. Learn how to use DB builtin sequences/autoincrement IDs. Refer the DB specific manual or ask DB admin for help.
...
statement1.execute("INSERT INTO PTO_employee VALUES ("+empID+","+lName+","+fName+","+0+","+2+","+networkID);
...
You should put quotes around string values in the SQL query. Assuming that lName, fName and networkID are strings, not numbers, then it should look like this:
statement1.execute("INSERT INTO PTO_employee VALUES (" + empID + ",'" + lName + "','" + fName + "'," + 0 + "," + 2 + ",'" + networkID + "'");
But you have there a huge SQL injection attack hole and you also don't seem to close DB resources at all after use, so they may leak away and cause your webapp to crash sooner or later because the DB runs out of resources. Use PreparedStatement to create a parameterized SQL query and use its setters to set the values. Close the resources in finally block.
After all, reading the server logs should provide you information about compile errors and any server exceptions. Reading the ajax response should provide you information about the response status and body. Your core problem was that you ignored it and thus didn't have any chance to understand what is happening.
iam using a simple insert script function to pass the values from registration html page to register php page.
Here is my script:
function insert() {
// Optional: Show a waiting message in the layer with ID login_response
document.getElementById('insert_response').innerHTML = "Just a second..."
// Required: verify that all fileds is not empty. Use encodeURI() to solve some issues about character encoding.
var dispname= encodeURI(document.getElementById('disp_name').value);
var firstname= (document.getElementById('first_name').value);
var lastname= (document.getElementById('last_name').value);
var gender= (document.getElementById('genderreg').value);
var day= (document.getElementById('day').value);
var month= (document.getElementById('month').value);
var year= (document.getElementById('year').value);
var country= (document.getElementById('countryreg').value);
var city= (document.getElementById('cityreg').value);
var suburb= (document.getElementById('suburbreg').value);
var email= (document.getElementById('emailreg').value);
var password= (document.getElementById('regpassword').value);
var code= (document.getElementById('code').value);
var service= (document.getElementById('termservice').value);
// Set te random number to add to URL request
nocache = Math.random();
// Pass the login variables like URL variable
http.open('get', 'register_insert.php?site_url='+dispname+'&fname= '+firstname+'&lname= '+lastname+'&gender= '+gender
+'&day= '+day+'&month= '+month+'&year= '+year+'&country= '+country+'&city= '+city+'&suburb= '+suburb
+'&email= '+email+'&password= '+password+'&code= '+code+'&service= '+service+'&nocache= '+nocache);
http.onreadystatechange = insertReply;
http.send(null);
}
I just have a small question that is it a good pratice of passing the password values like this from html to php page. If it is not good idea then what is the best why to do so.
Thanks in advance for sugesstions.
Yeah, ordinarily I wouldn't immediately suggest that someone go to a javascript library, but I make an exception for AJAX. Getting that sort of thing to work cross-browser is just plain not-worth-it. Go for jQuery and save yourself a heap of stress.
Also take a look at the jQuery Form Plugin - it'll do all this for you in a very easy way. The site is at http://malsup.com/jquery/form/ .
But to answer your question, I'd use POST data. The general rule of thumb is that if you're retrieving something, use GET, but if you're sending or changing something, use POST.
Another quick pointer is that the code could be made a lot more legible by doing something like this:
var fields = {'disp_name', 'first_name', 'last_name', 'genderreg' /* etc ... */ ];
var values = {};
for (var i = 0, l = fields.length; i < l; ++i) {
values[fields[i]] = document.getElementById(fields[i]).value;
}
http.open(
'get',
'register_insert.php'
+ '?site_url=' + values.dispname
+ '&fname=' + values.first_name
+ '&lname=' + values.last_name /* etc */
);
...but it's not really that important I suppose.
Sending The password in this manner is no different from a regular From Submission via GET. Generally however, you would use POST. The downside to using get is that the password will appear in the URL. If you care about security, you should be using SSL.
Consider using a JS library like jQuery... it would make what you are doing above.. very easy.