Nicolas Garnier
8 years ago
11 changed files with 399 additions and 48 deletions
@ -0,0 +1,44 @@ |
|||
# Send Confirmation Emails with Firebase Cloud Functions |
|||
|
|||
This template shows how to send a confirmation emails to users who are subscribing/un-subscribing to a newsletter. |
|||
|
|||
|
|||
## Cloud Function Code |
|||
|
|||
See file [index.js](index.js) for the moderation code. |
|||
|
|||
Sending emails is performed using [nodemailer](https://www.npmjs.com/package/bad-words) a node based Email client with comprehensive EMail server setup. In this sample we're showing how to send email through SMTP using a Gmail account. |
|||
|
|||
The dependencies are listed in [package.json](package.json). |
|||
|
|||
|
|||
## Sample Database Structure |
|||
|
|||
When a signed-in user subscribes or unsubscribes to the mailing list we change the `subscribedToMailingList` boolean: |
|||
|
|||
``` |
|||
/functions-project-12345 |
|||
/users |
|||
/$uid |
|||
subscribedToMailingList: true, |
|||
email: "user@domain.com" |
|||
``` |
|||
|
|||
Then the email stored here is used |
|||
|
|||
|
|||
## Trigger rules |
|||
|
|||
The function triggers on changes to `/users/$uid` and exits if there are no changes to `subscribedToMailingList`. |
|||
|
|||
|
|||
## Deploy and test |
|||
|
|||
This sample comes with a web-based UI for testing the function. To test it out: |
|||
|
|||
- Create a Firebase Project using the Firebase Developer Console |
|||
- Enable Google Provider in the Auth section |
|||
- Import and configure Firebase in the `index.html` where the `TODO` is located |
|||
- Setup your email transport in the `functions/index.html` where the `TODO` is located |
|||
- Deploy your project using `firebase deploy` |
|||
- Open the app, Sign in using Google Sign-In and subscribe/unsubscribe to the newsletter and you should receive email confirmations |
@ -0,0 +1,10 @@ |
|||
{ |
|||
"rules": { |
|||
"users": { |
|||
"$uid": { |
|||
".read": "auth.uid === $uid", |
|||
".write": "auth.uid === $uid" |
|||
} |
|||
} |
|||
} |
|||
} |
After Width: | Height: | Size: 3.5 KiB |
@ -0,0 +1,8 @@ |
|||
{ |
|||
"database": { |
|||
"rules": "database.rules.json" |
|||
}, |
|||
"hosting": { |
|||
"public": "./" |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright 2015 Google Inc. All Rights Reserved. |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
'use strict'; |
|||
|
|||
var functions = require('firebase-functions'); |
|||
var nodemailer = require('nodemailer'); |
|||
|
|||
// Configure the email transport using the default SMTP transport and a GMail account.
|
|||
// See: https://nodemailer.com/
|
|||
// For other types of transports (Amazon SES, Sendgrid...) see https://nodemailer.com/2-0-0-beta/setup-transporter/
|
|||
// TODO(DEVELOPER): Configure your email transport below. For GMail replace the <USER> and <PASSWORD> placeholders.
|
|||
var mailTransport = nodemailer.createTransport('smtps://<USER>:<PASSWORD>@smtp.gmail.com'); |
|||
|
|||
// Sends an email confirmation when a user changes his mailing list subscription.
|
|||
exports.sendEmailConfirmation = functions.database().path('/users/{uid}').on('value', function(event) { |
|||
var data = event.data; |
|||
var val = data.val(); |
|||
|
|||
if (!data.changed('subscribedToMailingList')) { |
|||
return; |
|||
} |
|||
|
|||
var mailOptions = { |
|||
from: '"Spammy Corp." <noreply@firebase.com>', |
|||
to: val.email |
|||
}; |
|||
|
|||
// The user just subscribed to our newsletter.
|
|||
if (val.subscribedToMailingList) { |
|||
mailOptions.subject = 'Thanks for subscribing to our newsletter'; |
|||
mailOptions.text = 'I will now spam you forever muahahahahah!!!'; |
|||
return mailTransport.sendMail(mailOptions).then(function() { |
|||
console.log('New subscription confirmation email sent to: ' + val.email); |
|||
}); |
|||
} |
|||
|
|||
// The user unsubscribed to the newsletter.
|
|||
mailOptions.subject = 'Sad to see you go :`('; |
|||
mailOptions.text = 'I hereby confirm that I will stop the spamming.'; |
|||
return mailTransport.sendMail(mailOptions).then(function() { |
|||
console.log('New unsubscription confirmation email sent to: ' + val.email); |
|||
}); |
|||
}); |
@ -0,0 +1,9 @@ |
|||
{ |
|||
"name": "functions", |
|||
"description": "Firebase Functions", |
|||
"dependencies": { |
|||
"nodemailer": "^2.4.1", |
|||
"firebase": "http://www.gstatic.com/firebasejs/staging/3.1.0-functions/firebase.tgz", |
|||
"firebase-functions": "https://storage.googleapis.com/firebase-preview-drop/node/firebase-functions/firebase-functions-0.2.1.tar.gz" |
|||
} |
|||
} |
@ -0,0 +1,109 @@ |
|||
<!doctype html> |
|||
<!-- |
|||
Copyright 2015 Google Inc. All rights reserved. |
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
https://www.apache.org/licenses/LICENSE-2.0 |
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License |
|||
--> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="description" content="Demonstrates the use of Google Cloud Functions with a Firebase DB"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Functions Email Confirmation Demo</title> |
|||
|
|||
<!-- Disable tap highlight on IE --> |
|||
<meta name="msapplication-tap-highlight" content="no"> |
|||
|
|||
<!-- Web Application Manifest --> |
|||
<link rel="manifest" href="manifest.json"> |
|||
|
|||
<!-- Add to homescreen for Chrome on Android --> |
|||
<meta name="mobile-web-app-capable" content="yes"> |
|||
<meta name="application-name" content="Firebase Functions Quickstart"> |
|||
<meta name="theme-color" content="#303F9F"> |
|||
|
|||
<!-- Add to homescreen for Safari on iOS --> |
|||
<meta name="apple-mobile-web-app-capable" content="yes"> |
|||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
|||
<meta name="apple-mobile-web-app-title" content="Firebase Functions Quickstart"> |
|||
<meta name="apple-mobile-web-app-status-bar-style" content="#303F9F"> |
|||
|
|||
<!-- Tile icon for Win8 --> |
|||
<meta name="msapplication-TileColor" content="#3372DF"> |
|||
<meta name="msapplication-navbutton-color" content="#303F9F"> |
|||
|
|||
<!-- Material Design Lite --> |
|||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> |
|||
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css"> |
|||
<script defer src="https://code.getmdl.io/1.1.3/material.min.js"></script> |
|||
|
|||
<link rel="stylesheet" href="main.css"> |
|||
</head> |
|||
<body> |
|||
<div class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header"> |
|||
|
|||
<!-- Header section containing title --> |
|||
<header class="mdl-layout__header mdl-color-text--white mdl-color--light-blue-700"> |
|||
<div class="mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid"> |
|||
<div class="mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop"> |
|||
<h3>Very interesting website with Newsletter</h3> |
|||
</div> |
|||
</div> |
|||
</header> |
|||
|
|||
<main class="mdl-layout__content mdl-color--grey-100"> |
|||
<div class="mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid"> |
|||
|
|||
<!-- Card containing the sign-in UI --> |
|||
<div id="demo-signed-out-card" class="mdl-card mdl-shadow--2dp mdl-cell"> |
|||
<div class="mdl-card__supporting-text mdl-color-text--grey-600"> |
|||
<p> |
|||
Welcome to my very interesting website. <strong>Would you like to sign up to my Newsletter?</strong> |
|||
</p> |
|||
<p> |
|||
To subscribe just: |
|||
</p> |
|||
<button id="demo-sign-in-button" class="mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect"><i class="material-icons">account_circle</i> Sign in with Google</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Card containing the signed-in UI --> |
|||
<div id="demo-signed-in-card" class="mdl-card mdl-shadow--2dp mdl-cell"> |
|||
<div class="mdl-card__supporting-text mdl-color-text--grey-600"> |
|||
<p> |
|||
Welcome <span id="demo-name-container"></span>. |
|||
</p> |
|||
<p id="demo-subscribed-text-container"> |
|||
You are currently subscribed to the Mailing list. |
|||
</p> |
|||
<p id="demo-unsubscribed-text-container"> |
|||
You are <strong>not</strong> subscribed to the Mailing list. Click the button below to subscribe. |
|||
</p> |
|||
<p> |
|||
You will receive emails to <strong><span id="demo-email-container"></span></strong>. |
|||
</p> |
|||
<button id="demo-subscribe-button" class="mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect">Subscribe</button> |
|||
<button id="demo-unsubscribe-button" class="mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect">Unsubscribe</button> |
|||
<button id="demo-sign-out-button" class="mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect">Sign out</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</main> |
|||
</div> |
|||
|
|||
<!-- Firebase --> |
|||
<!-- *********************************************************************************************************************** |
|||
* TODO(DEVELOPER): Paste the initialization snippet from: Firebase Console > Overview > Add Firebase to your web app. * |
|||
*********************************************************************************************************************** --> |
|||
|
|||
<script src="scripts/main.js"></script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,62 @@ |
|||
/** |
|||
* Copyright 2015 Google Inc. All Rights Reserved. |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
html, body { |
|||
font-family: 'Roboto', 'Helvetica', sans-serif; |
|||
} |
|||
.mdl-grid { |
|||
max-width: 1024px; |
|||
margin: auto; |
|||
} |
|||
.mdl-card { |
|||
min-height: 0; |
|||
padding-bottom: 5px; |
|||
} |
|||
.mdl-layout__header-row { |
|||
padding: 0; |
|||
} |
|||
#message-form { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
#message-form button { |
|||
max-width: 300px; |
|||
} |
|||
#message-list { |
|||
padding: 0; |
|||
width: 100%; |
|||
} |
|||
#message-list > div { |
|||
padding: 15px; |
|||
border-bottom: 1px #f1f1f1 solid; |
|||
} |
|||
h3 { |
|||
background: url('firebase-logo.png') no-repeat; |
|||
background-size: 40px; |
|||
padding-left: 50px; |
|||
} |
|||
#demo-signed-out-card, |
|||
#demo-signed-in-card, |
|||
#demo-subscribe-button, |
|||
#demo-unsubscribe-button, |
|||
#demo-subscribed-text-container, |
|||
#demo-unsubscribed-text-container { |
|||
display: none; |
|||
} |
|||
#demo-subscribe-button, |
|||
#demo-unsubscribe-button { |
|||
margin-right: 20px; |
|||
} |
@ -0,0 +1,100 @@ |
|||
/** |
|||
* Copyright 2015 Google Inc. All Rights Reserved. |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
'use strict'; |
|||
|
|||
// Initializes the Demo.
|
|||
function Demo() { |
|||
// Shortcuts to DOM Elements.
|
|||
this.signInButton = document.getElementById('demo-sign-in-button'); |
|||
this.signOutButton = document.getElementById('demo-sign-out-button'); |
|||
this.subscribeButton = document.getElementById('demo-subscribe-button'); |
|||
this.unsubscribeButton = document.getElementById('demo-unsubscribe-button'); |
|||
this.emailContainer = document.getElementById('demo-email-container'); |
|||
this.subscribedTextContainer = document.getElementById('demo-subscribed-text-container'); |
|||
this.unsubscribedTextContainer = document.getElementById('demo-unsubscribed-text-container'); |
|||
this.nameContainer = document.getElementById('demo-name-container'); |
|||
this.signedOutCard = document.getElementById('demo-signed-out-card'); |
|||
this.signedInCard = document.getElementById('demo-signed-in-card'); |
|||
|
|||
// Bind events.
|
|||
this.signInButton.addEventListener('click', this.signIn.bind(this)); |
|||
this.signOutButton.addEventListener('click', this.signOut.bind(this)); |
|||
this.subscribeButton.addEventListener('click', this.subscribe.bind(this)); |
|||
this.unsubscribeButton.addEventListener('click', this.unsubscribe.bind(this)); |
|||
firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this)); |
|||
} |
|||
|
|||
Demo.prototype.onAuthStateChanged = function(user) { |
|||
if (user) { |
|||
this.nameContainer.innerText = user.displayName; |
|||
this.emailContainer.innerText = user.email; |
|||
this.signedOutCard.style.display = 'none'; |
|||
this.signedInCard.style.display = 'block'; |
|||
this.userRef = firebase.database().ref('users/' + user.uid); |
|||
this.userRef.on('value', function(data) { |
|||
if (data.val() && data.val().subscribedToMailingList) { |
|||
this.subscribedTextContainer.style.display = 'block'; |
|||
this.unsubscribedTextContainer.style.display = 'none'; |
|||
this.subscribeButton.style.display = 'none'; |
|||
this.unsubscribeButton.style.display = 'inline-block'; |
|||
} else { |
|||
this.subscribedTextContainer.style.display = 'none'; |
|||
this.unsubscribedTextContainer.style.display = 'block'; |
|||
this.subscribeButton.style.display = 'inline-block'; |
|||
this.unsubscribeButton.style.display = 'none'; |
|||
} |
|||
}.bind(this)); |
|||
} else { |
|||
if (this.userRef) { |
|||
this.userRef.off(); |
|||
} |
|||
this.signedOutCard.style.display = 'block'; |
|||
this.signedInCard.style.display = 'none'; |
|||
this.lastUid = null; |
|||
} |
|||
}; |
|||
|
|||
// Signs-in Firebase.
|
|||
Demo.prototype.signIn = function() { |
|||
firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider()).then(function(result) { |
|||
// If the user signs-in we automatically signs-him up for the newsletter.
|
|||
this.onAuthStateChanged(result.user); |
|||
this.subscribe(); |
|||
}.bind(this)); |
|||
}; |
|||
|
|||
// Signs-out of Firebase.
|
|||
Demo.prototype.signOut = function() { |
|||
firebase.auth().signOut(); |
|||
}; |
|||
|
|||
// Subscribe to the newsletter.
|
|||
Demo.prototype.subscribe = function() { |
|||
return firebase.database().ref('users/' + firebase.auth().currentUser.uid).set({ |
|||
subscribedToMailingList: true, |
|||
email: firebase.auth().currentUser.email |
|||
}); |
|||
}; |
|||
|
|||
// Unsubscribe to the newsletter.
|
|||
Demo.prototype.unsubscribe = function() { |
|||
return firebase.database().ref('users/' + firebase.auth().currentUser.uid + '/subscribedToMailingList').set(false); |
|||
}; |
|||
|
|||
// Bindings on load.
|
|||
window.addEventListener('load', function() { |
|||
window.demo = new Demo(); |
|||
}); |
@ -1,47 +0,0 @@ |
|||
/** |
|||
* Copyright 2015 Google Inc. All Rights Reserved. |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
'use strict'; |
|||
|
|||
var capitalizeSentence = require('capitalize-sentence'); |
|||
var BadWordsFilter = require('bad-words'); |
|||
var filter = new BadWordsFilter(); |
|||
|
|||
// Adds moderation functions to String prototype.
|
|||
exports.loadModerationStringUtils = function() { |
|||
|
|||
// Returns true of the string contains swearwords.
|
|||
String.prototype.containsSwearwords = function () { |
|||
return this != filter.clean(this); |
|||
}; |
|||
|
|||
// Hide all swearwords. e.g: Crap => ****.
|
|||
String.prototype.moderateSwearwords = function () { |
|||
return filter.clean(this); |
|||
}; |
|||
|
|||
// Detect if the current message is yelling. i.e. there are too many Uppercase
|
|||
// characters or exclamation points.
|
|||
String.prototype.isYelling = function () { |
|||
return this.replace(/[^A-Z]/g, '').length > this.length / 2 |
|||
|| this.replace(/[^!]/g, '').length >= 3; |
|||
}; |
|||
|
|||
// Correctly capitalize the string as a sentence (e.g. uppercase after dots)
|
|||
// and remove exclamation points.
|
|||
String.prototype.capitalizeSentence = function () { |
|||
return capitalizeSentence(this.toLowerCase()).replace(/!+/g, '.'); |
|||
} |
|||
}; |
Loading…
Reference in new issue