<commit_before>var orm = require('../../../dbbs');
var Subscriber = orm.model("subscriber");
var request = require('request');
var assert = require('assert');
var fs = require('fs');

orm.setup()

var testEmail = 'ash.1382@gmail.com';
var token;

describe('Testing registration requests:',function() {
  it('User registered successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\", \"ip\": \"192.162.0.8\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert(JSON.parse(body)['value'],'Unable to register user');
      console.log('\tResponse received : ', body);
      fs.exists('/home/dev/main/flowsim/src/backend/temp', function (exists) {
        if(exists) {
          fs.readFile('/home/dev/main/flowsim/src/backend/temp','utf8',function (err,data) {
            if (err) console.log('Unable to read token in file for restTest');
            else{ token = data;}
          });
        }
      });
      done();
    });
  });
  it('Email Already in Use',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\", \"ip\": \"192.162.0.8\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'emailInUse');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"Something@gmailler.com\", \"password\": \"my\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"asdf.com\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing Email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });
});

describe('Testing verification requests:',function() {
  it('User verified successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('User already verified',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/Bad-Token',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
});

<commit_msg>updated resttest<commit_after>var orm = require('../../../dbbs');
var Subscriber = orm.model("subscriber");
var request = require('request');
var assert = require('assert');
var fs = require('fs');

orm.setup()

var testEmail = 'ash.1382@gmail.com';
var token;

describe('Testing registration requests:',function() {
  it('User registered successfully',function(done) {
    this.timeout(5000);
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert(JSON.parse(body)['value'],'Unable to register user');
      console.log('\tResponse received : ', body);
      fs.exists('/home/dev/main/flowsim/src/backend/temp', function (exists) {
        if(exists) {
          fs.readFile('/home/dev/main/flowsim/src/backend/temp','utf8',function (err,data) {
            if (err) console.log('Unable to read token in file for restTest');
            else{ token = data;}
          });
        }
      });
      done();
    });
  });
  it('Email Already in Use',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\", \"ip\": \"192.162.0.8\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'emailInUse');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"Something@gmailler.com\", \"password\": \"my\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"asdf.com\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing Email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });
});

describe('Testing verification requests:',function() {
  it('User verified successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('User already verified',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/Bad-Token',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
});

<|endoftext|>


<commit_before>var fs = require("fs");

function plugins() {

}

require.uncached = function(name) {
	delete require.cache[require.resolve(name)];
	return require(name);
};

plugins._load = function(p, enable) {
	if (p.depend) {
		for (var i = 0; i < p.depend.length; i++)
			this.load(p.depend[i]);
	}
	this[p.name] = p;
	var self = this;
	fs.readFile("./data/" + p.name + ".json", function(err, data) {
		(p.load || function(){})(data ? JSON.parse(data) : undefined);

		if (enable !== false) { // usual || doesn't work for true value
			bot.out.ok("plugins", "loaded " + p.name);
			self._enable(p);
		}
		else
			bot.out.warn("plugins", "loaded " + p.name);
	});
};

plugins.load = function(name) {
	var enable = true;
	if (name[0] === "*") {
		enable = false;
		name = name.substr(1);
	}

	if (!(name in this)) {
		try {
			var r = require.uncached("./plugins/" + name);
			var p = new r(bot);
			p.enabled = false;
			this._load(p, enable);
		}
		catch (e) {
			bot.out.error("plugins", "couldn't load " + name + ": " + e.stack);
		}
	}
};

plugins.loadEvents = function(events) {
	for (var cmd in events) {
		//console.log("Adding " + cmd);
		bot.addListener(cmd, events[cmd]);
	}
};

plugins._enable = function(p) {
	this.loadEvents(p.events);
	(p.enable || function(){})();
	p.enabled = true;
};

plugins.enable = function(name) {
	this._enable(this[name]);
};

plugins._save = function(p) {
	var data = (p.save || function(){})();
	if (data) {
		fs.writeFileSync("./data/" + p.name + ".json", JSON.stringify(data, null, 4));
	}
};

plugins.save = function(name) {
	this._save(this[name]);
};

plugins._unload = function(p) {
	this._disable(p);
	this._save(p);
	delete this[p.name];
	bot.out.ok("plugins", "unloaded " + p.name);
};

plugins.unload = function(name) {
	this._unload(this[name]);
};

plugins.unloadEvents = function(events) {
	for (var cmd in events) {
		//console.log("Removing " + cmd);
		bot.removeListener(cmd, events[cmd]);
	}
};

plugins._disable = function(p) {
	(p.disable || function(){})();
	this.unloadEvents(p.events);
	p.enabled = false;
};

plugins.disable = function(name) {
	this._disable(this[name]);
};

plugins.reload = function(name) {
	this.unload(name);
	this.load(name);
};

module.exports = plugins;
<commit_msg>plugins: allow reloading without enabling<commit_after>var fs = require("fs");

function plugins() {

}

require.uncached = function(name) {
	delete require.cache[require.resolve(name)];
	return require(name);
};

plugins._load = function(p, enable) {
	if (p.depend) {
		for (var i = 0; i < p.depend.length; i++)
			this.load(p.depend[i]);
	}
	this[p.name] = p;
	var self = this;
	fs.readFile("./data/" + p.name + ".json", function(err, data) {
		(p.load || function(){})(data ? JSON.parse(data) : undefined);

		if (enable !== false) { // usual || doesn't work for true value
			bot.out.ok("plugins", "loaded " + p.name);
			self._enable(p);
		}
		else
			bot.out.warn("plugins", "loaded " + p.name);
	});
};

plugins.load = function(name) {
	var enable = true;
	if (name[0] === "*") {
		enable = false;
		name = name.substr(1);
	}

	if (!(name in this)) {
		try {
			var r = require.uncached("./plugins/" + name);
			var p = new r(bot);
			p.enabled = false;
			this._load(p, enable);
		}
		catch (e) {
			bot.out.error("plugins", "couldn't load " + name + ": " + e.stack);
		}
	}
};

plugins.loadEvents = function(events) {
	for (var cmd in events) {
		//console.log("Adding " + cmd);
		bot.addListener(cmd, events[cmd]);
	}
};

plugins._enable = function(p) {
	this.loadEvents(p.events);
	(p.enable || function(){})();
	p.enabled = true;
};

plugins.enable = function(name) {
	this._enable(this[name]);
};

plugins._save = function(p) {
	var data = (p.save || function(){})();
	if (data) {
		fs.writeFileSync("./data/" + p.name + ".json", JSON.stringify(data, null, 4));
	}
};

plugins.save = function(name) {
	this._save(this[name]);
};

plugins._unload = function(p) {
	this._disable(p);
	this._save(p);
	delete this[p.name];
	bot.out.ok("plugins", "unloaded " + p.name);
};

plugins.unload = function(name) {
	this._unload(this[name]);
};

plugins.unloadEvents = function(events) {
	for (var cmd in events) {
		//console.log("Removing " + cmd);
		bot.removeListener(cmd, events[cmd]);
	}
};

plugins._disable = function(p) {
	(p.disable || function(){})();
	this.unloadEvents(p.events);
	p.enabled = false;
};

plugins.disable = function(name) {
	this._disable(this[name]);
};

plugins.reload = function(name) {
	this.unload(name.replace(/^\*/, ""));
	this.load(name);
};

module.exports = plugins;
<|endoftext|>


<commit_before>angular.module('swailMail').controller('LoginController', ['$scope', '$location', function ($scope, $location) {
    // This flag we use to show or hide the button in our HTML.
    $scope.signedIn = false;

    // Here we do the authentication processing and error handling.
    // Note that authResult is a JSON object.
    $scope.processAuth = function(authResult) {
        // Do a check if authentication has been successful.
        if(authResult['access_token']) {
            // Successful sign in.
            $scope.signedIn = true;
            console.log('Successfully signed in');

        } else if(authResult['error']) {
            // Error while signing in.
            $scope.signedIn = false;
            $location.path('login');
        }
    };

    // When callback is received, we need to process authentication.
    $scope.signInCallback = function (authResult) {
        setTimeout(function () {
            $scope.$apply(function () {
                $scope.processAuth(authResult);
            });
        }, 0);
    };

    // Render the sign in button.
    $scope.renderSignInButton = function() {
        gapi.signin.render('signInButton',
            {
                'callback': $scope.signInCallback, // Function handling the callback.
                'clientid': '793225560426-to0jcpob9p2jklejgmp2iv0maumqe5to.apps.googleusercontent.com', // CLIENT_ID from developer console which has been explained earlier.
                'scope': 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email',
                'cookiepolicy': 'single_host_origin'
            }
        );
    };

    // Start function in this example only renders the sign in button.
    $scope.start = function() {
        $scope.renderSignInButton();
    };

    // Call start function on load.
    $scope.start();

    $scope.logout = function () {
        gapi.auth.signOut();
    }
}]);<commit_msg>Checking SignOut Functionality<commit_after>angular.module('swailMail').controller('LoginController', ['$scope', '$location', function ($scope, $location) {
    // This flag we use to show or hide the button in our HTML.
    $scope.signedIn = false;

    // Here we do the authentication processing and error handling.
    // Note that authResult is a JSON object.
    $scope.processAuth = function(authResult) {
        // Do a check if authentication has been successful.
        if(authResult['access_token']) {
            // Successful sign in.
            $scope.signedIn = true;
            console.log('Successfully signed in');

        } else if(authResult['error']) {
            // Error while signing in.
            $scope.signedIn = false;
            $location.path('login');
        }
    };

    // When callback is received, we need to process authentication.
    $scope.signInCallback = function (authResult) {
        setTimeout(function () {
            $scope.$apply(function () {
                $scope.processAuth(authResult);
            });
        }, 0);
    };

    // Render the sign in button.
    $scope.renderSignInButton = function() {
        gapi.signin.render('signInButton',
            {
                'callback': $scope.signInCallback, // Function handling the callback.
                'clientid': '793225560426-to0jcpob9p2jklejgmp2iv0maumqe5to.apps.googleusercontent.com', // CLIENT_ID from developer console which has been explained earlier.
                'scope': 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email',
                'cookiepolicy': 'single_host_origin'
            }
        );
    };

    // Start function in this example only renders the sign in button.
    $scope.start = function() {
        $scope.renderSignInButton();
    };

    // Call start function on load.
    $scope.start();

    $scope.logout = function () {
        gapi.auth.signOut();
        console.log('Logged Out');
    }
}]);<|endoftext|>


<commit_before>const BinaryPjax         = require('../../base/binary_pjax');
const Client             = require('../../base/client');
const BinarySocket       = require('../../base/socket');
const Currency           = require('../../common/currency');
const FormManager        = require('../../common/form_manager');
const elementTextContent = require('../../../_common/common_functions').elementTextContent;
const getElementById     = require('../../../_common/common_functions').getElementById;
const localize           = require('../../../_common/localize').localize;
const State              = require('../../../_common/storage').State;
const createElement      = require('../../../_common/utility').createElement;
const getPropertyValue   = require('../../../_common/utility').getPropertyValue;

const AccountTransfer = (() => {
    const form_id       = 'frm_account_transfer';
    const form_id_hash  = `#${form_id}`;

    const messages = {
        parent : 'client_message',
        error  : 'no_account',
        balance: 'not_enough_balance',
        deposit: 'no_balance',
        limit  : 'limit_reached',
    };

    let el_transfer_from,
        el_transfer_to,
        el_reset_transfer,
        el_transfer_fee,
        el_transfer_info,
        el_success_form,
        client_balance,
        client_currency,
        client_loginid,
        withdrawal_limit;

    const populateAccounts = (accounts) => {
        client_loginid   = Client.get('loginid');
        el_transfer_from = getElementById('lbl_transfer_from');
        el_transfer_to   = getElementById('transfer_to');

        elementTextContent(el_transfer_from, `${client_loginid} ${client_currency ? `(${client_currency})` : ''}`);

        const fragment_transfer_to = document.createElement('div');

        accounts.forEach((account) => {
            if (Client.canTransferFunds(account)) {
                const option = document.createElement('option');
                option.appendChild(document.createTextNode(`${account.loginid}${account.currency ? ` (${account.currency})` : ''}`));
                fragment_transfer_to.appendChild(option);
            }
        });

        if (!fragment_transfer_to.childElementCount) {
            showError();
            return;
        }
        if (fragment_transfer_to.childElementCount > 1) {
            el_transfer_to.innerHTML = fragment_transfer_to.innerHTML;
        } else {
            const label = createElement('label', { 'data-value': fragment_transfer_to.innerText });
            label.appendChild(document.createTextNode(fragment_transfer_to.innerText));
            label.id = 'transfer_to';

            el_transfer_to.parentNode.replaceChild(label, el_transfer_to);
            el_transfer_to = getElementById('transfer_to');
        }

        showForm();

        if (Client.hasCurrencyType('crypto') && Client.hasCurrencyType('fiat')) {
            el_transfer_fee.setVisibility(1);
        } else {
            el_transfer_info.setVisibility(1);
        }
    };

    const hasError = (response) => {
        const error = response.error;
        if (error) {
            const el_error = getElementById('error_message').getElementsByTagName('p')[0];
            elementTextContent(el_error, error.message);
            if (el_error.parentNode) {
                el_error.parentNode.setVisibility(1);
            }
            return true;
        }
        return false;
    };

    const showError = () => {
        getElementById(messages.parent).setVisibility(1);
        getElementById(messages.error).setVisibility(1);
    };

    const showForm = () => {
        elementTextContent(document.querySelector(`${form_id_hash} #currency`), client_currency);

        getElementById(form_id).setVisibility(1);

        FormManager.init(form_id_hash, [
            { selector: '#amount', validations: [['req', { hide_asterisk: true }], ['number', { type: 'float', decimals: Currency.getDecimalPlaces(client_currency), min: Currency.getMinTransfer(client_currency), max: Math.min(+withdrawal_limit, +client_balance), format_money: true }]] },

            { request_field: 'transfer_between_accounts', value: 1 },
            { request_field: 'account_from',              value: client_loginid },
            { request_field: 'account_to',                value: () => (el_transfer_to.value || el_transfer_to.getAttribute('data-value') || '').split(' (')[0] },
            { request_field: 'currency',                  value: client_currency },
        ]);

        FormManager.handleSubmit({
            form_selector       : form_id_hash,
            fnc_response_handler: responseHandler,
            enable_button       : true,
        });
    };

    const responseHandler = (response) => {
        if (response.error) {
            const el_error = getElementById('form_error');
            elementTextContent(el_error, response.error.message);
            el_error.setVisibility(1);
            // Auto hide error after 5 seconds.
            setTimeout(() => el_error.setVisibility(0), 5000);
        } else {
            BinarySocket.send({ transfer_between_accounts: 1 }).then(data => populateReceipt(response, data));
        }
    };

    const populateReceipt = (response_submit_success, response) => {
        getElementById(form_id).setVisibility(0);

        elementTextContent(getElementById('from_loginid'), client_loginid);
        elementTextContent(getElementById('to_loginid'), response_submit_success.client_to_loginid);

        response.accounts.forEach((account) => {
            if (account.loginid === client_loginid) {
                elementTextContent(getElementById('from_currency'), account.currency);
                elementTextContent(getElementById('from_balance'), account.balance);
            } else if (account.loginid === response_submit_success.client_to_loginid) {
                elementTextContent(getElementById('to_currency'), account.currency);
                elementTextContent(getElementById('to_balance'), account.balance);
            }
        });

        el_transfer_fee.setVisibility(0);
        el_transfer_info.setVisibility(0);
        el_success_form.setVisibility(1);
    };

    const onClickReset = () => {
        el_success_form.setVisibility(0);
        getElementById('amount').value = '';
        onLoad();
    };

    const onLoad = () => {
        if (!Client.canTransferFunds()) {
            BinaryPjax.loadPreviousUrl();
            return;
        }

        el_transfer_fee   = getElementById('transfer_fee');
        el_transfer_info  = getElementById('transfer_info');
        el_success_form   = getElementById('success_form');
        el_reset_transfer = getElementById('reset_transfer');
        el_reset_transfer.addEventListener('click', onClickReset);

        BinarySocket.wait('balance').then((response) => {
            client_balance   = +getPropertyValue(response, ['balance', 'balance']);
            client_currency  = Client.get('currency');
            const min_amount = Currency.getMinTransfer(client_currency);
            if (!client_balance || client_balance < min_amount) {
                getElementById(messages.parent).setVisibility(1);
                if (client_currency) {
                    elementTextContent(getElementById('min_required_amount'), `${client_currency} ${min_amount}`);
                    getElementById(messages.balance).setVisibility(1);
                }
                getElementById(messages.deposit).setVisibility(1);
            } else {
                const req_transfer_between_accounts = BinarySocket.send({ transfer_between_accounts: 1 });
                const req_get_limits                = BinarySocket.send({ get_limits: 1 });

                Promise.all([req_transfer_between_accounts, req_get_limits]).then(() => {
                    const response_transfer = State.get(['response', 'transfer_between_accounts']);
                    const response_limits   = State.get(['response', 'get_limits']);

                    if (hasError(response_transfer)) {
                        return;
                    }
                    const accounts = response_transfer.accounts;
                    if (!accounts || !accounts.length) {
                        showError();
                        return;
                    }
                    if (hasError(response_limits)) {
                        return;
                    }
                    withdrawal_limit = +response_limits.get_limits.remainder;
                    if (withdrawal_limit < min_amount) {
                        getElementById(messages.limit).setVisibility(1);
                        getElementById(messages.parent).setVisibility(1);
                        return;
                    }
                    getElementById('range_hint').textContent = `${localize('Min')}: ${min_amount} ${localize('Max')}: ${client_balance <= withdrawal_limit ? localize('Current balance') : localize('Withdrawal limit')}`;
                    populateAccounts(accounts);
                });
            }
        });
    };

    const onUnload = () => {
        if (el_reset_transfer) el_reset_transfer.removeEventListener('click', onClickReset);
    };

    return {
        onLoad,
        onUnload,
    };
})();

module.exports = AccountTransfer;
<commit_msg>Hide note for same-currency transfers<commit_after>const BinaryPjax         = require('../../base/binary_pjax');
const Client             = require('../../base/client');
const BinarySocket       = require('../../base/socket');
const Currency           = require('../../common/currency');
const FormManager        = require('../../common/form_manager');
const elementTextContent = require('../../../_common/common_functions').elementTextContent;
const getElementById     = require('../../../_common/common_functions').getElementById;
const localize           = require('../../../_common/localize').localize;
const State              = require('../../../_common/storage').State;
const createElement      = require('../../../_common/utility').createElement;
const getPropertyValue   = require('../../../_common/utility').getPropertyValue;

const AccountTransfer = (() => {
    const form_id       = 'frm_account_transfer';
    const form_id_hash  = `#${form_id}`;

    const messages = {
        parent : 'client_message',
        error  : 'no_account',
        balance: 'not_enough_balance',
        deposit: 'no_balance',
        limit  : 'limit_reached',
    };

    let el_transfer_from,
        el_transfer_to,
        el_reset_transfer,
        el_transfer_fee,
        el_transfer_info,
        el_success_form,
        client_balance,
        client_currency,
        client_loginid,
        withdrawal_limit;

    const populateAccounts = (accounts) => {
        client_loginid   = Client.get('loginid');
        el_transfer_from = getElementById('lbl_transfer_from');
        el_transfer_to   = getElementById('transfer_to');

        elementTextContent(el_transfer_from, `${client_loginid} ${client_currency ? `(${client_currency})` : ''}`);
        el_transfer_from.setAttribute('data-currency', client_currency);

        const fragment_transfer_to = document.createElement('div');

        accounts.forEach((account) => {
            if (Client.canTransferFunds(account)) {
                const option = document.createElement('option');
                option.setAttribute('data-currency', account.currency);
                option.appendChild(document.createTextNode(`${account.loginid}${account.currency ? ` (${account.currency})` : ''}`));
                fragment_transfer_to.appendChild(option);
            }
        });

        if (!fragment_transfer_to.childElementCount) {
            showError();
            return;
        }
        if (fragment_transfer_to.childElementCount > 1) {
            el_transfer_to.innerHTML = fragment_transfer_to.innerHTML;
            el_transfer_to.onchange = () => {
                const from_currency = el_transfer_from.getAttribute('data-currency');
                const to_currency = el_transfer_to.options[el_transfer_to.selectedIndex].getAttribute('data-currency');
                el_transfer_info.setVisibility(from_currency === to_currency ? 0 : 1);
            };
        } else {
            const label = createElement('label', {
                'data-value'   : fragment_transfer_to.innerText,
                'data-currency': fragment_transfer_to.firstChild.getAttribute('data-currency'),
            });
            label.appendChild(document.createTextNode(fragment_transfer_to.innerText));
            label.id = 'transfer_to';

            el_transfer_to.parentNode.replaceChild(label, el_transfer_to);
            el_transfer_to = getElementById('transfer_to');
        }

        showForm();

        if (Client.hasCurrencyType('crypto') && Client.hasCurrencyType('fiat')) {
            el_transfer_fee.setVisibility(1);
        } else {
            const from_currency = el_transfer_from.getAttribute('data-currency');
            const to_currency = el_transfer_to.getAttribute('data-currency');
            el_transfer_info.setVisibility(from_currency === to_currency ? 0 : 1);
        }
    };

    const hasError = (response) => {
        const error = response.error;
        if (error) {
            const el_error = getElementById('error_message').getElementsByTagName('p')[0];
            elementTextContent(el_error, error.message);
            if (el_error.parentNode) {
                el_error.parentNode.setVisibility(1);
            }
            return true;
        }
        return false;
    };

    const showError = () => {
        getElementById(messages.parent).setVisibility(1);
        getElementById(messages.error).setVisibility(1);
    };

    const showForm = () => {
        elementTextContent(document.querySelector(`${form_id_hash} #currency`), client_currency);

        getElementById(form_id).setVisibility(1);

        FormManager.init(form_id_hash, [
            { selector: '#amount', validations: [['req', { hide_asterisk: true }], ['number', { type: 'float', decimals: Currency.getDecimalPlaces(client_currency), min: Currency.getMinTransfer(client_currency), max: Math.min(+withdrawal_limit, +client_balance), format_money: true }]] },

            { request_field: 'transfer_between_accounts', value: 1 },
            { request_field: 'account_from',              value: client_loginid },
            { request_field: 'account_to',                value: () => (el_transfer_to.value || el_transfer_to.getAttribute('data-value') || '').split(' (')[0] },
            { request_field: 'currency',                  value: client_currency },
        ]);

        FormManager.handleSubmit({
            form_selector       : form_id_hash,
            fnc_response_handler: responseHandler,
            enable_button       : true,
        });
    };

    const responseHandler = (response) => {
        if (response.error) {
            const el_error = getElementById('form_error');
            elementTextContent(el_error, response.error.message);
            el_error.setVisibility(1);
            // Auto hide error after 5 seconds.
            setTimeout(() => el_error.setVisibility(0), 5000);
        } else {
            BinarySocket.send({ transfer_between_accounts: 1 }).then(data => populateReceipt(response, data));
        }
    };

    const populateReceipt = (response_submit_success, response) => {
        getElementById(form_id).setVisibility(0);

        elementTextContent(getElementById('from_loginid'), client_loginid);
        elementTextContent(getElementById('to_loginid'), response_submit_success.client_to_loginid);

        response.accounts.forEach((account) => {
            if (account.loginid === client_loginid) {
                elementTextContent(getElementById('from_currency'), account.currency);
                elementTextContent(getElementById('from_balance'), account.balance);
            } else if (account.loginid === response_submit_success.client_to_loginid) {
                elementTextContent(getElementById('to_currency'), account.currency);
                elementTextContent(getElementById('to_balance'), account.balance);
            }
        });

        el_transfer_fee.setVisibility(0);
        el_transfer_info.setVisibility(0);
        el_success_form.setVisibility(1);
    };

    const onClickReset = () => {
        el_success_form.setVisibility(0);
        getElementById('amount').value = '';
        onLoad();
    };

    const onLoad = () => {
        if (!Client.canTransferFunds()) {
            BinaryPjax.loadPreviousUrl();
            return;
        }

        el_transfer_fee   = getElementById('transfer_fee');
        el_transfer_info  = getElementById('transfer_info');
        el_success_form   = getElementById('success_form');
        el_reset_transfer = getElementById('reset_transfer');
        el_reset_transfer.addEventListener('click', onClickReset);

        BinarySocket.wait('balance').then((response) => {
            client_balance   = +getPropertyValue(response, ['balance', 'balance']);
            client_currency  = Client.get('currency');
            const min_amount = Currency.getMinTransfer(client_currency);
            if (!client_balance || client_balance < min_amount) {
                getElementById(messages.parent).setVisibility(1);
                if (client_currency) {
                    elementTextContent(getElementById('min_required_amount'), `${client_currency} ${min_amount}`);
                    getElementById(messages.balance).setVisibility(1);
                }
                getElementById(messages.deposit).setVisibility(1);
            } else {
                const req_transfer_between_accounts = BinarySocket.send({ transfer_between_accounts: 1 });
                const req_get_limits                = BinarySocket.send({ get_limits: 1 });

                Promise.all([req_transfer_between_accounts, req_get_limits]).then(() => {
                    const response_transfer = State.get(['response', 'transfer_between_accounts']);
                    const response_limits   = State.get(['response', 'get_limits']);

                    if (hasError(response_transfer)) {
                        return;
                    }
                    const accounts = response_transfer.accounts;
                    if (!accounts || !accounts.length) {
                        showError();
                        return;
                    }
                    if (hasError(response_limits)) {
                        return;
                    }
                    withdrawal_limit = +response_limits.get_limits.remainder;
                    if (withdrawal_limit < min_amount) {
                        getElementById(messages.limit).setVisibility(1);
                        getElementById(messages.parent).setVisibility(1);
                        return;
                    }
                    getElementById('range_hint').textContent = `${localize('Min')}: ${min_amount} ${localize('Max')}: ${client_balance <= withdrawal_limit ? localize('Current balance') : localize('Withdrawal limit')}`;
                    populateAccounts(accounts);
                });
            }
        });
    };

    const onUnload = () => {
        if (el_reset_transfer) el_reset_transfer.removeEventListener('click', onClickReset);
    };

    return {
        onLoad,
        onUnload,
    };
})();

module.exports = AccountTransfer;
<|endoftext|>


<commit_before>'use strict'

const co = require('co')
const cli = require('heroku-cli-util')

let empty = (o) => Object.keys(o).length === 0

function displayFormation (formation) {
  const groupBy = require('lodash.groupby')
  const map = require('lodash.map')
  const sumBy = require('lodash.sumby')

  formation = groupBy(formation, 'size')
  formation = map(formation, (p, size) => `${bold(sumBy(p, 'quantity'))} | ${size}`)
  cli.log(`  ${label('Dynos:')} ${formation.join(', ')}`)
}

function displayErrors (metrics) {
  const toPairs = require('lodash.topairs')
  const sum = require('lodash.sum')

  let errors = []
  if (metrics.routerErrors) {
    errors = errors.concat(toPairs(metrics.routerErrors.data).map((e) => cli.color.red(`${sum(e[1])} ${e[0]}`)))
  }
  if (metrics.dynoErrors) {
    metrics.dynoErrors.filter((d) => d).forEach((dynoErrors) => {
      errors = errors.concat(toPairs(dynoErrors.data).map((e) => cli.color.red(`${sum(e[1])} ${e[0]}`)))
    })
  }
  if (errors.length > 0) cli.log(`  ${label('Errors:')} ${errors.join(dim(', '))} (see details with ${cli.color.cmd('heroku apps:errors')})`)
}

function displayMetrics (metrics) {
  const flatten = require('lodash.flatten')
  const mean = require('lodash.mean')
  const round = require('lodash.round')
  const sum = require('lodash.sum')
  const values = require('lodash.values')

  function rpmSparkline () {
    if (['win32', 'windows'].includes(process.platform)) return ''
    let sparkline = require('sparkline')
    let points = []
    values(metrics.routerStatus.data).forEach((cur) => {
      for (let i = 0; i < cur.length; i++) {
        let j = Math.floor(i / 3)
        points[j] = (points[j] || 0) + cur[i]
      }
    })
    points.pop()
    return dim(sparkline(points)) + ' last 24 hours rpm'
  }
  let ms = ''
  let rpm = ''
  if (metrics.routerLatency && !empty(metrics.routerLatency.data)) {
    let latency = metrics.routerLatency.data['latency.ms.p50']
    if (latency) ms = `${round(mean(latency))} ms `
  }
  if (metrics.routerStatus && !empty(metrics.routerStatus.data)) {
    rpm = `${round(sum(flatten(values(metrics.routerStatus.data))) / 24 / 60)} rpm ${rpmSparkline()}`
  }
  if (rpm || ms) cli.log(`  ${label('Metrics:')} ${ms}${rpm}`)
}

function displayNotifications (notifications) {
  if (!notifications) return

  notifications = notifications.filter((n) => !n.read)
  if (notifications.length > 0) {
    cli.log(`
You have ${cli.color.yellow(notifications.length)} unread notifications. Read them with ${cli.color.cmd('heroku notifications')}`)
  }
}

let dim = (s) => cli.color.dim(s)
let bold = (s) => cli.color.bold(s)
let label = (s) => cli.color.blue(s)

function displayApps (apps, appsMetrics) {
  const zip = require('lodash.zip')
  const time = require('../lib/time')

  let owner = (owner) => owner.email.endsWith('@herokumanager.com') ? owner.email.split('@')[0] : owner.email

  for (let a of zip(apps, appsMetrics)) {
    let app = a[0]
    let metrics = a[1]
    cli.log(cli.color.app(app.app.name))
    cli.log(`  ${label('Owner:')} ${owner(app.app.owner)}`)
    if (app.pipeline) {
      cli.log(`  ${label('Pipeline:')} ${app.pipeline.pipeline.name}`)
    }
    displayFormation(app.formation)
    cli.log(`  ${label('Last release:')} ${time.ago(new Date(app.app.released_at))}`)
    displayMetrics(metrics)
    displayErrors(metrics)
    cli.log()
  }
}

function * run (context, heroku) {
  const img = require('term-img')
  const sortBy = require('lodash.sortby')
  const path = require('path')

  function favoriteApps () {
    return heroku.request({
      host: 'longboard.heroku.com',
      path: '/favorites',
      headers: {Range: ''}
    }).then((apps) => apps.map((app) => app.app_name))
  }

  function fetchMetrics (apps) {
    const NOW = new Date().toISOString()
    const YESTERDAY = new Date(new Date().getTime() - (24 * 60 * 60 * 1000)).toISOString()
    let date = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`
    return apps.map((app) => {
      let types = app.formation.map((p) => p.type)
      return {
        dynoErrors: types.map((type) => heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/formation/${type}/metrics/errors?${date}`, headers: {Range: ''}}).catch(() => null)),
        routerLatency: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/latency?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerErrors: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/errors?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerStatus: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/status?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null)
      }
    })
  }

  let apps, data, metrics

  img(path.join(__dirname, '..', 'assets', 'heroku.png'), {fallback: () => {}})

  yield cli.action('Loading', {clear: true}, co(function * () {
    apps = yield favoriteApps()

    data = yield {
      orgs: heroku.request({path: '/organizations'}),
      notifications: heroku.request({host: 'telex.heroku.com', path: '/user/notifications'}).catch(() => null),
      apps: apps.map((app) => ({
        app: heroku.get(`/apps/${app}`),
        formation: heroku.get(`/apps/${app}/formation`),
        pipeline: heroku.get(`/apps/${app}/pipeline-couplings`).catch(() => null)
      }))
    }
    metrics = yield fetchMetrics(data.apps)
  }))

  if (apps.length > 0) displayApps(data.apps, metrics)
  else cli.warn(`Add apps to this dashboard by favoriting them with ${cli.color.cmd('heroku apps:favorites:add')}`)

  cli.log(`See all add-ons with ${cli.color.cmd('heroku addons')}`)
  let sampleOrg = sortBy(data.orgs.filter((o) => o.role !== 'collaborator'), (o) => new Date(o.created_at))[0]
  if (sampleOrg) cli.log(`See all apps in ${cli.color.yellow.dim(sampleOrg.name)} with ${cli.color.cmd('heroku apps --team ' + sampleOrg.name)}`)
  cli.log(`See all apps with ${cli.color.cmd('heroku apps --all')}`)
  displayNotifications(data.notifications)
  cli.log(`
See other CLI commands with ${cli.color.cmd('heroku help')}
`)
}

module.exports = {
  topic: 'dashboard',
  description: 'display information about favorite apps',
  hidden: true,
  needsAuth: true,
  run: cli.command(co.wrap(run))
}
<commit_msg>fix latency when empty array<commit_after>'use strict'

const co = require('co')
const cli = require('heroku-cli-util')

let empty = (o) => Object.keys(o).length === 0

function displayFormation (formation) {
  const groupBy = require('lodash.groupby')
  const map = require('lodash.map')
  const sumBy = require('lodash.sumby')

  formation = groupBy(formation, 'size')
  formation = map(formation, (p, size) => `${bold(sumBy(p, 'quantity'))} | ${size}`)
  cli.log(`  ${label('Dynos:')} ${formation.join(', ')}`)
}

function displayErrors (metrics) {
  const toPairs = require('lodash.topairs')
  const sum = require('lodash.sum')

  let errors = []
  if (metrics.routerErrors) {
    errors = errors.concat(toPairs(metrics.routerErrors.data).map((e) => cli.color.red(`${sum(e[1])} ${e[0]}`)))
  }
  if (metrics.dynoErrors) {
    metrics.dynoErrors.filter((d) => d).forEach((dynoErrors) => {
      errors = errors.concat(toPairs(dynoErrors.data).map((e) => cli.color.red(`${sum(e[1])} ${e[0]}`)))
    })
  }
  if (errors.length > 0) cli.log(`  ${label('Errors:')} ${errors.join(dim(', '))} (see details with ${cli.color.cmd('heroku apps:errors')})`)
}

function displayMetrics (metrics) {
  const flatten = require('lodash.flatten')
  const mean = require('lodash.mean')
  const round = require('lodash.round')
  const sum = require('lodash.sum')
  const values = require('lodash.values')

  function rpmSparkline () {
    if (['win32', 'windows'].includes(process.platform)) return ''
    let sparkline = require('sparkline')
    let points = []
    values(metrics.routerStatus.data).forEach((cur) => {
      for (let i = 0; i < cur.length; i++) {
        let j = Math.floor(i / 3)
        points[j] = (points[j] || 0) + cur[i]
      }
    })
    points.pop()
    return dim(sparkline(points)) + ' last 24 hours rpm'
  }
  let ms = ''
  let rpm = ''
  if (metrics.routerLatency && !empty(metrics.routerLatency.data)) {
    let latency = metrics.routerLatency.data['latency.ms.p50']
    if (!empty(latency)) ms = `${round(mean(latency))} ms `
  }
  if (metrics.routerStatus && !empty(metrics.routerStatus.data)) {
    rpm = `${round(sum(flatten(values(metrics.routerStatus.data))) / 24 / 60)} rpm ${rpmSparkline()}`
  }
  if (rpm || ms) cli.log(`  ${label('Metrics:')} ${ms}${rpm}`)
}

function displayNotifications (notifications) {
  if (!notifications) return

  notifications = notifications.filter((n) => !n.read)
  if (notifications.length > 0) {
    cli.log(`
You have ${cli.color.yellow(notifications.length)} unread notifications. Read them with ${cli.color.cmd('heroku notifications')}`)
  }
}

let dim = (s) => cli.color.dim(s)
let bold = (s) => cli.color.bold(s)
let label = (s) => cli.color.blue(s)

function displayApps (apps, appsMetrics) {
  const zip = require('lodash.zip')
  const time = require('../lib/time')

  let owner = (owner) => owner.email.endsWith('@herokumanager.com') ? owner.email.split('@')[0] : owner.email

  for (let a of zip(apps, appsMetrics)) {
    let app = a[0]
    let metrics = a[1]
    cli.log(cli.color.app(app.app.name))
    cli.log(`  ${label('Owner:')} ${owner(app.app.owner)}`)
    if (app.pipeline) {
      cli.log(`  ${label('Pipeline:')} ${app.pipeline.pipeline.name}`)
    }
    displayFormation(app.formation)
    cli.log(`  ${label('Last release:')} ${time.ago(new Date(app.app.released_at))}`)
    displayMetrics(metrics)
    displayErrors(metrics)
    cli.log()
  }
}

function * run (context, heroku) {
  const img = require('term-img')
  const sortBy = require('lodash.sortby')
  const path = require('path')

  function favoriteApps () {
    return heroku.request({
      host: 'longboard.heroku.com',
      path: '/favorites',
      headers: {Range: ''}
    }).then((apps) => apps.map((app) => app.app_name))
  }

  function fetchMetrics (apps) {
    const NOW = new Date().toISOString()
    const YESTERDAY = new Date(new Date().getTime() - (24 * 60 * 60 * 1000)).toISOString()
    let date = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`
    return apps.map((app) => {
      let types = app.formation.map((p) => p.type)
      return {
        dynoErrors: types.map((type) => heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/formation/${type}/metrics/errors?${date}`, headers: {Range: ''}}).catch(() => null)),
        routerLatency: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/latency?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerErrors: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/errors?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerStatus: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/status?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null)
      }
    })
  }

  let apps, data, metrics

  img(path.join(__dirname, '..', 'assets', 'heroku.png'), {fallback: () => {}})

  yield cli.action('Loading', {clear: true}, co(function * () {
    apps = yield favoriteApps()

    data = yield {
      orgs: heroku.request({path: '/organizations'}),
      notifications: heroku.request({host: 'telex.heroku.com', path: '/user/notifications'}).catch(() => null),
      apps: apps.map((app) => ({
        app: heroku.get(`/apps/${app}`),
        formation: heroku.get(`/apps/${app}/formation`),
        pipeline: heroku.get(`/apps/${app}/pipeline-couplings`).catch(() => null)
      }))
    }
    metrics = yield fetchMetrics(data.apps)
  }))

  if (apps.length > 0) displayApps(data.apps, metrics)
  else cli.warn(`Add apps to this dashboard by favoriting them with ${cli.color.cmd('heroku apps:favorites:add')}`)

  cli.log(`See all add-ons with ${cli.color.cmd('heroku addons')}`)
  let sampleOrg = sortBy(data.orgs.filter((o) => o.role !== 'collaborator'), (o) => new Date(o.created_at))[0]
  if (sampleOrg) cli.log(`See all apps in ${cli.color.yellow.dim(sampleOrg.name)} with ${cli.color.cmd('heroku apps --team ' + sampleOrg.name)}`)
  cli.log(`See all apps with ${cli.color.cmd('heroku apps --all')}`)
  displayNotifications(data.notifications)
  cli.log(`
See other CLI commands with ${cli.color.cmd('heroku help')}
`)
}

module.exports = {
  topic: 'dashboard',
  description: 'display information about favorite apps',
  hidden: true,
  needsAuth: true,
  run: cli.command(co.wrap(run))
}
<|endoftext|>


<commit_before>var _ = require('@sailshq/lodash');
var async = require('async');

module.exports = {

  improve: 'apostrophe-pages',

  construct: function(self, options) {

    var superGetPathIndexParams = self.getPathIndexParams;
    self.getPathIndexParams = function() {
      var params = superGetPathIndexParams();
      params.workflowLocaleForPathIndex = 1;
      return params;
    };

    var superRemoveTrailingSlugSlashes = self.removeTrailingSlugSlashes;
    self.removeTrailingSlugSlashes = function(req, slug) {
      if (arguments.length === 1) {
        // bc workaround
        slug = req;
        req = self.apos.tasks.getAnonReq();
      }
      var workflow = self.apos.modules['apostrophe-workflow'];
      if (!workflow.prefixes) {
        return superRemoveTrailingSlugSlashes(slug);
      }
      var locale = workflow.liveify(req.locale);
      if (_.has(workflow.prefixes, locale)) {
        var matches = slug.match(/^(\/[^/]+)(\/?)$/);
        if (matches && (workflow.prefixes[locale] === matches[1])) {
          // Something like /en/, leave it alone,
          // it's a localized homepage. However if the
          // trailing slash *after* the locale is missing,
          // add it and redirect
          if (matches[2] === '') {
            return slug + '/';
          } else {
            return slug;
          }
        }
      }
      return superRemoveTrailingSlugSlashes(slug);
    };

    var superPruneCurrentPageForBrowser = self.pruneCurrentPageForBrowser;
    self.pruneCurrentPageForBrowser = function(page) {
      var pruned = superPruneCurrentPageForBrowser(page);
      pruned.workflowLocale = page.workflowLocale;
      pruned.workflowGuid = page.workflowGuid;
      return pruned;
    };

    var superGetEditControls = self.getEditControls;
    self.getEditControls = function(req) {
      return upgradeControls(req, superGetEditControls(req), 'edit');
    };

    var superGetCreateControls = self.getCreateControls;
    self.getCreateControls = function(req) {
      return upgradeControls(req, superGetCreateControls(req), 'create');
    };

    function upgradeControls(req, controls, verb) {
      var workflow = self.apos.modules['apostrophe-workflow'];
      if (!workflow.includeType(self.name)) {
        // Not subject to workflow
        return controls;
      }
      var save = _.find(controls, { action: 'save' });
      if (save) {
        save.label = 'Save Draft';
      }
      controls.push({
        type: 'dropdown',
        label: 'Workflow',
        name: 'workflow',
        dropdownOptions: {
          direction: 'down'
        },
        items: [
          {
            label: 'Submit',
            action: 'workflow-submit'
          },
          {
            label: 'Commit',
            action: 'workflow-commit'
          }
        ].concat(
          (workflow.localized && (verb === 'edit'))
            ? [
              {
                label: 'History',
                action: 'workflow-history'
              }
            ] : []
        ).concat(workflow.localized
          ? [
            {
              label: 'Force Export',
              action: 'workflow-force-export'
            }
          ] : []
        )
      });
      return controls;
    }

    var superMovePermissions = self.movePermissions;

    // "Based on `req`, `moved`, `data.oldParent` and `data.parent`, decide whether
    // this move should be permitted. If it should not be, report an error." The `apostrophe-pages`
    // module did this already, but we must consider the impact on all locales.

    self.movePermissions = function(req, moved, data, options, callback) {
      return superMovePermissions(req, moved, data, options, function(err) {
        if (err) {
          return callback(err);
        }
        if (!moved.workflowGuid) {
          // No localization for pages. That's unusual but allowed
          return callback(null);
        }
        // Grab the pages of interest across all locales other than the original (already checked).
        return self.apos.docs.find(req, {
          workflowGuid: { $in: [ moved.workflowGuid, data.oldParent.workflowGuid, data.parent.workflowGuid ] },
          workflowLocale: { $ne: moved.workflowLocale }
        }).joins(false).areas(false).workflowLocale(null).permission(false).published(null).trash(null).toArray(function(err, pages) {
          if (err) {
            return callback(err);
          }
          var error = null;
          var locales = {};
          _.each(pages, function(page) {
            if (!locales[page.workflowLocale]) {
              locales[page.workflowLocale] = {};
            }
            if (page.workflowGuid === moved.workflowGuid) {
              locales[page.workflowLocale].moved = page;
            }
            // Parent does not always change, else statement is not appropriate here
            if (page.workflowGuid === data.parent.workflowGuid) {
              locales[page.workflowLocale].parent = page;
            }
            if (page.workflowGuid === data.oldParent.workflowGuid) {
              locales[page.workflowLocale].oldParent = page;
            }
          });
          _.each(locales, function(locale, name) {
          // Repeat the permissions check for every locale
            if (!locale.moved._publish) {
              error = new Error('forbidden');
              return false;
            }
            if ((locale.oldParent._id !== locale.parent._id) && (!locale.parent._edit)) {
              error = new Error('forbidden');
              return false;
            }
          });
          return callback(error);
        });
      });
    };

    // On the initial invocation of `apos.pages.move`, modify the criteria and filters
    // to ensure only the relevant locale is in play

    var superBeforeMove = self.beforeMove;
    self.beforeMove = function(req, moved, target, position, options, callback) {
      return superBeforeMove(req, moved, target, position, options, function(err) {
        if (err) {
          return callback(err);
        }
        if (options.workflowRecursing) {
          return callback(null);
        }
        if (moved.workflowLocale) {
          options.criteria = _.assign({}, options.criteria || {}, { workflowLocale: moved.workflowLocale });
          options.filters = _.assign({}, options.filters || {}, { workflowLocale: moved.workflowLocale });
        }
        return callback(null);
      });
    };

    // After a page is moved in one locale, with all of the ripple effects that go with it,
    // make the same move in all other locales. Note that we already verified we have permissions
    // across all locales.

    var superAfterMove = self.afterMove;
    self.afterMove = function(req, moved, info, callback) {
      return superAfterMove(req, moved, info, function(err) {
        if (err) {
          return callback(err);
        }
        if (info.options.workflowRecursing) {
          return callback(null);
        }
        if (!moved.workflowGuid) {
          return callback(null);
        }
        var locales = {};
        return async.series([
          get,
          invoke
        ], function(err) {
          return callback(err);
        });
        function get(callback) {
          // Locate the doc moved and the doc it is moved relative to (target) in all locales other than
          // the original one
          return self.apos.docs.db.find({ workflowGuid: { $in: [ moved.workflowGuid, info.target.workflowGuid ] }, workflowLocale: { $ne: moved.workflowLocale } }, { workflowGuid: 1, _id: 1, workflowLocale: 1 }).toArray(function(err, docs) {
            if (err) {
              return callback(err);
            }
            _.each(docs, function(doc) {
              locales[doc.workflowLocale] = locales[doc.workflowLocale] || {};
              if (doc.workflowGuid === moved.workflowGuid) {
                locales[doc.workflowLocale].movedId = doc._id;
              }
              if (doc.workflowGuid === info.target.workflowGuid) {
                locales[doc.workflowLocale].targetId = doc._id;
              }
            });
            return callback(null);
          });
        }
        function invoke(callback) {
          // Reinvoke apos.pages.move
          return async.eachSeries(_.keys(locales), function(locale, callback) {
            var _options = _.clone(info.options);
            _options.criteria = _.assign({}, info.options.criteria || {}, { workflowLocale: locale });
            _options.filters = _.assign({}, info.options.filters || {}, { workflowLocale: locale });
            _options.workflowRecursing = true;
            return self.move(req, locales[locale].movedId, locales[locale].targetId, info.position, _options, callback);
          }, callback);
        }
      });
    };

    // `implementParkAll` must be reimplemented to cover all of
    // the locales, and to first invoke the add-missing-locales and
    // add-locale-prefixes logic, if needed

    self.implementParkAll = function(callback) {
      if (
        (self.apos.argv._[0] === 'apostrophe-workflow:add-missing-locales') ||
        (self.apos.argv._[0] === 'apostrophe-workflow:add-locale-prefixes') ||
        (self.apos.argv._[0] === 'apostrophe-workflow:remove-numbered-parked-pages') ||
        (self.apos.argv._[0] === 'apostrophe-workflow:remove-numbered-parked-pages') ||
        (self.apos.argv._[0] === 'apostrophe-workflow:harmonize-workflow-guids-by-parked-id')) {
        // If we park pages in this scenario, we'll wind up with
        // duplicate pages in the new locales after the task
        // fills them in
        return setImmediate(callback);
      }

      var workflow = self.apos.modules['apostrophe-workflow'];

      return async.series([
        workflow.updatePerConfiguration,
        parkBody
      ], callback);

      function parkBody(callback) {
        var workflow = self.apos.modules['apostrophe-workflow'];
        var locales = _.keys(workflow.locales);
        return async.eachSeries(locales, function(locale, callback) {
          var parked = _.cloneDeep(self.parked);
          if (workflow.prefixes) {
            fixPrefixes(parked, locale);
          }

          var req = self.apos.tasks.getReq();
          req.locale = locale;

          return async.eachSeries(parked, function(item, callback) {
            return self.implementParkOne(req, item, callback);
          }, callback);

          function fixPrefixes(parked, locale) {
            var prefix = workflow.prefixes[workflow.liveify(locale)];
            if (!prefix) {
              return;
            }
            _.each(parked, function(item) {
              if (item.slug === '/') {
                // Hint to the implementParkOne implementation that
                // this is still a homepage even if its slug is
                // no longer / due to locale prefixes
                item.level = 0;
              }
              if (item.parent) {
                item.parent = prefix + item.parent;
              } else if (item.level !== 0) {
                item.parent = prefix + '/';
              }
              item.slug = prefix + item.slug;
              fixPrefixes(item._children || [], locale);
            });
          }
        }, callback);
      }
    };

    var superGetBaseUrl = self.getBaseUrl;
    // Return the appropriate base URL for constructing absolute
    // URLs in the relevant locale, if the `hostnames` option is
    // in play for this locale, otherwise fall back to the
    // standard behavior
    self.getBaseUrl = function(req) {
      var workflow = self.apos.modules['apostrophe-workflow'];
      var live = workflow.liveify(req.locale || workflow.defaultLocale);
      if (!(workflow.hostnames && workflow.hostnames[live])) {
        return superGetBaseUrl(req);
      }
      var url = req.protocol + '://' + workflow.hostnames[live];
      // If the request URL was on a particular port, the
      // new URL should be on that port too. Helpful for
      // debugging locally
      var matches = (req.get('host') || '').match(/:(\d+$)/);
      var port;
      if (matches) {
        port = matches[1];
        if (((req.protocol === 'http') && (port !== '80')) ||
          ((req.protocol === 'https') && (port !== '443'))) {
          url += ':' + port;
        }
      }
      return url;
    };

  }

};
<commit_msg>removed redundant line<commit_after>var _ = require('@sailshq/lodash');
var async = require('async');

module.exports = {

  improve: 'apostrophe-pages',

  construct: function(self, options) {

    var superGetPathIndexParams = self.getPathIndexParams;
    self.getPathIndexParams = function() {
      var params = superGetPathIndexParams();
      params.workflowLocaleForPathIndex = 1;
      return params;
    };

    var superRemoveTrailingSlugSlashes = self.removeTrailingSlugSlashes;
    self.removeTrailingSlugSlashes = function(req, slug) {
      if (arguments.length === 1) {
        // bc workaround
        slug = req;
        req = self.apos.tasks.getAnonReq();
      }
      var workflow = self.apos.modules['apostrophe-workflow'];
      if (!workflow.prefixes) {
        return superRemoveTrailingSlugSlashes(slug);
      }
      var locale = workflow.liveify(req.locale);
      if (_.has(workflow.prefixes, locale)) {
        var matches = slug.match(/^(\/[^/]+)(\/?)$/);
        if (matches && (workflow.prefixes[locale] === matches[1])) {
          // Something like /en/, leave it alone,
          // it's a localized homepage. However if the
          // trailing slash *after* the locale is missing,
          // add it and redirect
          if (matches[2] === '') {
            return slug + '/';
          } else {
            return slug;
          }
        }
      }
      return superRemoveTrailingSlugSlashes(slug);
    };

    var superPruneCurrentPageForBrowser = self.pruneCurrentPageForBrowser;
    self.pruneCurrentPageForBrowser = function(page) {
      var pruned = superPruneCurrentPageForBrowser(page);
      pruned.workflowLocale = page.workflowLocale;
      pruned.workflowGuid = page.workflowGuid;
      return pruned;
    };

    var superGetEditControls = self.getEditControls;
    self.getEditControls = function(req) {
      return upgradeControls(req, superGetEditControls(req), 'edit');
    };

    var superGetCreateControls = self.getCreateControls;
    self.getCreateControls = function(req) {
      return upgradeControls(req, superGetCreateControls(req), 'create');
    };

    function upgradeControls(req, controls, verb) {
      var workflow = self.apos.modules['apostrophe-workflow'];
      if (!workflow.includeType(self.name)) {
        // Not subject to workflow
        return controls;
      }
      var save = _.find(controls, { action: 'save' });
      if (save) {
        save.label = 'Save Draft';
      }
      controls.push({
        type: 'dropdown',
        label: 'Workflow',
        name: 'workflow',
        dropdownOptions: {
          direction: 'down'
        },
        items: [
          {
            label: 'Submit',
            action: 'workflow-submit'
          },
          {
            label: 'Commit',
            action: 'workflow-commit'
          }
        ].concat(
          (workflow.localized && (verb === 'edit'))
            ? [
              {
                label: 'History',
                action: 'workflow-history'
              }
            ] : []
        ).concat(workflow.localized
          ? [
            {
              label: 'Force Export',
              action: 'workflow-force-export'
            }
          ] : []
        )
      });
      return controls;
    }

    var superMovePermissions = self.movePermissions;

    // "Based on `req`, `moved`, `data.oldParent` and `data.parent`, decide whether
    // this move should be permitted. If it should not be, report an error." The `apostrophe-pages`
    // module did this already, but we must consider the impact on all locales.

    self.movePermissions = function(req, moved, data, options, callback) {
      return superMovePermissions(req, moved, data, options, function(err) {
        if (err) {
          return callback(err);
        }
        if (!moved.workflowGuid) {
          // No localization for pages. That's unusual but allowed
          return callback(null);
        }
        // Grab the pages of interest across all locales other than the original (already checked).
        return self.apos.docs.find(req, {
          workflowGuid: { $in: [ moved.workflowGuid, data.oldParent.workflowGuid, data.parent.workflowGuid ] },
          workflowLocale: { $ne: moved.workflowLocale }
        }).joins(false).areas(false).workflowLocale(null).permission(false).published(null).trash(null).toArray(function(err, pages) {
          if (err) {
            return callback(err);
          }
          var error = null;
          var locales = {};
          _.each(pages, function(page) {
            if (!locales[page.workflowLocale]) {
              locales[page.workflowLocale] = {};
            }
            if (page.workflowGuid === moved.workflowGuid) {
              locales[page.workflowLocale].moved = page;
            }
            // Parent does not always change, else statement is not appropriate here
            if (page.workflowGuid === data.parent.workflowGuid) {
              locales[page.workflowLocale].parent = page;
            }
            if (page.workflowGuid === data.oldParent.workflowGuid) {
              locales[page.workflowLocale].oldParent = page;
            }
          });
          _.each(locales, function(locale, name) {
          // Repeat the permissions check for every locale
            if (!locale.moved._publish) {
              error = new Error('forbidden');
              return false;
            }
            if ((locale.oldParent._id !== locale.parent._id) && (!locale.parent._edit)) {
              error = new Error('forbidden');
              return false;
            }
          });
          return callback(error);
        });
      });
    };

    // On the initial invocation of `apos.pages.move`, modify the criteria and filters
    // to ensure only the relevant locale is in play

    var superBeforeMove = self.beforeMove;
    self.beforeMove = function(req, moved, target, position, options, callback) {
      return superBeforeMove(req, moved, target, position, options, function(err) {
        if (err) {
          return callback(err);
        }
        if (options.workflowRecursing) {
          return callback(null);
        }
        if (moved.workflowLocale) {
          options.criteria = _.assign({}, options.criteria || {}, { workflowLocale: moved.workflowLocale });
          options.filters = _.assign({}, options.filters || {}, { workflowLocale: moved.workflowLocale });
        }
        return callback(null);
      });
    };

    // After a page is moved in one locale, with all of the ripple effects that go with it,
    // make the same move in all other locales. Note that we already verified we have permissions
    // across all locales.

    var superAfterMove = self.afterMove;
    self.afterMove = function(req, moved, info, callback) {
      return superAfterMove(req, moved, info, function(err) {
        if (err) {
          return callback(err);
        }
        if (info.options.workflowRecursing) {
          return callback(null);
        }
        if (!moved.workflowGuid) {
          return callback(null);
        }
        var locales = {};
        return async.series([
          get,
          invoke
        ], function(err) {
          return callback(err);
        });
        function get(callback) {
          // Locate the doc moved and the doc it is moved relative to (target) in all locales other than
          // the original one
          return self.apos.docs.db.find({ workflowGuid: { $in: [ moved.workflowGuid, info.target.workflowGuid ] }, workflowLocale: { $ne: moved.workflowLocale } }, { workflowGuid: 1, _id: 1, workflowLocale: 1 }).toArray(function(err, docs) {
            if (err) {
              return callback(err);
            }
            _.each(docs, function(doc) {
              locales[doc.workflowLocale] = locales[doc.workflowLocale] || {};
              if (doc.workflowGuid === moved.workflowGuid) {
                locales[doc.workflowLocale].movedId = doc._id;
              }
              if (doc.workflowGuid === info.target.workflowGuid) {
                locales[doc.workflowLocale].targetId = doc._id;
              }
            });
            return callback(null);
          });
        }
        function invoke(callback) {
          // Reinvoke apos.pages.move
          return async.eachSeries(_.keys(locales), function(locale, callback) {
            var _options = _.clone(info.options);
            _options.criteria = _.assign({}, info.options.criteria || {}, { workflowLocale: locale });
            _options.filters = _.assign({}, info.options.filters || {}, { workflowLocale: locale });
            _options.workflowRecursing = true;
            return self.move(req, locales[locale].movedId, locales[locale].targetId, info.position, _options, callback);
          }, callback);
        }
      });
    };

    // `implementParkAll` must be reimplemented to cover all of
    // the locales, and to first invoke the add-missing-locales and
    // add-locale-prefixes logic, if needed

    self.implementParkAll = function(callback) {
      if (
        (self.apos.argv._[0] === 'apostrophe-workflow:add-missing-locales') ||
        (self.apos.argv._[0] === 'apostrophe-workflow:add-locale-prefixes') ||
        (self.apos.argv._[0] === 'apostrophe-workflow:remove-numbered-parked-pages') ||
        (self.apos.argv._[0] === 'apostrophe-workflow:harmonize-workflow-guids-by-parked-id')) {
        // If we park pages in this scenario, we'll wind up with
        // duplicate pages in the new locales after the task
        // fills them in
        return setImmediate(callback);
      }

      var workflow = self.apos.modules['apostrophe-workflow'];

      return async.series([
        workflow.updatePerConfiguration,
        parkBody
      ], callback);

      function parkBody(callback) {
        var workflow = self.apos.modules['apostrophe-workflow'];
        var locales = _.keys(workflow.locales);
        return async.eachSeries(locales, function(locale, callback) {
          var parked = _.cloneDeep(self.parked);
          if (workflow.prefixes) {
            fixPrefixes(parked, locale);
          }

          var req = self.apos.tasks.getReq();
          req.locale = locale;

          return async.eachSeries(parked, function(item, callback) {
            return self.implementParkOne(req, item, callback);
          }, callback);

          function fixPrefixes(parked, locale) {
            var prefix = workflow.prefixes[workflow.liveify(locale)];
            if (!prefix) {
              return;
            }
            _.each(parked, function(item) {
              if (item.slug === '/') {
                // Hint to the implementParkOne implementation that
                // this is still a homepage even if its slug is
                // no longer / due to locale prefixes
                item.level = 0;
              }
              if (item.parent) {
                item.parent = prefix + item.parent;
              } else if (item.level !== 0) {
                item.parent = prefix + '/';
              }
              item.slug = prefix + item.slug;
              fixPrefixes(item._children || [], locale);
            });
          }
        }, callback);
      }
    };

    var superGetBaseUrl = self.getBaseUrl;
    // Return the appropriate base URL for constructing absolute
    // URLs in the relevant locale, if the `hostnames` option is
    // in play for this locale, otherwise fall back to the
    // standard behavior
    self.getBaseUrl = function(req) {
      var workflow = self.apos.modules['apostrophe-workflow'];
      var live = workflow.liveify(req.locale || workflow.defaultLocale);
      if (!(workflow.hostnames && workflow.hostnames[live])) {
        return superGetBaseUrl(req);
      }
      var url = req.protocol + '://' + workflow.hostnames[live];
      // If the request URL was on a particular port, the
      // new URL should be on that port too. Helpful for
      // debugging locally
      var matches = (req.get('host') || '').match(/:(\d+$)/);
      var port;
      if (matches) {
        port = matches[1];
        if (((req.protocol === 'http') && (port !== '80')) ||
          ((req.protocol === 'https') && (port !== '443'))) {
          url += ':' + port;
        }
      }
      return url;
    };

  }

};
<|endoftext|>