Browse Source

Added email-confirmation sample Firebase Functions sample.

Change-Id: I86ff8ac3f2a5b88454f9677e7b08129353cd30f6
ryanpbrewster-patch-1
Nicolas Garnier 8 years ago
parent
commit
dcecd07e9e
  1. 44
      email-confirmation/README.md
  2. 10
      email-confirmation/database.rules.json
  3. BIN
      email-confirmation/firebase-logo.png
  4. 8
      email-confirmation/firebase.json
  5. 56
      email-confirmation/functions/index.js
  6. 9
      email-confirmation/functions/package.json
  7. 109
      email-confirmation/index.html
  8. 62
      email-confirmation/main.css
  9. 100
      email-confirmation/scripts/main.js
  10. 2
      lastmodified-tracking/index.js
  11. 47
      text-moderation/moderation-string-utils.js

44
email-confirmation/README.md

@ -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

10
email-confirmation/database.rules.json

@ -0,0 +1,10 @@
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid"
}
}
}
}

BIN
email-confirmation/firebase-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

8
email-confirmation/firebase.json

@ -0,0 +1,8 @@
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "./"
}
}

56
email-confirmation/functions/index.js

@ -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);
});
});

9
email-confirmation/functions/package.json

@ -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"
}
}

109
email-confirmation/index.html

@ -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>

62
email-confirmation/main.css

@ -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;
}

100
email-confirmation/scripts/main.js

@ -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();
});

2
lastmodified-tracking/index.js

@ -24,4 +24,4 @@ exports.touch = function(context) {
console.log('Authenticated successfully with admin rights');
ref.child('lastmodified').set(Firebase.ServerValue.TIMESTAMP).then(context.done)
.catch(context.done);
};
};

47
text-moderation/moderation-string-utils.js

@ -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…
Cancel
Save