<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2015 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

/**
 * Bakery holds the context for making HTTP requests
 * that automatically acquire and discharge macaroons.
 *
 * @module env
 * @submodule env.bakery
 */

YUI.add('juju-env-bakery', function(Y) {

  var module = Y.namespace('juju.environments.web');
  var macaroon = Y.namespace('macaroon');

  /**
   * Bakery client inspired by the equivalent GO code.
   *
   * This object exposes the ability to perform requests
   * that automatically acquire and discharge macaroons
   *
   * @class Bakery
   */

  var Bakery = Y.Base.create('Bakery',
      Y.Base, [], {

      /**
       Initialize.

       @method initializer
       @param {Object} cfg A config object providing webhandler and visit method
           information. A visitMethod can be provided, which becomes the
           bakery's visitMethod. Alternatively, the bakery can be configured to
           non interactive mode. If neither is true, the default method is used.
           {visitMethod: fn, interactive: boolean,
            webhandler: obj, serviceName: string}
       @return {undefined} Nothing.
       */
      initializer: function (cfg) {
        this.webhandler = cfg.webhandler;
        if (cfg.visitMethod) {
          this.visitMethod = cfg.visitMethod;
        } else if (cfg.interactive !== undefined && !cfg.interactive) {
          this.visitMethod = this._nonInteractiveVisitMethod;
        } else {
          this.visitMethod = this._defaultVisitMethod;
        }
        this.macaroonName = 'Macaroons-' + cfg.serviceName;
        this.setCookiePath = cfg.setCookiePath;
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 except 401/407 where it does authentication.
       */
      sendGetRequest: function (path, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {'Bakery-Protocol-Version': 1};
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendGetRequest(
          path, headers, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this,
            path,
            successCallback,
            failureCallback
          )
        );
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param path {String} The path to make the api request to.
       @param data {String} Stringified JSON of parameters to send to the POST
           endpoint.
       @param successCallback {Function} Called when the api request completes
           successfully.
       @param failureCallback {Function} Called when the api request fails
           with a response of >= 400 except 401/407 where it does
           authentication.
       */
      sendPostRequest: function (path, data, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-type': 'application/json'
        };
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendPostRequest(
          path, headers, data, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this, path, successCallback, failureCallback)
        );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback. For 407/401
       response it will request authentication through the macaroon provided in
       the 401/407 response.

       @method _requestHandlerWithInteraction
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       @param {Object} response The XHR response object.
       */
      _requestHandlerWithInteraction: function (path, successCallback,
                                                failureCallback, response) {
        var target = response.target;
        if (target.status === 401 &&
          target.getResponseHeader('Www-Authenticate') === 'Macaroon') {
          var jsonResponse = JSON.parse(target.responseText);
          this._authenticate(
            jsonResponse.Info.Macaroon,
            this._sendOriginalRequest.bind(
              this, path, successCallback, failureCallback
            ),
            failureCallback
          );
        } else {
          this._requestHandler(successCallback, failureCallback, response);
        }
      },

      /**
       Used to resend the original request without any interaction this time..

       @method _sendOriginalRequest
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       */
      _sendOriginalRequest: function(path, successCallback, failureCallback) {
          var macaroons = this._getMacaroon();
          var headers = {'Bakery-Protocol-Version': 1};
          if (macaroons !== null) {
            headers['Macaroons'] = macaroons;
          }
          this.webhandler.sendGetRequest(
            path, headers, null, null, false, null,
            this._requestHandler.bind(
              this, successCallback, failureCallback
            )
          );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback.

       @method _requestHandler
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400.
       @param {Object} response The XHR response object.
       */
      _requestHandler: function (successCallback, failureCallback, response) {
        var target = response.target;
        if (target.status >= 400) {
          failureCallback(response);
          return;
        }
        successCallback(response);
      },

      /**
       Authenticate by discharging the macaroon and
       then set the cookie by calling the authCookiePath provided

       @method authenticate
       @param {Macaroon} The macaroon to be discharged
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _authenticate: function (m, requestFunction, failureCallback) {
        try {
          macaroon.discharge(
            macaroon.import(m),
            this._obtainThirdPartyDischarge.bind(this),
            this._processDischarges.bind(
              this,
              requestFunction,
              failureCallback
            ),
            failureCallback
          );
        } catch (ex) {
          failureCallback(ex.message);
        }
      },

      /**
       Process the discharged macaroon and call the end point to be able to set
       a cookie for the origin domain only when an auth cookie path is
       provided, then call the original function.

       @method _processDischarges
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       @param {[Macaroon]} The macaroons being discharged
       */
      _processDischarges: function (requestFunction, failureCallback,
                                    discharges) {
        var jsonMacaroon;
        try {
          jsonMacaroon = macaroon.export(discharges);
        } catch (ex) {
          failureCallback(ex.message);
        }
        var content = JSON.stringify({'Macaroons': jsonMacaroon});
        if (this.setCookiePath === undefined) {
          this._setMacaroonsCookie(requestFunction, jsonMacaroon);
          return;
        }
        this.webhandler.sendPutRequest(
          this.setCookiePath,
          null, content, null, null, true, null,
          this._requestHandler.bind(
            this,
            this._setMacaroonsCookie.bind(this, requestFunction, jsonMacaroon),
            failureCallback
          )
        );
      },

      /**
       Process successful discharge by setting Macaroons Cookie
       and invoke the original request

       @method _setMacaroonsCookie
       @param {Function} The path where to send put request
              to set the cookie back
       @param {Object} an exported Macaroon
       */
      _setMacaroonsCookie: function (originalRequest, jsonMacaroon) {
        var prefix = this.macaroonName + '=';
        document.cookie = prefix + btoa(JSON.stringify(jsonMacaroon));
        window.addEventListener('unload', function() {
          document.cookie = prefix + ';'
        });
        originalRequest();
      },

      /**
       Go to the discharge endpoint to obtain the third party discharge.

       @method obtainThirdPartyDischarge
       @param {String} The origin location
       @param {String} The third party location where to discharge
       @param {Function} The macaroon to be discharge
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _obtainThirdPartyDischarge: function (location,
                                            thirdPartyLocation, condition,
                                            successCallback, failureCallback) {
        thirdPartyLocation += '/discharge';
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-Type': 'application/x-www-form-urlencoded'
        };
        var content = 'id=' + encodeURI(condition) +
          '&location=' + encodeURI(location);
        this.webhandler.sendPostRequest(
          thirdPartyLocation,
          headers, content, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            this._interact.bind(this, successCallback, failureCallback)
          )
        );
      },

    /**
      Get a JSON response from authentication either trusted or with
      interaction that contains a macaroon.

      @method _exportMacaroon
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
      @param {Object} response The XHR response object.
    */
    _exportMacaroon: function (successCallback, failureCallback, response) {
      try {
        var dm = macaroon.import(
          JSON.parse(response.target.responseText).Macaroon
        );
        successCallback(dm);
      } catch (ex) {
        failureCallback(ex.message);
      }
    },

    /**
      Interact to be able to sign-in to get authenticated.

      @method _interact
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
    */
    _interact: function(successCallback, failureCallback, e) {
      var response = JSON.parse(e.target.responseText);
      if (response.Code !== 'interaction required') {
        failureCallback(response.Code);
        return;
      }

      this.visitMethod(response);

      this.webhandler.sendGetRequest(
          response.Info.WaitURL,
          null, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            failureCallback
          )
      );
    },

    /**
      Non interactive visit method which sends the jujugui "auth" blob
      to the IdM to login.

      @method _nonInteractiveVisitMethod
      @param {Object} response An xhr response object.
    */
    _nonInteractiveVisitMethod: function(response) {
      var acceptHeaders = {'Accept': 'application/json'};
      var contentHeaders = {'Content-Type': 'application/json'};
      var login = function(response) {
        var method = JSON.parse(response.target.responseText).jujugui;
        var data = JSON.stringify({login: window.juju_config.auth});
        this.webhandler.sendPostRequest(
            method, contentHeaders, data,
            null, null, false, null, null);
      };

      this.webhandler.sendGetRequest(
          response.Info.VisitURL,
          acceptHeaders, null, null, false, null, login.bind(this));
    },

    /**
      Default visit method which is to open a window to the
      response.Info.VisitURL.

      @method _defaultVisitMethod
      @param {Object} response An xhr response object.
    */
    _defaultVisitMethod: function(response) {
      window.open(response.Info.VisitURL, 'Login');
    },

    /**
      Get macaroon from local cookie.

      @method _getMacaroon
      @return {String} Macaroons that was set in local cookie.
    */
    _getMacaroon: function() {
      var name = this.macaroonName + '=';
      var ca = document.cookie.split(';');
      for(var i=0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1);
        if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
      }
      return null;
    }
  });

  module.Bakery = Bakery;

}, '0.1.0', {
  requires: [
    'base',
    'cookie',
    'node',
    'juju-env-base',
    'juju-env-web-handler',
    'macaroon'
  ]
});
<commit_msg>Semicolons!<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2015 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

/**
 * Bakery holds the context for making HTTP requests
 * that automatically acquire and discharge macaroons.
 *
 * @module env
 * @submodule env.bakery
 */

YUI.add('juju-env-bakery', function(Y) {

  var module = Y.namespace('juju.environments.web');
  var macaroon = Y.namespace('macaroon');

  /**
   * Bakery client inspired by the equivalent GO code.
   *
   * This object exposes the ability to perform requests
   * that automatically acquire and discharge macaroons
   *
   * @class Bakery
   */

  var Bakery = Y.Base.create('Bakery',
      Y.Base, [], {

      /**
       Initialize.

       @method initializer
       @param {Object} cfg A config object providing webhandler and visit method
           information. A visitMethod can be provided, which becomes the
           bakery's visitMethod. Alternatively, the bakery can be configured to
           non interactive mode. If neither is true, the default method is used.
           {visitMethod: fn, interactive: boolean,
            webhandler: obj, serviceName: string}
       @return {undefined} Nothing.
       */
      initializer: function (cfg) {
        this.webhandler = cfg.webhandler;
        if (cfg.visitMethod) {
          this.visitMethod = cfg.visitMethod;
        } else if (cfg.interactive !== undefined && !cfg.interactive) {
          this.visitMethod = this._nonInteractiveVisitMethod;
        } else {
          this.visitMethod = this._defaultVisitMethod;
        }
        this.macaroonName = 'Macaroons-' + cfg.serviceName;
        this.setCookiePath = cfg.setCookiePath;
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 except 401/407 where it does authentication.
       */
      sendGetRequest: function (path, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {'Bakery-Protocol-Version': 1};
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendGetRequest(
          path, headers, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this,
            path,
            successCallback,
            failureCallback
          )
        );
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param path {String} The path to make the api request to.
       @param data {String} Stringified JSON of parameters to send to the POST
           endpoint.
       @param successCallback {Function} Called when the api request completes
           successfully.
       @param failureCallback {Function} Called when the api request fails
           with a response of >= 400 except 401/407 where it does
           authentication.
       */
      sendPostRequest: function (path, data, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-type': 'application/json'
        };
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendPostRequest(
          path, headers, data, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this, path, successCallback, failureCallback)
        );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback. For 407/401
       response it will request authentication through the macaroon provided in
       the 401/407 response.

       @method _requestHandlerWithInteraction
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       @param {Object} response The XHR response object.
       */
      _requestHandlerWithInteraction: function (path, successCallback,
                                                failureCallback, response) {
        var target = response.target;
        if (target.status === 401 &&
          target.getResponseHeader('Www-Authenticate') === 'Macaroon') {
          var jsonResponse = JSON.parse(target.responseText);
          this._authenticate(
            jsonResponse.Info.Macaroon,
            this._sendOriginalRequest.bind(
              this, path, successCallback, failureCallback
            ),
            failureCallback
          );
        } else {
          this._requestHandler(successCallback, failureCallback, response);
        }
      },

      /**
       Used to resend the original request without any interaction this time..

       @method _sendOriginalRequest
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       */
      _sendOriginalRequest: function(path, successCallback, failureCallback) {
          var macaroons = this._getMacaroon();
          var headers = {'Bakery-Protocol-Version': 1};
          if (macaroons !== null) {
            headers['Macaroons'] = macaroons;
          }
          this.webhandler.sendGetRequest(
            path, headers, null, null, false, null,
            this._requestHandler.bind(
              this, successCallback, failureCallback
            )
          );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback.

       @method _requestHandler
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400.
       @param {Object} response The XHR response object.
       */
      _requestHandler: function (successCallback, failureCallback, response) {
        var target = response.target;
        if (target.status >= 400) {
          failureCallback(response);
          return;
        }
        successCallback(response);
      },

      /**
       Authenticate by discharging the macaroon and
       then set the cookie by calling the authCookiePath provided

       @method authenticate
       @param {Macaroon} The macaroon to be discharged
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _authenticate: function (m, requestFunction, failureCallback) {
        try {
          macaroon.discharge(
            macaroon.import(m),
            this._obtainThirdPartyDischarge.bind(this),
            this._processDischarges.bind(
              this,
              requestFunction,
              failureCallback
            ),
            failureCallback
          );
        } catch (ex) {
          failureCallback(ex.message);
        }
      },

      /**
       Process the discharged macaroon and call the end point to be able to set
       a cookie for the origin domain only when an auth cookie path is
       provided, then call the original function.

       @method _processDischarges
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       @param {[Macaroon]} The macaroons being discharged
       */
      _processDischarges: function (requestFunction, failureCallback,
                                    discharges) {
        var jsonMacaroon;
        try {
          jsonMacaroon = macaroon.export(discharges);
        } catch (ex) {
          failureCallback(ex.message);
        }
        var content = JSON.stringify({'Macaroons': jsonMacaroon});
        if (this.setCookiePath === undefined) {
          this._setMacaroonsCookie(requestFunction, jsonMacaroon);
          return;
        }
        this.webhandler.sendPutRequest(
          this.setCookiePath,
          null, content, null, null, true, null,
          this._requestHandler.bind(
            this,
            this._setMacaroonsCookie.bind(this, requestFunction, jsonMacaroon),
            failureCallback
          )
        );
      },

      /**
       Process successful discharge by setting Macaroons Cookie
       and invoke the original request

       @method _setMacaroonsCookie
       @param {Function} The path where to send put request
              to set the cookie back
       @param {Object} an exported Macaroon
       */
      _setMacaroonsCookie: function (originalRequest, jsonMacaroon) {
        var prefix = this.macaroonName + '=';
        document.cookie = prefix + btoa(JSON.stringify(jsonMacaroon));
        window.addEventListener('unload', function() {
          document.cookie = prefix + ';';
        });
        originalRequest();
      },

      /**
       Go to the discharge endpoint to obtain the third party discharge.

       @method obtainThirdPartyDischarge
       @param {String} The origin location
       @param {String} The third party location where to discharge
       @param {Function} The macaroon to be discharge
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _obtainThirdPartyDischarge: function (location,
                                            thirdPartyLocation, condition,
                                            successCallback, failureCallback) {
        thirdPartyLocation += '/discharge';
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-Type': 'application/x-www-form-urlencoded'
        };
        var content = 'id=' + encodeURI(condition) +
          '&location=' + encodeURI(location);
        this.webhandler.sendPostRequest(
          thirdPartyLocation,
          headers, content, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            this._interact.bind(this, successCallback, failureCallback)
          )
        );
      },

    /**
      Get a JSON response from authentication either trusted or with
      interaction that contains a macaroon.

      @method _exportMacaroon
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
      @param {Object} response The XHR response object.
    */
    _exportMacaroon: function (successCallback, failureCallback, response) {
      try {
        var dm = macaroon.import(
          JSON.parse(response.target.responseText).Macaroon
        );
        successCallback(dm);
      } catch (ex) {
        failureCallback(ex.message);
      }
    },

    /**
      Interact to be able to sign-in to get authenticated.

      @method _interact
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
    */
    _interact: function(successCallback, failureCallback, e) {
      var response = JSON.parse(e.target.responseText);
      if (response.Code !== 'interaction required') {
        failureCallback(response.Code);
        return;
      }

      this.visitMethod(response);

      this.webhandler.sendGetRequest(
          response.Info.WaitURL,
          null, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            failureCallback
          )
      );
    },

    /**
      Non interactive visit method which sends the jujugui "auth" blob
      to the IdM to login.

      @method _nonInteractiveVisitMethod
      @param {Object} response An xhr response object.
    */
    _nonInteractiveVisitMethod: function(response) {
      var acceptHeaders = {'Accept': 'application/json'};
      var contentHeaders = {'Content-Type': 'application/json'};
      var login = function(response) {
        var method = JSON.parse(response.target.responseText).jujugui;
        var data = JSON.stringify({login: window.juju_config.auth});
        this.webhandler.sendPostRequest(
            method, contentHeaders, data,
            null, null, false, null, null);
      };

      this.webhandler.sendGetRequest(
          response.Info.VisitURL,
          acceptHeaders, null, null, false, null, login.bind(this));
    },

    /**
      Default visit method which is to open a window to the
      response.Info.VisitURL.

      @method _defaultVisitMethod
      @param {Object} response An xhr response object.
    */
    _defaultVisitMethod: function(response) {
      window.open(response.Info.VisitURL, 'Login');
    },

    /**
      Get macaroon from local cookie.

      @method _getMacaroon
      @return {String} Macaroons that was set in local cookie.
    */
    _getMacaroon: function() {
      var name = this.macaroonName + '=';
      var ca = document.cookie.split(';');
      for(var i=0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1);
        if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
      }
      return null;
    }
  });

  module.Bakery = Bakery;

}, '0.1.0', {
  requires: [
    'base',
    'cookie',
    'node',
    'juju-env-base',
    'juju-env-web-handler',
    'macaroon'
  ]
});
<|endoftext|>


<commit_before>app.factory('administrationRestService', ['$http', 'HOST',

function($http, HOST) {

	'use strict';

	function getCostCategories() {
		return $http.get(HOST + '/api/public/cost-categories');
	}

	function postCostCategory(data) {
		return $http({
			method: 'POST',
			url: HOST + '/api/finance-admin/cost-categories',
			data: data
		});
	}

	function putCostCategory(data, uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/finance-admin/cost-categories/' + uid,
			data: data
		});
	}

	function deactivateCostCategory(uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/finance-admin/cost-categories/' + uid + '/deactivate'
		});
	}

	function activateCostCategory(uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/finance-admin/cost-categories/' + uid + '/activate'
		});
	}

	function getRoles() {
		return $http.get(HOST + '/api/finance-admin/roles');
	}

	function getExpenseStates() {
		return $http.get(HOST + '/api/expenses/expense-states');
	}

	function search(data) {
		return $http({
			method: 'POST',
			url: HOST + '/api/expenses/search',
			data: data
		});
	}

	function getExpenseStateRawData() {
		return $http.get(HOST + '/api/expenses/statistics/states');
	}

	function assignToMe(uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/expenses/' + uid + '/assign-to-me'
		});
	}

	return {
		getCostCategories: getCostCategories,
		postCostCategory: postCostCategory,
		putCostCategory: putCostCategory,
		deactivateCostCategory: deactivateCostCategory,
		activateCostCategory: activateCostCategory,
		getRoles: getRoles,
		getExpenseStates: getExpenseStates,
		search: search,
		getExpenseStateRawData: getExpenseStateRawData,
		assignToMe: assignToMe
	};
}]);
<commit_msg>adjusted administration restservice to also get inactive costCategories<commit_after>app.factory('administrationRestService', ['$http', 'HOST',

function($http, HOST) {

	'use strict';

	function getCostCategories() {
		return $http.get(HOST + '/api/finance-admin/cost-categories');
	}

	function postCostCategory(data) {
		return $http({
			method: 'POST',
			url: HOST + '/api/finance-admin/cost-categories',
			data: data
		});
	}

	function putCostCategory(data, uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/finance-admin/cost-categories/' + uid,
			data: data
		});
	}

	function deactivateCostCategory(uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/finance-admin/cost-categories/' + uid + '/deactivate'
		});
	}

	function activateCostCategory(uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/finance-admin/cost-categories/' + uid + '/activate'
		});
	}

	function getRoles() {
		return $http.get(HOST + '/api/finance-admin/roles');
	}

	function getExpenseStates() {
		return $http.get(HOST + '/api/expenses/expense-states');
	}

	function search(data) {
		return $http({
			method: 'POST',
			url: HOST + '/api/expenses/search',
			data: data
		});
	}

	function getExpenseStateRawData() {
		return $http.get(HOST + '/api/expenses/statistics/states');
	}

	function assignToMe(uid) {
		return $http({
			method: 'PUT',
			url: HOST + '/api/expenses/' + uid + '/assign-to-me'
		});
	}

	return {
		getCostCategories: getCostCategories,
		postCostCategory: postCostCategory,
		putCostCategory: putCostCategory,
		deactivateCostCategory: deactivateCostCategory,
		activateCostCategory: activateCostCategory,
		getRoles: getRoles,
		getExpenseStates: getExpenseStates,
		search: search,
		getExpenseStateRawData: getExpenseStateRawData,
		assignToMe: assignToMe
	};
}]);
<|endoftext|>


<commit_before>import layerKeys from 'constants/LayerConstants';
import geometryUtils from 'utils/geometryUtils';
import mapActions from 'actions/MapActions';
import Draw from 'esri/toolbars/draw';
import text from 'js/languages';
import SVGIcon from 'utils/svgIcon';

import React, {
  Component,
  PropTypes
} from 'react';

export default class CoordinatesTools extends Component {

    static contextTypes = {
        language: PropTypes.string.isRequired,
        map: PropTypes.object.isRequired
      };

      componentDidMount () {
        const {map} = this.context;
        // If this component unmounts and destroys itself, recreate it
        if (!this.toolbar && map.loaded) {
          this.createToolbar(map);
        }
      }

      componentDidUpdate(prevProps, prevState, prevContext) {
        const {map} = this.context;
        // Wait for the map to load and create it
        if (!this.toolbar && map.loaded) {
          this.createToolbar(map);
        } else if (prevContext.map !== map && map.loaded) { // If the map changes, recreate it
          this.createToolbar(map);
        }
      }

      renderInstructionList = (instruction, index) => {
        return (

        );
      };

      enterValues = () => {
       // if active, toggle it off
        if (this.props.enterValuesButtonActive) {
            mapActions.toggleCoordinatesModal({ visible: false });
        } else {
          this.activate();
          //- If the analysis modal is visible, hide it
          mapActions.toggleCoordinatesModal({ visible: true });
        }
      };

      activate = () => {
        // const {map} = this.context;
        // this.toolbar.activate(Draw.POLYGON);
        // mapActions.activateDrawButton(true);
        // Disable popups while this is active, this function is only available to webmaps when usePopupManager is true
        //
        mapActions.activateEnterValuesButton(true);
      };

      deactivate = () => {
        // const {map} = this.context;
        // this.toolbar.deactivate();
        // mapActions.activateDrawButton(false);
        // Reconnect the popups, this function is only available to webmaps when usePopupManager is true
        // map.setInfoWindowOnClick(true);
        mapActions.activateEnterValuesButton(true);
      };

      createToolbar = (map) => {
        this.toolbar = new Draw(map);
        this.toolbar.on('draw-end', (evt) => {
          this.deactivate();
          // Add graphic to map and set as active feature
          geometryUtils.generateDrawnPolygon(evt.geometry).then(graphic => {
            const layer = map.getLayer(layerKeys.USER_FEATURES);
            if (layer) {
              layer.add(graphic);
              map.infoWindow.setFeatures([graphic]);
            }
          });
        });
      };

      render() {
        const {language} = this.context;

        return (

              {text[language].ANALYSIS_COORDINATES_HEADER}

              {text[language].ANALYSIS_COORDINATES_INSTRUCTIONS.map(this.renderInstructionList)}

              <SVGIcon id={'icon-enter-values'} />
              {text[language].ANALYSIS_COORDINATES_BUTTONS[0]}

              {text[language].ANALYSIS_OR}

        );
      }
}
<commit_msg>Remove activate and deactivate functions - replace with mapActions.activateEnterValuesButton(true) whenever either was called.<commit_after>import layerKeys from 'constants/LayerConstants';
import geometryUtils from 'utils/geometryUtils';
import mapActions from 'actions/MapActions';
import Draw from 'esri/toolbars/draw';
import text from 'js/languages';
import SVGIcon from 'utils/svgIcon';

import React, {
  Component,
  PropTypes
} from 'react';

export default class CoordinatesTools extends Component {

    static contextTypes = {
        language: PropTypes.string.isRequired,
        map: PropTypes.object.isRequired
      };

      componentDidMount () {
        const {map} = this.context;
        // If this component unmounts and destroys itself, recreate it
        if (!this.toolbar && map.loaded) {
          this.createToolbar(map);
        }
      }

      componentDidUpdate(prevProps, prevState, prevContext) {
        const {map} = this.context;
        // Wait for the map to load and create it
        if (!this.toolbar && map.loaded) {
          this.createToolbar(map);
        } else if (prevContext.map !== map && map.loaded) { // If the map changes, recreate it
          this.createToolbar(map);
        }
      }

      renderInstructionList = (instruction, index) => {
        return (

        );
      };

      enterValues = () => {
       // if active, toggle it off
        if (this.props.enterValuesButtonActive) {
            mapActions.toggleCoordinatesModal({ visible: false });
        } else {
          mapActions.activateEnterValuesButton(true);
          //- If the analysis modal is visible, hide it
          mapActions.toggleCoordinatesModal({ visible: true });
        }
      };

      createToolbar = (map) => {
        this.toolbar = new Draw(map);
        this.toolbar.on('draw-end', (evt) => {
          mapActions.activateEnterValuesButton(true);
          // Add graphic to map and set as active feature
          geometryUtils.generateDrawnPolygon(evt.geometry).then(graphic => {
            const layer = map.getLayer(layerKeys.USER_FEATURES);
            if (layer) {
              layer.add(graphic);
              map.infoWindow.setFeatures([graphic]);
            }
          });
        });
      };

      render() {
        const {language} = this.context;

        return (

              {text[language].ANALYSIS_COORDINATES_HEADER}

              {text[language].ANALYSIS_COORDINATES_INSTRUCTIONS.map(this.renderInstructionList)}

              <SVGIcon id={'icon-enter-values'} />
              {text[language].ANALYSIS_COORDINATES_BUTTONS[0]}

              {text[language].ANALYSIS_OR}

        );
      }
}
<|endoftext|>


<commit_before>app.factory("createExpenseRestService", ['$http', 'HOST',

function($http, HOST) {
	"use strict";

	function postCreateExpense(accounting) {
		return $http({
			method: 'POST',
			url: HOST + '/api/user/expenses',
			data: {
				accounting: accounting,
				// TODO remove state, not necessary in the beginning
				state: 'CREATED'
			}
		});
	}

	return {
		postCreateExpense: postCreateExpense
	};

}]);
<commit_msg>remove state from CreateExpenseDto<commit_after>app.factory("createExpenseRestService", ['$http', 'HOST',

function($http, HOST) {
	"use strict";

	function postCreateExpense(accounting) {
		return $http({
			method: 'POST',
			url: HOST + '/api/user/expenses',
			data: {
				accounting: accounting
			}
		});
	}

	return {
		postCreateExpense: postCreateExpense
	};

}]);
<|endoftext|>


<commit_before>document.write('
The purpose of this page is to access your camera and microphone.
');
document.write('
You can REMOVE i.e. DELETE camera permissions anytime on this page:
');
document.write('
chrome://settings/content/camera?search=camera</pr>');

var constraints = {
    audio: true,
    video: true
};

navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
    document.write('

<video autoplay controls src="' + URL.createObjectURL(stream) + '"></video>');
    document.querySelector('h1').innerHTML = 'Now you can close this page and click extension icon again.'
}).catch(function() {
    document.querySelector('h1').innerHTML = 'Unable to capture your camera and microphone.';
});
<commit_msg>Fixed #63<commit_after>document.write('
The purpose of this page is to access your camera and microphone.
');
document.write('
You can REMOVE i.e. DELETE camera permissions anytime on this page:
');
document.write('
chrome://settings/content/camera?search=camera</pr>');

var constraints = {
    audio: true,
    video: true
};

navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
    document.write('

<video muted volume=0 autoplay controls></video>');
    document.querySelector('h1').innerHTML = 'Now you can close this page and click extension icon again.'

    document.querySelector('video').srcObject = stream;
}).catch(function(error) {
    document.querySelector('h1').innerHTML = 'Unable to capture your camera and microphone.
' + error;
});
<|endoftext|>


<commit_before>'use strict';

var I = require('immutable');
var DS = require('./delaney');
var Partition = require('../common/partition');

var _fold = function _fold(partition, a, b, matchP, spreadFn) {
  var p = partition;
  var q = I.List().push(I.List([a, b]));

  while (!q.isEmpty()) {
    var _tmp = q.first();
    var x = _tmp.get(0);
    var y = _tmp.get(1);

    q = q.rest();

    if (!matchP(x, y))
      return;
    else if (p.get(x) == p.get(y))
      continue;
    else {
      p = p.union(x, y);
      q = q.concat(I.List(spreadFn(x, y)).map(I.List));
    }
  }

  return p;
};

var _typeMap = function _typeMap(ds) {
  var base = I.Map(ds.elements().map(function(D) {
    return [D, I.List()];
  }));
  var idcs = DS.indices(ds);

  return base.withMutations(function(map) {
    idcs.zip(idcs.rest()).forEach(function(p) {
      var i = p[0];
      var j = p[1];

      DS.orbitReps2(ds, i, j).forEach(function(D) {
        var m = DS.m(ds, i, j, D);
        DS.orbit2(ds, i, j, D).forEach(function(E) {
          map.set(E, map.get(E).push(m));
        });
      });
    });
  });
};

var isMinimal = function isMinimal(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().every(function(D) {
    return _fold(Partition(), D0, D, match, spread) === undefined;
  });
};

var typePartition = function typePartition(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().reduce(
    function(p, D) {
      return _fold(p, D0, D, match, spread) || p;
    },
    Partition()
  );
};

var Traversal = function Traversal(ds, indices, seeds) {
  var seedsLeft = (seeds.constructor == Array) ? seeds.slice() : seeds.toJS();
  var todo = {};
  var seen = {};
  var emap = {};
  var n    = 1;
  indices = (indices.constructor == Array) ? indices : indices.toJS();
  indices.forEach(function(i) { seen[i] = {}; todo[i] = [] });
  seen[root] = {};

  return {
    next: function() {
      while (true) {
        var i = null, D = null;
        for (var k = 0; k < indices.length; ++k)
          if (todo[indices[k]].length > 0) {
            i = indices[k];
            D = todo[i].shift();
            break;
          }

        if (D == null && seedsLeft.length > 0)
          D = seedsLeft.pop();

        if (D == null)
          return { done: true };

        if (!seen[i][D]) {
          var Di = (i == root) ? D : ds.s(i, D);
          var Ei = emap[Di] || n;

          if (Ei == n) {
            emap[Di] = Ei;
            ++n;
          }

          indices.forEach(function(i) {
            if (!seen[i][Di]) {
              if (i < 2)
                todo[i].unshift(Di);
              else
                todo[i].push(Di);
            }
          });

          seen[root][Di] = true;
          seen[i][D]     = true;
          seen[i][Di]    = true;

          return { done: false, value: [D, i, Di, emap[D], Ei] };
        }
      }
    }
  };
};

var traversal = function traversal(ds, indices, seeds) {
  return I.Seq(Traversal(ds, indices, seeds));
};

var root = traversal.root = null;

var orbitReps = function orbitReps(ds, indices, seeds) {
  return traversal(ds, indices, seeds || ds.elements())
    .filter(function(e) { return e[1] == root; })
    .map(function(e) { return e[2]; });
};

var isConnected = function isConnected(ds) {
  return orbitReps(ds, ds.indices()).count() < 2;
};

var orbit = function orbit(ds, indices, seed) {
  var seen = I.Set().asMutable();
  var result = I.List().asMutable();

  traversal(ds, indices, [seed]).forEach(function(e) {
    var D = e[2];
    if (D && !seen.contains(D)) {
      seen.add(D);
      result.push(D);
    }
  });

  return result.asImmutable();
};

var partialOrientation = function partialOrientation(ds) {
  var ori = I.Map().asMutable();

  traversal(ds, ds.indices(), ds.elements()).forEach(function(e) {
    var Di = e[0];
    var i = e[1];
    var D = e[2];

    if (D && !ori.get(D))
      ori.set(D, i == root ? 1 : -ori.get(Di));
  });

  return ori.asImmutable();
};

var isLoopless = function isLoopless(ds) {
  return ds.elements().every(function(D) {
    return ds.indices().every(function(i) {
      return D != ds.s(i, D);
    });
  });
};

var isOriented = function isOriented(ds) {
  var ori = partialOrientation(ds);

  return ds.elements().every(function(D) {
    return ds.indices().every(function(i) {
      return ori.get(D) != ori.get(ds.s(i, D));
    });
  });
};

var isWeaklyOriented = function isWeaklyOriented(ds) {
  var ori = partialOrientation(ds);

  return ds.elements().every(function(D) {
    return ds.indices().every(function(i) {
      var Di = ds.s(i, D);
      return D == Di || ori.get(D) != ori.get(Di);
    });
  });
};

var _protocol = function _protocol(ds, idcs, gen) {
  var buffer = [];
  var max = 0;

  var _advance = function _advance() {
    var next = gen.next();
    if (next.done)
      return false;
    var entry = next.value;

    var D = entry[2];
    buffer.push(entry[1] == root ? -1 : entry[1]);
    buffer.push(entry[3]);

    if (entry[1] != root)
      buffer.push(entry[4]);

    if (entry[4] > max) {
      for (var i = 0; i < idcs.length - 1; ++i)
        buffer.push(ds.v(idcs[i], idcs[i+1], D));
      max = entry[4];
    }

    return true;
  };

  return {
    get: function(i) {
      while (buffer.length <= i && _advance())
        ;
      return buffer[i];
    },
    content: function(fn) {
      while (_advance())
        ;
      return buffer;
    }
  };
};

var invariant = function invariant(ds) {
  var idcs = DS.indices(ds).toJS();
  var best = null;

  ds.elements().forEach(function(D0) {
    var trav = _protocol(ds, idcs, Traversal(ds, idcs, [D0]));

    if (best == null)
      best = trav;
    else {
      for (var i = 0; ; ++i) {
        var next = trav.get(i);
        if (next == undefined)
          break;

        var d = next - best.get(i);
        if (d != 0) {
          if (d < 0)
            best = trav;
          break;
        }
      }
    }
  });

  return I.List(best.content());
};

module.exports = {
  isMinimal         : isMinimal,
  typePartition     : typePartition,
  traversal         : traversal,
  orbitReps         : orbitReps,
  isConnected       : isConnected,
  orbit             : orbit,
  partialOrientation: partialOrientation,
  isLoopless        : isLoopless,
  isOriented        : isOriented,
  isWeaklyOriented  : isWeaklyOriented,
  invariant         : invariant
};

if (require.main == module) {
  var test = function test(ds) {
    console.log('ds = '+ds);
    console.log();

    console.log('    symbol is '+(isConnected(ds) ? '' : 'not ')+'connected.');
    console.log('    symbol is '+(isMinimal(ds) ? '' : 'not ')+'minimal.');
    console.log('    symbol is '+(isLoopless(ds) ? '' : 'not ')+'loopless.');
    console.log('    symbol is '+(isOriented(ds) ? '' : 'not ')+'oriented.');
    console.log('    symbol is '+(isWeaklyOriented(ds) ? '' : 'not ')
                +'weakly oriented.');
    console.log('    type partition: '+typePartition(ds));
    var trav = traversal(ds, ds.indices(), ds.elements());
    console.log('    traversal: ' + trav);
    console.log('    invariant: ' + invariant(ds));
    console.log();

    console.log('    0,1 orbit reps: '+orbitReps(ds, [0, 1]));
    console.log('    1,2 orbit reps: '+orbitReps(ds, [1, 2]));
    console.log('    0,2 orbit reps: '+orbitReps(ds, [0, 2]));
    console.log();

    console.log('    0,1 orbit of 1: '+orbit(ds, [0, 1], 1));
    console.log('    1,2 orbit of 1: '+orbit(ds, [1, 2], 1));
    console.log('    0,2 orbit of 1: '+orbit(ds, [0, 2], 1));
    console.log();

    console.log('    partial orientation: '+partialOrientation(ds));
    console.log();
    console.log();
  };

  test(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>'));

  test(DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(DS.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(DS.parse('<1.1:6:4 6 5,5 4 6,4 6 5:3,6>'));
}
<commit_msg>move renumbering out of Traversal() and into _protocol()<commit_after>'use strict';

var I = require('immutable');
var DS = require('./delaney');
var Partition = require('../common/partition');

var _fold = function _fold(partition, a, b, matchP, spreadFn) {
  var p = partition;
  var q = I.List().push(I.List([a, b]));

  while (!q.isEmpty()) {
    var _tmp = q.first();
    var x = _tmp.get(0);
    var y = _tmp.get(1);

    q = q.rest();

    if (!matchP(x, y))
      return;
    else if (p.get(x) == p.get(y))
      continue;
    else {
      p = p.union(x, y);
      q = q.concat(I.List(spreadFn(x, y)).map(I.List));
    }
  }

  return p;
};

var _typeMap = function _typeMap(ds) {
  var base = I.Map(ds.elements().map(function(D) {
    return [D, I.List()];
  }));
  var idcs = DS.indices(ds);

  return base.withMutations(function(map) {
    idcs.zip(idcs.rest()).forEach(function(p) {
      var i = p[0];
      var j = p[1];

      DS.orbitReps2(ds, i, j).forEach(function(D) {
        var m = DS.m(ds, i, j, D);
        DS.orbit2(ds, i, j, D).forEach(function(E) {
          map.set(E, map.get(E).push(m));
        });
      });
    });
  });
};

var isMinimal = function isMinimal(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().every(function(D) {
    return _fold(Partition(), D0, D, match, spread) === undefined;
  });
};

var typePartition = function typePartition(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().reduce(
    function(p, D) {
      return _fold(p, D0, D, match, spread) || p;
    },
    Partition()
  );
};

var Traversal = function Traversal(ds, indices, seeds) {
  var seedsLeft = (seeds.constructor == Array) ? seeds.slice() : seeds.toJS();
  var todo = {};
  var seen = {};
  indices = (indices.constructor == Array) ? indices : indices.toJS();
  indices.forEach(function(i) { seen[i] = {}; todo[i] = [] });
  seen[root] = {};

  return {
    next: function() {
      while (true) {
        var i = null, D = null;
        for (var k = 0; k < indices.length; ++k)
          if (todo[indices[k]].length > 0) {
            i = indices[k];
            D = todo[i].shift();
            break;
          }

        if (D == null && seedsLeft.length > 0)
          D = seedsLeft.pop();

        if (D == null)
          return { done: true };

        if (!seen[i][D]) {
          var Di = (i == root) ? D : ds.s(i, D);

          indices.forEach(function(i) {
            if (!seen[i][Di]) {
              if (i < 2)
                todo[i].unshift(Di);
              else
                todo[i].push(Di);
            }
          });

          seen[root][Di] = true;
          seen[i][D]     = true;
          seen[i][Di]    = true;

          return { done: false, value: [D, i, Di] };
        }
      }
    }
  };
};

var traversal = function traversal(ds, indices, seeds) {
  return I.Seq(Traversal(ds, indices, seeds));
};

var root = traversal.root = null;

var orbitReps = function orbitReps(ds, indices, seeds) {
  return traversal(ds, indices, seeds || ds.elements())
    .filter(function(e) { return e[1] == root; })
    .map(function(e) { return e[2]; });
};

var isConnected = function isConnected(ds) {
  return orbitReps(ds, ds.indices()).count() < 2;
};

var orbit = function orbit(ds, indices, seed) {
  var seen = I.Set().asMutable();
  var result = I.List().asMutable();

  traversal(ds, indices, [seed]).forEach(function(e) {
    var D = e[2];
    if (D && !seen.contains(D)) {
      seen.add(D);
      result.push(D);
    }
  });

  return result.asImmutable();
};

var partialOrientation = function partialOrientation(ds) {
  var ori = I.Map().asMutable();

  traversal(ds, ds.indices(), ds.elements()).forEach(function(e) {
    var Di = e[0];
    var i = e[1];
    var D = e[2];

    if (D && !ori.get(D))
      ori.set(D, i == root ? 1 : -ori.get(Di));
  });

  return ori.asImmutable();
};

var isLoopless = function isLoopless(ds) {
  return ds.elements().every(function(D) {
    return ds.indices().every(function(i) {
      return D != ds.s(i, D);
    });
  });
};

var isOriented = function isOriented(ds) {
  var ori = partialOrientation(ds);

  return ds.elements().every(function(D) {
    return ds.indices().every(function(i) {
      return ori.get(D) != ori.get(ds.s(i, D));
    });
  });
};

var isWeaklyOriented = function isWeaklyOriented(ds) {
  var ori = partialOrientation(ds);

  return ds.elements().every(function(D) {
    return ds.indices().every(function(i) {
      var Di = ds.s(i, D);
      return D == Di || ori.get(D) != ori.get(Di);
    });
  });
};

var _protocol = function _protocol(ds, idcs, gen) {
  var buffer = [];
  var n = 1;
  var emap = {};

  var _advance = function _advance() {
    var next = gen.next();
    if (next.done)
      return false;
    var entry = next.value;

    var Di = entry[0];
    var i = entry[1];
    var D = entry[2];
    var E = emap[D] || n;

    if (E == n)
      emap[D] = E;

    buffer.push(i == root ? -1 : i);
    buffer.push(emap[Di]);

    if (i != root)
      buffer.push(E);

    if (E == n) {
      for (var i = 0; i < idcs.length - 1; ++i)
        buffer.push(ds.v(idcs[i], idcs[i+1], D));
      ++n;
    }

    return true;
  };

  return {
    get: function(i) {
      while (buffer.length <= i && _advance())
        ;
      return buffer[i];
    },
    content: function(fn) {
      while (_advance())
        ;
      return buffer;
    }
  };
};

var invariant = function invariant(ds) {
  var idcs = DS.indices(ds).toJS();
  var best = null;

  ds.elements().forEach(function(D0) {
    var trav = _protocol(ds, idcs, Traversal(ds, idcs, [D0]));

    if (best == null)
      best = trav;
    else {
      for (var i = 0; ; ++i) {
        var next = trav.get(i);
        if (next == undefined)
          break;

        var d = next - best.get(i);
        if (d != 0) {
          if (d < 0)
            best = trav;
          break;
        }
      }
    }
  });

  return I.List(best.content());
};

module.exports = {
  isMinimal         : isMinimal,
  typePartition     : typePartition,
  traversal         : traversal,
  orbitReps         : orbitReps,
  isConnected       : isConnected,
  orbit             : orbit,
  partialOrientation: partialOrientation,
  isLoopless        : isLoopless,
  isOriented        : isOriented,
  isWeaklyOriented  : isWeaklyOriented,
  invariant         : invariant
};

if (require.main == module) {
  var test = function test(ds) {
    console.log('ds = '+ds);
    console.log();

    console.log('    symbol is '+(isConnected(ds) ? '' : 'not ')+'connected.');
    console.log('    symbol is '+(isMinimal(ds) ? '' : 'not ')+'minimal.');
    console.log('    symbol is '+(isLoopless(ds) ? '' : 'not ')+'loopless.');
    console.log('    symbol is '+(isOriented(ds) ? '' : 'not ')+'oriented.');
    console.log('    symbol is '+(isWeaklyOriented(ds) ? '' : 'not ')
                +'weakly oriented.');
    console.log('    type partition: '+typePartition(ds));
    var trav = traversal(ds, ds.indices(), ds.elements());
    console.log('    traversal: ' + trav);
    console.log('    invariant: ' + invariant(ds));
    console.log();

    console.log('    0,1 orbit reps: '+orbitReps(ds, [0, 1]));
    console.log('    1,2 orbit reps: '+orbitReps(ds, [1, 2]));
    console.log('    0,2 orbit reps: '+orbitReps(ds, [0, 2]));
    console.log();

    console.log('    0,1 orbit of 1: '+orbit(ds, [0, 1], 1));
    console.log('    1,2 orbit of 1: '+orbit(ds, [1, 2], 1));
    console.log('    0,2 orbit of 1: '+orbit(ds, [0, 2], 1));
    console.log();

    console.log('    partial orientation: '+partialOrientation(ds));
    console.log();
    console.log();
  };

  test(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>'));

  test(DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(DS.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(DS.parse('<1.1:6:4 6 5,5 4 6,4 6 5:3,6>'));
}
<|endoftext|>


<commit_before>// # Bootup
// This file needs serious love & refactoring

// Module dependencies
var express     = require('express'),
    hbs         = require('express-hbs'),
    compress    = require('compression'),
    uuid        = require('node-uuid'),
    Promise     = require('bluebird'),
    i18n        = require('./i18n'),
    api         = require('./api'),
    config      = require('./config'),
    errors      = require('./errors'),
    helpers     = require('./helpers'),
    middleware  = require('./middleware'),
    migrations  = require('./data/migration'),
    models      = require('./models'),
    permissions = require('./permissions'),
    apps        = require('./apps'),
    sitemap     = require('./data/xml/sitemap'),
    xmlrpc      = require('./data/xml/xmlrpc'),
    slack       = require('./data/slack'),
    GhostServer = require('./ghost-server'),
    validateThemes = require('./utils/validate-themes'),

    dbHash;

function initDbHashAndFirstRun() {
    return api.settings.read({key: 'dbHash', context: {internal: true}}).then(function (response) {
        var hash = response.settings[0].value,
            initHash;

        dbHash = hash;

        if (dbHash === null) {
            initHash = uuid.v4();
            return api.settings.edit({settings: [{key: 'dbHash', value: initHash}]}, {context: {internal: true}})
                .then(function (response) {
                    dbHash = response.settings[0].value;
                    return dbHash;
                    // Use `then` here to do 'first run' actions
                });
        }

        return dbHash;
    });
}

// ## Initialise Ghost
// Sets up the express server instances, runs init on a bunch of stuff, configures views, helpers, routes and more
// Finally it returns an instance of GhostServer
function init(options) {
    // Get reference to an express app instance.
    var blogApp = express(),
        adminApp = express();

    // ### Initialisation
    // The server and its dependencies require a populated config
    // It returns a promise that is resolved when the application
    // has finished starting up.

    // Initialize Internationalization
    i18n.init();

    // Load our config.js file from the local file system.
    return config.load(options.config).then(function () {
        return config.checkDeprecated();
    }).then(function () {
        // Initialise the models
        models.init();
    }).then(function () {
        // Initialize migrations
        return migrations.init();
    }).then(function () {
        // Populate any missing default settings
        return models.Settings.populateDefaults();
    }).then(function () {
        // Initialize the settings cache
        return api.init();
    }).then(function () {
        // Initialize the permissions actions and objects
        // NOTE: Must be done before initDbHashAndFirstRun calls
        return permissions.init();
    }).then(function () {
        return Promise.join(
            // Check for or initialise a dbHash.
            initDbHashAndFirstRun(),
            // Initialize apps
            apps.init(),
            // Initialize sitemaps
            sitemap.init(),
            // Initialize xmrpc ping
            xmlrpc.init(),
            // Initialize slack ping
            slack.init()
        );
    }).then(function () {
        var adminHbs = hbs.create();

        // ##Configuration

        // return the correct mime type for woff files
        express.static.mime.define({'application/font-woff': ['woff']});

        // enabled gzip compression by default
        if (config.server.compress !== false) {
            blogApp.use(compress());
        }

        // ## View engine
        // set the view engine
        blogApp.set('view engine', 'hbs');

        // Create a hbs instance for admin and init view engine
        adminApp.set('view engine', 'hbs');
        adminApp.engine('hbs', adminHbs.express3({}));

        // Load helpers
        helpers.loadCoreHelpers(adminHbs);

        // ## Middleware and Routing
        middleware(blogApp, adminApp);

        // Log all theme errors and warnings
        validateThemes(config.paths.themePath)
            .catch(function (result) {
                // TODO: change `result` to something better
                result.errors.forEach(function (err) {
                    errors.logError(err.message, err.context, err.help);
                });

                result.warnings.forEach(function (warn) {
                    errors.logWarn(warn.message, warn.context, warn.help);
                });
            });

        return new GhostServer(blogApp);
    });
}

module.exports = init;
<commit_msg>Removing very old fix for woff files<commit_after>// # Bootup
// This file needs serious love & refactoring

// Module dependencies
var express     = require('express'),
    hbs         = require('express-hbs'),
    compress    = require('compression'),
    uuid        = require('node-uuid'),
    Promise     = require('bluebird'),
    i18n        = require('./i18n'),
    api         = require('./api'),
    config      = require('./config'),
    errors      = require('./errors'),
    helpers     = require('./helpers'),
    middleware  = require('./middleware'),
    migrations  = require('./data/migration'),
    models      = require('./models'),
    permissions = require('./permissions'),
    apps        = require('./apps'),
    sitemap     = require('./data/xml/sitemap'),
    xmlrpc      = require('./data/xml/xmlrpc'),
    slack       = require('./data/slack'),
    GhostServer = require('./ghost-server'),
    validateThemes = require('./utils/validate-themes'),

    dbHash;

function initDbHashAndFirstRun() {
    return api.settings.read({key: 'dbHash', context: {internal: true}}).then(function (response) {
        var hash = response.settings[0].value,
            initHash;

        dbHash = hash;

        if (dbHash === null) {
            initHash = uuid.v4();
            return api.settings.edit({settings: [{key: 'dbHash', value: initHash}]}, {context: {internal: true}})
                .then(function (response) {
                    dbHash = response.settings[0].value;
                    return dbHash;
                    // Use `then` here to do 'first run' actions
                });
        }

        return dbHash;
    });
}

// ## Initialise Ghost
// Sets up the express server instances, runs init on a bunch of stuff, configures views, helpers, routes and more
// Finally it returns an instance of GhostServer
function init(options) {
    // Get reference to an express app instance.
    var blogApp = express(),
        adminApp = express();

    // ### Initialisation
    // The server and its dependencies require a populated config
    // It returns a promise that is resolved when the application
    // has finished starting up.

    // Initialize Internationalization
    i18n.init();

    // Load our config.js file from the local file system.
    return config.load(options.config).then(function () {
        return config.checkDeprecated();
    }).then(function () {
        // Initialise the models
        models.init();
    }).then(function () {
        // Initialize migrations
        return migrations.init();
    }).then(function () {
        // Populate any missing default settings
        return models.Settings.populateDefaults();
    }).then(function () {
        // Initialize the settings cache
        return api.init();
    }).then(function () {
        // Initialize the permissions actions and objects
        // NOTE: Must be done before initDbHashAndFirstRun calls
        return permissions.init();
    }).then(function () {
        return Promise.join(
            // Check for or initialise a dbHash.
            initDbHashAndFirstRun(),
            // Initialize apps
            apps.init(),
            // Initialize sitemaps
            sitemap.init(),
            // Initialize xmrpc ping
            xmlrpc.init(),
            // Initialize slack ping
            slack.init()
        );
    }).then(function () {
        var adminHbs = hbs.create();

        // ##Configuration

        // enabled gzip compression by default
        if (config.server.compress !== false) {
            blogApp.use(compress());
        }

        // ## View engine
        // set the view engine
        blogApp.set('view engine', 'hbs');

        // Create a hbs instance for admin and init view engine
        adminApp.set('view engine', 'hbs');
        adminApp.engine('hbs', adminHbs.express3({}));

        // Load helpers
        helpers.loadCoreHelpers(adminHbs);

        // ## Middleware and Routing
        middleware(blogApp, adminApp);

        // Log all theme errors and warnings
        validateThemes(config.paths.themePath)
            .catch(function (result) {
                // TODO: change `result` to something better
                result.errors.forEach(function (err) {
                    errors.logError(err.message, err.context, err.help);
                });

                result.warnings.forEach(function (warn) {
                    errors.logWarn(warn.message, warn.context, warn.help);
                });
            });

        return new GhostServer(blogApp);
    });
}

module.exports = init;
<|endoftext|>


<commit_before>'use strict';

var I = require('immutable');
var DS = require('./delaney');
var Partition = require('../common/partition');

var _fold = function _fold(partition, a, b, matchP, spreadFn) {
  var p = partition;
  var q = I.List().push(I.List([a, b]));

  while (!q.isEmpty()) {
    var _tmp = q.first();
    var x = _tmp.get(0);
    var y = _tmp.get(1);

    q = q.rest();

    if (!matchP(x, y))
      return;
    else if (p.get(x) == p.get(y))
      continue;
    else {
      p = p.union(x, y);
      q = q.concat(I.List(spreadFn(x, y)).map(I.List));
    }
  }

  return p;
};

var _typeMap = function _typeMap(ds) {
  var base = I.Map(ds.elements().map(function(D) {
    return [D, I.List()];
  }));
  var idcs = DS.indices(ds);

  return base.withMutations(function(map) {
    idcs.zip(idcs.rest()).forEach(function(p) {
      var i = p[0];
      var j = p[1];

      DS.orbitReps2(ds, i, j).forEach(function(D) {
        var m = DS.m(ds, i, j, D);
        DS.orbit2(ds, i, j, D).forEach(function(E) {
          map.set(E, map.get(E).push(m));
        });
      });
    });
  });
};

var isMinimal = function isMinimal(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().every(function(D) {
    return _fold(Partition(), D0, D, match, spread) === undefined;
  });
};

var typePartition = function typePartition(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().reduce(
    function(p, D) {
      return _fold(p, D0, D, match, spread) || p;
    },
    Partition()
  );
};

module.exports = {
  isMinimal: isMinimal,
  typePartition: typePartition
};

if (require.main == module) {
  var ds = DS.parse('<1.1:3:1 2 3,1 3,2 3:4 4,3>');
  var tm = _typeMap(ds);

  console.log(tm);
  console.log('' +
              _fold(Partition(), 1, 2,
                    function(D, E) {
                      return tm.get(D).equals(tm.get(E));
                    },
                    function(D, E) {
                      return ds.indices().map(function(i) {
                        return [ds.s(i, D), ds.s(i, E)];
                      });
                    }));

  var test = function(ds) {
    console.log(ds+' is '+(isMinimal(ds) ? '' : 'not ')+'minimal.');
    console.log('    '+typePartition(ds));
  };

  test(ds);
  test(DS.withBranchings(ds, 0, [[2, 4]]));
  test(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>'));
}
<commit_msg>implement D-symbol traversal<commit_after>'use strict';

var I = require('immutable');
var DS = require('./delaney');
var Partition = require('../common/partition');
var seq = require('../common/lazyseq');

var _fold = function _fold(partition, a, b, matchP, spreadFn) {
  var p = partition;
  var q = I.List().push(I.List([a, b]));

  while (!q.isEmpty()) {
    var _tmp = q.first();
    var x = _tmp.get(0);
    var y = _tmp.get(1);

    q = q.rest();

    if (!matchP(x, y))
      return;
    else if (p.get(x) == p.get(y))
      continue;
    else {
      p = p.union(x, y);
      q = q.concat(I.List(spreadFn(x, y)).map(I.List));
    }
  }

  return p;
};

var _typeMap = function _typeMap(ds) {
  var base = I.Map(ds.elements().map(function(D) {
    return [D, I.List()];
  }));
  var idcs = DS.indices(ds);

  return base.withMutations(function(map) {
    idcs.zip(idcs.rest()).forEach(function(p) {
      var i = p[0];
      var j = p[1];

      DS.orbitReps2(ds, i, j).forEach(function(D) {
        var m = DS.m(ds, i, j, D);
        DS.orbit2(ds, i, j, D).forEach(function(E) {
          map.set(E, map.get(E).push(m));
        });
      });
    });
  });
};

var isMinimal = function isMinimal(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().every(function(D) {
    return _fold(Partition(), D0, D, match, spread) === undefined;
  });
};

var typePartition = function typePartition(ds) {
  var D0 = ds.elements().first();
  var tm = _typeMap(ds);

  var match = function(D, E) { return tm.get(D).equals(tm.get(E)); };
  var spread = function(D, E) {
    return ds.indices().map(function(i) {
      return [ds.s(i, D), ds.s(i, E)];
    });
  };

  return ds.elements().rest().reduce(
    function(p, D) {
      return _fold(p, D0, D, match, spread) || p;
    },
    Partition()
  );
};

var traversal = function traversal(ds, indices, seeds) {
  var root;
  var todo = I.List(indices)
    .map(function(i) { return [i, I.List()]; })
    .push([root, I.List(seeds)]);
  var seen = I.Set();

  var trim = function trim(entry, seen) {
    return [
      entry[0],
      entry[1].skipWhile(function(x) {
        return seen.contains(I.List([x, entry[0]]));
      })
    ];
  };

  var extend = function extend(entry, D) {
    return [
      entry[0],
      (entry[0] == root) ? entry[1] : entry[1].push(D)
    ];
  };

  var step = function step(todo, seen) {
    var remaining = todo.map(function(entry) { return trim(entry, seen); });
    var next = remaining.filter(function(entry) {
      return !entry[1].isEmpty();
    }).first();

    if (next) {
      var i = next[0];
      var D = next[1].first();
      var Di = (i == root) ? D : ds.s(i, D);

      var t = (D
               ? remaining.map(function(entry) { return extend(entry, Di); })
               : remaining);
      var s = seen.concat([I.List([Di,root]), I.List([D,i]), I.List([Di,i])]);

      return seq.seq([D, i, Di], function() { return step(t, s); });
    }
  };

  return step(todo, seen);
};

module.exports = {
  isMinimal    : isMinimal,
  typePartition: typePartition,
  traversal    : traversal
};

if (require.main == module) {
  var ds = DS.parse('<1.1:3:1 2 3,1 3,2 3:4 4,3>');
  var tm = _typeMap(ds);

  console.log(tm);
  console.log('' +
              _fold(Partition(), 1, 2,
                    function(D, E) {
                      return tm.get(D).equals(tm.get(E));
                    },
                    function(D, E) {
                      return ds.indices().map(function(i) {
                        return [ds.s(i, D), ds.s(i, E)];
                      });
                    }));

  var test = function(ds) {
    console.log(ds+' is '+(isMinimal(ds) ? '' : 'not ')+'minimal.');
    console.log('    '+typePartition(ds));
  };

  test(ds);
  test(DS.withBranchings(ds, 0, [[2, 4]]));
  test(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>'));

  console.log('' + traversal(ds, ds.indices(), ds.elements()));
}
<|endoftext|>


<commit_before>/**
 * make sure overrides get's called first!
 * - keeping the overrides import here works for installing Ghost as npm!
 *
 * the call order is the following:
 * - root index requires core module
 * - core index requires server
 * - overrides is the first package to load
 */
require('./overrides');

const debug = require('ghost-ignition').debug('boot:init');
const Promise = require('bluebird');
const config = require('./config');
const common = require('./lib/common');
const migrator = require('./data/db/migrator');
const urlService = require('./services/url');
let parentApp;

function initialiseServices() {
    // CASE: When Ghost is ready with bootstrapping (db migrations etc.), we can trigger the router creation.
    //       Reason is that the routers access the routes.yaml, which shouldn't and doesn't have to be validated to
    //       start Ghost in maintenance mode.
    const routing = require('./services/routing');
    routing.bootstrap.start();

    const permissions = require('./services/permissions'),
        auth = require('./services/auth'),
        apps = require('./services/apps'),
        xmlrpc = require('./services/xmlrpc'),
        slack = require('./services/slack'),
        webhooks = require('./services/webhooks'),
        scheduling = require('./adapters/scheduling');

    debug('`initialiseServices` Start...');

    return Promise.join(
        // Initialize the permissions actions and objects
        permissions.init(),
        xmlrpc.listen(),
        slack.listen(),
        webhooks.listen(),
        apps.init(),
        scheduling.init({
            schedulerUrl: config.get('scheduling').schedulerUrl,
            active: config.get('scheduling').active,
            apiUrl: urlService.utils.urlFor('api', {version: 'v0.1', versionType: 'content'}, true),
            internalPath: config.get('paths').internalSchedulingPath,
            contentPath: config.getContentPath('scheduling')
        })
    ).then(function () {
        debug('XMLRPC, Slack, Webhooks, Apps, Scheduling, Permissions done');

        // Initialise analytics events
        if (config.get('segment:key')) {
            require('./analytics-events').init();
        }
    }).then(function () {
        parentApp.use(auth.init());
        debug('Auth done');

        debug('...`initialiseServices` End');
    });
}

/**
 * - initialise models
 * - initialise i18n
 * - load all settings into settings cache (almost every component makes use of this cache)
 * - load active theme
 * - create our express apps (site, admin, api)
 * - start the ghost server
 * - enable maintenance mode if migrations are missing
 */
const minimalRequiredSetupToStartGhost = (dbState) => {
    const settings = require('./services/settings');
    const models = require('./models');
    const themes = require('./services/themes');
    const GhostServer = require('./ghost-server');

    let ghostServer;

    // Initialize Ghost core internationalization
    common.i18n.init();
    debug('Default i18n done for core');

    models.init();
    debug('Models done');

    return settings.init()
        .then(() => {
            debug('Settings done');
            return themes.init();
        })
        .then(() => {
            debug('Themes done');

            parentApp = require('./web/parent-app')();
            debug('Express Apps done');

            return new GhostServer(parentApp);
        })
        .then((_ghostServer) => {
            ghostServer = _ghostServer;

            // CASE: all good or db was just initialised
            if (dbState === 1 || dbState === 2) {
                common.events.emit('db.ready');

                return initialiseServices()
                    .then(() => {
                        return ghostServer;
                    });
            }

            // CASE: migrations required, put blog into maintenance mode
            if (dbState === 4) {
                common.logging.info('Blog is in maintenance mode.');

                config.set('maintenance:enabled', true);

                migrator.migrate()
                    .then(() => {
                        common.events.emit('db.ready');
                        return initialiseServices();
                    })
                    .then(() => {
                        config.set('maintenance:enabled', false);
                        common.logging.info('Blog is out of maintenance mode.');
                        return GhostServer.announceServerStart();
                    })
                    .catch((err) => {
                        return GhostServer.announceServerStopped(err)
                            .finally(() => {
                                common.logging.error(err);
                                setTimeout(() => {
                                    process.exit(-1);
                                }, 100);
                            });
                    });

                return ghostServer;
            }
        });
};

/**
 * Connect to database.
 * Check db state.
 */
const isDatabaseInitialisationRequired = () => {
    const db = require('./data/db/connection');
    let dbState;

    return migrator.getState()
        .then((state) => {
            dbState = state;

            // CASE: db initialisation required, wait till finished
            if (dbState === 2) {
                return migrator.dbInit();
            }

            // CASE: is db incompatible? e.g. you can't connect a 0.11 database with Ghost 1.0 or 2.0
            if (dbState === 3) {
                return migrator.isDbCompatible(db)
                    .then(() => {
                        dbState = 2;
                        return migrator.dbInit();
                    });
            }
        })
        .then(() => {
            return minimalRequiredSetupToStartGhost(dbState);
        });
};

module.exports = isDatabaseInitialisationRequired;
<commit_msg>Added comment about future scheduler migration<commit_after>/**
 * make sure overrides get's called first!
 * - keeping the overrides import here works for installing Ghost as npm!
 *
 * the call order is the following:
 * - root index requires core module
 * - core index requires server
 * - overrides is the first package to load
 */
require('./overrides');

const debug = require('ghost-ignition').debug('boot:init');
const Promise = require('bluebird');
const config = require('./config');
const common = require('./lib/common');
const migrator = require('./data/db/migrator');
const urlService = require('./services/url');
let parentApp;

function initialiseServices() {
    // CASE: When Ghost is ready with bootstrapping (db migrations etc.), we can trigger the router creation.
    //       Reason is that the routers access the routes.yaml, which shouldn't and doesn't have to be validated to
    //       start Ghost in maintenance mode.
    const routing = require('./services/routing');
    routing.bootstrap.start();

    const permissions = require('./services/permissions'),
        auth = require('./services/auth'),
        apps = require('./services/apps'),
        xmlrpc = require('./services/xmlrpc'),
        slack = require('./services/slack'),
        webhooks = require('./services/webhooks'),
        scheduling = require('./adapters/scheduling');

    debug('`initialiseServices` Start...');

    return Promise.join(
        // Initialize the permissions actions and objects
        permissions.init(),
        xmlrpc.listen(),
        slack.listen(),
        webhooks.listen(),
        apps.init(),
        scheduling.init({
            schedulerUrl: config.get('scheduling').schedulerUrl,
            active: config.get('scheduling').active,
            // NOTE: When changing API version need to consider how to migrate custom scheduling adapters
            //       that rely on URL to lookup persisted scheduled records (jobs, etc.). Ref: https://github.com/TryGhost/Ghost/pull/10726#issuecomment-489557162
            apiUrl: urlService.utils.urlFor('api', {version: 'v0.1', versionType: 'content'}, true),
            internalPath: config.get('paths').internalSchedulingPath,
            contentPath: config.getContentPath('scheduling')
        })
    ).then(function () {
        debug('XMLRPC, Slack, Webhooks, Apps, Scheduling, Permissions done');

        // Initialise analytics events
        if (config.get('segment:key')) {
            require('./analytics-events').init();
        }
    }).then(function () {
        parentApp.use(auth.init());
        debug('Auth done');

        debug('...`initialiseServices` End');
    });
}

/**
 * - initialise models
 * - initialise i18n
 * - load all settings into settings cache (almost every component makes use of this cache)
 * - load active theme
 * - create our express apps (site, admin, api)
 * - start the ghost server
 * - enable maintenance mode if migrations are missing
 */
const minimalRequiredSetupToStartGhost = (dbState) => {
    const settings = require('./services/settings');
    const models = require('./models');
    const themes = require('./services/themes');
    const GhostServer = require('./ghost-server');

    let ghostServer;

    // Initialize Ghost core internationalization
    common.i18n.init();
    debug('Default i18n done for core');

    models.init();
    debug('Models done');

    return settings.init()
        .then(() => {
            debug('Settings done');
            return themes.init();
        })
        .then(() => {
            debug('Themes done');

            parentApp = require('./web/parent-app')();
            debug('Express Apps done');

            return new GhostServer(parentApp);
        })
        .then((_ghostServer) => {
            ghostServer = _ghostServer;

            // CASE: all good or db was just initialised
            if (dbState === 1 || dbState === 2) {
                common.events.emit('db.ready');

                return initialiseServices()
                    .then(() => {
                        return ghostServer;
                    });
            }

            // CASE: migrations required, put blog into maintenance mode
            if (dbState === 4) {
                common.logging.info('Blog is in maintenance mode.');

                config.set('maintenance:enabled', true);

                migrator.migrate()
                    .then(() => {
                        common.events.emit('db.ready');
                        return initialiseServices();
                    })
                    .then(() => {
                        config.set('maintenance:enabled', false);
                        common.logging.info('Blog is out of maintenance mode.');
                        return GhostServer.announceServerStart();
                    })
                    .catch((err) => {
                        return GhostServer.announceServerStopped(err)
                            .finally(() => {
                                common.logging.error(err);
                                setTimeout(() => {
                                    process.exit(-1);
                                }, 100);
                            });
                    });

                return ghostServer;
            }
        });
};

/**
 * Connect to database.
 * Check db state.
 */
const isDatabaseInitialisationRequired = () => {
    const db = require('./data/db/connection');
    let dbState;

    return migrator.getState()
        .then((state) => {
            dbState = state;

            // CASE: db initialisation required, wait till finished
            if (dbState === 2) {
                return migrator.dbInit();
            }

            // CASE: is db incompatible? e.g. you can't connect a 0.11 database with Ghost 1.0 or 2.0
            if (dbState === 3) {
                return migrator.isDbCompatible(db)
                    .then(() => {
                        dbState = 2;
                        return migrator.dbInit();
                    });
            }
        })
        .then(() => {
            return minimalRequiredSetupToStartGhost(dbState);
        });
};

module.exports = isDatabaseInitialisationRequired;
<|endoftext|>