<commit_before>/**
 * Created by Carson on 15/01/2017.
 * File to handle logic of users subscribing to posts/comments.
 * Inserts information when a user subscribes,
 * queries when content added/edited to see if email notification
 * should be sent to users that are subscribed.
 */
var nodeMailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var log = require('./log.js');
var dbr = require('./DBRow.js');
var generator = require('./IDGenerator.js');
var lit = require('./Literals.js');

//object that holds the mailing information - who sends it (and their authentication) and connection details
var transport = nodeMailer.createTransport(smtpTransport({
    HOST: 'outlook.office.com',
    secureConnection: false, //use SSL as this is not a secure connection
    port: 587,
    auth: {
        user: "<EMAIL>",
        pass: "<PASSWORD>" //TODO don't store here
    }
}));

//object that holds the actual email's information - subject, receiver, text etc.
var mailOptions = {
    from: '<EMAIL>',
    to: '', //will be changed to <EMAIL> in emailUser()
    subject: 'Forum Subscription',
    text: 'There was a change to content you are subscribed to! Click here to see: ' //will add url here
};

/**
 * Function to be called when a user clicks a subscribe button.
 * Adds their information to the subscriptions table.
 * @param contentID The ID of the post/comment the user subscribed to.
 * @param userID The ID of the user who subscribed.
 */
function onSubscribed(contentID, userID) {
    //query comment table for content ID, if in there, set type to comment
    //if not in there, then type is a post
    var commentTable = new dbr.DBRow(lit.COMMENT_TABLE);
    commentTable.addQuery(lit.FIELD_ID, contentID);
    commentTable.query().then(function () {
        //set in database all the information
        var newRow = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
        if (!commentTable.next()) { //type is table
            newRow.setValue(lit.TYPE, lit.POST_TABLE);
        } else {
            newRow.setValue(lit.TYPE, lit.COMMENT_TABLE);
        }

        newRow.setValue(lit.FIELD_USER_ID, userID);
        newRow.setValue(lit.FIELD_ITEM_ID, contentID);
        newRow.setValue(lit.FIELD_ID, generator.generate());
        newRow.setValue(lit.FIELD_DATE_SUBSCRIBED, new Date().toISOString());

        newRow.insert();
    }).catch(function (err) {
        log.log("onSubscribed error: " + err);
    });
}

/**
 * Function to be called when content edited or
 * a child comment is added.
 * @param contentID ID of content added/edited.
 */
onContentAddedOrChanged('3ntcfsbrqunk27yymvmtetdvkrg33es1');
function onContentAddedOrChanged(contentID) {
    //get ID of content user actually subscribed to
    getParentContentID(contentID).then(function (contentID) {
        //add to number of notifications missed
        return addToNotificationsMissed(contentID);
    }).then(function (contentID) {
        //go through email logic
        return emailUsers(contentID);
    }).catch(function (error) {
        log.log("onContentAddedOrChanged error: " + error);
    });
}

/**
 * Function that emails users.
 * Computes logic to see if an individual *should* be emailed as well.
 * @param contentID ID of the content the user is to be emailed about.
 * @returns {Promise} Asynch tasks called/done from this function, so
 * use promise to make it synchronous. Chained from  caller method.
 */
function emailUsers(contentID) {
    return new Promise(function (resolve, reject) {
            getUserIDs(contentID).then(function (userIDs) {
                return userIDs;
            }).then(function (userIDs) {
                //get userIDs for those who should be emailed
                return findUsersToEmail(userIDs);
            }).then(function (userIDs) {
                //get net IDs from user IDs
                return getNetIDs(userIDs);
            }).then(function (netIDs) {
                //email users
                for (var i in netIDs) {
                    //TODO add url to give info
                    mailOptions[lit.TO] = netIDs[i] + lit.QUEENS_EMAIL;
                    log.log("MAIL SENT");
                    // transport.sendMail(mailOptions);//TODO uncomment
                }
                return netIDs;
            }).then(function () {
                return setNotificationsMissedToZero(contentID);
            }).then(function () {
                //update last notified to current time
                setLastNotifiedToNow(contentID);
            }).catch(function (err) {
                reject(err);
            });
        }
    );
}

/**
 * Function that increments the number of edits/child content that have gone without
 * an email to subscribed users.
 * @param contentID ID of the content to increase numMissedNotifications for
 * @returns {Promise} Asynch tasks called/done from this function, so
 * use promise to make it synchronous. Chained from  caller method.
 */
function addToNotificationsMissed(contentID) {
    var row = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
    row.addQuery(lit.FIELD_ITEM_ID, contentID);
    return new Promise(function (resolve, reject) {
        row.query().then(function () {
            while (row.next()) { //increment notifications missed for all users that subscribed to that content
                row.setValue(lit.FIELD_NUM_NOTIFICATIONS_MISSED, row.getValue(lit.FIELD_NUM_NOTIFICATIONS_MISSED) + 1);
                row.update().then(function (err) {
                    reject(err);
                });
            }
            resolve(contentID);
        }, function (err) {
            reject(err);
        });
    });
}

/**
 * Sets numNotificationsMissed to 0 for all pieces of content that had an email sent out.
 * @param contentID ID of the content that was emailed for, so any row with this
 * itemID needs its notifications missed to be set to 0.
 * @returns {Promise} Asynch tasks called/done from this function, so
 * use promise to make it synchronous. Chained from caller method.
 */
function setNotificationsMissedToZero(contentID) {
    var row = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
    row.addQuery(lit.FIELD_ITEM_ID, contentID);
    return new Promise(function (resolve, reject) {
        row.query().then(function () {
            while (row.next()) { //set notifications missed to zero for all users that subscribed to that content
                row.setValue(lit.FIELD_NUM_NOTIFICATIONS_MISSED, 0);
                row.update();
            }
            resolve(contentID);
        }, function (err) {
            reject(err);
        });
    });
}

function setLastNotifiedToNow(contentID) {
    var row = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
    row.addQuery(lit.FIELD_ITEM_ID, contentID);
    row.query().then(function () {
        while (row.next()) {<commit_msg>Added documentation to subscriptions function<commit_after>/**
 * Created by Carson on 15/01/2017.
 * File to handle logic of users subscribing to posts/comments.
 * Inserts information when a user subscribes,
 * queries when content added/edited to see if email notification
 * should be sent to users that are subscribed.
 */
var nodeMailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var log = require('./log.js');
var dbr = require('./DBRow.js');
var generator = require('./IDGenerator.js');
var lit = require('./Literals.js');

//object that holds the mailing information - who sends it (and their authentication) and connection details
var transport = nodeMailer.createTransport(smtpTransport({
    HOST: 'outlook.office.com',
    secureConnection: false, //use SSL as this is not a secure connection
    port: 587,
    auth: {
        user: "<EMAIL>",
        pass: "<PASSWORD>" //TODO don't store here
    }
}));

//object that holds the actual email's information - subject, receiver, text etc.
var mailOptions = {
    from: 'do-<EMAIL>',
    to: '', //will be changed to <EMAIL> in emailUser()
    subject: 'Forum Subscription',
    text: 'There was a change to content you are subscribed to! Click here to see: ' //will add url here
};

/**
 * Function to be called when a user clicks a subscribe button.
 * Adds their information to the subscriptions table.
 * @param contentID The ID of the post/comment the user subscribed to.
 * @param userID The ID of the user who subscribed.
 */
function onSubscribed(contentID, userID) {
    //query comment table for content ID, if in there, set type to comment
    //if not in there, then type is a post
    var commentTable = new dbr.DBRow(lit.COMMENT_TABLE);
    commentTable.addQuery(lit.FIELD_ID, contentID);
    commentTable.query().then(function () {
        //set in database all the information
        var newRow = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
        if (!commentTable.next()) { //type is table
            newRow.setValue(lit.TYPE, lit.POST_TABLE);
        } else {
            newRow.setValue(lit.TYPE, lit.COMMENT_TABLE);
        }

        newRow.setValue(lit.FIELD_USER_ID, userID);
        newRow.setValue(lit.FIELD_ITEM_ID, contentID);
        newRow.setValue(lit.FIELD_ID, generator.generate());
        newRow.setValue(lit.FIELD_DATE_SUBSCRIBED, new Date().toISOString());

        newRow.insert();
    }).catch(function (err) {
        log.log("onSubscribed error: " + err);
    });
}

/**
 * Function to be called when content edited or
 * a child comment is added.
 * @param contentID ID of content added/edited.
 */
onContentAddedOrChanged('3ntcfsbrqunk27yymvmtetdvkrg33es1');
function onContentAddedOrChanged(contentID) {
    //get ID of content user actually subscribed to
    getParentContentID(contentID).then(function (contentID) {
        //add to number of notifications missed
        return addToNotificationsMissed(contentID);
    }).then(function (contentID) {
        //go through email logic
        return emailUsers(contentID);
    }).catch(function (error) {
        log.log("onContentAddedOrChanged error: " + error);
    });
}

/**
 * Function that emails users.
 * Computes logic to see if an individual *should* be emailed as well.
 * @param contentID ID of the content the user is to be emailed about.
 * @returns {Promise} Asynch tasks called/done from this function, so
 * use promise to make it synchronous. Chained from  caller method.
 */
function emailUsers(contentID) {
    return new Promise(function (resolve, reject) {
            getUserIDs(contentID).then(function (userIDs) {
                return userIDs;
            }).then(function (userIDs) {
                //get userIDs for those who should be emailed
                return findUsersToEmail(userIDs);
            }).then(function (userIDs) {
                //get net IDs from user IDs
                return getNetIDs(userIDs);
            }).then(function (netIDs) {
                //email users
                for (var i in netIDs) {
                    //TODO add url to give info
                    mailOptions[lit.TO] = netIDs[i] + lit.QUEENS_EMAIL;
                    log.log("MAIL SENT");
                    // transport.sendMail(mailOptions);//TODO uncomment
                }
                return netIDs;
            }).then(function () {
                return setNotificationsMissedToZero(contentID);
            }).then(function () {
                //update last notified to current time
                setLastNotifiedToNow(contentID);
            }).catch(function (err) {
                reject(err);
            });
        }
    );
}

/**
 * Function that increments the number of edits/child content that have gone without
 * an email to subscribed users.
 * @param contentID ID of the content to increase numMissedNotifications for
 * @returns {Promise} Asynch tasks called/done from this function, so
 * use promise to make it synchronous. Chained from  caller method.
 */
function addToNotificationsMissed(contentID) {
    var row = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
    row.addQuery(lit.FIELD_ITEM_ID, contentID);
    return new Promise(function (resolve, reject) {
        row.query().then(function () {
            while (row.next()) { //increment notifications missed for all users that subscribed to that content
                row.setValue(lit.FIELD_NUM_NOTIFICATIONS_MISSED, row.getValue(lit.FIELD_NUM_NOTIFICATIONS_MISSED) + 1);
                row.update().then(function (err) {
                    reject(err);
                });
            }
            resolve(contentID);
        }, function (err) {
            reject(err);
        });
    });
}

/**
 * Sets numNotificationsMissed to 0 for all pieces of content that had an email sent out.
 * @param contentID ID of the content that was emailed for, so any row with this
 * itemID needs its notifications missed to be set to 0.
 * @returns {Promise} Asynch tasks called/done from this function, so
 * use promise to make it synchronous. Chained from caller method.
 */
function setNotificationsMissedToZero(contentID) {
    var row = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
    row.addQuery(lit.FIELD_ITEM_ID, contentID);
    return new Promise(function (resolve, reject) {
        row.query().then(function () {
            while (row.next()) { //set notifications missed to zero for all users that subscribed to that content
                row.setValue(lit.FIELD_NUM_NOTIFICATIONS_MISSED, 0);
                row.update();
            }
            resolve(contentID);
        }, function (err) {
            reject(err);
        });
    });
}

/**
 * Function that sets the lastNotified field for content that users have been emailed about.
 * Field is set to the current date/time.
 * @param contentID ID of content that was emailed for.
 * No return because this is the last function called from the promise chain.
 */
function setLastNotifiedToNow(contentID) {
    var row = new dbr.DBRow(lit.SUBSCRIPTIONS_TABLE);
    row.addQuery(lit.FIELD_ITEM_ID, contentID);
    row.query().then(function () {
        while (row.next()) {
{'fraction_non_alphanumeric': 0.10239931448157669, 'fraction_numerical': 0.0029991431019708655, 'mean_word_length': 2.603035760226396, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5497951', 'n_tokens_mistral': 3960, 'n_tokens_neox': 3837, 'n_words': 1265}