Merge pull request #9 from quenenni/optionsAdded

Options added
This commit is contained in:
John McLear 2013-04-04 14:51:14 -07:00
commit f56846309a
15 changed files with 1105 additions and 147 deletions

View file

@ -1,5 +1,8 @@
# Description
This plugin allows users to subscribe to pads and receive email updates when a pad is being modified. You can modify the frequency. This plugin is very much in alpha stage and has a lot of things TODO (See TODO).
This plugin allows users to subscribe to pads and receive email updates when a pad is being modified. You can modify the frequency. This plugin is still in early stage and has things TODO (See TODO).
# Source code
On Github : https://github.com/JohnMcLear/ep_email_notifications
# Installation
Make sure an SMTP gateway is installed IE postfix
@ -11,6 +14,10 @@ NOTE: You will NOT receive an email if you(the author that registered their emai
```
"ep_email_notifications" : {
panelDisplayLocation: { // Where you want to have the subscription panel
mysettings: true, // In the "mysettings" menu
popup: true // A popup that pop in the bottom right corner of the pad after 10 seconds
},
checkFrequency: 6000, // checkFrequency = How frequently(milliseconds) to check for pad updates -- Move me to the settings file
staleTime: 30000, // staleTime = How stale(milliseconds) does a pad need to be before notifying subscribers? Move me to settings
fromName: "Etherpad SETTINGS FILE!",
@ -22,10 +29,15 @@ NOTE: You will NOT receive an email if you(the author that registered their emai
}
```
# Translation
This plugin has for now an english and french translation.
In case you would like to have it in another language, you can easily translate the few sentences and then contact us on irc (#etherpad-lite-dev on irc.freenode.net) or create a Pull-Request on the GitHub repository.
You can find the sentences to translate in the ep_email_notifications/locales/ directory.
Specials chars written in unicode (See https://fr.wikipedia.org/wiki/Table_des_caract%C3%A8res_Unicode_%280000-0FFF%29)
# TODO
* Clean up all code
# FUTURE VERSIONS TODO
* v2 - Get the modified contents from the API HTML diff and append that to the Email and make the email from the server HTML not plain text
* v2 - a point to unsubscribe and validate/verify email https://github.com/alfredwesterveld/node-email-verification
* v2 - Keep a record of when a user was last on a pad

View file

@ -1,11 +1,23 @@
var eejs = require("ep_etherpad-lite/node/eejs");
var settings = require('../../src/node/utils/Settings');
exports.eejsBlock_scripts = function (hook_name, args, cb) {
args.content = args.content + eejs.require("ep_email_notifications/templates/scripts.html", {}, module);
return cb();
};
exports.eejsBlock_embedPopup = function (hook_name, args, cb) {
args.content = args.content + eejs.require("ep_email_notifications/templates/embedFrame.html", {}, module);
exports.eejsBlock_mySettings = function (hook_name, args, cb) {
args.content = args.content + eejs.require('ep_email_notifications/templates/email_notifications_settings.ejs');
return cb();
};
exports.eejsBlock_styles = function (hook_name, args, cb) {
args.content = args.content + '<link href="../static/plugins/ep_email_notifications/static/css/email_notifications.css" rel="stylesheet">';
};
exports.clientVars = function(hook, context, callback) {
var pluginSettings = settings.ep_email_notifications;
var panelDisplayLocation = (pluginSettings && pluginSettings.panelDisplayLocation)?pluginSettings.panelDisplayLocation:"undefiend";
// return the setting to the clientVars, sending the value
return callback({ "panelDisplayLocation": panelDisplayLocation });
};

10
ep.json
View file

@ -6,11 +6,17 @@
"padUpdate": "ep_email_notifications/update",
"handleMessage": "ep_email_notifications/handleMessage",
"eejsBlock_scripts": "ep_email_notifications/client",
"eejsBlock_embedPopup": "ep_email_notifications/client:eejsBlock_embedPopup"
"eejsBlock_mySettings": "ep_email_notifications/client:eejsBlock_mySettings",
"eejsBlock_styles": "ep_email_notifications/client:eejsBlock_styles",
"clientVars": "ep_email_notifications/client:clientVars",
"expressCreateServer" : "ep_email_notifications/index:registerRoute"
},
"client_hooks": {
"postAceInit":"ep_email_notifications/static/js/ep_email:postAceInit",
"handleClientMessage_emailSubscriptionSuccess":"ep_email_notifications/static/js/ep_email"
"handleClientMessage_emailSubscriptionSuccess":"ep_email_notifications/static/js/ep_email",
"handleClientMessage_emailUnsubscriptionSuccess":"ep_email_notifications/static/js/ep_email",
"handleClientMessage_emailNotificationGetUserInfo":"ep_email_notifications/static/js/ep_email",
"handleClientMessage_emailNotificationMissingParams":"ep_email_notifications/static/js/ep_email"
}
}
]

View file

@ -1,38 +1,72 @@
var db = require('../../src/node/db/DB').db,
API = require('../../src/node/db/API.js'),
async = require('../../src/node_modules/async'),
check = require('validator').check,
email = require('emailjs'),
randomString = require('../../src/static/js/pad_utils').randomString;
settings = require('../../src/node/utils/Settings');
var pluginSettings = settings.ep_email_notifications;
var areParamsOk = (pluginSettings)?true:false;
var fromName = (pluginSettings && pluginSettings.fromName)?pluginSettings.fromName:"Etherpad";
var fromEmail = (pluginSettings && pluginSettings.fromEmail)?pluginSettings.fromEmail:"pad@etherpad.org";
var urlToPads = (pluginSettings && pluginSettings.urlToPads)?pluginSettings.urlToPads:"http://beta.etherpad.org/p/";
var emailServer = (pluginSettings && pluginSettings.emailServer)?pluginSettings.emailServer:{host:"127.0.0.1"};
if (areParamsOk == false) console.warn("Settings for ep_email_notifications plugin are missing in settings.json file");
// Connect to the email server -- This might not be the ideal place to connect but it stops us having lots of connections
var server = email.server.connect(emailServer);
// When a new message comes in from the client - FML this is ugly
exports.handleMessage = function(hook_name, context, callback){
if (context.message && context.message.data){
if (context.message.data.type == 'USERINFO_UPDATE' ) { // if it's a request to update an authors email
if (context.message.data.userInfo){
if (areParamsOk == false) {
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailNotificationMissingParams",
payload: true
}
});
console.warn("Settings for ep_email_notifications plugin are missing in settings.json file");
callback([null]); // don't run onto passing colorId or anything else to the message handler
} else if (context.message.data.userInfo){
if(context.message.data.userInfo.email){ // it contains email
console.debug(context.message);
// does email Subscription already exist for this email address?
// does email (Un)Subscription already exist for this email address?
db.get("emailSubscription:"+context.message.data.padId, function(err, userIds){
var alreadyExists = false;
if(userIds){
async.forEach(Object.keys(userIds), function(user, cb){
console.debug("UserIds subscribed by email to this pad:", userIds);
if(user == context.message.data.userInfo.email){ // If we already have this email registered for this pad
if(user == context.message.data.userInfo.email){ // If we already have this email registered for this pad
// This user ID is already assigned to this padId so don't do anything except tell the user they are already subscribed somehow..
alreadyExists = true;
console.debug("email ", user, "already subscribed to ", context.message.data.padId, " so sending message to client");
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailSubscriptionSuccess",
payload: false
}
});
if(context.message.data.userInfo.email_option == 'subscribe') {
// Subscription process
exports.subscriptionEmail(
context,
context.message.data.userInfo.email,
alreadyExists,
context.message.data.userInfo,
context.message.data.padId,
callback
);
} else if (context.message.data.userInfo.email_option == 'unsubscribe') {
// Unsubscription process
exports.unsubscriptionEmail(
context,
alreadyExists,
context.message.data.userInfo,
context.message.data.padId
);
}
}
cb();
},
@ -41,68 +75,321 @@ exports.handleMessage = function(hook_name, context, callback){
// There should be something in here!
}); // end async for each
}
var validatesAsEmail = check(context.message.data.userInfo.email).isEmail();
if(!validatesAsEmail){ // send validation failed if it's malformed.. y'know in general fuck em!
console.warn("Dropped email subscription due to malformed email address");
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailSubscriptionSuccess",
payload: false
}
});
// In case we didn't find it in the Db
if (alreadyExists == false) {
if(context.message.data.userInfo.email_option == 'subscribe') {
// Subscription process
exports.subscriptionEmail(
context,
context.message.data.userInfo.email,
alreadyExists,
context.message.data.userInfo,
context.message.data.padId,
callback
);
} else if (context.message.data.userInfo.email_option == 'unsubscribe') {
// Unsubscription process
exports.unsubscriptionEmail(
context,
alreadyExists,
context.message.data.userInfo,
context.message.data.padId
);
}
}
if(alreadyExists == false && validatesAsEmail){
console.debug ("Wrote to the database and sent client a positive response ",context.message.data.userInfo.email);
exports.setAuthorEmail(
context.message.data.userInfo.userId,
context.message.data.userInfo.email, callback
);
exports.setAuthorEmailRegistered(
context.message.data.userInfo.email,
context.message.data.userInfo.userId,
context.message.data.padId
);
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailSubscriptionSuccess",
payload: true
}
});
}
}); // close db get
callback([null]); // don't run onto passing colorId or anything else to the message handler
}
}
} else if (context.message.data.type == 'USERINFO_GET' ) { // A request to find datas for a userId
if (context.message.data.userInfo){
if(context.message.data.userInfo.userId){ // it contains the userId
console.debug(context.message);
// does email Subscription already exist for this UserId?
db.get("emailSubscription:"+context.message.data.padId, function(err, userIds){
var userIdFound = false;
if(userIds){
async.forEach(Object.keys(userIds), function(user, cb){
if(userIds[user].authorId == context.message.data.userInfo.userId){ // if we find the same Id in the Db as the one used by the user
console.debug("Options for this pad ", userIds[user].authorId, " found in the Db");
userIdFound = true;
// Request user subscription info process
exports.sendUserInfo (
context,
userIdFound,
user,
userIds[user]
);
}
cb();
},
function(err){
// There should be something in here!
}); // end async for each
}
if (userIdFound == false) {
// Request user subscription info process
exports.sendUserInfo (
context,
userIdFound,
"",
""
);
}
});
callback([null]);
}
}
}
}
callback();
}
// Updates the database with the email record
exports.setAuthorEmail = function (author, email, callback){
db.setSub("globalAuthor:" + author, ["email"], email, callback);
/**
* Subscription process
*/
exports.subscriptionEmail = function (context, email, emailFound, userInfo, padId, callback) {
var validatesAsEmail = exports.checkEmailValidation(email);
var subscribeId = randomString(25);
if(emailFound == false && validatesAsEmail){
// Subscription -> Go for it
console.debug ("Subscription: Wrote to the database and sent client a positive response ",context.message.data.userInfo.email);
exports.setAuthorEmailRegistered(
userInfo,
userInfo.userId,
subscribeId,
padId
);
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailSubscriptionSuccess",
payload: {
formName: userInfo.formName,
success: true
}
}
});
// Send mail to user with the link for validation
server.send(
{
text: "Please click on this link in order to validate your subscription to the pad " + padId + "\n" + urlToPads+padId + "/subscribe=" + subscribeId,
from: fromName+ "<"+fromEmail+">",
to: userInfo.email,
subject: "Email subscription confirmation for pad "+padId
},
function(err, message) {
console.log(err || message);
}
);
} else if (!validatesAsEmail) {
// Subscription -> failed coz mail malformed.. y'know in general fuck em!
console.warn("Dropped email subscription due to malformed email address");
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailSubscriptionSuccess",
payload: {
type: "malformedEmail",
formName: userInfo.formName,
success: false
}
}
});
} else {
// Subscription -> failed coz email already subscribed for this pad
console.debug("email ", context.message.data.userInfo.email, "already subscribed to ", context.message.data.padId, " so sending message to client");
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailSubscriptionSuccess",
payload: {
type: "alreadyRegistered",
formName: userInfo.formName,
success: false
}
}
});
}
}
// Write email and padId to the database
exports.setAuthorEmailRegistered = function(email, authorId, padId){
/**
* UnsUbscription process
*/
exports.unsubscriptionEmail = function (context, emailFound, userInfo, padId) {
var unsubscribeId = randomString(25);
if(emailFound == true) {
// Unsubscription -> Go for it
console.debug ("Unsubscription: Remove from the database and sent client a positive response ",context.message.data.userInfo.email);
exports.unsetAuthorEmailRegistered(
userInfo,
userInfo.userId,
unsubscribeId,
padId
);
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailUnsubscriptionSuccess",
payload: {
formName: userInfo.formName,
success: true
}
}
});
// Send mail to user with the link for validation
server.send(
{
text: "Please click on this link in order to validate your unsubscription to the pad " + padId + "\n" + urlToPads+padId + "/unsubscribe=" + unsubscribeId,
from: fromName+ "<"+fromEmail+">",
to: userInfo.email,
subject: "Email unsubscription confirmation for pad "+padId
},
function(err, message) {
console.log(err || message);
}
);
} else {
// Unsubscription -> Send failed as email not found
console.debug ("Unsubscription: Send client a negative response ",context.message.data.userInfo.email);
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailUnsubscriptionSuccess",
payload: {
formName: userInfo.formName,
success: false
}
}
});
}
}
/**
* Request user subscription info process
*/
exports.sendUserInfo = function (context, emailFound, email, userInfo) {
var defaultOnStartOption = true;
var defaultOnEndOption = false;
if (typeof userInfo.onStart == 'boolean' && typeof userInfo.onEnd == 'boolean') {
var onStart = userInfo.onStart;
var onEnd = userInfo.onEnd;
} else { // In case these options are not yet defined for this userId
var onStart = defaultOnStartOption;
var onEnd = defaultOnEndOption;
}
if (emailFound == true) {
// We send back the options associated to this userId
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailNotificationGetUserInfo",
payload: {
email: email,
onStart: onStart,
onEnd: onEnd,
formName: context.message.data.userInfo.formName,
success:true
}
}
});
} else {
// No options set for this userId
context.client.json.send({ type: "COLLABROOM",
data:{
type: "emailNotificationGetUserInfo",
payload: {
formName: context.message.data.userInfo.formName,
success:false
}
}
});
}
}
/**
* Function to check if an email is valid
*/
exports.checkEmailValidation = function (email) {
var Validator = require('validator').Validator;
var validator = new Validator();
validator.error = function() {
return false;
};
return validator.check(email).isEmail();
}
/**
* Database manipulation
*/
// Write email, options, authorId and pendingId to the database
exports.setAuthorEmailRegistered = function(userInfo, authorId, subscribeId, padId){
var timestamp = new Date().getTime();
var registered = {
authorId: authorId,
onStart: userInfo.email_onStart,
onEnd: userInfo.email_onEnd,
subscribeId: subscribeId,
timestamp: timestamp
};
console.debug("registered", registered, " to ", padId);
// Here we have to basically hack a new value into the database, this isn't clean or polite.
db.get("emailSubscription:" + padId, function(err, value){ // get the current value
if(!value){value = {};} // if an emailSubscription doesnt exist yet for this padId don't panic
value[email] = registered; // add the registered values to the object
console.warn("written to database");
if(!value){
// if an emailSubscription doesnt exist yet for this padId don't panic
value = {"pending":{}};
} else if (!value['pending']) {
// if the pending section doesn't exist yet for this padId, we create it
value['pending'] = {};
}
// add the registered values to the pending section of the object
value['pending'][userInfo.email] = registered;
// Write the modified datas back in the Db
db.set("emailSubscription:" + padId, value); // stick it in the database
});
}
// Write email, authorId and pendingId to the database
exports.unsetAuthorEmailRegistered = function(userInfo, authorId, unsubscribeId, padId){
var timestamp = new Date().getTime();
var registered = {
authorId: authorId,
unsubscribeId: unsubscribeId,
timestamp: timestamp
};
console.debug("unregistered", userInfo.email, " to ", padId);
db.get("emailSubscription:" + padId, function(err, value){ // get the current value
// if the pending section doesn't exist yet for this padId, we create it (this shouldn't happen)
if (!value['pending']) {value['pending'] = {};}
// add the registered values to the pending section of the object
value['pending'][userInfo.email] = registered;
// Write the modified datas back in the Db
db.set("emailSubscription:" + padId, value);
});
}

254
index.js Normal file
View file

@ -0,0 +1,254 @@
var db = require('ep_etherpad-lite/node/db/DB').db,
fs = require("fs"),
async = require('../../src/node_modules/async'),
settings = require('../../src/node/utils/Settings');
// Remove cache for this procedure
db['dbSettings'].cache = 0;
exports.registerRoute = function (hook_name, args, callback) {
// Catching (un)subscribe addresses
args.app.get('/p/*/(un){0,1}subscribe=*', function(req, res) {
var fullURL = req.protocol + "://" + req.get('host') + req.url;
var path=req.url.split("/");
var padId=path[2];
var param = path[3].split("=");
var action = param[0];
var actionId = param[1];
var padURL = req.protocol + "://" + req.get('host') + "/p/" +padId;
async.waterfall(
[
function(cb) {
// Is the (un)subscription valid (exists & not older than 24h)
db.get("emailSubscription:"+padId, function(err, userIds){
var foundInDb = false;
var timeDiffGood = false;
var email = "your email";
var resultDb = {
"foundInDb": foundInDb,
"timeDiffGood": timeDiffGood,
"email": email
}
if(userIds && userIds['pending']){
async.forEach(Object.keys(userIds['pending']), function(user){
var userInfo = userIds['pending'][user];
// If we have Id int the Db, then we are good ot really unsubscribe the user
if(userInfo[action + 'Id'] == actionId){
console.debug("emailSubscription:", user, "found in DB:", userInfo);
foundInDb = true;
email = user;
// Checking if the demand is not older than 24h
var timeDiff = new Date().getTime() - userInfo.timestamp;
timeDiffGood = timeDiff < 1000 * 60 * 60 * 24;
if(action == 'subscribe' && timeDiffGood == true) {
// Subscription process
setAuthorEmail(
userInfo,
user
);
setAuthorEmailRegistered(
userIds,
userInfo,
user,
padId
);
} else if (action == 'unsubscribe' && timeDiffGood == true) {
// Unsubscription process
unsetAuthorEmail(
userInfo,
user
);
unsetAuthorEmailRegistered(
userIds,
user,
padId
);
}
resultDb = {
"foundInDb": foundInDb,
"timeDiffGood": timeDiffGood,
"email": user
}
}
},
function(err, msg){
if (err != null) {
console.error("Error in async.forEach", err, " -> ", msg);
}
}); // end async for each
}
cb(null, resultDb);
});
},
function(resultDb, cb) {
// Create and send the output message
sendContent(res, args, action, padId, padURL, resultDb);
cb(null, resultDb);
},
function(resultDb, cb) {
// Take a moment to clean all obsolete pending data
cleanPendingData(padId);
cb(null, resultDb);
}
],
function(err, results){
if (err != null) {
console.error("Callback async.series: Err -> ", err, " / results -> ", results);
}
}
);
});
callback(); // Am I even called?
}
/**
* Database manipulation
*/
// Updates the database with the email record
setAuthorEmail = function (userInfo, email){
db.setSub("globalAuthor:" + userInfo.authorId, ["email"], email);
}
// Write email and padId to the database
setAuthorEmailRegistered = function(userIds, userInfo, email, padId){
console.debug("setAuthorEmailRegistered: Initial userIds:", userIds);
var timestamp = new Date().getTime();
var registered = {
authorId: userInfo.authorId,
onStart: userInfo.onStart,
onEnd: userInfo.onEnd,
timestamp: timestamp
};
// add the registered values to the object
userIds[email] = registered;
// remove the pending data
delete userIds['pending'][email];
// Write the modified datas back in the Db
console.warn("written to database");
db.set("emailSubscription:" + padId, userIds); // stick it in the database
console.debug("setAuthorEmailRegistered: Modified userIds:", userIds);
}
// Updates the database by removing the email record for that AuthorId
unsetAuthorEmail = function (userInfo, email){
db.get("globalAuthor:" + userInfo.authorId, function(err, value){ // get the current value
if (value['email'] == email) {
// Remove the email option from the datas
delete value['email'];
// Write the modified datas back in the Db
db.set("globalAuthor:" + userInfo.authorId, value);
}
});
}
// Remove email, options and padId from the database
unsetAuthorEmailRegistered = function(userIds, email, padId){
console.debug("unsetAuthorEmailRegistered: initial userIds:", userIds);
// remove the registered options from the object
delete userIds[email];
// remove the pending data
delete userIds['pending'][email];
// Write the modified datas back in the Db
console.warn("written to database");
db.set("emailSubscription:" + padId, userIds);
console.debug("unsetAuthorEmailRegistered: modified userIds:", userIds);
}
/**
* We take a moment to remove too old pending (un)subscription
*/
cleanPendingData = function (padId) {
var modifiedData, areDataModified = false;
db.get("emailSubscription:" + padId, function(err, userIds){ // get the current value
console.debug("cleanPendingData: Initial userIds:", userIds);
modifiedData = userIds;
if(userIds && userIds['pending']){
async.forEach(Object.keys(userIds['pending']), function(user){
var timeDiff = new Date().getTime() - userIds['pending'][user].timestamp;
var timeDiffGood = timeDiff < 1000 * 60 * 60 * 24;
if(timeDiffGood == false) {
delete modifiedData['pending'][user];
areDataModified = true;
}
});
}
if (areDataModified == true) {
// Write the modified datas back in the Db
db.set("emailSubscription:" + padId, modifiedData);
}
console.debug("cleanPendingData: Modified userIds:", modifiedData, " / areDataModified:", areDataModified);
});
}
/**
* Create html output with the status of the process
*/
function sendContent(res, args, action, padId, padURL, resultDb) {
console.debug("starting sendContent: args ->", action, " / ", padId, " / ", padURL, " / ", resultDb);
if (action == 'subscribe') {
var actionMsg = "Subscribing '" + resultDb.email + "' to pad " + padId;
} else {
var actionMsg = "Unsubscribing '" + resultDb.email + "' from pad " + padId;
}
var msgCause, resultMsg, classResult;
if (resultDb.foundInDb == true && resultDb.timeDiffGood == true) {
// Pending data were found un Db and updated -> good
resultMsg = "Success";
classResult = "validationGood";
if (action == 'subscribe') {
msgCause = "You will receive email when someone changes this pad.";
} else {
msgCause = "You won't receive anymore email when someone changes this pad.";
}
} else if (resultDb.foundInDb == true) {
// Pending data were found but older than a day -> fail
resultMsg = "Too late!";
classResult = "validationBad";
msgCause = "You have max 24h to click the link in your confirmation email.";
} else {
// Pending data weren't found in Db -> fail
resultMsg = "Fail";
classResult = "validationBad";
msgCause = "We couldn't find any pending " + (action == 'subscribe'?'subscription':'unsubscription') + "<br />in our system with this Id.<br />Maybe you wait more than 24h before validating";
}
args.content = fs.readFileSync(__dirname + "/templates/response.ejs", 'utf-8');
args.content = args.content
.replace(/\<%action%\>/, actionMsg)
.replace(/\<%classResult%\>/, classResult)
.replace(/\<%result%\>/, resultMsg)
.replace(/\<%explanation%\>/, msgCause)
.replace(/\<%padUrl%\>/g, padURL);
res.contentType("text/html; charset=utf-8");
res.send(args.content); // Send it to the requester*/
}

19
locales/en.json Normal file
View file

@ -0,0 +1,19 @@
{ "ep_email_notifications.titleGritterError": "Email subscription error"
, "ep_email_notifications.titleGritterSubscr": "Email subscription"
, "ep_email_notifications.titleGritterUnsubscr": "Email unsubscription"
, "ep_email_notifications.headerGritterSubscr": "(Receive an email when someone modifies this pad)"
, "ep_email_notifications.msgOptionsNotChecked": "You need to check at least one of the two options from 'Send a mail when someone..'"
, "ep_email_notifications.msgParamsMissing": "Settings for the 'email_Notifications' plugin are missing.<br />Please contact your administrator."
, "ep_email_notifications.msgEmailMalformed": "The email address is malformed"
, "ep_email_notifications.msgAlreadySubscr": "You are already registered for emails for this pad"
, "ep_email_notifications.msgUnsubscrNotExisting": "This email address is not registered for this pad"
, "ep_email_notifications.msgUnknownErr": "Unknown error"
, "ep_email_notifications.msgSubscrSuccess": "An email was sent to your address.<br />Click on the link in order to validate your subscription."
, "ep_email_notifications.msgUnsubscrSuccess": "An email was sent to your address.<br />Click on the link in order to validate your unsubscription"
, "ep_email_notifications.menuLabel": "Email Notifications"
, "ep_email_notifications.formOptionsTitle": "Send a mail when someone.."
, "ep_email_notifications.formOptionOnStart": "starts editing the pad"
, "ep_email_notifications.formOptionOnEnd": "finish editing the pad"
, "ep_email_notifications.formBtnSubscr": "subscribe"
, "ep_email_notifications.formBtnUnsubscr": "unsubscribe"
}

19
locales/fr.json Normal file
View file

@ -0,0 +1,19 @@
{ "ep_email_notifications.titleGritterError": "Notification par email - Erreur"
, "ep_email_notifications.titleGritterSubscr": "Notification par email"
, "ep_email_notifications.titleGritterUnsubscr": "D\u00e9sinscription d\u2019email"
, "ep_email_notifications.headerGritterSubscr": "(\u00catre pr\u00e9venu lorsque quelqu\u2019un modifie ce pad)"
, "ep_email_notifications.msgOptionsNotChecked": "Il faut cocher au moins une des 2 options pour l\u2019inscription de l\u2019email..\u2019"
, "ep_email_notifications.msgParamsMissing": "Les param\u00e8tres de configurations du plugin \u2019email_Notifications\u2019 sont manquants.<br />Veuillez contacter votre administrateur."
, "ep_email_notifications.msgEmailMalformed": "L\u2019adresse email n\u2019est pas valide"
, "ep_email_notifications.msgAlreadySubscr": "Vous avez d\u00e9j\u00e0 enregistr\u00e9 cette adresse email pour ce pad"
, "ep_email_notifications.msgUnsubscrNotExisting": "Cette adresse email n\u2019est pas enregistr\u00e9e pour ce pad"
, "ep_email_notifications.msgUnknownErr": "Erreur inconnue"
, "ep_email_notifications.msgSubscrSuccess": "Un email a \u00e9t\u00e9 envoy\u00e9 \u00e0 votre adresse.<br />Cliquez sur le lien afin de valider votre inscription."
, "ep_email_notifications.msgUnsubscrSuccess": "Un email a \u00e9t\u00e9 envoy\u00e9 \u00e0 votre adresse.<br />Cliquez sur le lien afin de valider votre d\u00e9sinscription."
, "ep_email_notifications.menuLabel": "Notification par email"
, "ep_email_notifications.formOptionsTitle": "Envoyer un email quand qqu\u2019un.."
, "ep_email_notifications.formOptionOnStart": "commence l\u2019\u00e9dition du pad"
, "ep_email_notifications.formOptionOnEnd": "a fini d\u2019\u00e9diter le pad"
, "ep_email_notifications.formBtnSubscr": "inscription"
, "ep_email_notifications.formBtnUnsubscr": "d\u00e9sinscription"
}

View file

@ -1,13 +1,17 @@
{
"name": "ep_email_notifications",
"description": "Subscribe to a pad and receive an email when someone edits your pad",
"version": "0.0.6",
"version": "0.0.7",
"author": {
"name": "johnyma22",
"email": "john@mclear.co.uk",
"url": "John McLear"
},
"contributors": [],
"contributors": [{
"name": "quenenni",
"email": "quenenni@bruxxel.org",
"url": "https://github.com/quenenni"
}],
"dependencies": {
"emailjs": ">= 0.2.7",
"buffertools": ">= 1.0.8",
@ -15,5 +19,12 @@
},
"engines": {
"node": ">= 0.4.1"
}
},
"readme": "# Description\nThis plugin allows users to subscribe to pads and receive email updates when a pad is being modified. You can modify the frequency. This plugin is very much in alpha stage and has a lot of things TODO (See TODO).\n\n# Installation\nMake sure an SMTP gateway is installed IE postfix\nConfigure SPF and RDNS records to ensure proper mail flow <-- Search online\nCopy/Edit the below to your settings.json\nConnect to a pad, Click on the Share/Embed link and enter in your email address.\nOpen that pad in ANOTHER BROWSER then begin modifying, you should receive an email when the pad has begun editing and once the pad has gone stale (when everyone stops editing it and a time period passes).\nNOTE: You will NOT receive an email if you(the author that registered their email) are currently on or editing that pad!\n\n```\n \"ep_email_notifications\" : {\n checkFrequency: 6000, // checkFrequency = How frequently(milliseconds) to check for pad updates -- Move me to the settings file\n staleTime: 30000, // staleTime = How stale(milliseconds) does a pad need to be before notifying subscribers? Move me to settings\n fromName: \"Etherpad SETTINGS FILE!\",\n fromEmail: \"pad@etherpad.org\",\n urlToPads: \"http://beta.etherpad.org/p/\", // urlToPads = The URL to your pads note the trailing /\n emailServer: { // See https://github.com/eleith/emailjs for settings\n host: \"127.0.0.1\"\n }\n }\n```\n\n# TODO\n* Clean up all code\n\n# FUTURE VERSIONS TODO\n* v2 - Get the modified contents from the API HTML diff and append that to the Email and make the email from the server HTML not plain text\n* v2 - a point to unsubscribe and validate/verify email https://github.com/alfredwesterveld/node-email-verification\n* v2 - Keep a record of when a user was last on a pad\n",
"readmeFilename": "README.md",
"_id": "ep_email_notifications@0.0.7",
"dist": {
"shasum": "1f32eee4c8d5f3903c549b5a7985afc0053ed451"
},
"_from": "ep_email_notifications"
}

View file

@ -0,0 +1,64 @@
.ep_email_settings {
display: none;
padding: 0.2em 0.2em 0.2em 0.5em;
}
.ep_email_buttons {
padding:5px;
}
.ep_email_checkbox {
margin-left:0.3em;
}
.ep_email_input {
padding:.2em;
width:177px;
}
.ep_email_form_popup_header {
font-size:x-small;
}
#ep_email_form_popup .ep_email_buttons {
margin-top: .5em;
}
#ep_email_form_popup .ep_email_checkbox {
margin-top: .2em;
}
#ep_email_form_popup .ep_email_input {
margin: .3em 0;
width:95%;
}
/* (un)subscription validation page */
.validationTitle {
margin: 0 auto;
width: 600px;
text-align: center;
}
.validationEmailSubscription {
width: 600px;
margin: 0 auto;
text-align: center;
font-size: bigger;
font-weight: bold;
font-color: green;
}
.validationEmailSubscription > div {
border: solid 2px #333;
padding: .3em;
margin: .5em 0;
}
.validationEmailSubscription > div > div {
margin:0;
padding:.2em;
font-weight:normal;
}
.validationGood {
background-color: #CCFF66;
}
.validationBad {
background-color: #FF3300;
}

View file

@ -1,62 +1,156 @@
var cookie = require('ep_etherpad-lite/static/js/pad_cookie').padcookie;
var optionsAlreadyRecovered = false;
if(typeof exports == 'undefined'){
var exports = this['mymodule'] = {};
}
exports.postAceInit = function(hook, context){
// after 10 seconds if we dont already have an email for this author then prompt them
setTimeout(function(){init()},10000);
// If panelDisplayLocation setting is missing, set default value
if (typeof clientVars.panelDisplayLocation != "object") {
clientVars.panelDisplayLocation = {
mysettings: true, // In the "mysettings" menu
popup: true
}
}
// subscribe by email can be active..
$('.ep_email_form').submit(function(){
sendEmailToServer();
return false;
});
// If plugin settings set panel form in mysettings menu
if (clientVars.panelDisplayLocation.mysettings == true) {
// Uncheck the checkbox incase of reminiscence
$('#options-emailNotifications').prop('checked', false);
}
$('#options-emailNotifications').on('click', function() {
if (!optionsAlreadyRecovered) {
getDataForUserId('ep_email_form_mysettings');
optionsAlreadyRecovered = true;
} else {
$('.ep_email_settings').slideToggle();
}
});
exports.handleClientMessage_emailSubscriptionSuccess = function(hook, context){ // was subscribing to the email a big win or fail?
if(context.payload == false){
showAlreadyRegistered();
}else{
showRegistrationSuccess();
// Prepare subscription before submit form
$('[name=ep_email_subscribe]').on('click', function(e) {
$('[name=ep_email_option]').val('subscribe');
checkAndSend(e);
});
// Prepare unsubscription before submit form
$('[name=ep_email_unsubscribe]').on('click', function(e) {
$('[name=ep_email_option]').val('unsubscribe');
checkAndSend(e);
});
// subscribe by email can be active..
$('#ep_email_form_mysettings').submit(function(){
sendEmailToServer('ep_email_form_mysettings');
return false;
});
} else {
// Hide the notification menu in mysettings
$('#options-emailNotifications').parent().hide();
}
// If settings set popup panel form to true, show it
if (clientVars.panelDisplayLocation.popup == true) {
// after 10 seconds if we dont already have an email for this author then prompt them
setTimeout(function(){initPopupForm()},10000);
}
}
function init(){
var popUpIsAlreadyVisible = $('.ep_email_form').is(":visible");
if(!popUpIsAlreadyVisible){ // if the popup isn't already visible
if(clientHasAlreadyRegistered()){ // if the client has already registered for emails on this pad.
// showAlreadyRegistered(); // client has already registered, let em know..
}else{
var cookieVal = pad.getPadId() + "email";
if(cookie.getPref(cookieVal) !== "true"){ // if this user hasn't already subscribed
askClientToEnterEmail(); // ask the client to register TODO uncomment me for a pop up
}
exports.handleClientMessage_emailSubscriptionSuccess = function(hook, context){ // was subscribing to the email a big win or fail?
if(context.payload.success == false) {
showAlreadyRegistered(context.payload.type);
$('#' + context.payload.formName + ' [name=ep_email]').select();
} else {
showRegistrationSuccess();
// Add cookie to say an email is registered for this pad
cookie.setPref(pad.getPadId() + "email", "true");
if (clientVars.panelDisplayLocation.mysettings == true && $('.ep_email_settings').is(":visible")) {
$('.ep_email_settings').slideToggle();
$('#options-emailNotifications').prop('checked', false);
}
if (clientVars.panelDisplayLocation.popup == true && $('#ep_email_form_popup').is(":visible")) {
$('#ep_email_form_popup').parent().parent().parent().hide();
}
}
}
function showRegistrationSuccess(){ // show a successful registration message
$.gritter.add({
// (string | mandatory) the heading of the notification
title: "Email subscribed",
// (string | mandatory) the text inside the notification
text: "You will receive email when someone changes this pad. If this is the first time you have requested emails you may need to confirm your email address"
});
exports.handleClientMessage_emailUnsubscriptionSuccess = function(hook, context){ // was subscribing to the email a big win or fail?
if(context.payload.success == false) {
showWasNotRegistered();
$('#' + context.payload.formName + ' [name=ep_email]').select();
} else {
showUnregistrationSuccess();
// Set cookie to say no email is registered for this pad
cookie.setPref(pad.getPadId() + "email", "false");
if (clientVars.panelDisplayLocation.mysettings == true && $('.ep_email_settings').is(":visible")) {
$('.ep_email_settings').slideToggle();
$('#options-emailNotifications').prop('checked', false);
}
if (clientVars.panelDisplayLocation.popup == true && $('#ep_email_form_popup').is(":visible")) {
$('#ep_email_form_popup').parent().parent().parent().hide();
}
}
}
function showAlreadyRegistered(){ // the client already registered for emails on this pad so notify the UI
$.gritter.add({
// (string | mandatory) the heading of the notification
title: "Email subscription",
// (string | mandatory) the text inside the notification
text: "You are already registered for emails for this pad",
// (bool | optional) if you want it to fade out on its own or just sit there
sticky: false
});
exports.handleClientMessage_emailNotificationGetUserInfo = function (hook, context) { // return the existing options for this userId
var result = context.payload;
if(result.success == true){ // If data found, set the options with them
$('[name=ep_email]').val(result.email);
$('[name=ep_email_onStart]').prop('checked', result.onStart);
$('[name=ep_email_onEnd]').prop('checked', result.onEnd);
} else { // No data found, set the options to default values
$('[name=ep_email_onStart]').prop('checked', true);
$('[name=ep_email_onEnd]').prop('checked', false);
}
if (result.formName == 'ep_email_form_mysettings') {
$('.ep_email_settings').slideToggle();
}
}
exports.handleClientMessage_emailNotificationMissingParams = function (hook, context) { // Settings are missing in settings.json file
if (context.payload == true) {
$.gritter.add({
// (string | mandatory) the heading of the notification
title: window._('ep_email_notifications.titleGritterError'),
// (string | mandatory) the text inside the notification
text: window._('ep_email_notifications.msgParamsMissing'),
// (bool | optional) if you want it to fade out on its own or just sit there
sticky: true
});
// Hide the notification menu in mysettings
if (clientVars.panelDisplayLocation.mysettings == true && $('.ep_email_settings').is(":visible")) {
$('.ep_email_settings').slideToggle();
$('#options-emailNotifications').prop('checked', false);
$('#options-emailNotifications').parent().hide();
}
// Hide the popup if it is visible
if (clientVars.panelDisplayLocation.popup == true && $('#ep_email_form_popup').is(":visible")) {
$('#ep_email_form_popup').parent().parent().parent().hide();
}
}
}
/**
* Initialize the popup panel form for subscription
*/
function initPopupForm(){
var popUpIsAlreadyVisible = $('#ep_email_form_popup').is(":visible");
if(!popUpIsAlreadyVisible){ // if the popup isn't already visible
var cookieVal = pad.getPadId() + "email";
if(cookie.getPref(cookieVal) !== "true"){ // if this user hasn't already subscribed
askClientToEnterEmail(); // ask the client to register TODO uncomment me for a pop up
}
}
}
function clientHasAlreadyRegistered(){ // Has the client already registered for emails on this?
@ -72,41 +166,169 @@ function clientHasAlreadyRegistered(){ // Has the client already registered for
}
function askClientToEnterEmail(){
var formContent = $('.ep_email_settings')
.html()
.replace('ep_email_form_mysettings', 'ep_email_form_popup');
$.gritter.add({
// (string | mandatory) the heading of the notification
title: "Enter your email to receive an email when someone modifies this pad",
title: window._('ep_email_notifications.titleGritterSubscr'),
// (string | mandatory) the text inside the notification
text: "<form class='ep_email_form'><label for='ep_email'><input id='ep_email_notification' placeholder='your@email.com' style='padding:5px;width:180px;' type=email><input type=submit value=subscribe style='padding:5px;'></form>",
text: "<p class='ep_email_form_popup_header'>" + window._('ep_email_notifications.headerGritterSubscr') + "</p>" + formContent,
// (bool | optional) if you want it to fade out on its own or just sit there
sticky: true,
// (int | optional) the time you want it to be alive for before fading out
time: '2000',
time: 2000,
// the function to bind to the form
after_open: function(e){
$('.ep_email_form').submit(function(){
$(e).hide();
sendEmailToServer();
$('#ep_email_form_popup').submit(function(){
sendEmailToServer('ep_email_form_popup');
return false;
});
// Prepare subscription before submit form
$('#ep_email_form_popup [name=ep_email_subscribe]').on('click', function(e) {
$('#ep_email_form_popup [name=ep_email_option]').val('subscribe');
checkAndSend(e);
});
// Prepare unsubscription before submit form
$('#ep_email_form_popup [name=ep_email_unsubscribe]').on('click', function(e) {
$('#ep_email_form_popup [name=ep_email_option]').val('unsubscribe');
checkAndSend(e);
});
getDataForUserId('ep_email_form_popup');
optionsAlreadyRecovered = true;
}
});
}
function sendEmailToServer(){
var email = $('#ep_email').val();
if(!email){ // if we're not using the top value use the notification value
email = $('#ep_email_notification').val();
/**
* Control options before submitting the form
*/
function checkAndSend(e) {
var formName = $(e.currentTarget.parentNode).attr('id');
var email = $('#' + formName + ' [name=ep_email]').val();
if (email && $('#' + formName + ' [name=ep_email_option]').val() == 'subscribe'
&& !$('#' + formName + ' [name=ep_email_onStart]').is(':checked')
&& !$('#' + formName + ' [name=ep_email_onEnd]').is(':checked')) {
$.gritter.add({
// (string | mandatory) the heading of the notification
title: window._('ep_email_notifications.titleGritterError'),
// (string | mandatory) the text inside the notification
text: window._('ep_email_notifications.msgOptionsNotChecked')
});
} else if (email) {
$('#' + formName).submit();
}
return false;
}
/**
* Ask the server to register the email
*/
function sendEmailToServer(formName){
var email = $('#' + formName + ' [name=ep_email]').val();
var userId = pad.getUserId();
var message = {};
message.type = 'USERINFO_UPDATE';
message.userInfo = {};
message.padId = pad.getPadId();
message.userInfo.email = email;
message.userInfo.email_option = $('#' + formName + ' [name=ep_email_option]').val();
message.userInfo.email_onStart = $('#' + formName + ' [name=ep_email_onStart]').is(':checked');
message.userInfo.email_onEnd = $('#' + formName + ' [name=ep_email_onEnd]').is(':checked');
message.userInfo.formName = formName;
message.userInfo.userId = userId;
if(email){
pad.collabClient.sendMessage(message);
cookie.setPref(message.padId+"email", "true");
}
}
/**
* Thanks to the userId, we can get back from the Db the options set for this user
* and fill the fields with them
*/
function getDataForUserId(formName) {
var userId = pad.getUserId();
var message = {};
message.type = 'USERINFO_GET';
message.padId = pad.getPadId();
message.userInfo = {};
message.userInfo.userId = userId;
message.userInfo.formName = formName;
pad.collabClient.sendMessage(message);
}
/*************************************
Manage return msgs from server
*************************************/
/**
* Show a successful registration message
*/
function showRegistrationSuccess(){
$.gritter.add({
// (string | mandatory) the heading of the notification
title: window._('ep_email_notifications.titleGritterSubscr'),
// (string | mandatory) the text inside the notification
text: window._('ep_email_notifications.msgSubscrSuccess'),
// (int | optional) the time you want it to be alive for before fading out
time: 10000
});
}
/**
* The client already registered for emails on this pad so notify the UI
*/
function showAlreadyRegistered(type){
if (type == "malformedEmail") {
var msg = window._('ep_email_notifications.msgEmailMalformed');
} else if (type == "alreadyRegistered") {
var msg = window._('ep_email_notifications.msgAlreadySubscr');
} else {
var msg = window._('ep_email_notifications.msgUnknownErr');
}
$.gritter.add({
// (string | mandatory) the heading of the notification
title: window._('ep_email_notifications.titleGritterSubscr'),
// (string | mandatory) the text inside the notification
text: msg,
// (int | optional) the time you want it to be alive for before fading out
time: 7000
});
}
/**
* Show a successful unregistration message
*/
function showUnregistrationSuccess(){
$.gritter.add({
// (string | mandatory) the heading of the notification
title: window._('ep_email_notifications.titleGritterUnsubscr'),
// (string | mandatory) the text inside the notification
text: window._('ep_email_notifications.msgUnsubscrSuccess'),
// (int | optional) the time you want it to be alive for before fading out
time: 10000
});
}
/**
* The client wasn't registered for emails
*/
function showWasNotRegistered(){
$.gritter.add({
// (string | mandatory) the heading of the notification
title: window._('ep_email_notifications.titleGritterUnsubscr'),
// (string | mandatory) the text inside the notification
text: window._('ep_email_notifications.msgUnsubscrNotExisting'),
// (int | optional) the time you want it to be alive for before fading out
time: 7000
});
}

View file

View file

@ -0,0 +1,21 @@
<p>
<input type="checkbox" id="options-emailNotifications"></input>
<label for="options-emailNotifications" data-l10n-id="ep_email_notifications.menuLabel"></label>
<div class="ep_email_settings">
<form id='ep_email_form_mysettings'>
<input name='ep_email' class='ep_email_input' placeholder='your@email.com' type=email>
<br />
<label data-l10n-id="ep_email_notifications.formOptionsTitle"></label>
<br />
<input name='ep_email_onStart' type="checkbox" class="ep_email_checkbox"></input>
<label for="ep_email_onStart" data-l10n-id="ep_email_notifications.formOptionOnStart"></label>
<br />
<input name='ep_email_onEnd' type="checkbox" class="ep_email_checkbox"></input>
<label for="ep_email_onEnd" data-l10n-id="ep_email_notifications.formOptionOnEnd"></label>
<input name='ep_email_option' type=hidden >
<br />
<button name='ep_email_subscribe' type=button class="ep_email_buttons" data-l10n-id="ep_email_notifications.formBtnSubscr"></button>
<button name='ep_email_unsubscribe'type=button class="ep_email_buttons" data-l10n-id="ep_email_notifications.formBtnUnsubscr"></button>
</form>
</div>
</p>

View file

@ -1,3 +0,0 @@
<br>
<h2>Recieve email notifications on change</h2>
<form class='ep_email_form'><label for='ep_email'><input id='ep_email' placeholder='your@email.com' style="padding:5px;margin-top:10px;width:300px;" type=email><input style="padding:5px;" type=submit value=subscribe></form>

22
templates/response.ejs Normal file
View file

@ -0,0 +1,22 @@
<html>
<head>
<meta charset="utf-8">
<title>Email Notifications Subscription</title>
<link href="../../static/plugins/ep_email_notifications/static/css/email_notifications.css" media="all" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="validationTitle">
<h1>Email notifications</h1>
</div>
<div class="validationEmailSubscription">
<%action%>
<div class="<%classResult%>">
<%result%>
<div>
<%explanation%>
</div>
</div>
Go to the pad: <a href="<%padUrl%>"><%padUrl%></a>
</div>
</body>
</html>

View file

@ -8,12 +8,13 @@ settings = require('../../src/node/utils/Settings');
// Settings -- EDIT THESE IN settings.json not here..
var pluginSettings = settings.ep_email_notifications;
var checkFrequency = pluginSettings.checkFrequency || 60000; // 10 seconds
var staleTime = pluginSettings.staleTime || 300000; // 5 minutes
var fromName = pluginSettings.fromName || "Etherpad";
var fromEmail = pluginSettings.fromEmail || "pad@etherpad.org";
var urlToPads = pluginSettings.urlToPads || "http://beta.etherpad.org/p/";
var emailServer = pluginSettings.emailServer || {host:"127.0.0.1"};
var areParamsOk = (pluginSettings)?true:false;
var checkFrequency = (pluginSettings && pluginSettings.checkFrequency)?pluginSettings.checkFrequency:60000; // 10 seconds
var staleTime = (pluginSettings && pluginSettings.staleTime)?pluginSettings.staleTime:300000; // 5 minutes
var fromName = (pluginSettings && pluginSettings.fromName)?pluginSettings.fromName:"Etherpad";
var fromEmail = (pluginSettings && pluginSettings.fromEmail)?pluginSettings.fromEmail:"pad@etherpad.org";
var urlToPads = (pluginSettings && pluginSettings.urlToPads)?pluginSettings.urlToPads:"http://beta.etherpad.org/p/";
var emailServer = (pluginSettings && pluginSettings.emailServer)?pluginSettings.emailServer:{host:"127.0.0.1"};
// A timer object we maintain to control how we send emails
var timers = {};
@ -22,6 +23,8 @@ var timers = {};
var server = email.server.connect(emailServer);
exports.padUpdate = function (hook_name, _pad) {
if (areParamsOk == false) return false;
var pad = _pad.pad;
var padId = pad.id;
exports.sendUpdates(padId);
@ -43,21 +46,25 @@ exports.notifyBegin = function(padId){
db.get("emailSubscription:" + padId, function(err, recipients){ // get everyone we need to email
if(recipients){
async.forEach(Object.keys(recipients), function(recipient, cb){
// Is this recipient already on the pad?
exports.isUserEditingPad(padId, recipients[recipient].authorId, function(err,userIsOnPad){ // is the user already on the pad?
if(!userIsOnPad){
console.debug("Emailing "+recipient +" about a new begin update");
server.send({
text: "Your pad at "+urlToPads+padId +" is being edited, we're just emailing you let you know :)\n\n -- This plugin is in alpha state, can you help fund it's development? https://github.com/johnmclear/ep_email_notifications",
from: fromName+ "<"+fromEmail+">",
to: recipient,
subject: "Someone started editing "+padId
}, function(err, message) { console.log(err || message); });
}
else{
console.debug("Didn't send an email because user is already on the pad");
}
});
//avoid the 'pending' section
if (recipient != 'pending') {
// Is this recipient already on the pad?
exports.isUserEditingPad(padId, recipients[recipient].authorId, function(err,userIsOnPad){ // is the user already on the pad?
var onStart = typeof(recipients[recipient].onStart) == "undefined" || recipients[recipient].onStart?true:false; // In case onStart wasn't defined we set it to true
if(!userIsOnPad && onStart){
console.debug("Emailing "+recipient +" about a new begin update");
server.send({
text: "Your pad at "+urlToPads+padId +" is being edited, we're just emailing you let you know :)",
from: fromName+ "<"+fromEmail+">",
to: recipient,
subject: "Someone started editing "+padId
}, function(err, message) { console.log(err || message); });
}
else{
console.debug("Didn't send an email because user is already on the pad");
}
});
}
cb(); // finish each user
},
function(err){
@ -74,21 +81,26 @@ exports.notifyEnd = function(padId){
db.get("emailSubscription:" + padId, function(err, recipients){ // get everyone we need to email
if(recipients){
async.forEach(Object.keys(recipients), function(recipient, cb){
// Is this recipient already on the pad?
exports.isUserEditingPad(padId, recipients[recipient].authorId, function(err,userIsOnPad){ // is the user already on the$
if(!userIsOnPad){
console.debug("Emailing "+recipient +" about a pad finished being updated");
server.send({
text: "Your pad at "+urlToPads+padId +" has finished being edited, we're just emailing you let you know :) \n\n The changes look like this: \n" + changesToPad,
from: fromName+ "<"+fromEmail+">",
to: recipient,
subject: "Someone finished editing "+padId
}, function(err, message) { console.log(err || message); });
}
else{
console.debug("Didn't send an email because user is already on the pad");
}
});
//avoid the 'pending' section
if (recipient != 'pending') {
// Is this recipient already on the pad?
exports.isUserEditingPad(padId, recipients[recipient].authorId, function(err,userIsOnPad){ // is the user already on the$
var onEnd = typeof(recipients[recipient].onEnd) == "undefined" || recipients[recipient].onEnd?true:false; // In case onEnd wasn't defined we set it to false
if(!userIsOnPad && onEnd){
console.debug("Emailing "+recipient +" about a pad finished being updated");
server.send({
text: "Your pad at "+urlToPads+padId +" has finished being edited, we're just emailing you let you know :) \n\n The changes look like this: \n" + changesToPad,
from: fromName+ "<"+fromEmail+">",
to: recipient,
subject: "Someone finished editing "+padId
}, function(err, message) { console.log(err || message); });
}
else{
console.debug("Didn't send an email because user is already on the pad");
}
});
}
cb(); // finish each user
},
function(err){