<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2017, Joyent, Inc.
 */

/*
 * JSLint has a problem with 'use strict', but we want it on so
 * const fails if reassignment is attempted. With strict off, const
 * reassignments are silently dropped on the floor
 */
/*jsl:ignore*/
'use strict';
/*jsl:end*/

const fs = require('fs');
const assert = require('assert-plus');
const backoff = require('backoff');
const bunyan = require('bunyan');
const dashdash = require('dashdash');
const forkexec = require('forkexec');
const net = require('net');
const once = require('once');
const VError = require('verror');
const zkstream = require('zkstream');
const core = require('./lib');

///--- Helper functions

function getUntrustedIPs(cfg, callback) {
    // Allow hardcoding addresses in the configuration.
    if (cfg.hasOwnProperty('untrustedIPs')) {
        callback();
        return;
    }

    cfg.untrustedIPs = [];

    const args = [ '/usr/sbin/mdata-get', 'sdc:nics' ];
    cfg.log.info({ cmd: args }, 'Loading NIC information');
    forkexec.forkExecWait({
        argv: args
    }, function (err, info) {
        if (err) {
            cfg.log.error(info, 'Failed to load NIC information');
            setImmediate(callback,
                new VError(err, 'Fetching untrusted IPs failed'));
            return;
        }

        const nics = JSON.parse(info.stdout);
        assert.array(nics, 'nics');

        cfg.log.info({ nics: nics }, 'Looked up NICs');

        function addIPsFromNics(nic) {
            // Skip NICs on trusted networks.
            if (nic.nic_tag === 'admin' || nic.nic_tag === 'manta') {
                return;
            }

            if (nic.hasOwnProperty('ips')) {
                nic.ips.forEach(function parseIP(addr) {
                    const ip = addr.split('/')[0];
                    if (net.isIPv4(ip) || net.isIPv6(ip)) {
                        cfg.untrustedIPs.push(ip);
                    }
                });
            } else if (nic.hasOwnProperty('ip')) {
                if (net.isIPv4(nic.ip)) {
                    cfg.untrustedIPs.push(nic.ip);
                }
            } else {
                cfg.log.warn({ nic: nic }, 'NIC has no IP addresses');
            }
        }

        nics.forEach(addIPsFromNics);
        callback();
    });
}

///--- CLI Functions

function configure() {
    const cli_options = [
        {
            names: ['help', 'h'],
            type: 'bool',
            help: 'Print this help and exit.'
        },
        {
            names: ['verbose', 'v'],
            type: 'arrayOfBool',
            help: 'Verbose output. Use multiple times for more verbose.'
        },
        {
            names: ['file', 'f'],
            type: 'string',
            help: 'File to process',
            helpArg: 'FILE'
        }
    ];

    const parser = new dashdash.Parser({options: cli_options});
    var log = bunyan.createLogger({
        level: (process.env.LOG_LEVEL || 'info'),
        name: 'muppet',
        stream: process.stdout,
        serializers: {
            err: bunyan.stdSerializers.err
        }
    });

    var opts;
    try {
        opts = parser.parse(process.argv);
        assert.object(opts, 'options');
    } catch (e) {
        log.fatal(e, 'invalid options');
        process.exit(1);
    }

    if (opts.help) {
        usage();
    }

    var cfg;
    try {
        const _f = opts.file || __dirname + '/etc/config.json';
        cfg = JSON.parse(fs.readFileSync(_f, 'utf8'));
    } catch (e) {
        log.fatal(e, 'unable to parse %s', _f);
        process.exit(1);
    }

    assert.string(cfg.name, 'config.name');
    assert.string(cfg.trustedIP, 'config.trustedIP');
    assert.object(cfg.zookeeper, 'config.zookeeper');
    assert.optionalArrayOfString(cfg.untrustedIPs,
        'config.untrustedIPs');

    if (cfg.logLevel)
        log.level(cfg.logLevel);

    if (opts.verbose) {
        opts.verbose.forEach(function () {
            log.level(Math.max(bunyan.TRACE, (log.level() - 10)));
        });
    }

    if (log.level() <= bunyan.DEBUG)
        log = log.child({src: true});

    cfg.log = log;
    cfg.zookeeper.log = log;

    return (cfg);
}

function usage(msg) {
    if (msg)
        console.error(msg);

    var str = 'usage: ' + require('path').basename(process.argv[1]);
    str += '[-v] [-f file]';
    console.error(str);
    process.exit(msg ? 1 : 0);
}

///--- Internal Functions

function startWatch(opts, cb) {
    assert.object(opts, 'options');
    assert.object(opts.config, 'options.config');
    assert.object(opts.config.log, 'options.config.log');
    assert.object(opts.zk, 'options.zk');
    assert.func(cb, 'callback');

    cb = once(cb);

    function _start(_, _cb) {
        _cb = once(_cb);

        const cfg = opts.config;
        const watch = new core.createWatch({
            domain: cfg.name,
            log: cfg.log,
            zk: opts.zk
        });
        watch.start(function onStart(startErr) {
            if (startErr) {
                _cb(startErr);
                return;
            }

            // Watcher emits `hosts` on a change to hosts in ZK
            watch.on('hosts', function onHosts(hosts) {
                const _opts = {
                    trustedIP: cfg.trustedIP,
                    untrustedIPs: cfg.untrustedIPs,
                    hosts: hosts || [],
                    log: cfg.log.child({component: 'lb_manager'}),
                    restart: cfg.restart
                };
                core.restartLB(_opts, function (err) {
                    if (err) {
                        cfg.log.error({
                            hosts: hosts,
                            err: err
                        }, 'lb restart failed');
                        return;
                    }

                    cfg.log.info({
                        hosts: hosts
                    }, 'lb restarted');
                });
            });

            _cb(null, watch);
        });
    }

    const retry = backoff.call(_start, {}, cb);
    retry.failAfter(Infinity);
    retry.setStrategy(new backoff.ExponentialStrategy());

    retry.on('backoff', function (num, delay, err) {
        opts.config.log.warn({
            err: err,
            num_attempts: num,
            delay: delay
        }, 'failed to start ZooKeeper watch');
    });
    retry.start();
}

function watcher(cfg, zk_client) {
    startWatch({
        config: cfg,
        zk: zk_client
    }, function onStartWatch(err, watch) {
        if (err) {
            cfg.log.fatal(err, 'Failed to start watch');
            process.exit(1);
        }
        cfg.log.info('ZooKeeper watch created');
    });
}

function zookeeper(cfg) {
    assert.object(cfg, 'cfg');
    assert.object(cfg.log, 'cfg.log');

    const zk_client = core.createZKClient(cfg);

    zk_client.on('session', function onSession() {
        cfg.log.info('ZooKeeper session started');
        watcher(cfg, zk_client);
    });

    zk_client.on('connect', function onConnect() {
        cfg.log.info('ZooKeeper successfully connected');
    });

    zk_client.on('close', function onClose() {
        cfg.log.warn('ZooKeeper connection closed');
    });

    zk_client.on('expire', function onExpire() {
        cfg.log.warn('ZooKeeper connection expired');
    });

    /*
     * zkstream attempts to retry the connection to zookeeper. On retry
     * exhaustion it will emit a `failed` event.
     */
    zk_client.on('failed', function onFailed(err) {
        cfg.log.fatal(err, 'ZooKeeper: received failed event');
        process.exit(1);
    });
}

///--- Mainline

(function main() {
    var cfg = configure();

    getUntrustedIPs(cfg, function (err) {
        if (err) {
            // We failed to load our IPs: abort.
            cfg.log.fatal(err, 'Failed to look up any IPs');
            process.exit(1);
        }

        cfg.log.info({
            trustedIP: cfg.trustedIP,
            untrustedIPs: cfg.untrustedIPs
        }, 'Selected IPs for untrusted networks');

        zookeeper(cfg);
    });
})();
<commit_msg>MANTA-3553 Muppet should recover when Zookeeper returns after being down Reviewed by: Kelly McLaughlin <kelly.mclaughlin@joyent.com> Approved by: Kelly McLaughlin <kelly.mclaughlin@joyent.com><commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2017, Joyent, Inc.
 */

/*
 * JSLint has a problem with 'use strict', but we want it on so
 * const fails if reassignment is attempted. With strict off, const
 * reassignments are silently dropped on the floor
 */
/*jsl:ignore*/
'use strict';
/*jsl:end*/

const fs = require('fs');
const assert = require('assert-plus');
const backoff = require('backoff');
const bunyan = require('bunyan');
const dashdash = require('dashdash');
const forkexec = require('forkexec');
const net = require('net');
const once = require('once');
const VError = require('verror');
const zkstream = require('zkstream');
const core = require('./lib');

///--- Helper functions

function getUntrustedIPs(cfg, callback) {
    // Allow hardcoding addresses in the configuration.
    if (cfg.hasOwnProperty('untrustedIPs')) {
        callback();
        return;
    }

    cfg.untrustedIPs = [];

    const args = [ '/usr/sbin/mdata-get', 'sdc:nics' ];
    cfg.log.info({ cmd: args }, 'Loading NIC information');
    forkexec.forkExecWait({
        argv: args
    }, function (err, info) {
        if (err) {
            cfg.log.error(info, 'Failed to load NIC information');
            setImmediate(callback,
                new VError(err, 'Fetching untrusted IPs failed'));
            return;
        }

        const nics = JSON.parse(info.stdout);
        assert.array(nics, 'nics');

        cfg.log.info({ nics: nics }, 'Looked up NICs');

        function addIPsFromNics(nic) {
            // Skip NICs on trusted networks.
            if (nic.nic_tag === 'admin' || nic.nic_tag === 'manta') {
                return;
            }

            if (nic.hasOwnProperty('ips')) {
                nic.ips.forEach(function parseIP(addr) {
                    const ip = addr.split('/')[0];
                    if (net.isIPv4(ip) || net.isIPv6(ip)) {
                        cfg.untrustedIPs.push(ip);
                    }
                });
            } else if (nic.hasOwnProperty('ip')) {
                if (net.isIPv4(nic.ip)) {
                    cfg.untrustedIPs.push(nic.ip);
                }
            } else {
                cfg.log.warn({ nic: nic }, 'NIC has no IP addresses');
            }
        }

        nics.forEach(addIPsFromNics);
        callback();
    });
}

///--- CLI Functions

function configure() {
    const cli_options = [
        {
            names: ['help', 'h'],
            type: 'bool',
            help: 'Print this help and exit.'
        },
        {
            names: ['verbose', 'v'],
            type: 'arrayOfBool',
            help: 'Verbose output. Use multiple times for more verbose.'
        },
        {
            names: ['file', 'f'],
            type: 'string',
            help: 'File to process',
            helpArg: 'FILE'
        }
    ];

    const parser = new dashdash.Parser({options: cli_options});
    var log = bunyan.createLogger({
        level: (process.env.LOG_LEVEL || 'info'),
        name: 'muppet',
        stream: process.stdout,
        serializers: {
            err: bunyan.stdSerializers.err
        }
    });

    var opts;
    try {
        opts = parser.parse(process.argv);
        assert.object(opts, 'options');
    } catch (e) {
        log.fatal(e, 'invalid options');
        process.exit(1);
    }

    if (opts.help) {
        usage();
    }

    var cfg;
    try {
        const _f = opts.file || __dirname + '/etc/config.json';
        cfg = JSON.parse(fs.readFileSync(_f, 'utf8'));
    } catch (e) {
        log.fatal(e, 'unable to parse %s', _f);
        process.exit(1);
    }

    assert.string(cfg.name, 'config.name');
    assert.string(cfg.trustedIP, 'config.trustedIP');
    assert.object(cfg.zookeeper, 'config.zookeeper');
    assert.optionalArrayOfString(cfg.untrustedIPs,
        'config.untrustedIPs');

    if (cfg.logLevel)
        log.level(cfg.logLevel);

    if (opts.verbose) {
        opts.verbose.forEach(function () {
            log.level(Math.max(bunyan.TRACE, (log.level() - 10)));
        });
    }

    if (log.level() <= bunyan.DEBUG)
        log = log.child({src: true});

    cfg.log = log;
    cfg.zookeeper.log = log;

    return (cfg);
}

function usage(msg) {
    if (msg)
        console.error(msg);

    var str = 'usage: ' + require('path').basename(process.argv[1]);
    str += '[-v] [-f file]';
    console.error(str);
    process.exit(msg ? 1 : 0);
}

///--- Internal Functions

function startWatch(opts, cb) {
    assert.object(opts, 'options');
    assert.object(opts.config, 'options.config');
    assert.object(opts.config.log, 'options.config.log');
    assert.object(opts.zk, 'options.zk');
    assert.func(cb, 'callback');

    cb = once(cb);

    function _start(_, _cb) {
        _cb = once(_cb);

        const cfg = opts.config;
        const watch = new core.createWatch({
            domain: cfg.name,
            log: cfg.log,
            zk: opts.zk
        });
        watch.start(function onStart(startErr) {
            if (startErr) {
                _cb(startErr);
                return;
            }

            // Watcher emits `hosts` on a change to hosts in ZK
            watch.on('hosts', function onHosts(hosts) {
                const _opts = {
                    trustedIP: cfg.trustedIP,
                    untrustedIPs: cfg.untrustedIPs,
                    hosts: hosts || [],
                    log: cfg.log.child({component: 'lb_manager'}),
                    restart: cfg.restart
                };
                core.restartLB(_opts, function (err) {
                    if (err) {
                        cfg.log.error({
                            hosts: hosts,
                            err: err
                        }, 'lb restart failed');
                        return;
                    }

                    cfg.log.info({
                        hosts: hosts
                    }, 'lb restarted');
                });
            });

            _cb(null, watch);
        });
    }

    const retry = backoff.call(_start, {}, cb);
    retry.failAfter(Infinity);
    retry.setStrategy(new backoff.ExponentialStrategy());

    retry.on('backoff', function (num, delay, err) {
        opts.config.log.warn({
            err: err,
            num_attempts: num,
            delay: delay
        }, 'failed to start ZooKeeper watch');
    });
    retry.start();
}

function watcher(cfg, zk_client) {
    startWatch({
        config: cfg,
        zk: zk_client
    }, function onStartWatch(err, watch) {
        if (err) {
            cfg.log.fatal(err, 'Failed to start watch');
            process.exit(1);
        }
        cfg.log.info('ZooKeeper watch created');
    });
}

function zookeeper(cfg) {
    assert.object(cfg, 'cfg');
    assert.object(cfg.log, 'cfg.log');

    const zk_client = core.createZKClient(cfg);

    zk_client.on('session', function onSession() {
        cfg.log.info('ZooKeeper session started');
        watcher(cfg, zk_client);
    });

    zk_client.on('connect', function onConnect() {
        cfg.log.info('ZooKeeper successfully connected');
    });

    zk_client.on('close', function onClose() {
        cfg.log.warn('ZooKeeper connection closed');
    });

    zk_client.on('expire', function onExpire() {
        cfg.log.warn('ZooKeeper connection expired');
    });

    /*
     * zkstream attempts to retry the connection to zookeeper. On retry
     * exhaustion it will emit a `failed` event.
     * For this we only log an error, because when ZK comes back up, zkstream
     * will automatically reconnect and re-arm the watchers (if they existed)
     */
    zk_client.on('failed', function onFailed(err) {
        cfg.log.error(err, 'ZooKeeper: received failed event');
    });
}

///--- Mainline

(function main() {
    var cfg = configure();

    getUntrustedIPs(cfg, function (err) {
        if (err) {
            // We failed to load our IPs: abort.
            cfg.log.fatal(err, 'Failed to look up any IPs');
            process.exit(1);
        }

        cfg.log.info({
            trustedIP: cfg.trustedIP,
            untrustedIPs: cfg.untrustedIPs
        }, 'Selected IPs for untrusted networks');

        zookeeper(cfg);
    });
})();
<|endoftext|>


<commit_before>// login.js
// don't remove this line (used in test)

window.disable_signup = {{ disable_signup and "true" or "false" }};

window.login = {};

window.verify = {};

login.bind_events = function() {
	$(window).on("hashchange", function() {
		login.route();
	});

	$(".form-login").on("submit", function(event) {
		event.preventDefault();
		var args = {};
		args.cmd = "login";
		args.usr = frappe.utils.xss_sanitise(($("#login_email").val() || "").trim());
		args.pwd = $("#login_password").val();
		args.device = "desktop";
		if(!args.usr || !args.pwd) {
			frappe.msgprint('{{ _("Both login and password required") }}');
			return false;
		}
		login.call(args);
		return false;
	});

	$(".form-signup").on("submit", function(event) {
		event.preventDefault();
		var args = {};
		args.cmd = "frappe.core.doctype.user.user.sign_up";
		args.email = ($("#signup_email").val() || "").trim();
		args.redirect_to = frappe.utils.get_url_arg("redirect-to") || '';
		args.full_name = ($("#signup_fullname").val() || "").trim();
		if(!args.email || !validate_email(args.email) || !args.full_name) {
			login.set_indicator('{{ _("Valid email and name required") }}', 'red');
			return false;
		}
		login.call(args);
		return false;
	});

	$(".form-forgot").on("submit", function(event) {
		event.preventDefault();
		var args = {};
		args.cmd = "frappe.core.doctype.user.user.reset_password";
		args.user = ($("#forgot_email").val() || "").trim();
		if(!args.user) {
			login.set_indicator('{{ _("Valid Login id required.") }}', 'red');
			return false;
		}
		login.call(args);
		return false;
	});

	$(".toggle-password").click(function() {
		$(this).toggleClass("fa-eye fa-eye-slash");
		var input = $($(this).attr("toggle"));
		if (input.attr("type") == "password") {
			input.attr("type", "text");
		} else {
			input.attr("type", "password");
		}
	});

	{% if ldap_settings and ldap_settings.enabled %}
		$(".btn-ldap-login").on("click", function(){
			var args = {};
			args.cmd = "{{ ldap_settings.method }}";
			args.usr = ($("#login_email").val() || "").trim();
			args.pwd = $("#login_password").val();
			args.device = "desktop";
			if(!args.usr || !args.pwd) {
				login.set_indicator('{{ _("Both login and password required") }}', 'red');
				return false;
			}
			login.call(args);
			return false;
		});
	{% endif %}
}

login.route = function() {
	var route = window.location.hash.slice(1);
	if(!route) route = "login";
	login[route]();
}

login.reset_sections = function(hide) {
	if(hide || hide===undefined) {
		$("section.for-login").toggle(false);
		$("section.for-forgot").toggle(false);
		$("section.for-signup").toggle(false);
	}
	$('section .indicator').each(function() {
		$(this).removeClass().addClass('indicator').addClass('blue')
			.text($(this).attr('data-text'));
	});
}

login.login = function() {
	login.reset_sections();
	$(".for-login").toggle(true);
}

login.steptwo = function() {
	login.reset_sections();
	$(".for-login").toggle(true);
}

login.forgot = function() {
	login.reset_sections();
	$(".for-forgot").toggle(true);
}

login.signup = function() {
	login.reset_sections();
	$(".for-signup").toggle(true);
}

// Login
login.call = function(args, callback) {
	login.set_indicator('{{ _("Verifying...") }}', 'blue');

	return frappe.call({
		type: "POST",
		args: args,
		callback: callback,
		freeze: true,
		statusCode: login.login_handlers
	});
}

login.set_indicator = function(message, color) {
	$('section:visible .indicator')
		.removeClass().addClass('indicator').addClass(color).text(message)
}

login.login_handlers = (function() {
	var get_error_handler = function(default_message) {
		return function(xhr, data) {
			if(xhr.responseJSON) {
				data = xhr.responseJSON;
			}

			var message = default_message;
			if (data._server_messages) {
				message = ($.map(JSON.parse(data._server_messages || '[]'), function(v) {
					// temp fix for messages sent as dict
					try {
						return JSON.parse(v).message;
					} catch (e) {
						return v;
					}
				}) || []).join('
') || default_message;
			}

			if(message===default_message) {
				login.set_indicator(message, 'red');
			} else {
				login.reset_sections(false);
			}

		};
	}

	var login_handlers = {
		200: function(data) {
			if(data.message == 'Logged In'){
				login.set_indicator('{{ _("Success") }}', 'green');
				window.location.href = frappe.utils.get_url_arg("redirect-to") || data.home_page;
			} else if(data.message == 'Password Reset'){
				window.location.href = data.redirect_to;
			} else if(data.message=="No App") {
				login.set_indicator("{{ _("Success") }}", 'green');
				if(localStorage) {
					var last_visited =
						localStorage.getItem("last_visited")
						|| frappe.utils.get_url_arg("redirect-to");
					localStorage.removeItem("last_visited");
				}

				if(data.redirect_to) {
					window.location.href = data.redirect_to;
				}

				if(last_visited && last_visited != "/login") {
					window.location.href = last_visited;
				} else {
					window.location.href = data.home_page;
				}
			} else if(window.location.hash === '#forgot') {
				if(data.message==='not found') {
					login.set_indicator('{{ _("Not a valid user") }}', 'red');
				} else if (data.message=='not allowed') {
					login.set_indicator('{{ _("Not Allowed") }}', 'red');
				} else if (data.message=='disabled') {
					login.set_indicator('{{ _("Not Allowed: Disabled User") }}', 'red');
				} else {
					login.set_indicator('{{ _("Instructions Emailed") }}', 'green');
				}

			} else if(window.location.hash === '#signup') {
				if(cint(data.message[0])==0) {
					login.set_indicator(data.message[1], 'red');
				} else {
					login.set_indicator('{{ _("Success") }}', 'green');
					frappe.msgprint(data.message[1])
				}
				//login.set_indicator(__(data.message), 'green');
			}

			//OTP verification
			if(data.verification && data.message != 'Logged In') {
				login.set_indicator('{{ _("Success") }}', 'green');

				document.cookie = "tmp_id="+data.tmp_id;

				if (data.verification.method == 'OTP App'){
					continue_otp_app(data.verification.setup, data.verification.qrcode);
				} else if (data.verification.method == 'SMS'){
					continue_sms(data.verification.setup, data.verification.prompt);
				} else if (data.verification.method == 'Email'){
					continue_email(data.verification.setup, data.verification.prompt);
				}
			}
		},
		401: get_error_handler('{{ _("Invalid Login. Try again.") }}'),
		417: get_error_handler('{{ _("Oops! Something went wrong") }}')
	};

	return login_handlers;
} )();

frappe.ready(function() {

	login.bind_events();

	if (!window.location.hash) {
		window.location.hash = "#login";
	} else {
		$(window).trigger("hashchange");
	}

	$(".form-signup, .form-forgot").removeClass("hide");
	$(document).trigger('login_rendered');
});

var verify_token =  function(event) {
	$(".form-verify").on("submit", function(eventx) {
		eventx.preventDefault();
		var args = {};
		args.cmd = "login";
		args.otp = $("#login_token").val();
		args.tmp_id = frappe.get_cookie('tmp_id');
		if(!args.otp) {
			frappe.msgprint('{{ _("Login token required") }}');
			return false;
		}
		login.call(args);
		return false;
	});
}

var request_otp = function(r){
	$('.login-content').empty().append($('
').attr({'id':'twofactor_div'}).html(
		'<form class="form-verify">\

\
				{{ _("Verification") }}\

\

\
			<input type="text" id="login_token" autocomplete="off" class="form-control" placeholder={{ _("Verification Code") }} required="" autofocus="">\
			<button class="btn btn-sm btn-primary btn-block" id="verify_token">{{ _("Verify") }}</button>\
		</form>'));
	// add event handler for submit button
	verify_token();
}

var continue_otp_app = function(setup, qrcode){
	request_otp();
	var qrcode_div = $('

');

	if (setup){
		direction = $('
').attr('id','qr_info').text('{{ _("Enter Code displayed in OTP App.") }}');
		qrcode_div.append(direction);
		$('#otp_div').prepend(qrcode_div);
	} else {
		direction = $('
').attr('id','qr_info').text('{{ _("OTP setup using OTP App was not completed. Please contact Administrator.") }}');
		qrcode_div.append(direction);
		$('#otp_div').prepend(qrcode_div);
	}
}

var continue_sms = function(setup, prompt){
	request_otp();
	var sms_div = $('

');

	if (setup){
		sms_div.append(prompt)
		$('#otp_div').prepend(sms_div);
	} else {
		direction = $('
').attr('id','qr_info').text(prompt || '{{ _("SMS was not sent. Please contact Administrator.") }}');
		sms_div.append(direction);
		$('#otp_div').prepend(sms_div)
	}
}

var continue_email = function(setup, prompt){
	request_otp();
	var email_div = $('

');

	if (setup){
		email_div.append(prompt)
		$('#otp_div').prepend(email_div);
	} else {
		var direction = $('
').attr('id','qr_info').text(prompt || '{{ _("Verification code email not sent. Please contact Administrator.") }}');
		email_div.append(direction);
		$('#otp_div').prepend(email_div);
	}
}
<commit_msg>fix: pass all redirect-to args through sanitise_redirect<commit_after>// login.js
// don't remove this line (used in test)

window.disable_signup = {{ disable_signup and "true" or "false" }};

window.login = {};

window.verify = {};

login.bind_events = function() {
	$(window).on("hashchange", function() {
		login.route();
	});

	$(".form-login").on("submit", function(event) {
		event.preventDefault();
		var args = {};
		args.cmd = "login";
		args.usr = frappe.utils.xss_sanitise(($("#login_email").val() || "").trim());
		args.pwd = $("#login_password").val();
		args.device = "desktop";
		if(!args.usr || !args.pwd) {
			frappe.msgprint('{{ _("Both login and password required") }}');
			return false;
		}
		login.call(args);
		return false;
	});

	$(".form-signup").on("submit", function(event) {
		event.preventDefault();
		var args = {};
		args.cmd = "frappe.core.doctype.user.user.sign_up";
		args.email = ($("#signup_email").val() || "").trim();
		args.redirect_to = frappe.utils.sanitise_redirect(frappe.utils.get_url_arg("redirect-to"));
		args.full_name = ($("#signup_fullname").val() || "").trim();
		if(!args.email || !validate_email(args.email) || !args.full_name) {
			login.set_indicator('{{ _("Valid email and name required") }}', 'red');
			return false;
		}
		login.call(args);
		return false;
	});

	$(".form-forgot").on("submit", function(event) {
		event.preventDefault();
		var args = {};
		args.cmd = "frappe.core.doctype.user.user.reset_password";
		args.user = ($("#forgot_email").val() || "").trim();
		if(!args.user) {
			login.set_indicator('{{ _("Valid Login id required.") }}', 'red');
			return false;
		}
		login.call(args);
		return false;
	});

	$(".toggle-password").click(function() {
		$(this).toggleClass("fa-eye fa-eye-slash");
		var input = $($(this).attr("toggle"));
		if (input.attr("type") == "password") {
			input.attr("type", "text");
		} else {
			input.attr("type", "password");
		}
	});

	{% if ldap_settings and ldap_settings.enabled %}
		$(".btn-ldap-login").on("click", function(){
			var args = {};
			args.cmd = "{{ ldap_settings.method }}";
			args.usr = ($("#login_email").val() || "").trim();
			args.pwd = $("#login_password").val();
			args.device = "desktop";
			if(!args.usr || !args.pwd) {
				login.set_indicator('{{ _("Both login and password required") }}', 'red');
				return false;
			}
			login.call(args);
			return false;
		});
	{% endif %}
}

login.route = function() {
	var route = window.location.hash.slice(1);
	if(!route) route = "login";
	login[route]();
}

login.reset_sections = function(hide) {
	if(hide || hide===undefined) {
		$("section.for-login").toggle(false);
		$("section.for-forgot").toggle(false);
		$("section.for-signup").toggle(false);
	}
	$('section .indicator').each(function() {
		$(this).removeClass().addClass('indicator').addClass('blue')
			.text($(this).attr('data-text'));
	});
}

login.login = function() {
	login.reset_sections();
	$(".for-login").toggle(true);
}

login.steptwo = function() {
	login.reset_sections();
	$(".for-login").toggle(true);
}

login.forgot = function() {
	login.reset_sections();
	$(".for-forgot").toggle(true);
}

login.signup = function() {
	login.reset_sections();
	$(".for-signup").toggle(true);
}

// Login
login.call = function(args, callback) {
	login.set_indicator('{{ _("Verifying...") }}', 'blue');

	return frappe.call({
		type: "POST",
		args: args,
		callback: callback,
		freeze: true,
		statusCode: login.login_handlers
	});
}

login.set_indicator = function(message, color) {
	$('section:visible .indicator')
		.removeClass().addClass('indicator').addClass(color).text(message)
}

login.login_handlers = (function() {
	var get_error_handler = function(default_message) {
		return function(xhr, data) {
			if(xhr.responseJSON) {
				data = xhr.responseJSON;
			}

			var message = default_message;
			if (data._server_messages) {
				message = ($.map(JSON.parse(data._server_messages || '[]'), function(v) {
					// temp fix for messages sent as dict
					try {
						return JSON.parse(v).message;
					} catch (e) {
						return v;
					}
				}) || []).join('
') || default_message;
			}

			if(message===default_message) {
				login.set_indicator(message, 'red');
			} else {
				login.reset_sections(false);
			}

		};
	}

	var login_handlers = {
		200: function(data) {
			if(data.message == 'Logged In'){
				login.set_indicator('{{ _("Success") }}', 'green');
				window.location.href = frappe.utils.sanitise_redirect(frappe.utils.get_url_arg("redirect-to")) || data.home_page;
			} else if(data.message == 'Password Reset'){
				window.location.href = data.redirect_to;
			} else if(data.message=="No App") {
				login.set_indicator("{{ _("Success") }}", 'green');
				if(localStorage) {
					var last_visited =
						localStorage.getItem("last_visited")
						|| frappe.utils.sanitise_redirect(frappe.utils.get_url_arg("redirect-to"));
					localStorage.removeItem("last_visited");
				}

				if(data.redirect_to) {
					window.location.href = data.redirect_to;
				}

				if(last_visited && last_visited != "/login") {
					window.location.href = last_visited;
				} else {
					window.location.href = data.home_page;
				}
			} else if(window.location.hash === '#forgot') {
				if(data.message==='not found') {
					login.set_indicator('{{ _("Not a valid user") }}', 'red');
				} else if (data.message=='not allowed') {
					login.set_indicator('{{ _("Not Allowed") }}', 'red');
				} else if (data.message=='disabled') {
					login.set_indicator('{{ _("Not Allowed: Disabled User") }}', 'red');
				} else {
					login.set_indicator('{{ _("Instructions Emailed") }}', 'green');
				}

			} else if(window.location.hash === '#signup') {
				if(cint(data.message[0])==0) {
					login.set_indicator(data.message[1], 'red');
				} else {
					login.set_indicator('{{ _("Success") }}', 'green');
					frappe.msgprint(data.message[1])
				}
				//login.set_indicator(__(data.message), 'green');
			}

			//OTP verification
			if(data.verification && data.message != 'Logged In') {
				login.set_indicator('{{ _("Success") }}', 'green');

				document.cookie = "tmp_id="+data.tmp_id;

				if (data.verification.method == 'OTP App'){
					continue_otp_app(data.verification.setup, data.verification.qrcode);
				} else if (data.verification.method == 'SMS'){
					continue_sms(data.verification.setup, data.verification.prompt);
				} else if (data.verification.method == 'Email'){
					continue_email(data.verification.setup, data.verification.prompt);
				}
			}
		},
		401: get_error_handler('{{ _("Invalid Login. Try again.") }}'),
		417: get_error_handler('{{ _("Oops! Something went wrong") }}')
	};

	return login_handlers;
} )();

frappe.ready(function() {

	login.bind_events();

	if (!window.location.hash) {
		window.location.hash = "#login";
	} else {
		$(window).trigger("hashchange");
	}

	$(".form-signup, .form-forgot").removeClass("hide");
	$(document).trigger('login_rendered');
});

var verify_token =  function(event) {
	$(".form-verify").on("submit", function(eventx) {
		eventx.preventDefault();
		var args = {};
		args.cmd = "login";
		args.otp = $("#login_token").val();
		args.tmp_id = frappe.get_cookie('tmp_id');
		if(!args.otp) {
			frappe.msgprint('{{ _("Login token required") }}');
			return false;
		}
		login.call(args);
		return false;
	});
}

var request_otp = function(r){
	$('.login-content').empty().append($('
').attr({'id':'twofactor_div'}).html(
		'<form class="form-verify">\

\
				{{ _("Verification") }}\

\

\
			<input type="text" id="login_token" autocomplete="off" class="form-control" placeholder={{ _("Verification Code") }} required="" autofocus="">\
			<button class="btn btn-sm btn-primary btn-block" id="verify_token">{{ _("Verify") }}</button>\
		</form>'));
	// add event handler for submit button
	verify_token();
}

var continue_otp_app = function(setup, qrcode){
	request_otp();
	var qrcode_div = $('

');

	if (setup){
		direction = $('
').attr('id','qr_info').text('{{ _("Enter Code displayed in OTP App.") }}');
		qrcode_div.append(direction);
		$('#otp_div').prepend(qrcode_div);
	} else {
		direction = $('
').attr('id','qr_info').text('{{ _("OTP setup using OTP App was not completed. Please contact Administrator.") }}');
		qrcode_div.append(direction);
		$('#otp_div').prepend(qrcode_div);
	}
}

var continue_sms = function(setup, prompt){
	request_otp();
	var sms_div = $('

');

	if (setup){
		sms_div.append(prompt)
		$('#otp_div').prepend(sms_div);
	} else {
		direction = $('
').attr('id','qr_info').text(prompt || '{{ _("SMS was not sent. Please contact Administrator.") }}');
		sms_div.append(direction);
		$('#otp_div').prepend(sms_div)
	}
}

var continue_email = function(setup, prompt){
	request_otp();
	var email_div = $('

');

	if (setup){
		email_div.append(prompt)
		$('#otp_div').prepend(email_div);
	} else {
		var direction = $('
').attr('id','qr_info').text(prompt || '{{ _("Verification code email not sent. Please contact Administrator.") }}');
		email_div.append(direction);
		$('#otp_div').prepend(email_div);
	}
}
<|endoftext|>


<commit_before>var timespans = [
    { start: -7200, end: -1, title: 'last 2 hours' },
    { start: -21600, end: -1, title: 'last 6 hours' },
    { start: -86400, end: -1, title: 'last day' },
    { start: -259200, end: -1, title: 'last 3 days' },
    { start: -604800, end: -1, title: 'last week' },
    { start: -2678400, end: -1, title: 'last month' },
    { start: -8035200, end: -1, title: 'last 3 months' },
    { start: -16070400, end: -1, title: 'last 6 months' },
    { start: -31536000, end: -1, title: 'last year' },
];

function parse_qs(qs) {
    var dict = {};
    if (qs) {
        $.each(qs.split('&'), function(i, v){
          var pair = v.split('=');
          dict[pair[0]] = decodeURIComponent(pair[1]);
        });
    }
    return dict;
}

function encode_qs(dict) {
    var qs = '';
    for (var opt in dict) {
        if (qs != '')
            qs += '&';
        qs += opt + '=' + encodeURIComponent(dict[opt]);
    }
    return qs;
}

function timegraph_controls(controls, images) {
    var bits = images.attr('src').split('?');
    var dict = parse_qs(bits[1]);

    function date_to_offset(date) {
        var now = new Date();
        return Math.floor((date.getTime() - now.getTime()) / 1000);
    }

    function offset_to_date(offset) {
        var now = new Date();
        return new Date(now.getTime() + 1000 * offset);
    }

    // determine current values
    var start = dict['start'] ? parseInt(dict['start']) : -86400;
    var end = dict['end'] ? parseInt(dict['end']) : -1;

    var html = '<select class="timegraph-preset">';
    for (var i in timespans) {
        html += '<option value="' + i + '"';
        if (timespans[i].start == start)
            html += ' selected="selected"';
        html += '>' + timespans[i].title + '</option>';
    }
    html += '</select>';
    html += '';
    html += '<input data-format="dd/MM/yyyy hh:mm:ss" type="text"/>';
    html += '';
    html += '';
    html += '';
    html += '';
    html += '';
    html += '<input data-format="dd/MM/yyyy hh:mm:ss" type="text"/>';
    html += '';
    html += '';
    html += '';
    html += '';
    controls.html(html);

    var start_field = controls.find('.timegraph-start').datetimepicker();
    var start_picker = start_field.data('datetimepicker');
    start_field.on('changeDate', function(e) {
        update_image(images, {start: date_to_offset(start_picker.getDate())});
    });
    start_picker.setDate(offset_to_date(start));

    var end_field = controls.find('.timegraph-end').datetimepicker();
    var end_picker = end_field.data('datetimepicker');
    end_field.on('changeDate', function(e) {
        update_image(images, {end: date_to_offset(end_picker.getDate())});
    });
    end_picker.setDate(offset_to_date(end));

    var timespan = controls.find('select.timegraph-preset');
    timespan.change(function() {
        var i = parseInt(timespan.val());
        if (i >= 0 && i < timespans.length) {
            // update start / end
            start_picker.setDate(offset_to_date(timespans[i].start));
            end_picker.setDate(offset_to_date(timespans[i].end));

            // update images
            update_image(images, {
                'start': timespans[i].start,
                'end': timespans[i].end,
            });
        }
    });

}

function update_image(images, options) {
    images.each(function() {
        var image = $(this);
        var bits = image.attr('src').split('?');
        var dict = parse_qs(bits[1]);
        for (var opt in options)
            dict[opt] = options[opt];
        image.attr('src', bits[0] + '?' + encode_qs(dict));
    });
}

$('.timegraph-graphs img').click(function() {
    var overlay = $('#timegraph-overlay');
    if (!overlay.length) {
        $('body').append('

');
        overlay = $('#timegraph-overlay');
    }

    var qs = $(this).attr('src').split('?')[1];
    var nvpair = parse_qs(qs);
    var title = nvpair['title'];

    var html = '
';
    html += '
';
    html += '<button aria-hidden="true" data-dismiss="modal" class="close" type="button">×</button>';
    html += '
' + title + '
';
    html += '
';
    html += '
';
    html += '';
    html += '<form class="timegraph-controls" style="margin: 10px 0 0"></form>';
    html += '
';
    html += '
';

    overlay.html(html);
    overlay.find('div.modal').modal().css({width: '670px', 'margin-left': '-335px'});
    overlay.find('div.modal-body').css({'max-height': '460px'});

    var image = overlay.find('img');
    timegraph_controls(overlay.find('form'), image);

/*
    // Handle time-shifting using drags.
    var start_offset;
    image.load(function() {
        $(this).css('left', 0);
    });
    image.draggable({
        axis: 'x',
        start: function(event, ui) {
            start_offset = ui.offset;
        },
        stop: function(event, ui) {
            var dict = parse_qs(image.attr('src').split('?')[1]);
            var start = dict['start'] ? parseInt(dict['start']) : -86400;
            var end = dict['end'] ? parseInt(dict['end']) : -1;
            var width = parseInt(dict['width']);

            var time_offset = Math.min(-end-1, -Math.floor((ui.offset.left - start_offset.left) * (end - start) / width));
            update_image(image, {
                'start': start + time_offset,
                'end': end + time_offset});
        },
    });
*/

    // Remove title and adjust image size.
    update_image(image, {
        'width': image.attr('width'),
        'height': image.attr('height'),
        'title': ''});
});

$('.timegraph-controls').each(function() {
    timegraph_controls($(this), $('.timegraph-graphs img'));
});
<commit_msg>Made timegraph display local dates/times instead of UTC<commit_after>var timespans = [
    { start: -7200, end: -1, title: 'last 2 hours' },
    { start: -21600, end: -1, title: 'last 6 hours' },
    { start: -86400, end: -1, title: 'last day' },
    { start: -259200, end: -1, title: 'last 3 days' },
    { start: -604800, end: -1, title: 'last week' },
    { start: -2678400, end: -1, title: 'last month' },
    { start: -8035200, end: -1, title: 'last 3 months' },
    { start: -16070400, end: -1, title: 'last 6 months' },
    { start: -31536000, end: -1, title: 'last year' },
];

function parse_qs(qs) {
    var dict = {};
    if (qs) {
        $.each(qs.split('&'), function(i, v){
          var pair = v.split('=');
          dict[pair[0]] = decodeURIComponent(pair[1]);
        });
    }
    return dict;
}

function encode_qs(dict) {
    var qs = '';
    for (var opt in dict) {
        if (qs != '')
            qs += '&';
        qs += opt + '=' + encodeURIComponent(dict[opt]);
    }
    return qs;
}

function timegraph_controls(controls, images) {
    var bits = images.attr('src').split('?');
    var dict = parse_qs(bits[1]);

    function date_to_offset(date) {
        var now = new Date();
        return Math.floor((date.getTime() - now.getTime()) / 1000);
    }

    function offset_to_date(offset) {
        var now = new Date();
        return new Date(now.getTime() + 1000 * offset);
    }

    // determine current values
    var start = dict['start'] ? parseInt(dict['start']) : -86400;
    var end = dict['end'] ? parseInt(dict['end']) : -1;

    var html = '<select class="timegraph-preset">';
    for (var i in timespans) {
        html += '<option value="' + i + '"';
        if (timespans[i].start == start)
            html += ' selected="selected"';
        html += '>' + timespans[i].title + '</option>';
    }
    html += '</select>';
    html += '';
    html += '<input data-format="dd/MM/yyyy hh:mm:ss" type="text"/>';
    html += '';
    html += '';
    html += '';
    html += '';
    html += '';
    html += '<input data-format="dd/MM/yyyy hh:mm:ss" type="text"/>';
    html += '';
    html += '';
    html += '';
    html += '';
    controls.html(html);

    var start_field = controls.find('.timegraph-start').datetimepicker();
    var start_picker = start_field.data('datetimepicker');
    start_field.on('changeDate', function(e) {
        update_image(images, {start: date_to_offset(start_picker.getLocalDate())});
    });
    start_picker.setLocalDate(offset_to_date(start));

    var end_field = controls.find('.timegraph-end').datetimepicker();
    var end_picker = end_field.data('datetimepicker');
    end_field.on('changeDate', function(e) {
        update_image(images, {end: date_to_offset(end_picker.getLocalDate())});
    });
    end_picker.setLocalDate(offset_to_date(end));

    var timespan = controls.find('select.timegraph-preset');
    timespan.change(function() {
        var i = parseInt(timespan.val());
        if (i >= 0 && i < timespans.length) {
            // update start / end
            start_picker.setLocalDate(offset_to_date(timespans[i].start));
            end_picker.setLocalDate(offset_to_date(timespans[i].end));

            // update images
            update_image(images, {
                'start': timespans[i].start,
                'end': timespans[i].end,
            });
        }
    });

}

function update_image(images, options) {
    images.each(function() {
        var image = $(this);
        var bits = image.attr('src').split('?');
        var dict = parse_qs(bits[1]);
        for (var opt in options)
            dict[opt] = options[opt];
        image.attr('src', bits[0] + '?' + encode_qs(dict));
    });
}

$('.timegraph-graphs img').click(function() {
    var overlay = $('#timegraph-overlay');
    if (!overlay.length) {
        $('body').append('

');
        overlay = $('#timegraph-overlay');
    }

    var qs = $(this).attr('src').split('?')[1];
    var nvpair = parse_qs(qs);
    var title = nvpair['title'];

    var html = '
';
    html += '
';
    html += '<button aria-hidden="true" data-dismiss="modal" class="close" type="button">×</button>';
    html += '
' + title + '
';
    html += '
';
    html += '
';
    html += '';
    html += '<form class="timegraph-controls" style="margin: 10px 0 0"></form>';
    html += '
';
    html += '
';

    overlay.html(html);
    overlay.find('div.modal').modal().css({width: '670px', 'margin-left': '-335px'});
    overlay.find('div.modal-body').css({'max-height': '460px'});

    var image = overlay.find('img');
    timegraph_controls(overlay.find('form'), image);

/*
    // Handle time-shifting using drags.
    var start_offset;
    image.load(function() {
        $(this).css('left', 0);
    });
    image.draggable({
        axis: 'x',
        start: function(event, ui) {
            start_offset = ui.offset;
        },
        stop: function(event, ui) {
            var dict = parse_qs(image.attr('src').split('?')[1]);
            var start = dict['start'] ? parseInt(dict['start']) : -86400;
            var end = dict['end'] ? parseInt(dict['end']) : -1;
            var width = parseInt(dict['width']);

            var time_offset = Math.min(-end-1, -Math.floor((ui.offset.left - start_offset.left) * (end - start) / width));
            update_image(image, {
                'start': start + time_offset,
                'end': end + time_offset});
        },
    });
*/

    // Remove title and adjust image size.
    update_image(image, {
        'width': image.attr('width'),
        'height': image.attr('height'),
        'title': ''});
});

$('.timegraph-controls').each(function() {
    timegraph_controls($(this), $('.timegraph-graphs img'));
});
<|endoftext|>


<commit_before>import React from "react";
import PropTypes from 'prop-types';
import { Route, Switch } from 'react-router-dom';
import "./styles.scss";
import Footer from "components/Footer";
import Auth from 'components/Auth';

const App = props => [
  // Nav,
  props.isLoggedIn ? <PrivateRoutes key={2} /> : <PublicRoutes key={2} />,
  <Footer key={3} />
];

App.propTypes = {
  isLoggedIn: PropTypes.bool.isRequired
}

const PrivateRoutes = props => (
  <Switch>
    <Route exact path='/' render={() => 'feed'} />
    <Route exact path='/explore' render={() => 'explore'} />
  </Switch>
)

const PublicRoutes = props => (
  <Switch>
    <Route exact path='/' component={Auth} />
    <Route exact path='/forgot' render={() => 'password'} />
  </Switch>
)

export default App;
<commit_msg>Refactoring presenter.js<commit_after>import React from "react";
import PropTypes from 'prop-types';
import { Route, Switch } from 'react-router-dom';
import "./styles.scss";
import Footer from "components/Footer";
import Auth from 'components/Auth';

const App = props => [
  // Nav,
  props.isLoggedIn ? <PrivateRoutes key={2} /> : <PublicRoutes key={2} />,
  <Footer key={3} />
];

App.propTypes = {
  isLoggedIn: PropTypes.bool.isRequired
}

const PrivateRoutes = props => (
  <Switch>
    <Route exact path='/' render={() => 'feed'} />
    <Route exact path='/explore' render={() => 'explore'} />
  </Switch>
)

const PublicRoutes = props => (
  <Switch>
    <Route exact path='/' component={Auth``} />
    <Route exact path='/forgot' render={() => 'password'} />
  </Switch>
)

export default App;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Menu from "../components/Menu"
import Events from "../components/Events"
import Stats from "../components/Stats"
import EventView from "../components/EventView"
import take from 'lodash/take'
import indexOf from 'lodash/indexOf'
import sortByOrder from 'lodash/sortBy'
import { eventsOnPage, eventView, eventsFreeze } from "../actions/event"
import { appReset } from "../actions/app"

class Board extends Component {

	constructor(props) {
		super(props)
	}

	render() {
		const { events, actors, repos, numOnPage, eventsOnPage, eventView, eventsFreeze, isFrozen, appReset, started, eventIds,
		eventViewerNext, eventViewerCurrent, eventViewerPrev,
		bestStargazers, bestForkers,
		mostStarred, mostForked } = this.props

		const viewingEvent = events[eventViewerCurrent]
		const viewingEventActor  = viewingEvent ? actors[viewingEvent.actor] : null
		const viewingEventRepo =  viewingEvent ? repos[viewingEvent.repo] : null

		return (

				<Menu
					numOnPage={ numOnPage } eventsOnPage={ eventsOnPage }
					eventsFreeze={ eventsFreeze } isFrozen={ isFrozen } appReset={ appReset }
					events={ events } actors={ actors } repos={ repos } />

							<EventView
								eventView={ eventView } event={ viewingEvent }
								actor={ viewingEventActor } repo={ viewingEventRepo }
								next={ eventViewerNext } prev={ eventViewerPrev } />

							<Events
								eventIds={ eventIds } events={ events }
								actors={ actors } repos={ repos }
								eventView={ eventView }
								numOnPage={ numOnPage }
								isFrozen={ isFrozen } />

							<Stats
								actors={ actors } repos={ repos } events={ events }  started={ started }
								bestStargazers={ bestStargazers } bestForkers={ bestForkers }
								mostStarred={ mostStarred } mostForked={ mostForked } />

		)
	}

}

Board.propTypes = {
	numOnPage: PropTypes.number.isRequired,
	eventIds: PropTypes.array.isRequired,
	isFrozen: PropTypes.bool.isRequired,

	events: PropTypes.object.isRequired,
	actors: PropTypes.object.isRequired,
	repos: PropTypes.object.isRequired,

	eventViewerNext: PropTypes.string,
	eventViewerCurrent: PropTypes.string,
	eventViewerPrev: PropTypes.string,

	eventsOnPage: PropTypes.func.isRequired,
	eventsFreeze: PropTypes.func.isRequired,
	eventView: PropTypes.func.isRequired,
}

function mapStateToProps(state) {

	let bestStargazers = sortByOrder(state.reports.actors, ((a)=>
		a.stars.length
	), ['desc'])

	let bestForkers = sortByOrder(state.reports.actors, ((a)=>
		a.forks.length
	), ['desc'])

	let mostStarred = sortByOrder(state.reports.repos, ((r)=>
		r.stars.length
	), ['desc'])

	let mostForked = sortByOrder(state.reports.repos, ((r)=>
		r.forks.length
	), ['desc'])

	let next = null
	let prev = null

	if (state.app.viewingEvent) {
		const index = indexOf(state.entities.eventIds, state.app.viewingEvent)
		next = state.entities.eventIds[index - 1]
		prev = state.entities.eventIds[index + 1]
	}

	return {
		started: state.app.started,
		numOnPage: state.app.numOnPage,
		eventIds: take(state.entities.eventIds, state.app.numOnPage),
		isFrozen: state.app.isFrozen,

		events: state.entities.events,
		actors: state.entities.actors,
		repos: state.entities.repos,

		eventViewerNext: next,
		eventViewerCurrent: state.app.viewingEvent,
		eventViewerPrev: prev,

		bestStargazers: take(bestStargazers, 3),
		bestForkers: take(bestForkers, 3),

		mostStarred: take(mostStarred, 3),
		mostForked: take(mostForked, 3)
	}
}

function mapDispatchToProps(dispatch) {
	return bindActionCreators({
		appReset: appReset,
		eventsOnPage: eventsOnPage,
		eventsFreeze: eventsFreeze,
		eventView: eventView
	}, dispatch)
}

export default connect(mapStateToProps, mapDispatchToProps)(Board)<commit_msg>fix sort lists<commit_after>import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Menu from "../components/Menu"
import Events from "../components/Events"
import Stats from "../components/Stats"
import EventView from "../components/EventView"
import take from 'lodash/take'
import indexOf from 'lodash/indexOf'
import sortBy from 'lodash/sortBy'
import { eventsOnPage, eventView, eventsFreeze } from "../actions/event"
import { appReset } from "../actions/app"

class Board extends Component {

	constructor(props) {
		super(props)
	}

	render() {
		const { events, actors, repos, numOnPage, eventsOnPage, eventView,
		eventsFreeze, isFrozen, appReset, started, eventIds,
		eventViewerNext, eventViewerCurrent, eventViewerPrev,
		bestStargazers, bestForkers,
		mostStarred, mostForked } = this.props

		const viewingEvent = events[eventViewerCurrent]
		const viewingEventActor  = viewingEvent ? actors[viewingEvent.actor] : null
		const viewingEventRepo =  viewingEvent ? repos[viewingEvent.repo] : null

		return (

				<Menu
					numOnPage={ numOnPage } eventsOnPage={ eventsOnPage }
					eventsFreeze={ eventsFreeze } isFrozen={ isFrozen } appReset={ appReset }
					events={ events } actors={ actors } repos={ repos } />

							<EventView
								eventView={ eventView } event={ viewingEvent }
								actor={ viewingEventActor } repo={ viewingEventRepo }
								next={ eventViewerNext } prev={ eventViewerPrev } />

							<Events
								eventIds={ eventIds } events={ events }
								actors={ actors } repos={ repos }
								eventView={ eventView }
								numOnPage={ numOnPage }
								isFrozen={ isFrozen } />

							<Stats
								actors={ actors } repos={ repos } events={ events }  started={ started }
								bestStargazers={ bestStargazers } bestForkers={ bestForkers }
								mostStarred={ mostStarred } mostForked={ mostForked } />

		)
	}

}

Board.propTypes = {
	numOnPage: PropTypes.number.isRequired,
	eventIds: PropTypes.array.isRequired,
	isFrozen: PropTypes.bool.isRequired,

	events: PropTypes.object.isRequired,
	actors: PropTypes.object.isRequired,
	repos: PropTypes.object.isRequired,

	eventViewerNext: PropTypes.string,
	eventViewerCurrent: PropTypes.string,
	eventViewerPrev: PropTypes.string,

	eventsOnPage: PropTypes.func.isRequired,
	eventsFreeze: PropTypes.func.isRequired,
	eventView: PropTypes.func.isRequired,
}

function mapStateToProps(state) {

	let bestStargazers = sortBy(state.reports.actors, ((a)=>
		a.stars.length
	)).reverse()

	let bestForkers = sortBy(state.reports.actors, ((a)=>
		a.forks.length
	)).reverse()

	let mostStarred = sortBy(state.reports.repos, ((r)=>
		r.stars.length
	)).reverse()

	let mostForked = sortBy(state.reports.repos, ((r)=>
		r.forks.length
	)).reverse()

	let next = null
	let prev = null

	if (state.app.viewingEvent) {
		const index = indexOf(state.entities.eventIds, state.app.viewingEvent)
		next = state.entities.eventIds[index - 1]
		prev = state.entities.eventIds[index + 1]
	}

	return {
		started: state.app.started,
		numOnPage: state.app.numOnPage,
		eventIds: take(state.entities.eventIds, state.app.numOnPage),
		isFrozen: state.app.isFrozen,

		events: state.entities.events,
		actors: state.entities.actors,
		repos: state.entities.repos,

		eventViewerNext: next,
		eventViewerCurrent: state.app.viewingEvent,
		eventViewerPrev: prev,

		bestStargazers: take(bestStargazers, 3),
		bestForkers: take(bestForkers, 3),

		mostStarred: take(mostStarred, 3),
		mostForked: take(mostForked, 3)
	}
}

function mapDispatchToProps(dispatch) {
	return bindActionCreators({
		appReset: appReset,
		eventsOnPage: eventsOnPage,
		eventsFreeze: eventsFreeze,
		eventView: eventView
	}, dispatch)
}

export default connect(mapStateToProps, mapDispatchToProps)(Board)<|endoftext|>


<commit_before>set_global('page_params', {
    alert_words: ['alertone', 'alerttwo', 'alertthree', 'al*rt.*s', '.+', 'emoji'],
});

set_global('feature_flags', {
    alert_words: true,
});

zrequire('people');
zrequire('alert_words');

global.people.add({
    email: 'tester@zulip.com',
    full_name: 'Tester von Tester',
    user_id: 42,
});

global.people.initialize_current_user(42);

var regular_message = { sender_email: 'another@zulip.com', content: '
a message
'};
var own_message = { sender_email: 'tester@zulip.com', content: '
hey this message alertone
',
                    alerted: true };
var other_message = { sender_email: 'another@zulip.com', content: '
another alertone message
',
                      alerted: true };
var caps_message = { sender_email: 'another@zulip.com', content: '
another ALERTtwo message
',
                     alerted: true };
var alertwordboundary_message = { sender_email: 'another@zulip.com',
                                  content: '
another alertthreemessage
', alerted: false };
var multialert_message = { sender_email: 'another@zulip.com', content:
                           '
another alertthreemessage alertone and then alerttwo
',
                           alerted: true };
var unsafe_word_message = { sender_email: 'another@zulip.com', content: '
gotta al*rt.*s all
',
                            alerted: true };
var alert_in_url_message = { sender_email: 'another@zulip.com', content: '
http://www.google.com/alertone/me
',
                            alerted: true };
var question_word_message = { sender_email: 'another@zulip.com', content: '
still alertone? me
',
                            alerted: true };

var alert_domain_message = { sender_email: 'another@zulip.com', content: '
now with link www.alerttwo.us/foo/bar
',
                     alerted: true };
// This test ensure we are not mucking up rendered HTML content.
var message_with_emoji = { sender_email: 'another@zulip.com', content: '
I  emoji!
',
                           alerted: true };

(function test_notifications() {
    assert(!alert_words.notifies(regular_message));
    assert(!alert_words.notifies(own_message));
    assert(alert_words.notifies(other_message));
    assert(alert_words.notifies(caps_message));
    assert(!alert_words.notifies(alertwordboundary_message));
    assert(alert_words.notifies(multialert_message));
    assert(alert_words.notifies(unsafe_word_message));
    assert(alert_words.notifies(alert_domain_message));
    assert(alert_words.notifies(message_with_emoji));
}());

(function test_munging() {
    var saved_content = regular_message.content;
    alert_words.process_message(regular_message);
    assert.equal(saved_content, regular_message.content);

    saved_content = alertwordboundary_message.content;
    alert_words.process_message(alertwordboundary_message);
    assert.equal(alertwordboundary_message.content, saved_content);

    alert_words.process_message(other_message);
    assert.equal(other_message.content, "
another alertone message
");
    alert_words.process_message(caps_message);
    assert.equal(caps_message.content, "
another ALERTtwo message
");

    alert_words.process_message(multialert_message);
    assert.equal(multialert_message.content, "
another alertthreemessage alertone and then alerttwo
");

    alert_words.process_message(unsafe_word_message);
    assert.equal(unsafe_word_message.content, "
gotta al*rt.*s all
");

    alert_words.process_message(alert_in_url_message);
    assert.equal(alert_in_url_message.content, "
http://www.google.com/alertone/me
");

    alert_words.process_message(question_word_message);
    assert.equal(question_word_message.content, "
still alertone? me
");

    alert_words.process_message(alert_domain_message);
    assert.equal(alert_domain_message.content, '
now with link www.alerttwo.us/foo/bar
');

    alert_words.process_message(message_with_emoji);
    assert.equal(message_with_emoji.content, '
I  emoji!
');
}());
<commit_msg>node_tests: Update alert_words.js to use es6.<commit_after>set_global('page_params', {
    alert_words: ['alertone', 'alerttwo', 'alertthree', 'al*rt.*s', '.+', 'emoji'],
});

set_global('feature_flags', {
    alert_words: true,
});

zrequire('people');
zrequire('alert_words');

global.people.add({
    email: 'tester@zulip.com',
    full_name: 'Tester von Tester',
    user_id: 42,
});

global.people.initialize_current_user(42);

const regular_message = { sender_email: 'another@zulip.com', content: '
a message
'};
const own_message = { sender_email: 'tester@zulip.com', content: '
hey this message alertone
',
                    alerted: true };
const other_message = { sender_email: 'another@zulip.com', content: '
another alertone message
',
                      alerted: true };
const caps_message = { sender_email: 'another@zulip.com', content: '
another ALERTtwo message
',
                     alerted: true };
const alertwordboundary_message = { sender_email: 'another@zulip.com',
                                  content: '
another alertthreemessage
', alerted: false };
const multialert_message = { sender_email: 'another@zulip.com', content:
                           '
another alertthreemessage alertone and then alerttwo
',
                           alerted: true };
const unsafe_word_message = { sender_email: 'another@zulip.com', content: '
gotta al*rt.*s all
',
                            alerted: true };
const alert_in_url_message = { sender_email: 'another@zulip.com', content: '
http://www.google.com/alertone/me
',
                            alerted: true };
const question_word_message = { sender_email: 'another@zulip.com', content: '
still alertone? me
',
                            alerted: true };

const alert_domain_message = { sender_email: 'another@zulip.com', content: '
now with link www.alerttwo.us/foo/bar
',
                     alerted: true };
// This test ensure we are not mucking up rendered HTML content.
const message_with_emoji = { sender_email: 'another@zulip.com', content: '
I  emoji!
',
                           alerted: true };

(function test_notifications() {
    assert(!alert_words.notifies(regular_message));
    assert(!alert_words.notifies(own_message));
    assert(alert_words.notifies(other_message));
    assert(alert_words.notifies(caps_message));
    assert(!alert_words.notifies(alertwordboundary_message));
    assert(alert_words.notifies(multialert_message));
    assert(alert_words.notifies(unsafe_word_message));
    assert(alert_words.notifies(alert_domain_message));
    assert(alert_words.notifies(message_with_emoji));
}());

(function test_munging() {
    let saved_content = regular_message.content;
    alert_words.process_message(regular_message);
    assert.equal(saved_content, regular_message.content);

    saved_content = alertwordboundary_message.content;
    alert_words.process_message(alertwordboundary_message);
    assert.equal(alertwordboundary_message.content, saved_content);

    alert_words.process_message(other_message);
    assert.equal(other_message.content, "
another alertone message
");
    alert_words.process_message(caps_message);
    assert.equal(caps_message.content, "
another ALERTtwo message
");

    alert_words.process_message(multialert_message);
    assert.equal(multialert_message.content, "
another alertthreemessage alertone and then alerttwo
");

    alert_words.process_message(unsafe_word_message);
    assert.equal(unsafe_word_message.content, "
gotta al*rt.*s all
");

    alert_words.process_message(alert_in_url_message);
    assert.equal(alert_in_url_message.content, "
http://www.google.com/alertone/me
");

    alert_words.process_message(question_word_message);
    assert.equal(question_word_message.content, "
still alertone? me
");

    alert_words.process_message(alert_domain_message);
    assert.equal(alert_domain_message.content, `<p>now with link <a href="http://www.alerttwo.us/foo/bar" target="_blank" title="http://www.alerttwo.us/foo/bar">www.<span class='alert-word'>alerttwo</span>.us/foo/bar</a></p>`);

    alert_words.process_message(message_with_emoji);
    assert.equal(message_with_emoji.content, `<p>I <img alt=":heart:" class="emoji" src="/static/generated/emoji/images/emoji/unicode/2764.png" title="heart"> <span class='alert-word'>emoji</span>!</p>`);
}());
<|endoftext|>


<commit_before>const {createLintJsFilesTask, createLintTsFilesTask} = require("../tasks");
const {parallel} = require("gulp");

exports.createLintWorkflow = moduleName => parallel(createLintJsFilesTask(moduleName), createLintTsFilesTask(moduleName));
<commit_msg>#52: 'lint' workflow should exit with 1 in case of an error.<commit_after>const {createLintJsFilesTask, createLintTsFilesTask} = require("../tasks");
const {parallel} = require("gulp");

const failAfterError = true;

exports.createLintWorkflow = moduleName =>
    parallel(createLintJsFilesTask(moduleName, failAfterError), createLintTsFilesTask(moduleName, failAfterError));
<|endoftext|>


<commit_before>import '../../plugins/uploader';
import initTinymce from '../../libs/tinymce';
import Tags from '../../libs/tags';
import Dialog from '../../libs/dialog';
import Map from '../../libs/map';

/**
 * Cast data from bool to int to properly display radio buttons (0 and 1 instade of true and false).
 *
 * @param data
 * @return {*}
 */
function toInt(data) {
    for (let item in data) {
        if (data.hasOwnProperty(item)) {
            if (typeof(data[item]) == 'boolean') {
                data[item] = +data[item];
            }

            if (typeof(data[item]) == 'object') {
                data[item] = toInt(data[item]);
            }
        }
    }

    return data;
}

new Vue({
    el: '.submit-form',
    delimiters: ['${', '}'],
    data: toInt(data),
    mounted: function () {
        initTinymce();

        this.marker = null;

        if (typeof google !== 'undefined') {
            this.map = new Map();

            if (this.firm.latitude && this.firm.longitude) {
                this._setupMarker();
            }

            this.map.setupGeocodeOnMapClick(result => {
                this.firm = Object.assign(this.firm, result);
                this._setupMarker();
            });
        }

        new Tags({
            onSelect: (value) => {
                this.tags.push({name: value, pivot: {priority: 1}});
                // fetch only tag name
                let pluck = this.tags.map(item => item.name);

                // request suggestions
                $.get($('#tag').data('suggestions-url'), {t: pluck}, result => {
                    this.suggestions = result;
                });
            }
        });

        $('#tags-container').each(function () {
            $(this).sortable();
        });

        $('[v-loader]').remove();
    },
    methods: {
        /**
         * Add tag after clicking on suggestion tag.
         *
         * @param {String} value
         */
        addTag: function (value) {
            this.tags.push({name: value, pivot: {priority: 1}});
        },
        removeTag: function (index) {
            this.tags.splice(index, 1);
        },
        isInvalid: function (fields) {
            return Object.keys(this.errors).findIndex(element => fields.indexOf(element) > -1) > -1;
        },
        charCounter: function (item, limit) {
            let model = item.split('.').reduce((o, i) => o[i], this);

            return limit - String(model !== null ? model : '').length;
        },
        toggleBenefit: function (item) {
            let index = this.benefits.indexOf(item);

            if (index === -1) {
                this.benefits.push(item);
            } else {
                this.benefits.splice(index, 1);
            }
        },
        addBenefit: function (e) {
            if (e.target.value.trim()) {
                this.benefits.push(e.target.value);
            }

            e.target.value = '';
        },
        removeBenefit: function (benefit) {
            this.benefits.splice(this.benefits.indexOf(benefit), 1);
        },
        updateBenefit: function () {
            //
        },
        addFirm: function () {
            let dialog = new Dialog({
                title: 'Dodanie nowej firmy',
                message: 'Czy na pewno chcesz dodać nową firme i przypisać ją do tego ogłoszenia?',
                buttons: [{
                    label: 'Anuluj',
                    attr: {
                        'class': 'btn btn-default',
                        'type': 'button',
                        'data-dismiss': 'modal'
                    }
                }, {
                    label: 'Tak',
                    attr: {
                        'class': 'btn btn-primary'
                    },
                    onClick: () => {
                        this.newFirm();
                        dialog.close();
                    }
                }]
            });

            dialog.show();
        },
        selectFirm: function (firmId) {
            let index = this.firms.findIndex(element => element.id == firmId);

            this.firm = this.firms[index];
            this.firm.is_private = +false; // must be the number - not bool

            this.benefits = this.firm.benefits;

            tinymce.get('description').setContent(this.firm.description);
        },
        changeFirm: function () {
            if (!this.firm.name) {
                return;
            }

            let dialog = new Dialog({
                title: 'Zmiana nazwy firmy?',
                message: 'Zamierzasz edytować nazwę tej firmy. Weź pod uwagę, że zmieniona nazwa będzie wyświetlana przy wszystkich Twoich ofertach. Czy może chcesz dodać nową firmę?',
                buttons: [{
                    label: 'Jest OK, chce tylko zmienić nazwę',
                    attr: {
                        'class': 'btn btn-default',
                        'type': 'button',
                        'data-dismiss': 'modal'
                    }
                }, {
                    label: 'Tak, chcę dodać nową firmę',
                    attr: {
                        'class': 'btn btn-primary'
                    },
                    onClick: () => {
                        this.newFirm();
                        dialog.close();
                    }
                }]
            });

            dialog.show();
        },
        newFirm: function () {
            this.firm = {
                'id': null,
                'name': null,
                'headline': '',
                'logo': null,
                'description': null,
                'website': null,
                'is_private': +false,
                'is_agency': +false,
                'employees': null,
                'founded': null
            };

            this.benefits = [];
            tinymce.get('description').setContent(''); // new firm - empty description
        },
        changeAddress: function (e) {
            let val = e.target.value.trim();

            if (val.length) {
                this.map.geocode(val, result => {
                    this.firm = Object.assign(this.firm, result);

                    this._setupMarker(); // must be inside closure
                });
            }
            else {
                ['longitude', 'latitude', 'country', 'city', 'street', 'postcode'].forEach(field => {
                    this.firm[field] = null;
                });

                this._setupMarker();
            }
        },
        _setupMarker: function () {
            this.map.removeMarker(this.marker);
            this.marker = this.map.addMarker(this.firm.latitude, this.firm.longitude);
        }
    },
    computed: {
        deadlineDate: function () {
            let value = parseInt(this.job.deadline);

            if (value > 0) {
                let date = new Date();
                date.setDate(date.getDate() + value);

                return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
            }
            else {
                return '--';
            }
        },
        address: function () {
            return String((this.firm.street || '') + ' ' + (this.firm.house || '') + ' ' + (this.firm.postcode || '') +  ' ' + (this.firm.city || '')).trim();
        }
    },
    watch: {
        'job.enable_apply': function (flag) {
            if (Boolean(parseInt(flag))) {
                tinymce.get('recruitment').hide();

                $('#recruitment').attr('disabled', 'disabled').hide();
            } else {
                tinymce.get('recruitment').show();

                $('#recruitment').removeAttr('disabled');
            }
        },
        'firm.is_private': function (flag) {
            if (!Boolean(parseInt(flag))) {
                google.maps.event.trigger(map, 'resize');
            }
        }
    }
});

$(() => {
    'use strict';

    let navigation = $('#form-navigation');
    let fixed = $('#form-navbar-fixed');

    $('#form-navigation-container')
        .html(navigation.html())
        .on('click', ':submit', () => $('.submit-form').submit())
        .on('click', 'button[data-submit-state]', e => $(e.currentTarget).attr('disabled', 'disabled').text($(e.currentTarget).data('submit-state')));

    if (navigation.length) {
        $(window).scroll(() => {
            let bottom = $(window).scrollTop() + $(window).height();

            if (bottom > navigation.offset().top) {
                fixed.fadeOut();
            }
            else {
                fixed.show();
            }
        }).trigger('scroll');
    }

    /**
     * Save and exit button
     */
    $('.btn-save').on('click', () => {
        $('input[name="done"]').val(1);
    });

    $('.jumbotron .btn-close').click(() => {
        $('.jumbotron .close').click();
    });

    $('.submit-form').on('focus', ':input', e => {
        let $this = $(e.currentTarget);
        let offset = $this.offset().top;
        let name = $this.attr('name');

        $('.sidebar-hint').hide();

        if (typeof name !== 'undefined') {
            name = name.replace('[', '').replace(']', '');

            $('#hint-' + name).fadeIn();
            offset -= $('aside').offset().top;

            $('#hint-container').css('top', offset);
        }
    });

    $.uploader({
        input: 'logo',
        onChanged: function(data) {
            $('#firm-form').find('input[name="logo"]').val(data.name);
        },
        onDeleted: function() {
            $('#firm-form').find('input[name="logo"]').val('');
        }
    });
});
<commit_msg>klikniecie na sugestie nie tylko powoduje dodanie tagu, ale rowniez wykonanie kolejnego zapytania o sugestie<commit_after>import '../../plugins/uploader';
import initTinymce from '../../libs/tinymce';
import Tags from '../../libs/tags';
import Dialog from '../../libs/dialog';
import Map from '../../libs/map';

/**
 * Cast data from bool to int to properly display radio buttons (0 and 1 instade of true and false).
 *
 * @param data
 * @return {*}
 */
function toInt(data) {
    for (let item in data) {
        if (data.hasOwnProperty(item)) {
            if (typeof(data[item]) == 'boolean') {
                data[item] = +data[item];
            }

            if (typeof(data[item]) == 'object') {
                data[item] = toInt(data[item]);
            }
        }
    }

    return data;
}

new Vue({
    el: '.submit-form',
    delimiters: ['${', '}'],
    data: toInt(data),
    mounted: function () {
        initTinymce();

        this.marker = null;

        if (typeof google !== 'undefined') {
            this.map = new Map();

            if (this.firm.latitude && this.firm.longitude) {
                this._setupMarker();
            }

            this.map.setupGeocodeOnMapClick(result => {
                this.firm = Object.assign(this.firm, result);
                this._setupMarker();
            });
        }

        this.tagComponent = new Tags({
            onSelect: (value) => {
                this.tags.push({name: value, pivot: {priority: 1}});
                // fetch only tag name
                let pluck = this.tags.map(item => item.name);

                // request suggestions
                $.get($('#tag').data('suggestions-url'), {t: pluck}, result => {
                    this.suggestions = result;
                });
            }
        });

        $('#tags-container').each(function () {
            $(this).sortable();
        });

        $('[v-loader]').remove();
    },
    methods: {
        /**
         * Add tag after clicking on suggestion tag.
         *
         * @param {String} value
         */
        addTag: function (value) {
            this.tagComponent.addTag(value);
        },
        removeTag: function (index) {
            this.tags.splice(index, 1);
        },
        isInvalid: function (fields) {
            return Object.keys(this.errors).findIndex(element => fields.indexOf(element) > -1) > -1;
        },
        charCounter: function (item, limit) {
            let model = item.split('.').reduce((o, i) => o[i], this);

            return limit - String(model !== null ? model : '').length;
        },
        toggleBenefit: function (item) {
            let index = this.benefits.indexOf(item);

            if (index === -1) {
                this.benefits.push(item);
            } else {
                this.benefits.splice(index, 1);
            }
        },
        addBenefit: function (e) {
            if (e.target.value.trim()) {
                this.benefits.push(e.target.value);
            }

            e.target.value = '';
        },
        removeBenefit: function (benefit) {
            this.benefits.splice(this.benefits.indexOf(benefit), 1);
        },
        updateBenefit: function () {
            //
        },
        addFirm: function () {
            let dialog = new Dialog({
                title: 'Dodanie nowej firmy',
                message: 'Czy na pewno chcesz dodać nową firme i przypisać ją do tego ogłoszenia?',
                buttons: [{
                    label: 'Anuluj',
                    attr: {
                        'class': 'btn btn-default',
                        'type': 'button',
                        'data-dismiss': 'modal'
                    }
                }, {
                    label: 'Tak',
                    attr: {
                        'class': 'btn btn-primary'
                    },
                    onClick: () => {
                        this.newFirm();
                        dialog.close();
                    }
                }]
            });

            dialog.show();
        },
        selectFirm: function (firmId) {
            let index = this.firms.findIndex(element => element.id == firmId);

            this.firm = this.firms[index];
            this.firm.is_private = +false; // must be the number - not bool

            this.benefits = this.firm.benefits;

            tinymce.get('description').setContent(this.firm.description);
        },
        changeFirm: function () {
            if (!this.firm.name) {
                return;
            }

            let dialog = new Dialog({
                title: 'Zmiana nazwy firmy?',
                message: 'Zamierzasz edytować nazwę tej firmy. Weź pod uwagę, że zmieniona nazwa będzie wyświetlana przy wszystkich Twoich ofertach. Czy może chcesz dodać nową firmę?',
                buttons: [{
                    label: 'Jest OK, chce tylko zmienić nazwę',
                    attr: {
                        'class': 'btn btn-default',
                        'type': 'button',
                        'data-dismiss': 'modal'
                    }
                }, {
                    label: 'Tak, chcę dodać nową firmę',
                    attr: {
                        'class': 'btn btn-primary'
                    },
                    onClick: () => {
                        this.newFirm();
                        dialog.close();
                    }
                }]
            });

            dialog.show();
        },
        newFirm: function () {
            this.firm = {
                'id': null,
                'name': null,
                'headline': '',
                'logo': null,
                'description': null,
                'website': null,
                'is_private': +false,
                'is_agency': +false,
                'employees': null,
                'founded': null
            };

            this.benefits = [];
            tinymce.get('description').setContent(''); // new firm - empty description
        },
        changeAddress: function (e) {
            let val = e.target.value.trim();

            if (val.length) {
                this.map.geocode(val, result => {
                    this.firm = Object.assign(this.firm, result);

                    this._setupMarker(); // must be inside closure
                });
            }
            else {
                ['longitude', 'latitude', 'country', 'city', 'street', 'postcode'].forEach(field => {
                    this.firm[field] = null;
                });

                this._setupMarker();
            }
        },
        _setupMarker: function () {
            this.map.removeMarker(this.marker);
            this.marker = this.map.addMarker(this.firm.latitude, this.firm.longitude);
        }
    },
    computed: {
        deadlineDate: function () {
            let value = parseInt(this.job.deadline);

            if (value > 0) {
                let date = new Date();
                date.setDate(date.getDate() + value);

                return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
            }
            else {
                return '--';
            }
        },
        address: function () {
            return String((this.firm.street || '') + ' ' + (this.firm.house || '') + ' ' + (this.firm.postcode || '') +  ' ' + (this.firm.city || '')).trim();
        }
    },
    watch: {
        'job.enable_apply': function (flag) {
            if (Boolean(parseInt(flag))) {
                tinymce.get('recruitment').hide();

                $('#recruitment').attr('disabled', 'disabled').hide();
            } else {
                tinymce.get('recruitment').show();

                $('#recruitment').removeAttr('disabled');
            }
        },
        'firm.is_private': function (flag) {
            if (!Boolean(parseInt(flag))) {
                google.maps.event.trigger(map, 'resize');
            }
        }
    }
});

$(() => {
    'use strict';

    let navigation = $('#form-navigation');
    let fixed = $('#form-navbar-fixed');

    $('#form-navigation-container')
        .html(navigation.html())
        .on('click', ':submit', () => $('.submit-form').submit())
        .on('click', 'button[data-submit-state]', e => $(e.currentTarget).attr('disabled', 'disabled').text($(e.currentTarget).data('submit-state')));

    if (navigation.length) {
        $(window).scroll(() => {
            let bottom = $(window).scrollTop() + $(window).height();

            if (bottom > navigation.offset().top) {
                fixed.fadeOut();
            }
            else {
                fixed.show();
            }
        }).trigger('scroll');
    }

    /**
     * Save and exit button
     */
    $('.btn-save').on('click', () => {
        $('input[name="done"]').val(1);
    });

    $('.jumbotron .btn-close').click(() => {
        $('.jumbotron .close').click();
    });

    $('.submit-form').on('focus', ':input', e => {
        let $this = $(e.currentTarget);
        let offset = $this.offset().top;
        let name = $this.attr('name');

        $('.sidebar-hint').hide();

        if (typeof name !== 'undefined') {
            name = name.replace('[', '').replace(']', '');

            $('#hint-' + name).fadeIn();
            offset -= $('aside').offset().top;

            $('#hint-container').css('top', offset);
        }
    });

    $.uploader({
        input: 'logo',
        onChanged: function(data) {
            $('#firm-form').find('input[name="logo"]').val(data.name);
        },
        onDeleted: function() {
            $('#firm-form').find('input[name="logo"]').val('');
        }
    });
});
<|endoftext|>


<commit_before>import {actionCreatorsFor} from 'redux-crud'
import cuid from 'cuid'
import * as displayErrorsActions from 'gModules/displayErrors/actions.js'
import * as membershipActions from 'gModules/userOrganizationMemberships/actions.js'
import {captureApiError} from 'lib/errors/index.js'
import {setupGuesstimateApi} from 'servers/guesstimate-api/constants.js'
import * as userOrganizationMembershipActions from 'gModules/userOrganizationMemberships/actions.js'
import * as userOrganizationInvitationActions from 'gModules/userOrganizationInvitations/actions.js'

let sActions = actionCreatorsFor('organizations')

function api(state) {
  function getToken(state) {
    return _.get(state, 'me.token')
  }
  return setupGuesstimateApi(getToken(state))
}

export function fetchById(organizationId) {
  return (dispatch, getState) => {
    api(getState()).organizations.get({organizationId}, (err, organization) => {
      if (err) {
        dispatch(displayErrorsActions.newError())
        captureApiError('OrganizationsFetch', null, null, err, {url: 'fetch'})
      } else if (organization) {
        dispatch(sActions.fetchSuccess([organization]))

        const members = !!organization.members ? organization.members : []
        const invitations = !!organization.invitations ? organization.invitations : []

        dispatch(userOrganizationMembershipActions.fetchSuccess(members))
        dispatch(userOrganizationInvitationActions.fetchSuccess(invitations))
      }
    })
  }
}

export function fetchSuccess(organizations) {
  return (dispatch) => {
    const formatted = organizations.map(o => _.pick(o, ['id', 'name', 'picture', 'admin_id']))
    dispatch(sActions.fetchSuccess(formatted))
  }
}

export function addMember(organizationId, email) {
  return (dispatch, getState) => {
    const cid = cuid()
    let object = {id: cid, organization_id: organizationId, user_id: 4}

    const action = sActions.createStart(object);

    api(getState()).organizations.addMember({organizationId, email}, (err, membership) => {
      if (err) {
        dispatch(sActions.createError(err, object))
      }
      else if (membership) {
        dispatch(userActions.fetchSuccess([membership._embedded.user]))
        dispatch(membershipActions.createSuccess([membership]))
      }
    })
  }
}
<commit_msg>Grab memberships (fixed typo members -> memberships)<commit_after>import {actionCreatorsFor} from 'redux-crud'
import cuid from 'cuid'
import * as displayErrorsActions from 'gModules/displayErrors/actions.js'
import * as membershipActions from 'gModules/userOrganizationMemberships/actions.js'
import {captureApiError} from 'lib/errors/index.js'
import {setupGuesstimateApi} from 'servers/guesstimate-api/constants.js'
import * as userOrganizationMembershipActions from 'gModules/userOrganizationMemberships/actions.js'
import * as userOrganizationInvitationActions from 'gModules/userOrganizationInvitations/actions.js'

let sActions = actionCreatorsFor('organizations')

function api(state) {
  function getToken(state) {
    return _.get(state, 'me.token')
  }
  return setupGuesstimateApi(getToken(state))
}

export function fetchById(organizationId) {
  return (dispatch, getState) => {
    api(getState()).organizations.get({organizationId}, (err, organization) => {
      if (err) {
        dispatch(displayErrorsActions.newError())
        captureApiError('OrganizationsFetch', null, null, err, {url: 'fetch'})
      } else if (organization) {
        dispatch(sActions.fetchSuccess([organization]))

        const memberships = !!organization.memberships ? organization.memberships : []
        const invitations = !!organization.invitations ? organization.invitations : []

        dispatch(userOrganizationMembershipActions.fetchSuccess(memberships))
        dispatch(userOrganizationInvitationActions.fetchSuccess(invitations))
      }
    })
  }
}

export function fetchSuccess(organizations) {
  return (dispatch) => {
    const formatted = organizations.map(o => _.pick(o, ['id', 'name', 'picture', 'admin_id']))
    dispatch(sActions.fetchSuccess(formatted))
  }
}

export function addMember(organizationId, email) {
  return (dispatch, getState) => {
    const cid = cuid()
    let object = {id: cid, organization_id: organizationId, user_id: 4}

    const action = sActions.createStart(object);

    api(getState()).organizations.addMember({organizationId, email}, (err, membership) => {
      if (err) {
        dispatch(sActions.createError(err, object))
      }
      else if (membership) {
        dispatch(userActions.fetchSuccess([membership._embedded.user]))
        dispatch(membershipActions.createSuccess([membership]))
      }
    })
  }
}
<|endoftext|>