<commit_before>import { A, isArray } from '@ember/array';
import { inject as service } from '@ember/service';
import { run } from '@ember/runloop';
import loadAll from 'ember-osf/utils/load-relationship';
import Route from '@ember/routing/route';
import $ from 'jquery';
import Analytics from 'ember-osf/mixins/analytics';
import config from 'ember-get-config';
import extractDoiFromString from 'ember-osf/utils/extract-doi-from-string';

import ResetScrollMixin from '../../mixins/reset-scroll';

const {
    OSF: { url: osfUrl },
} = config;

// Error handling for API
const handlers = new Map([
    // format: ['Message detail', 'page']
    ['Authentication credentials were not provided.', 'page-not-found'], // 401
    ['You do not have permission to perform this action.', 'page-not-found'], // 403
    ['Not found.', 'page-not-found'], // 404
    ['The requested node is no longer available.', 'resource-deleted'], // 410
]);

/**
 * @module ember-preprints
 * @submodule routes
 */

/**
 * Fetches current preprint. Redirects to preprint provider route if necessary.
 * @class Content Route Handler
 */
export default Route.extend(Analytics, ResetScrollMixin, {
    theme: service(),
    headTagsService: service('head-tags'),
    currentUser: service('currentUser'),
    toast: service('toast'),
    i18n: service(),

    downloadUrl: '',
    preprint: null,
    node: null,
    waitForMetaData: navigator.userAgent.includes('Prerender'),
    contributors: A(),

    afterModel(preprint) {
        const { location: { origin } } = window;

        const downloadUrl = [
            origin,
            this.get('theme.isSubRoute') ? `preprints/${this.get('theme.id')}` : null,
            preprint.get('id'),
            'download',
        ]
            .filter(part => !!part)
            .join('/');
        this.set('downloadUrl', downloadUrl);

        this.set('fileDownloadURL', downloadUrl);
        this.set('preprint', preprint);

        if (!preprint.get('dateWithdrawn')) {
            const setupMetaData = preprint.get('provider')
                .then(this._getProviderDetails.bind(this))
                .then(this._getUserPermissions.bind(this))
                .then(this._setupMetaData.bind(this));

            const setupPreprint = this._getPrimaryFile()
                .then(this._loadSupplementalNode.bind(this));

            return this.get('waitForMetaData') ? setupMetaData : setupPreprint;
        }
    },

    setupController(controller, model) {
        controller.setProperties({
            primaryFile: model.get('primaryFile'),
            node: this.get('node'),
            fileDownloadURL: this.get('fileDownloadURL'),
            isPendingWithdrawal: this.get('isPendingWithdrawal'),
            isWithdrawn: model.get('dateWithdrawn') !== null,
        });

        run.scheduleOnce('afterRender', this, function() {
            MathJax.Hub.Queue(['Typeset', MathJax.Hub, [$('.abstract')[0], $('#preprintTitle')[0]]]);
        });

        return this._super(...arguments);
    },

    actions: {
        error(error) {
            // Handle API Errors
            if (error && error.errors && isArray(error.errors)) {
                const { detail } = error.errors[0];
                const page = handlers.get(detail) || 'page-not-found';
                return this.intermediateTransitionTo(page);
            }
        },
        didTransition() {
            const ev = document.createEvent('HTMLEvents');
            ev.initEvent('ZoteroItemUpdated', true, true);
            document.dispatchEvent(ev);
            return true; // Bubble the didTransition event
        },
    },

    _getProviderDetails(provider) {
        const providerId = provider.get('id');
        const themeId = this.get('theme.id');
        const isOSF = providerId === 'osf';
        const preprint = this.get('preprint');

        // If we're on the proper branded site, stay here.
        if (themeId === providerId) {
            return Promise.all([
                provider,
            ]);
        }

        window.location.replace(`${osfUrl}${isOSF ? '' : `preprints/${providerId}/`}${preprint.get('id')}/`);
        return Promise.reject();
    },

    _getPrimaryFile() {
        return this.store.findRecord('file', this.get('preprint.primaryFile.id'), { reload: true })
            .then(this._successLoadingPrimaryFile.bind(this))
            .catch(this._errorLoadingPrimaryFile.bind(this));
    },

    _loadSupplementalNode() {
        if (this.get('preprint.node.id')) {
            return this.store.findRecord('node', this.get('preprint.node.id'))
                .then(this._successLoadingSupplemental.bind(this))
                .catch(this._errorLoadingSupplemental.bind(this));
        }
    },

    _successLoadingPrimaryFile(primaryFile) {
        return this.set('primaryFile', primaryFile);
    },

    _errorLoadingPrimaryFile() {
        this.get('toast').error(this.get('i18n').t('content.error_loading_preprint_file'));
    },

    _successLoadingSupplemental(node) {
        return this.set('node', node);
    },

    _errorLoadingSupplemental() {
        // User does not have permission to view the supplemental node - is private
        return this.set('node', null);
    },

    _getUserPermissions([provider]) {
        const contributors = this.get('contributors');
        const preprint = this.get('preprint');

        return Promise.all([
            provider,
            preprint.get('license'),
            loadAll(preprint, 'contributors', contributors, { filter: { bibliographic: true } }),
        ]);
    },

    _setupMetaData([provider, license]) {
        const { origin } = window.location;
        const preprint = this.get('preprint');
        const title = preprint.get('title');
        const description = preprint.get('description');
        const facebookAppId = provider.get('facebookAppId') || config.FB_APP_ID;
        const mintDoi = extractDoiFromString(preprint.get('preprintDoiUrl'));
        const peerDoi = preprint.get('doi');
        const image = this.get('theme.logoSharing');
        const imageUrl = /^https?:\/\//.test(image.path) ? image.path : origin + image.path;
        const dateCreated = new Date(preprint.get('dateCreated') || null);
        const dateModified = new Date(preprint.get('dateModified') || dateCreated);
        if (!preprint.get('datePublished')) { preprint.set('datePublished', dateCreated); }
        const providerName = provider.get('name') === 'Open Science Framework' ? 'OSF Preprints' : provider.get('name');
        const canonicalUrl = preprint.get('links.html');
        const contributors = this.get('contributors');
        const downloadUrl = this.get('downloadUrl');

        // NOTE: Ordering of meta tags matters for scrapers
        // EX: (Facebook, LinkedIn, Google, etc)

        // Open Graph Protocol
        const openGraph = [
            ['fb:app_id', facebookAppId],
            ['og:title', title],
            ['og:image', imageUrl],
            ['og:image:width', image.width.toString()],
            ['og:image:height', image.height.toString()],
            ['og:image:type', image.type],
            ['og:url', canonicalUrl],
            ['og:description', description],
            ['og:site_name', providerName],
            ['og:type', 'article'],
            ['article:published_time', dateCreated.toISOString()],
            ['article:modified_time', dateModified.toISOString()],
        ];

        // Highwire Press
        const highwirePress = [
            ['citation_title', title],
            ['citation_description', description],
            ['citation_public_url', canonicalUrl],
            ['citation_online_date', `${dateCreated.getFullYear()}/${dateCreated.getMonth() + 1}/${dateCreated.getDate()}`],
        ];
        if (mintDoi) {
            highwirePress.push(['citation_doi', mintDoi]);
        }

        // TODO map Eprints fields
        // Eprints
        const eprints = [];

        // TODO map BE Press fields
        // BE Press
        const bePress = [];

        // TODO map PRISM fields
        // PRISM
        const prism = [];

        // Dublin Core
        const dublinCore = [
            ['dc.title', title],
            ['dc.abstract', description],
            ['dc.identifier', canonicalUrl],
        ];
        if (mintDoi) {
            dublinCore.push(['dc.identifier', `doi:${mintDoi}`]);
        }
        if (peerDoi) {
            dublinCore.push(['dc.relation', `doi:${peerDoi}`]);
        }

        const tags = [
            ...preprint.get('subjects').map(subjectBlock => subjectBlock.map(subject => subject.text)),
            ...preprint.get('tags'),
        ];

        for (const tag of tags) {
            openGraph.push(['article:tag', tag]);
            highwirePress.push(['citation_keywords', tag]);
            dublinCore.push(['dc.subject', tag]);
        }

        for (const contributor of contributors) {
            let givenName = contributor.get('users.givenName');
            let familyName = contributor.get('users.familyName');
            let fullName = contributor.get('users.fullName');

            // If the contributor is unregistered, use the
            // unregistered_contributor field for first/last/middle names
            if (contributor.get('unregisteredContributor')) {
                const unregisteredName = contributor.get('unregisteredContributor').split(' ');
                [givenName] = unregisteredName;
                familyName = unregisteredName.length > 1 ? unregisteredName.pop() : '';
                fullName = contributor.get('unregisteredContributor');
            }

            openGraph.push(
                ['og:type', 'article:author'],
                ['profile:first_name', givenName],
                ['profile:last_name', familyName],
            );
            highwirePress.push(['citation_author', fullName]);
            dublinCore.push(['dc.creator', fullName]);
        }

        highwirePress.push(['citation_publisher', providerName]);
        dublinCore.push(
            ['dc.publisher', providerName],
            ['dc.license', license ? license.get('name') : 'No license'],
        );

        highwirePress.push(['citation_pdf_url', `${downloadUrl}?format=pdf`]);

        const openGraphTags = openGraph
            .map(([property, content]) => ({
                property,
                content,
            }));

        const googleScholarTags = [
            highwirePress,
            eprints,
            bePress,
            prism,
            dublinCore,
        ]
            .reduce((a, b) => a.concat(b), [])
            .map(([name, content]) => ({
                name,
                content,
            }));

        const headTags = [
            ...openGraphTags,
            ...googleScholarTags,
        ]
            .filter(({ content }) => content) // Only show tags with content
            .map(attrs => ({
                type: 'meta',
                attrs,
            }));

        this.set('headTags', headTags);
        this.get('headTagsService').collectHeadTags();
        this.controller.set('isPlauditReady', true);
    },
});
<commit_msg>make sure controller is set for the route by using scheduleOnce on afterRender<commit_after>import { A, isArray } from '@ember/array';
import { inject as service } from '@ember/service';
import { run } from '@ember/runloop';
import loadAll from 'ember-osf/utils/load-relationship';
import Route from '@ember/routing/route';
import $ from 'jquery';
import Analytics from 'ember-osf/mixins/analytics';
import config from 'ember-get-config';
import extractDoiFromString from 'ember-osf/utils/extract-doi-from-string';

import ResetScrollMixin from '../../mixins/reset-scroll';

const {
    OSF: { url: osfUrl },
} = config;

// Error handling for API
const handlers = new Map([
    // format: ['Message detail', 'page']
    ['Authentication credentials were not provided.', 'page-not-found'], // 401
    ['You do not have permission to perform this action.', 'page-not-found'], // 403
    ['Not found.', 'page-not-found'], // 404
    ['The requested node is no longer available.', 'resource-deleted'], // 410
]);

/**
 * @module ember-preprints
 * @submodule routes
 */

/**
 * Fetches current preprint. Redirects to preprint provider route if necessary.
 * @class Content Route Handler
 */
export default Route.extend(Analytics, ResetScrollMixin, {
    theme: service(),
    headTagsService: service('head-tags'),
    currentUser: service('currentUser'),
    toast: service('toast'),
    i18n: service(),

    downloadUrl: '',
    preprint: null,
    node: null,
    waitForMetaData: navigator.userAgent.includes('Prerender'),
    contributors: A(),

    afterModel(preprint) {
        const { location: { origin } } = window;

        const downloadUrl = [
            origin,
            this.get('theme.isSubRoute') ? `preprints/${this.get('theme.id')}` : null,
            preprint.get('id'),
            'download',
        ]
            .filter(part => !!part)
            .join('/');
        this.set('downloadUrl', downloadUrl);

        this.set('fileDownloadURL', downloadUrl);
        this.set('preprint', preprint);

        if (!preprint.get('dateWithdrawn')) {
            const setupMetaData = preprint.get('provider')
                .then(this._getProviderDetails.bind(this))
                .then(this._getUserPermissions.bind(this))
                .then(this._setupMetaData.bind(this));

            const setupPreprint = this._getPrimaryFile()
                .then(this._loadSupplementalNode.bind(this));

            return this.get('waitForMetaData') ? setupMetaData : setupPreprint;
        }
    },

    setupController(controller, model) {
        controller.setProperties({
            primaryFile: model.get('primaryFile'),
            node: this.get('node'),
            fileDownloadURL: this.get('fileDownloadURL'),
            isPendingWithdrawal: this.get('isPendingWithdrawal'),
            isWithdrawn: model.get('dateWithdrawn') !== null,
        });

        run.scheduleOnce('afterRender', this, function() {
            MathJax.Hub.Queue(['Typeset', MathJax.Hub, [$('.abstract')[0], $('#preprintTitle')[0]]]);
        });

        return this._super(...arguments);
    },

    actions: {
        error(error) {
            // Handle API Errors
            if (error && error.errors && isArray(error.errors)) {
                const { detail } = error.errors[0];
                const page = handlers.get(detail) || 'page-not-found';
                return this.intermediateTransitionTo(page);
            }
        },
        didTransition() {
            const ev = document.createEvent('HTMLEvents');
            ev.initEvent('ZoteroItemUpdated', true, true);
            document.dispatchEvent(ev);
            return true; // Bubble the didTransition event
        },
    },

    _getProviderDetails(provider) {
        const providerId = provider.get('id');
        const themeId = this.get('theme.id');
        const isOSF = providerId === 'osf';
        const preprint = this.get('preprint');

        // If we're on the proper branded site, stay here.
        if (themeId === providerId) {
            return Promise.all([
                provider,
            ]);
        }

        window.location.replace(`${osfUrl}${isOSF ? '' : `preprints/${providerId}/`}${preprint.get('id')}/`);
        return Promise.reject();
    },

    _getPrimaryFile() {
        return this.store.findRecord('file', this.get('preprint.primaryFile.id'), { reload: true })
            .then(this._successLoadingPrimaryFile.bind(this))
            .catch(this._errorLoadingPrimaryFile.bind(this));
    },

    _loadSupplementalNode() {
        if (this.get('preprint.node.id')) {
            return this.store.findRecord('node', this.get('preprint.node.id'))
                .then(this._successLoadingSupplemental.bind(this))
                .catch(this._errorLoadingSupplemental.bind(this));
        }
    },

    _successLoadingPrimaryFile(primaryFile) {
        return this.set('primaryFile', primaryFile);
    },

    _errorLoadingPrimaryFile() {
        this.get('toast').error(this.get('i18n').t('content.error_loading_preprint_file'));
    },

    _successLoadingSupplemental(node) {
        return this.set('node', node);
    },

    _errorLoadingSupplemental() {
        // User does not have permission to view the supplemental node - is private
        return this.set('node', null);
    },

    _getUserPermissions([provider]) {
        const contributors = this.get('contributors');
        const preprint = this.get('preprint');

        return Promise.all([
            provider,
            preprint.get('license'),
            loadAll(preprint, 'contributors', contributors, { filter: { bibliographic: true } }),
        ]);
    },

    _setupMetaData([provider, license]) {
        const { origin } = window.location;
        const preprint = this.get('preprint');
        const title = preprint.get('title');
        const description = preprint.get('description');
        const facebookAppId = provider.get('facebookAppId') || config.FB_APP_ID;
        const mintDoi = extractDoiFromString(preprint.get('preprintDoiUrl'));
        const peerDoi = preprint.get('doi');
        const image = this.get('theme.logoSharing');
        const imageUrl = /^https?:\/\//.test(image.path) ? image.path : origin + image.path;
        const dateCreated = new Date(preprint.get('dateCreated') || null);
        const dateModified = new Date(preprint.get('dateModified') || dateCreated);
        if (!preprint.get('datePublished')) { preprint.set('datePublished', dateCreated); }
        const providerName = provider.get('name') === 'Open Science Framework' ? 'OSF Preprints' : provider.get('name');
        const canonicalUrl = preprint.get('links.html');
        const contributors = this.get('contributors');
        const downloadUrl = this.get('downloadUrl');

        // NOTE: Ordering of meta tags matters for scrapers
        // EX: (Facebook, LinkedIn, Google, etc)

        // Open Graph Protocol
        const openGraph = [
            ['fb:app_id', facebookAppId],
            ['og:title', title],
            ['og:image', imageUrl],
            ['og:image:width', image.width.toString()],
            ['og:image:height', image.height.toString()],
            ['og:image:type', image.type],
            ['og:url', canonicalUrl],
            ['og:description', description],
            ['og:site_name', providerName],
            ['og:type', 'article'],
            ['article:published_time', dateCreated.toISOString()],
            ['article:modified_time', dateModified.toISOString()],
        ];

        // Highwire Press
        const highwirePress = [
            ['citation_title', title],
            ['citation_description', description],
            ['citation_public_url', canonicalUrl],
            ['citation_online_date', `${dateCreated.getFullYear()}/${dateCreated.getMonth() + 1}/${dateCreated.getDate()}`],
        ];
        if (mintDoi) {
            highwirePress.push(['citation_doi', mintDoi]);
        }

        // TODO map Eprints fields
        // Eprints
        const eprints = [];

        // TODO map BE Press fields
        // BE Press
        const bePress = [];

        // TODO map PRISM fields
        // PRISM
        const prism = [];

        // Dublin Core
        const dublinCore = [
            ['dc.title', title],
            ['dc.abstract', description],
            ['dc.identifier', canonicalUrl],
        ];
        if (mintDoi) {
            dublinCore.push(['dc.identifier', `doi:${mintDoi}`]);
        }
        if (peerDoi) {
            dublinCore.push(['dc.relation', `doi:${peerDoi}`]);
        }

        const tags = [
            ...preprint.get('subjects').map(subjectBlock => subjectBlock.map(subject => subject.text)),
            ...preprint.get('tags'),
        ];

        for (const tag of tags) {
            openGraph.push(['article:tag', tag]);
            highwirePress.push(['citation_keywords', tag]);
            dublinCore.push(['dc.subject', tag]);
        }

        for (const contributor of contributors) {
            let givenName = contributor.get('users.givenName');
            let familyName = contributor.get('users.familyName');
            let fullName = contributor.get('users.fullName');

            // If the contributor is unregistered, use the
            // unregistered_contributor field for first/last/middle names
            if (contributor.get('unregisteredContributor')) {
                const unregisteredName = contributor.get('unregisteredContributor').split(' ');
                [givenName] = unregisteredName;
                familyName = unregisteredName.length > 1 ? unregisteredName.pop() : '';
                fullName = contributor.get('unregisteredContributor');
            }

            openGraph.push(
                ['og:type', 'article:author'],
                ['profile:first_name', givenName],
                ['profile:last_name', familyName],
            );
            highwirePress.push(['citation_author', fullName]);
            dublinCore.push(['dc.creator', fullName]);
        }

        highwirePress.push(['citation_publisher', providerName]);
        dublinCore.push(
            ['dc.publisher', providerName],
            ['dc.license', license ? license.get('name') : 'No license'],
        );

        highwirePress.push(['citation_pdf_url', `${downloadUrl}?format=pdf`]);

        const openGraphTags = openGraph
            .map(([property, content]) => ({
                property,
                content,
            }));

        const googleScholarTags = [
            highwirePress,
            eprints,
            bePress,
            prism,
            dublinCore,
        ]
            .reduce((a, b) => a.concat(b), [])
            .map(([name, content]) => ({
                name,
                content,
            }));

        const headTags = [
            ...openGraphTags,
            ...googleScholarTags,
        ]
            .filter(({ content }) => content) // Only show tags with content
            .map(attrs => ({
                type: 'meta',
                attrs,
            }));

        this.set('headTags', headTags);
        this.get('headTagsService').collectHeadTags();
        run.scheduleOnce('afterRender', this, function() {
            this.controller.set('isPlauditReady', true);
        });
    },
});
<|endoftext|>


<commit_before>var ContributionsDAO = require("../data/contributions-dao").ContributionsDAO;

/* The ContributionsHandler must be constructed with a connected db */

function ContributionsHandler(db) {
    "use strict";

    var contributionsDAO = new ContributionsDAO(db);

    function getMyResults(req, res) {
    var yr = req.params.yr ;
    var jsonQuery = {"year":yr} ;  //add or remove comma seperated "key":values given your JSON collection
    var jsonProjection = {_id:0,"year":1,"quarter":1,"daily":1,"sms":1,"paid":1} ; //leave year out since that's specified in the query anyhow
    var jsort = {"some-thing-else":-1} ; //-1 descending or 1 ascending
    //db.collection("YOUR-COLLECTION_NAME", function(err, collection) {
    /*    collection.find( jsonQuery, jsonProjection).sort(jsort).toArray( function(err, items) {
      //      res.send(items);
        });
*/        //Use of untrusted input.
   // var cursor = db.collection.find({name: req.params.name}, function (err, docs){
  //Process the results
    });
    //Use of untrusted input in $where
    var condition = "this.group == "+req.params.group;
    var cursor = db.myCollection.find( { $where: condition}, function (err, docs){
  //Process the results
});
   // });
}

    this.displayContributions = function(req, res, next) {
        var userId = req.session.userId;

        contributionsDAO.getByUserId(userId, function(error, contrib) {
            if (error) return next(error);

            contrib.userId = userId; //set for nav menu items
            return res.render("contributions", contrib);
        });
    };

    this.handleContributionsUpdate = function(req, res, next) {

        /*jslint evil: true */
        // Insecure use of eval() to parse inputs
        var preTax = eval(req.body.preTax);
        var afterTax = eval(req.body.afterTax);
        var roth = eval(req.body.roth);

        /*
        //Fix for A1 -1 SSJS Injection attacks - uses alternate method to eval
        var preTax = parseInt(req.body.preTax);
        var afterTax = parseInt(req.body.afterTax);
        var roth = parseInt(req.body.roth);
        */
        var userId = req.session.userId;

        //validate contributions
        if (isNaN(preTax) || isNaN(afterTax) || isNaN(roth) || preTax < 0 || afterTax < 0 || roth < 0) {
            return res.render("contributions", {
                updateError: "Invalid contribution percentages",
                userId: userId
            });
        }
        // Prevent more than 30% contributions
        if (preTax + afterTax + roth > 30) {
            return res.render("contributions", {
                updateError: "Contribution percentages cannot exceed 30 %",
                userId: userId
            });
        }

        contributionsDAO.update(userId, preTax, afterTax, roth, function(err, contributions) {

            if (err) return next(err);

            contributions.updateSuccess = true;
            return res.render("contributions", contributions);
        });

    };

}

module.exports = ContributionsHandler;
<commit_msg>testing find<commit_after>var ContributionsDAO = require("../data/contributions-dao").ContributionsDAO;

/* The ContributionsHandler must be constructed with a connected db */

function ContributionsHandler(db) {
    "use strict";

    var contributionsDAO = new ContributionsDAO(db);

    function getMyResults(req, res) {
    var yr = req.params.yr ;
    var jsonQuery = {"year":yr} ;  //add or remove comma seperated "key":values given your JSON collection
    var jsonProjection = {_id:0,"year":1,"quarter":1,"daily":1,"sms":1,"paid":1} ; //leave year out since that's specified in the query anyhow
    var jsort = {"some-thing-else":-1} ; //-1 descending or 1 ascending
    //db.collection("YOUR-COLLECTION_NAME", function(err, collection) {
    /*    collection.find( jsonQuery, jsonProjection).sort(jsort).toArray( function(err, items) {
      //      res.send(items);
        });
        //Use of untrusted input.
   // var cursor = db.collection.find({name: req.params.name}, function (err, docs){
  //Process the results
    }); */
    //Use of untrusted input in $where
    var condition = "this.group == "+req.params.group;
    var cursor = db.myCollection.find( { $where: condition}, function (err, docs){
  //Process the results
});
   // });
};

    this.displayContributions = function(req, res, next) {
        var userId = req.session.userId;

        contributionsDAO.getByUserId(userId, function(error, contrib) {
            if (error) return next(error);

            contrib.userId = userId; //set for nav menu items
            return res.render("contributions", contrib);
        });
    };

    this.handleContributionsUpdate = function(req, res, next) {

        /*jslint evil: true */
        // Insecure use of eval() to parse inputs
        var preTax = eval(req.body.preTax);
        var afterTax = eval(req.body.afterTax);
        var roth = eval(req.body.roth);

        /*
        //Fix for A1 -1 SSJS Injection attacks - uses alternate method to eval
        var preTax = parseInt(req.body.preTax);
        var afterTax = parseInt(req.body.afterTax);
        var roth = parseInt(req.body.roth);
        */
        var userId = req.session.userId;

        //validate contributions
        if (isNaN(preTax) || isNaN(afterTax) || isNaN(roth) || preTax < 0 || afterTax < 0 || roth < 0) {
            return res.render("contributions", {
                updateError: "Invalid contribution percentages",
                userId: userId
            });
        }
        // Prevent more than 30% contributions
        if (preTax + afterTax + roth > 30) {
            return res.render("contributions", {
                updateError: "Contribution percentages cannot exceed 30 %",
                userId: userId
            });
        }

        contributionsDAO.update(userId, preTax, afterTax, roth, function(err, contributions) {

            if (err) return next(err);

            contributions.updateSuccess = true;
            return res.render("contributions", contributions);
        });

    };

}

module.exports = ContributionsHandler;
<|endoftext|>


<commit_before>(function(mifosX) {
    var defineHeaders = function($httpProvider , $translateProvider, ResourceFactoryProvider ,HttpServiceProvider) {
        var mainLink = getLocation(window.location.href);
        var baseApiUrl = "https://demo.openmf.org";
        var host = "";
        //accessing from openmf server
        if (mainLink.hostname.indexOf('openmf.org') >= 0) {
            var hostname = window.location.hostname;
            console.log('hostname---'+hostname);
            domains = hostname.split('.');
            console.log('domains---'+domains);
            // For multi tenant hosting
            if (domains[0] == "demo") {
                $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = 'default';
                ResourceFactoryProvider.setTenantIdenetifier('default');
                console.log("demo server",domains[0]);
            } else {
                $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = domains[0];
                ResourceFactoryProvider.setTenantIdenetifier(domains[0]);
                console.log("other than demo server", domains[0]);

            }
            host = mainLink.hostname;
            console.log('hostname from mainLink = ',host);
        }
        //accessing from a file system or other servers
        else {
            if (mainLink.hostname != "") {
                baseApiUrl = "https://"+mainLink.hostname+(mainLink.port ? ':'+mainLink.port : '');
            }

            if(QueryParameters["baseApiUrl"]) {
                baseApiUrl = QueryParameters["baseApiUrl"];
            }
            var queryLink = getLocation(baseApiUrl);
            host = "https://"+queryLink.hostname+(queryLink.port ? ':'+queryLink.port : '');
            // Angularjs strips everything after colon(:) so we are appending port number again
            // follow this link for more info https://github.com/angular/angular.js/issues/1243
            if (queryLink.port) {
                host = host.concat('\:'+queryLink.port);
            };
            console.log('hostname from baseApiUrl = ',host);

            $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = 'default';
            ResourceFactoryProvider.setTenantIdenetifier('default');
            if(QueryParameters["tenantIdentifier"]) {
                $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = QueryParameters["tenantIdentifier"];
                ResourceFactoryProvider.setTenantIdenetifier(QueryParameters["tenantIdentifier"]);
            }
        }

        ResourceFactoryProvider.setBaseUrl(host);
        HttpServiceProvider.addRequestInterceptor('demoUrl', function(config) {
          return _.extend(config, {url: host + config.url });
        });

        // Enable CORS! (see e.g. http://enable-cors.org/)
        $httpProvider.defaults.useXDomain = true;
        delete $httpProvider.defaults.headers.common['X-Requested-With'];

        //Set headers
        $httpProvider.defaults.headers.common['Content-Type'] = 'application/json; charset=utf-8';

        // Configure i18n and preffer language
              //$translateProvider.translations('en', translationsEN);
              //$translateProvider.translations('de', translationsDE);

        $translateProvider.useStaticFilesLoader({
              prefix: 'global-translations/locale-',
              suffix: '.json'
        });

        $translateProvider.preferredLanguage('en');
        $translateProvider.fallbackLanguage('en');

  };
  mifosX.ng.application.config(defineHeaders).run(function($log) {
    $log.info("Initial tasks are done!");
  });
}(mifosX || {}));

getLocation = function(href) {
    var l = document.createElement("a");
    l.href = href;
    return l;
};

QueryParameters = (function() {
    var result = {};
    if (window.location.search)
    {
        // split up the query string and store in an associative array
        var params = window.location.search.slice(1).split("&");
        for (var i = 0; i < params.length; i++)
        {
            var tmp = params[i].split("=");
            result[tmp[0]] = unescape(tmp[1]);
        }
    }
    return result;
}());
<commit_msg>port number is not escaped properly(fixed)<commit_after>(function(mifosX) {
    var defineHeaders = function($httpProvider , $translateProvider, ResourceFactoryProvider ,HttpServiceProvider) {
        var mainLink = getLocation(window.location.href);
        var baseApiUrl = "https://demo.openmf.org";
        var host = "";
        var portNumber = "";
        //accessing from openmf server
        if (mainLink.hostname.indexOf('openmf.org') >= 0) {
            var hostname = window.location.hostname;
            console.log('hostname---'+hostname);
            domains = hostname.split('.');
            console.log('domains---'+domains);
            // For multi tenant hosting
            if (domains[0] == "demo") {
                $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = 'default';
                ResourceFactoryProvider.setTenantIdenetifier('default');
                console.log("demo server",domains[0]);
            } else {
                $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = domains[0];
                ResourceFactoryProvider.setTenantIdenetifier(domains[0]);
                console.log("other than demo server", domains[0]);

            }
            host = mainLink.hostname;
            console.log('hostname from mainLink = ',host);
        }
        //accessing from a file system or other servers
        else {
            if (mainLink.hostname != "") {
                baseApiUrl = "https://"+mainLink.hostname+(mainLink.port ? ':'+mainLink.port : '');
            }

            if (QueryParameters["baseApiUrl"]) {
                baseApiUrl = QueryParameters["baseApiUrl"];
            }
            var queryLink = getLocation(baseApiUrl);
            host = "https://"+queryLink.hostname+(queryLink.port ? ':'+queryLink.port : '');
            portNumber = queryLink.port;

            $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = 'default';
            ResourceFactoryProvider.setTenantIdenetifier('default');
            if(QueryParameters["tenantIdentifier"]) {
                $httpProvider.defaults.headers.common['X-Mifos-Platform-TenantId'] = QueryParameters["tenantIdentifier"];
                ResourceFactoryProvider.setTenantIdenetifier(QueryParameters["tenantIdentifier"]);
            }
        }

        // Angularjs strips everything after colon(:) so we are appending port number again
        // follow this link for more info https://github.com/angular/angular.js/issues/1243
        if (portNumber && portNumber != "") {
            host = host.concat(':'+portNumber);
        };
        ResourceFactoryProvider.setBaseUrl(host);
        HttpServiceProvider.addRequestInterceptor('demoUrl', function(config) {
            if (portNumber && portNumber != ""){
                host = host.replace(':'+portNumber, "");
            }
          return _.extend(config, {url: host + config.url });
        });

        // Enable CORS! (see e.g. http://enable-cors.org/)
        $httpProvider.defaults.useXDomain = true;
        delete $httpProvider.defaults.headers.common['X-Requested-With'];

        //Set headers
        $httpProvider.defaults.headers.common['Content-Type'] = 'application/json; charset=utf-8';

        // Configure i18n and preffer language
              //$translateProvider.translations('en', translationsEN);
              //$translateProvider.translations('de', translationsDE);

        $translateProvider.useStaticFilesLoader({
              prefix: 'global-translations/locale-',
              suffix: '.json'
        });

        $translateProvider.preferredLanguage('en');
        $translateProvider.fallbackLanguage('en');

  };
  mifosX.ng.application.config(defineHeaders).run(function($log) {
    $log.info("Initial tasks are done!");
  });
}(mifosX || {}));

getLocation = function(href) {
    var l = document.createElement("a");
    l.href = href;
    return l;
};

QueryParameters = (function() {
    var result = {};
    if (window.location.search)
    {
        // split up the query string and store in an associative array
        var params = window.location.search.slice(1).split("&");
        for (var i = 0; i < params.length; i++)
        {
            var tmp = params[i].split("=");
            result[tmp[0]] = unescape(tmp[1]);
        }
    }
    return result;
}());
<|endoftext|>


<commit_before>import {getCommentDuringCreationDecoration, deactivateAllSelectedChanges} from "../state_plugins"
import {REVIEW_ROLES} from ".."
import {findTarget, post} from "../../common"
import {CommentEditor, CommentAnswerEditor, serializeComment} from "./editors"

/* Functions related to user interactions with comments */
export class ModCommentInteractions {
    constructor(mod) {
        mod.interactions = this
        this.mod = mod
        this.activeCommentId = false
        this.activeCommentAnswerId = false
        this.editComment = false
        this.editor = false
        this.bindEvents()
    }

    bindEvents() {
        // Bind all the click events related to comments
        document.body.addEventListener('click', event => {
            const el = {}
            let id
            switch (true) {
                case findTarget(event, '.edit-comment', el):
                    this.editComment = true
                    this.activeCommentAnswerId = false
                    id = el.target.dataset.id
                    if (this.activeCommentId !== id) {
                        this.deactivateSelectedChanges()
                        this.activateComment(id)
                    } else {
                        this.updateDOM()
                    }
                    break
                case findTarget(event, '.edit-comment-answer', el):
                    this.editComment = false
                    this.editAnswer(
                        el.target.dataset.id,
                        el.target.dataset.answer
                    )
                    break
                case findTarget(event, '.resolve-comment', el):
                    this.resolveComment(el.target.dataset.id)
                    break
                case findTarget(event, '.recreate-comment', el):
                    this.recreateComment(el.target.dataset.id)
                    break
                case findTarget(event, '.assign-comment', el):
                    this.assignComment(
                        el.target.dataset.id,
                        parseInt(el.target.dataset.user),
                        el.target.dataset.username
                    )
                    break
                case findTarget(event, '.unassign-comment', el):
                    this.unassignComment(el.target.dataset.id)
                    break
                case findTarget(event, '.delete-comment', el):
                    this.deleteComment(el.target.dataset.id)
                    break
                case findTarget(event, '.delete-comment-answer', el):
                    this.deleteCommentAnswer(
                        el.target.dataset.id,
                        el.target.dataset.answer
                    )
                    break
                default:
                    break
            }
        })
    }

    initEditor() {
        const commentEditorDOM = document.getElementById('comment-editor'),
            answerEditorDOM = document.getElementById('answer-editor')

        if (!(commentEditorDOM || answerEditorDOM)) {
            this.editor = false
            return
        }
        const id = this.activeCommentId
        if (commentEditorDOM) {
            const value = id === '-1' ?
                {text: [], isMajor: false} :
                {
                    text: this.mod.store.comments[id].comment,
                    isMajor: this.mod.store.comments[id].isMajor
                }
            this.editor = new CommentEditor(this.mod, id, commentEditorDOM, value.text, {isMajor: value.isMajor})
        } else {
            const answerId = this.activeCommentAnswerId,
                text = answerId ?
                    this.mod.store.comments[id].answers.find(answer => answer.id === answerId).answer :
                    []
            this.editor = new CommentAnswerEditor(this.mod, id, answerEditorDOM, text, {answerId})
        }

        this.editor.init()
    }

    updateDOM() {
        this.mod.editor.mod.marginboxes.updateDOM()
        this.initEditor()
    }

    findCommentIds(node) {
        return node.marks.filter(
            mark => mark.type.name === 'comment' && mark.attrs.id
        ).map(mark => mark.attrs.id)
    }

    findCommentsAt(node) {
        return this.findCommentIds(node).map(id => this.mod.store.findComment(id))
    }

    deactivateSelectedChanges() {
        const tr = deactivateAllSelectedChanges(this.mod.editor.view.state.tr)
        if (tr) {
            this.mod.editor.view.dispatch(tr)
        }
        const fnTr = deactivateAllSelectedChanges(this.mod.editor.mod.footnotes.fnEditor.view.state.tr)
        if (fnTr) {
            this.mod.editor.mod.footnotes.fnEditor.view.dispatch(fnTr)
        }
    }

    activateComment(id) {
        this.deactivateAll()
        this.activeCommentId = id
        this.updateDOM()
    }

    deactivateAll() {
        this.activeCommentId = false
        this.editComment = false
        this.activeCommentAnswerId = false
        // If there is a comment currently under creation, remove it.
        this.mod.store.removeCommentDuringCreation()
    }

    // Activate the comments included in the selection or the comment where the
    // caret is placed, if the editor is in focus.
    activateSelectedComment(view) {

        const selection = view.state.selection
        let comments = []

        if (selection.empty) {
            const node = view.state.doc.nodeAt(selection.from)
            if (node) {
                comments = this.findCommentsAt(node)
            }
        } else {
            view.state.doc.nodesBetween(
                selection.from,
                selection.to,
                node => {
                    if (!node.isInline) {
                        return
                    }
                    comments = comments.concat(this.findCommentsAt(node))
                }
            )
        }

        if (comments.length) {
            if (this.activeCommentId !== comments[0].id) {
              this.activateComment(comments[0].id)
            }
        } else {
            this.deactivateAll()
            this.updateDOM()
        }
    }

    isCurrentlyEditing() {
        // Returns true if
        // A) a comment form is currently open
        // B) the comment answer edit form is currently open
        // C) part of a new answer has been written
        // D) the focus is currently in new answer text area of a comment
        // E) The comment options are open
        // F) a new comment form is about to be displayed, but the updateDOM
        // call has not yet been made.
        if (!this.activeCommentId) {
            return false
        }
        if (document.querySelector('.submit-comment-answer-edit')) {
            // a comment answer edit form is currently open
            return true
        }
        if (this.editor && this.editor.view && this.editor.view.hasFocus()) {
            // There is currently focus in the comment (answer) form
            return true
        }
        if (this.editor && this.editor.view && this.editor.view.state.doc.content.content.length) {
            // Part of a comment (answer) has been entered.
            return true
        }
        if (document.querySelector('div.marginbox-options.fw-open')) {
            // A margin box options menu is open.
            return true
        }
        if (this.mod.store.commentDuringCreation.inDOM === false) {
            // A new comment is about to be created, but it has not
            // yet been added to the DOM.
            return true
        }
        return false
    }

    // Create a temporary empty comment for the current user that is not shared
    // with collaborators.
    createNewComment() {
        this.deactivateAll()
        this.mod.store.addCommentDuringCreation(this.mod.editor.currentView)
        this.activeCommentId = '-1'
        this.editComment = true
        this.updateDOM()
        this.editor.view.focus()
    }

    deleteComment(id) {
        if (id==='-1') {
            this.deactivateAll()
        } else {
            // Handle the deletion of a comment.
            this.mod.store.deleteComment(id, true)
        }
        this.updateDOM()
    }

    resolveComment(id) {
        this.mod.store.updateComment({id, resolved: true})
    }

    recreateComment(id) {
        this.mod.store.updateComment({id, resolved: false})
    }

    assignComment(id, user, username) {
        this.notifyAssignedUser(user, id)
        this.mod.store.updateComment({id, assignedUser: user, assignedUsername: username})
    }

    unassignComment(id) {
        this.mod.store.updateComment({id, assignedUser: false, assignedUsername: false})
    }

    notifyAssignedUser(user, id) {
        const comment = this.mod.store.findComment(id)
        const {html, text} = serializeComment(comment.comment)

        post(
            '/document/comment_notify/',
            {
                doc_id: this.mod.editor.docInfo.id,
                collaborator_id: user,
                comment_html: html,
                comment_text: text,
                type: 'assign'
            }
        )
    }

    updateComment({id, comment, isMajor}) {
        // Save the change to a comment and mark that the document has been changed
        if (id==='-1') {
            const referrer = getCommentDuringCreationDecoration(this.mod.store.commentDuringCreation.view.state)
            // This is a new comment. We need to get an ID for it if it has contents.

            let username

            if (REVIEW_ROLES.includes(this.mod.editor.docInfo.access_rights)) {
                username = `${gettext('Reviewer')} ${this.mod.editor.user.id}`
            } else {
                username = this.mod.editor.user.username
            }

            this.mod.store.addComment(
                {
                    user: this.mod.editor.user.id,
                    username,
                    date: Date.now()-this.mod.editor.clientTimeAdjustment, // We update the time to the time the comment was stored
                    comment,
                    isMajor
                },
                referrer.from,
                referrer.to,
                this.mod.store.commentDuringCreation.view
            )
        } else {
            this.mod.store.updateComment({id, comment, isMajor})
        }
        this.deactivateAll()
        this.updateDOM()

    }

    cancelSubmit() {
        // Handle a click on the cancel button of the comment submit form.
        const id = this.activeCommentId
        if (
            id==='-1' ||
            (
                this.mod.store.comments[id] &&
                this.mod.store.comments[id].comment.length === 0
            )
        ) {
            this.deleteComment(id)
        } else {
            this.deactivateAll()
        }
        this.updateDOM()
    }

    deleteCommentAnswer(id, answerId) {
        // Handle the deletion of a comment answer.
        this.mod.store.deleteAnswer(id, answerId)
        this.deactivateAll()
        this.updateDOM()
    }

    editAnswer(id, answerId) {
        // Mark a specific answer to a comment as active, then layout the
        // comments, which will make that answer editable.
        this.activeCommentId = id
        this.activeCommentAnswerId = answerId
        this.updateDOM()
    }

    createNewAnswer(id, answerText) {
        // Create a new answer to add to the comment store

        let username

        if (REVIEW_ROLES.includes(this.mod.editor.docInfo.access_rights)) {
            username = `${gettext('Reviewer')} ${this.mod.editor.user.id}`
        } else {
            username = this.mod.editor.user.username
        }

        const answer = {
            answer: answerText,
            user: this.mod.editor.user.id,
            username,
            date: Date.now()-this.mod.editor.clientTimeAdjustment
        }

        this.mod.store.addAnswer(id, answer)

        this.deactivateAll()
        this.updateDOM()
    }

    submitAnswerUpdate(id, answerId, commentText) {
        this.mod.store.updateAnswer(id, answerId, commentText)
        this.deactivateAll()
        this.updateDOM()
    }
}
<commit_msg>safeguard against setting up of double editors<commit_after>import {getCommentDuringCreationDecoration, deactivateAllSelectedChanges} from "../state_plugins"
import {REVIEW_ROLES} from ".."
import {findTarget, post} from "../../common"
import {CommentEditor, CommentAnswerEditor, serializeComment} from "./editors"

/* Functions related to user interactions with comments */
export class ModCommentInteractions {
    constructor(mod) {
        mod.interactions = this
        this.mod = mod
        this.activeCommentId = false
        this.activeCommentAnswerId = false
        this.editComment = false
        this.editor = false
        this.bindEvents()
    }

    bindEvents() {
        // Bind all the click events related to comments
        document.body.addEventListener('click', event => {
            const el = {}
            let id
            switch (true) {
                case findTarget(event, '.edit-comment', el):
                    this.editComment = true
                    this.activeCommentAnswerId = false
                    id = el.target.dataset.id
                    if (this.activeCommentId !== id) {
                        this.deactivateSelectedChanges()
                        this.activateComment(id)
                    } else {
                        this.updateDOM()
                    }
                    break
                case findTarget(event, '.edit-comment-answer', el):
                    this.editComment = false
                    this.editAnswer(
                        el.target.dataset.id,
                        el.target.dataset.answer
                    )
                    break
                case findTarget(event, '.resolve-comment', el):
                    this.resolveComment(el.target.dataset.id)
                    break
                case findTarget(event, '.recreate-comment', el):
                    this.recreateComment(el.target.dataset.id)
                    break
                case findTarget(event, '.assign-comment', el):
                    this.assignComment(
                        el.target.dataset.id,
                        parseInt(el.target.dataset.user),
                        el.target.dataset.username
                    )
                    break
                case findTarget(event, '.unassign-comment', el):
                    this.unassignComment(el.target.dataset.id)
                    break
                case findTarget(event, '.delete-comment', el):
                    this.deleteComment(el.target.dataset.id)
                    break
                case findTarget(event, '.delete-comment-answer', el):
                    this.deleteCommentAnswer(
                        el.target.dataset.id,
                        el.target.dataset.answer
                    )
                    break
                default:
                    break
            }
        })
    }

    initEditor() {
        const commentEditorDOM = document.querySelector('#comment-editor'),
            answerEditorDOM = document.querySelector('#answer-editor')

        if (
            (commentEditorDOM  && commentEditorDOM.matches(':not(:empty)')) ||
            (answerEditorDOM  && answerEditorDOM.matches(':not(:empty)'))
        ) {
            // Editor has been set up already. Abort.
            return
        }

        if (!(commentEditorDOM || answerEditorDOM)) {
            this.editor = false
            return
        }
        const id = this.activeCommentId
        if (commentEditorDOM) {
            const value = id === '-1' ?
                {text: [], isMajor: false} :
                {
                    text: this.mod.store.comments[id].comment,
                    isMajor: this.mod.store.comments[id].isMajor
                }
            this.editor = new CommentEditor(this.mod, id, commentEditorDOM, value.text, {isMajor: value.isMajor})
        } else {
            const answerId = this.activeCommentAnswerId,
                text = answerId ?
                    this.mod.store.comments[id].answers.find(answer => answer.id === answerId).answer :
                    []
            this.editor = new CommentAnswerEditor(this.mod, id, answerEditorDOM, text, {answerId})
        }

        this.editor.init()
    }

    updateDOM() {
        this.mod.editor.mod.marginboxes.updateDOM()
        this.initEditor()
    }

    findCommentIds(node) {
        return node.marks.filter(
            mark => mark.type.name === 'comment' && mark.attrs.id
        ).map(mark => mark.attrs.id)
    }

    findCommentsAt(node) {
        return this.findCommentIds(node).map(id => this.mod.store.findComment(id))
    }

    deactivateSelectedChanges() {
        const tr = deactivateAllSelectedChanges(this.mod.editor.view.state.tr)
        if (tr) {
            this.mod.editor.view.dispatch(tr)
        }
        const fnTr = deactivateAllSelectedChanges(this.mod.editor.mod.footnotes.fnEditor.view.state.tr)
        if (fnTr) {
            this.mod.editor.mod.footnotes.fnEditor.view.dispatch(fnTr)
        }
    }

    activateComment(id) {
        this.deactivateAll()
        this.activeCommentId = id
        this.updateDOM()
    }

    deactivateAll() {
        this.activeCommentId = false
        this.editComment = false
        this.activeCommentAnswerId = false
        // If there is a comment currently under creation, remove it.
        this.mod.store.removeCommentDuringCreation()
    }

    // Activate the comments included in the selection or the comment where the
    // caret is placed, if the editor is in focus.
    activateSelectedComment(view) {

        const selection = view.state.selection
        let comments = []

        if (selection.empty) {
            const node = view.state.doc.nodeAt(selection.from)
            if (node) {
                comments = this.findCommentsAt(node)
            }
        } else {
            view.state.doc.nodesBetween(
                selection.from,
                selection.to,
                node => {
                    if (!node.isInline) {
                        return
                    }
                    comments = comments.concat(this.findCommentsAt(node))
                }
            )
        }

        if (comments.length) {
            if (this.activeCommentId !== comments[0].id) {
              this.activateComment(comments[0].id)
            }
        } else {
            this.deactivateAll()
            this.updateDOM()
        }
    }

    isCurrentlyEditing() {
        // Returns true if
        // A) a comment form is currently open
        // B) the comment answer edit form is currently open
        // C) part of a new answer has been written
        // D) the focus is currently in new answer text area of a comment
        // E) The comment options are open
        // F) a new comment form is about to be displayed, but the updateDOM
        // call has not yet been made.
        if (!this.activeCommentId) {
            return false
        }
        if (document.querySelector('.submit-comment-answer-edit')) {
            // a comment answer edit form is currently open
            return true
        }
        if (this.editor && this.editor.view && this.editor.view.hasFocus()) {
            // There is currently focus in the comment (answer) form
            return true
        }
        if (this.editor && this.editor.view && this.editor.view.state.doc.content.content.length) {
            // Part of a comment (answer) has been entered.
            return true
        }
        if (document.querySelector('div.marginbox-options.fw-open')) {
            // A margin box options menu is open.
            return true
        }
        if (this.mod.store.commentDuringCreation.inDOM === false) {
            // A new comment is about to be created, but it has not
            // yet been added to the DOM.
            return true
        }
        return false
    }

    // Create a temporary empty comment for the current user that is not shared
    // with collaborators.
    createNewComment() {
        this.deactivateAll()
        this.mod.store.addCommentDuringCreation(this.mod.editor.currentView)
        this.activeCommentId = '-1'
        this.editComment = true
        this.updateDOM()
        this.editor.view.focus()
    }

    deleteComment(id) {
        if (id==='-1') {
            this.deactivateAll()
        } else {
            // Handle the deletion of a comment.
            this.mod.store.deleteComment(id, true)
        }
        this.updateDOM()
    }

    resolveComment(id) {
        this.mod.store.updateComment({id, resolved: true})
    }

    recreateComment(id) {
        this.mod.store.updateComment({id, resolved: false})
    }

    assignComment(id, user, username) {
        this.notifyAssignedUser(user, id)
        this.mod.store.updateComment({id, assignedUser: user, assignedUsername: username})
    }

    unassignComment(id) {
        this.mod.store.updateComment({id, assignedUser: false, assignedUsername: false})
    }

    notifyAssignedUser(user, id) {
        const comment = this.mod.store.findComment(id)
        const {html, text} = serializeComment(comment.comment)

        post(
            '/document/comment_notify/',
            {
                doc_id: this.mod.editor.docInfo.id,
                collaborator_id: user,
                comment_html: html,
                comment_text: text,
                type: 'assign'
            }
        )
    }

    updateComment({id, comment, isMajor}) {
        // Save the change to a comment and mark that the document has been changed
        if (id==='-1') {
            const referrer = getCommentDuringCreationDecoration(this.mod.store.commentDuringCreation.view.state)
            // This is a new comment. We need to get an ID for it if it has contents.

            let username

            if (REVIEW_ROLES.includes(this.mod.editor.docInfo.access_rights)) {
                username = `${gettext('Reviewer')} ${this.mod.editor.user.id}`
            } else {
                username = this.mod.editor.user.username
            }

            this.mod.store.addComment(
                {
                    user: this.mod.editor.user.id,
                    username,
                    date: Date.now()-this.mod.editor.clientTimeAdjustment, // We update the time to the time the comment was stored
                    comment,
                    isMajor
                },
                referrer.from,
                referrer.to,
                this.mod.store.commentDuringCreation.view
            )
        } else {
            this.mod.store.updateComment({id, comment, isMajor})
        }
        this.deactivateAll()
        this.updateDOM()

    }

    cancelSubmit() {
        // Handle a click on the cancel button of the comment submit form.
        const id = this.activeCommentId
        if (
            id==='-1' ||
            (
                this.mod.store.comments[id] &&
                this.mod.store.comments[id].comment.length === 0
            )
        ) {
            this.deleteComment(id)
        } else {
            this.deactivateAll()
        }
        this.updateDOM()
    }

    deleteCommentAnswer(id, answerId) {
        // Handle the deletion of a comment answer.
        this.mod.store.deleteAnswer(id, answerId)
        this.deactivateAll()
        this.updateDOM()
    }

    editAnswer(id, answerId) {
        // Mark a specific answer to a comment as active, then layout the
        // comments, which will make that answer editable.
        this.activeCommentId = id
        this.activeCommentAnswerId = answerId
        this.updateDOM()
    }

    createNewAnswer(id, answerText) {
        // Create a new answer to add to the comment store

        let username

        if (REVIEW_ROLES.includes(this.mod.editor.docInfo.access_rights)) {
            username = `${gettext('Reviewer')} ${this.mod.editor.user.id}`
        } else {
            username = this.mod.editor.user.username
        }

        const answer = {
            answer: answerText,
            user: this.mod.editor.user.id,
            username,
            date: Date.now()-this.mod.editor.clientTimeAdjustment
        }

        this.mod.store.addAnswer(id, answer)

        this.deactivateAll()
        this.updateDOM()
    }

    submitAnswerUpdate(id, answerId, commentText) {
        this.mod.store.updateAnswer(id, answerId, commentText)
        this.deactivateAll()
        this.updateDOM()
    }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { IntlProvider } from 'react-intl'
import 'bootstrap/dist/css/bootstrap.css'
import {
  Route,
  BrowserRouter as Router,
  Redirect,
  Switch
} from 'react-router-dom'
import Home from './Home'
import Intro from './Intro'
import DashboardContainer from '../containers/Dashboard'
import ServiceContainer from '../containers/Service'
import DepartmentContainer from '../containers/Department'
import LearnMore from './Department/LearnMore'
import ExplainContainer from '../containers/Explain'
import SubmitContainer from '../containers/Submit'
import User from './User'
import Done from './Done'
import { firebaseAuth } from '../config/constants'
import { logout } from '../helpers/auth'

import store from '../store';

export default class App extends Component {
  state = {
    authed: false,
    loading: true,
    user: {},
  }

  componentDidMount() {
    this.removeListener = firebaseAuth().onAuthStateChanged(user => this.updateAuthState(user))
  }

  componentWillUnmount() {
    this.removeListener()
  }

  componentDidMount() {
    this.removeListener = firebaseAuth().onAuthStateChanged(user => this.updateAuthState(user))
  }

  componentWillUnmount() {
    this.removeListener()
  }

  updateAuthState(user) {
    console.log('user in index.js', user);
    if (user) {
      this.setState({
        authed: true,
        loading: false,
        user,
      })
    } else {
      this.setState({
        loading: false,
      })
    }
  }

  handleLogout() {
    const warning = confirm('Are you sure you want to log out?')
    if (warning) {
      console.log("LOGGED OUT")
      logout()
      this.setState({ authed: false })
    }
  }

  render() {
    const { authed, user, loading } = this.state
    return loading === true ?
Loading
 : (
      <IntlProvider locale="en">
        <Provider store={store}>
          <Router>

                <Switch className="row">
                  <Route
                    path="/" exact render={() => {
                      return this.state.authed
                      ? <Redirect to="/dashboard" />
                      : <Redirect to="/login" />
                    }}
                  />
                  <Route
                    path="/login" isAuthed={this.state.authed} render={() => {
                      return this.state.authed
                      ? <Redirect to="/intro/1" />
                      : <Home />
                    }}
                  />
                  <Route path="/intro/:id" render={props => <Intro {...props} />} />
                  <Route path="/dashboard" render={props => <DashboardContainer {...props} />} />
                  <Route path="/service/:id" exact render={props => <ServiceContainer {...props} />} />
                  <Route path="/service/:service_id/department/:id" exact
                    render={props => <DepartmentContainer {...props} />}
                  />
                  <Route path="/service/:service_id/department/:id/learn-more"
                    render={props => <LearnMore {...props} />}
                  />
                  <Route path="/service/:service_id/department/:id/explain"
                    render={props => <ExplainContainer {...props} />}
                  />
                  <Route path="/user" render={() => {
                    return (
                      <User isAuthed={this.state.authed}
                        handleLogout={this.handleLogout.bind(this)}
                      />
                    )
                  }} />
                  <Route path="/submit" render={(props) => {
                    return <SubmitContainer {...props} />
                  }} />
                  <Route path="/done" render={(props) => {
                    return <Done {...props} />
                  }} />
                  <Route render={() =>
404, you ain't supposed to be here
} />
                </Switch>

          </Router>
        </Provider>
      </IntlProvider>
    );
  }
}
<commit_msg>Fix bug with user name and photo not showing up in nav bar<commit_after>import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { IntlProvider } from 'react-intl'
import 'bootstrap/dist/css/bootstrap.css'
import {
  Route,
  BrowserRouter as Router,
  Redirect,
  Switch
} from 'react-router-dom'
import Home from './Home'
import Intro from './Intro'
import DashboardContainer from '../containers/Dashboard'
import ServiceContainer from '../containers/Service'
import DepartmentContainer from '../containers/Department'
import LearnMore from './Department/LearnMore'
import ExplainContainer from '../containers/Explain'
import SubmitContainer from '../containers/Submit'
import User from './User'
import Done from './Done'
import { firebaseAuth } from '../config/constants'
import { logout } from '../helpers/auth'

import store from '../store';

export default class App extends Component {
  state = {
    authed: false,
    loading: true,
    user: {},
  }

  componentDidMount() {
    this.removeListener = firebaseAuth().onAuthStateChanged(user => this.updateAuthState(user))
  }

  componentWillUnmount() {
    this.removeListener()
  }

  componentDidMount() {
    this.removeListener = firebaseAuth().onAuthStateChanged(user => this.updateAuthState(user))
  }

  componentWillUnmount() {
    this.removeListener()
  }

  updateAuthState(user) {
    console.log('user in index.js', user);
    if (user) {
      this.setState({
        authed: true,
        loading: false,
        user,
      })
    } else {
      this.setState({
        loading: false,
      })
    }
  }

  handleLogout() {
    const warning = confirm('Are you sure you want to log out?')
    if (warning) {
      console.log("LOGGED OUT")
      logout()
      this.setState({ authed: false })
    }
  }

  render() {
    const { authed, user, loading } = this.state
    return loading === true ?
Loading
 : (
      <IntlProvider locale="en">
        <Provider store={store}>
          <Router>

                <Switch className="row">
                  <Route
                    path="/" exact render={() => {
                      return this.state.authed
                      ? <Redirect to="/dashboard" />
                      : <Redirect to="/login" />
                    }}
                  />
                  <Route
                    path="/login" isAuthed={this.state.authed} render={() => {
                      return this.state.authed
                      ? <Redirect to="/intro/1" />
                      : <Home />
                    }}
                  />
                  <Route path="/intro/:id" render={props => <Intro {...props} />} />
                  <Route path="/dashboard" render={props => <DashboardContainer {...props} user={user} />} />
                  <Route path="/service/:id" exact render={props => <ServiceContainer {...props} />} />
                  <Route path="/service/:service_id/department/:id" exact
                    render={props => <DepartmentContainer {...props} />}
                  />
                  <Route path="/service/:service_id/department/:id/learn-more"
                    render={props => <LearnMore {...props} />}
                  />
                  <Route path="/service/:service_id/department/:id/explain"
                    render={props => <ExplainContainer {...props} />}
                  />
                  <Route path="/user" render={() => {
                    return (
                      <User isAuthed={this.state.authed}
                        handleLogout={this.handleLogout.bind(this)}
                      />
                    )
                  }} />
                  <Route path="/submit" render={(props) => {
                    return <SubmitContainer {...props} />
                  }} />
                  <Route path="/done" render={(props) => {
                    return <Done {...props} />
                  }} />
                  <Route render={() =>
404, you ain't supposed to be here
} />
                </Switch>

          </Router>
        </Provider>
      </IntlProvider>
    );
  }
}
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import { authenticate } from './../actions/auth';
import { connect } from 'react-redux';
import LoginForm from './../components/LoginForm';

class Login extends React.Component {
    render() {
        return (
            <LoginForm onSubmit={this.props.onSubmit} />
        );
    }
};

Login.propTypes = {
    onSubmit: PropTypes.func.isRequired
};

const mapDispatchToProps = (dispatch) => {
    return {
        onSubmit: (credentials) => {
            dispatch(authenticate(credentials))
        }
    };
};

export default connect(null, mapDispatchToProps)(Login);<commit_msg>Simplify implementation<commit_after>import React, { PropTypes } from 'react';
import { authenticate } from './../actions/auth';
import { connect } from 'react-redux';
import LoginForm from './../components/LoginForm';

const Login = ({onSubmit}) => (
    <LoginForm onSubmit={onSubmit} />
);

Login.propTypes = {
    onSubmit: PropTypes.func.isRequired
};

const mapDispatchToProps = (dispatch) => {
    return {
        onSubmit: (credentials) => {
            dispatch(authenticate(credentials))
        }
    };
};

export default connect(null, mapDispatchToProps)(Login);<|endoftext|>


<commit_before>var prototype = Object.create(HTMLElement.prototype, {
  editable: {
    get: function(){
      return this.getAttribute('editable') == 'true';
    }
  },

  config: {
    get: function(){
      return this.readAttributeAsJson('data-config');
    }
  },

  src: {
    get: function(){
      return this.getAttribute('src') || 'about:blank';
    }
  },

  // TODO
  // pass this from the player
  componentName: {
    get: function(){
      return this.getAttribute('component-name');
    }
  }
});

prototype.readAttributeAsJson = function(name) {
  if(!this.hasAttribute(name)) { return {}; }
  return JSON.parse(this.getAttribute(name));
};

prototype.createdCallback = function() {
  var componentName = this.componentName || 'vs-texthd';
  if(componentName !== 'vs-texthd') {
    console.error('Component Launcher is not ready for anything rather than vs-texthd');
    return;
  }
  this.childComponent = document.createElement(componentName);
  this.appendChild(this.childComponent);
};

prototype.attachedCallback = function(){
  // import versal.html which has definitions for custom elements
  var link = document.createElement('link');
  link.rel = 'import';
  link.href = this.src;
  document.head.appendChild(link);

  this.initObserver();

  this.initChild();

  // necessary to remove spinner
  this.fireCustomEvent('rendered');
};

prototype.childAtSameStateAsLauncher = function(){
  return this.childComponent.getAttribute('data-config') === this.getAttribute('data-config');
};

prototype.initObserver = function(){
  var sendAttributesToPlayer = function(mutation){
    if(mutation.type === 'attributes' && mutation.attributeName === 'data-config') {
      var config = mutation.target.getAttribute('data-config');

      // return if child component is already at the correct state
      if(this.childAtSameStateAsLauncher()) {
        return;
      }

      this.fireCustomEvent('setAttributes', JSON.parse(config));
    }
  }.bind(this);

  // create an observer instance
  this.observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      sendAttributesToPlayer(mutation);
    });
  });

  // pass in the target node, as well as the observer options
  this.observer.observe(this.childComponent, { attributes: true });
};

prototype.initChild = function(){
  this.setChildEditable();
  this.setChildConfig();
};

prototype.detachedCallback = function(){
  this.observer.disconnect();
  this.removeChild(this.childComponent);
};

prototype.setChildEditable = function(){
  if(this.editable) {
    this.childComponent.setAttribute('editable', 'true');
  } else {
    this.childComponent.removeAttribute('editable');
  }
};

prototype.setChildConfig = function(){
  // return if child component is already at the correct state
  if(this.childAtSameStateAsLauncher()) {
    return;
  }

  this.childComponent.setAttribute('data-config', this.getAttribute('data-config'));
};

prototype.attributeChangedCallback = function(name){
  switch(name) {
    case 'editable':
      this.setChildEditable();
      break;
    case 'data-config':
      this.setChildConfig();
      break;
  }
};

prototype.fireCustomEvent = function(eventName, data, options) {
  options = options || {};
  var evt = new CustomEvent(eventName, { detail: data, bubbles: options.bubbles || false });
  this.dispatchEvent(evt);
};

document.registerElement('versal-component-launcher', { prototype: prototype });
<commit_msg>Avoid importing more than once<commit_after>var prototype = Object.create(HTMLElement.prototype, {
  editable: {
    get: function(){
      return this.getAttribute('editable') == 'true';
    }
  },

  config: {
    get: function(){
      return this.readAttributeAsJson('data-config');
    }
  },

  src: {
    get: function(){
      return this.getAttribute('src') || 'about:blank';
    }
  },

  // TODO
  // pass this from the player
  componentName: {
    get: function(){
      return this.getAttribute('component-name');
    }
  }
});

prototype.readAttributeAsJson = function(name) {
  if(!this.hasAttribute(name)) { return {}; }
  return JSON.parse(this.getAttribute(name));
};

prototype.createdCallback = function() {
  var componentName = this.componentName || 'vs-texthd';
  if(componentName !== 'vs-texthd') {
    console.error('Component Launcher is not ready for anything rather than vs-texthd');
    return;
  }
  this.childComponent = document.createElement(componentName);
  this.appendChild(this.childComponent);
};

prototype.attachedCallback = function(){
  // only import once
  var childImportSelector = 'link[href="' + this.src + '"]';
  if(!document.querySelectorAll(childImportSelector).length) {
    // import versal.html which has definitions for custom elements
    var link = document.createElement('link');
    link.rel = 'import';
    link.href = this.src;
    document.head.appendChild(link);
  }

  this.initObserver();

  this.setChildEditable();
  this.setChildConfig();

  // necessary to remove spinner
  this.fireCustomEvent('rendered');
};

prototype.childAtSameStateAsLauncher = function(){
  return this.childComponent.getAttribute('data-config') === this.getAttribute('data-config');
};

prototype.initObserver = function(){
  var sendAttributesToPlayer = function(mutation){
    if(mutation.type === 'attributes' && mutation.attributeName === 'data-config') {
      var config = mutation.target.getAttribute('data-config');

      // return if child component is already at the correct state
      if(this.childAtSameStateAsLauncher()) {
        return;
      }

      this.fireCustomEvent('setAttributes', JSON.parse(config));
    }
  }.bind(this);

  // create an observer instance
  this.observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      sendAttributesToPlayer(mutation);
    });
  });

  // pass in the target node, as well as the observer options
  this.observer.observe(this.childComponent, { attributes: true });
};

prototype.detachedCallback = function(){
  this.observer.disconnect();
  this.removeChild(this.childComponent);
};

prototype.setChildEditable = function(){
  if(this.editable) {
    this.childComponent.setAttribute('editable', 'true');
  } else {
    this.childComponent.removeAttribute('editable');
  }
};

prototype.setChildConfig = function(){
  // return if child component is already at the correct state
  if(this.childAtSameStateAsLauncher()) {
    return;
  }

  this.childComponent.setAttribute('data-config', this.getAttribute('data-config'));
};

prototype.attributeChangedCallback = function(name){
  switch(name) {
    case 'editable':
      this.setChildEditable();
      break;
    case 'data-config':
      this.setChildConfig();
      break;
  }
};

prototype.fireCustomEvent = function(eventName, data, options) {
  options = options || {};
  var evt = new CustomEvent(eventName, { detail: data, bubbles: options.bubbles || false });
  this.dispatchEvent(evt);
};

document.registerElement('versal-component-launcher', { prototype: prototype });
<|endoftext|>