I am trying to setup an email notification and my hope is to have a simple list of all the items in a datasource (separated into their fields).
Ex:
ItemName01, Cost01, Quantity01
ItemName02, Cost02, Quantity02
ItemName03, Cost03, Quantity03
Doing a projection of each (#datasources.Datasource.items..ItemName + #datasources.Datasource.items..Cost + #datasources.Datasource.items..Quantity) gives me everything, but not organized correctly.
Ex. [ItemName01,ItemName02,ItemName03],[Cost01,Cost02,Cost03],[Quantity01,Quantity02,Quantity03]
Any help/thoughts are appreciated.
Thanks!
I would recommend to use server script for this:
// query records
var records = app.models.Item.newQuery().run();
// generate email HTML body
var emailBody = records.reduce(function(str, item) {
str += '<p>' + item.Name + ', ' + item.Cost + ', ' + item.Quantity + '</p>'
});
// hand off generated HTML to other function
// that will actually send email
sendEmail(emailBody);
You can call this Server Script from Model Events or explicitly from client using google.script.run. You also can pass some filters to narrow records set to be sent.
Related
I have button with ID on the website.
At the moment we track button click with Tag Manager and Analytics.
But I would like to know, is there a way to save information about user ID and time when button was clicked?
Looking forward for any information.
Thank you!
I guess user ID, what you mentioned is internal ID in your website. First of all, you need to find a way how to receive user ID in your system. It can be many ways.
For example, let's say that your website has hidden input with user ID which has id="user-id" and you want to track button "Track this button". Basic html code if your page is:
<input type="hidden" id="user-id" value="unqieuserid123"/>
<button id="button-id">Track this button</button>
You need to create user defined variable (Variables->User-Defined Variables->New). Choose "Javascript" and enter name "UserId" and this code:
function () {
return document.getElementById("user-id") != null ?
document.getElementById("user-id").value : ""
}
At the end it should be like that:
Create variable for receiving user time (it will write user local time) with name "UserTime" and javascript code:
function() {
// Get local time as ISO string with offset at the end
var now = new Date();
var tzo = -now.getTimezoneOffset();
var dif = tzo >= 0 ? '+' : '-';
var pad = function(num) {
var norm = Math.abs(Math.floor(num));
return (norm < 10 ? '0' : '') + norm;
};
return now.getFullYear()
+ '-' + pad(now.getMonth()+1)
+ '-' + pad(now.getDate())
+ 'T' + pad(now.getHours())
+ ':' + pad(now.getMinutes())
+ ':' + pad(now.getSeconds())
+ '.' + pad(now.getMilliseconds())
+ dif + pad(tzo / 60)
+ ':' + pad(tzo % 60);
}
At the end it should be like that:
Enable built-in variable Click Element if it is not enabled yet.
Create new trigger, for your button. In my case it is like that:
Create new tag for tracking and sending data to GA. Here you can use your variables created on step #1 and #2. Like {{UserId}} and {{UserTime}}. In my example it is like that:
At the end in Google Analytics you will receive event with this label:
'user id: unqieuserid123; user time: 2017-05-31T12:20:19.06+04:00'
I solved this problem in the following way:
- Created variables in Google Tag Manager (GTM) which track the user ID and the date & time.
I already had tags which tracked button clicks on the website, so I added these variables in the categories of Tags ("Label" and "Value")
Click event
Every time user click on the button, these variables track information about ID and date&time.
So I have a Status Dropdown on a fragment page (used to edit database entries) that I want to send an email notification when the entry changes from "Pending" to "Ready to Build".
I am using the following code:
var widgets = widget.parent.descendants;
var to = 'notifications#xxx.com';
var subject = 'New System Order: ' + widgets.ProjectName.value;
var msg = "A new order for [ " + widgets.ProjectName.value + " ] has been created for [ " + widgets.UsersPosition.value + " ]";
sendMessage(to, subject, msg);
You can see that I also have it pulling the Project Name/User Position in the subject/body of the email. These are Text Boxes on that Fragment page displaying the information from the entry.
All of these works great and exactly as desired when the Dropdown and the Text Boxes are all in the same panel. However, when I separate them into separate panels (for aesthetics) the system cannot find widgets.ProjectName.value or widgets.UsersPosition.value.
I'm assuming I just need to adjust the var widgets = widget.parent.descendants; line, but I don't know to what.
Any help would be greatly appreciated. Thank you.
So it looks like I just need to change widget.parent.descendants; to widget.root.descendants;
I would recommend you to use Model events instead.
App Maker will fire the 'onSave' event every time a record is modified.
Go to the 'Events' tab in the model editor, and add something like:
if (oldRecord.State == "Pending" && record.State == 'Ready to Build') {
sendEmail_();
}
You can learn more about model events here.
Regards,
Julian.-
I have created a script to go along with a camp signup form. When I student signs up, I need an email sent to both the parent and the leader. I have the general script format correct as each one works when by itself (the script is bound to the spreadsheet, not the form). But if I have both functions in the code.gs file or in a separate file, neither of them work. How are we supposed to run more than one function attached to a form submission?
Here is my code for the two emails to be sent:
Here is my code for the two emails to be sent (if I add both these, it doesn't work):
function emailResponseToParent(e) {
var studentName = e.values[1];
var parentEmail = e.values[3];
var tripName = e.values[6]
var subject = "Your Young Life " + tripName + " registration has been submitted."
var message = "We have received " + studentName + "'s signup registration for the " + tripName + ". More words here....";
MailApp.sendEmail(parentEmail, subject, message);
}
function emailResponseToLeader(e) {
var studentName = e.values[1];
var leaderEmail = "meg#me.com";
var tripName = e.values[6]
var subject = "A Young Life student of yours has signed up for " + tripName + " camp."
var message = studentName + "has just registered for " + tripName + ". More words here....";
MailApp.sendEmail(leaderEmail, subject, message);
}
A separate question (less important) is how to create a script so that a later follow up email goes out based on a date (e.g. the week before camp starts).
I don't know if this will be of any use but you can create a list of recipients to receive the SAME email (just like any email can specify multiple recipients). To do this replace your MailApp with the following:
var Emails = parentEmail + "," + leaderEmail;
MailApp.sendEmail(Emails, subject, message);
It has worked for me (but both recipients wanted the same info).
I couldn't get my script to access the form data until I came across the method used in your script (e.values[ ] ) which worked a treat - so thanks for that!
I am using Firebase to develop an HTML5 mobile messaging app. I encountered an issue that I am unable to resolve. The app has multiple channels (chat rooms). When a message is added for the first time to a channel it works as expected but when I go to a different channel and post an new message to that channel then I return to the previous channel and post another message I get duplicates of the last posted message. When I reload the page the duplicates are gone but I'd prefer not to have duplicates showing at all. Below is my code:
function loadChatMessages(channelID) {
$('#chatMessages').html('');
var msgObj = {};
var channelRef = globals.channelsBase + '/' + channelID + '/messages';
var channelMessages = new Firebase(channelRef);
channelMessages.on('child_added', function (snapshot) {
msgObj = snapshot.val();
var id = snapshot.name().toString();
var messageTime = application.Functions.renderTime(msgObj.messageTime);
var tails = '<div class="message-tails-wrap"><div class="message-tails"></div></div>';
var html = '<li class="chatEl ' + sentByClass + '" id="'+id+'">';
html += tails;
html += msgObj.message;
html += '<span class="sender"> ' + by + ' </span> <span class="tmp-recipient"> ' + msgObj.recipient + ' </span>';
html += '<span class="time-stamp msg-time" >';
html += messageTime;
html += '</span></li>';
$(html).appendTo('#chatMessages');
/// TODO: TEMP solution!
var prevID = 0;
$('#chatMessages li').each(function(n) {
var id = $(this).attr('id');
if(id == prevID){
// console.log(id + ' is a duplicate. Remove it');
this.remove(); // the necessary evil....
}
prevID = id;
});
});
}
It sounds like you run loadChatMessages() every time you enter a room, and as your users move around rooms, you're seeing duplicate calls to your on('child_added' callback.
This is because you're adding a new callback every time you re-enter a room. To resolve this, do some clean up when your users leave a room. In that clean up function, make sure you remove the old listener using .off("child_added").
The event :
YOURREF.limitToLast(1).on('child_added', function (OBJECTADDED){
console.log(OBJECTADDED.key(), OBJECTADDED.val());
});
You may think the above code will e fired one time, this is not true. The above console.log will be fired twice, once with OBJECTADDED as the last object in the reference YOURREF before adding the new one, and another with the new one.
General question: in Meteor, what's the best way to implement business logic that triggers whenever a model is updated -- e.g., for updating dependent fields or validations or...
Specific example: I'd like to add a "slug" field to Lists collection in the Meteor todos example. The slug needs to automatically update whenever a list's name is changed.
Here's what I've got... I'm observing every change to a list to see if its slug needs to be created/updated. This is in a shared models.js (runs server and client-side, to get the benefits of latency compensation):
// Lists -- {name: String}
Lists = new Meteor.Collection("lists");
var listsObserver = Lists.find().observe({
added: updateSlug,
changed: updateSlug
});
function updateSlug(doc, idx) {
var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + doc.name + "' to " + slug);
Lists.update(doc._id, {$set: {slug: slug}});
}
}
(And as in the original todos example, server/publish.js publishes all of Lists.find() as "lists", and client/todos.js subscribes to that collection.)
The code above seems to work, but somehow doesn't look quite right to me. Questions:
Is observing the Lists collection like this a reasonable approach? It seems like
it could be inefficient -- any change to a Lists document will trigger this code.
Should I be doing a different (simulated) update client-side, or is it OK to let
this same Mongo/Minimongo update run on both?
Do I need to call listsObserver.stop() at some point to dispose the observer?
And if so, when?
(I'm just getting started with Meteor, so perhaps my biases from other environments are leaking through. The implied meta-question here is, am I even thinking about this problem in the right way?)
I would suggest using the Collection-Hooks package. It extends the collection operations with before and after hooks. This is better than having a lot of collection Observes or ObserveChanges, especially on the server where the overhead for collection observes can get very large.
This works on both the client and the server. If you implement it on the client you will get the benefit of updating the local collection (latency compensation) and the change will be pushed to the server so no need to do it again.
You also get the benefit of only doing one MongoDB operation instead of two or more like you would with observes or observeChanges.
You might use it like so:
var beforeInsertSlug = function(userId, doc) {
var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + doc.name + "' to " + slug);
doc.slug = slug;
}
};
var beforeUpdateSlug = function(userId, doc, fieldNames, modifier, options){
if(modifier && modifier.$set && modifier.$set.doc && _.isString(modifier.$set.doc.name)){
var slug = (modifier.$set.doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + modifier.$set.doc.name + "' to " + slug);
modifier.$set.doc.slug = slug;
}
}
};
Lists.before.insert(beforeInsertSlug);
Lists.before.update(beforeUpdateSlug);
You can find the package here: https://atmospherejs.com/matb33/collection-hooks
I did a similar thing in server code. Basically put this code in Meteor.methods(), along with any other checks and updates you want making to the Lists Collection.
Although the code below looks a bit messy, and certainly hard to understand with the line starting with var slug:
Meteor.methods({
myupdate: function (doc) {
var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + doc.name + "' to " + slug);
Lists.update(doc._id, {$set: {slug: slug}});
}
}
});
One way to implement this is to define a custom template function and trigger it in the template that is changing. For example:
In client.js
Template.myTemplate.custom_function_to_update = function() {
// do my update code. i.e. MyCollections.Update(...);
}
In the html file with the template
<template name="myTemplate">
<!-- Normal template code -->
{{ custom_function_to_update }}
</template>
and every time the template "myTemplate" updates, it will call your method.