<commit_before>import React from 'react';
import moment from 'moment';
import { weeks } from './utils';

const tableStyle = {display: 'table'};
const captionStyle = {display: 'table-caption', textAlign: 'center'};
const rowStyle = {display: 'table-row'};
const cellStyle = {display: 'table-cell', textAlign: 'center'};

const DayPicker = React.createClass({

  propTypes: {

    initialMonth: React.PropTypes.object, // default is current month
    modifiers: React.PropTypes.object,

    onDayTouchTap: React.PropTypes.func, // requires react-tap-event-plugin enabled
    onDayMouseEnter: React.PropTypes.func,
    onDayMouseLeave: React.PropTypes.func
  },

  getDefaultProps() {
    return { initialMonth: moment() };
  },

  componentWillReceiveProps(nextProps) {
    this.setState({month: nextProps.initialMonth});
  },

  componentDidUpdate(prevProps, prevState) {
    if(this.state.focus)
      this.refs['d'+this.state.focus].getDOMNode().focus();
  },

  getInitialState() {
    return { month: this.props.initialMonth.clone(), focus: null };
  },

  handleDayTouchTap(day, modifiers, e) {
    this.props.onDayTouchTap && this.props.onDayTouchTap(day, modifiers, e);
  },

  handleDayKeyUp(day, modifiers, e) {
    if (e.keyCode === 13 || e.keyCode === 32)
      this.props.onDayTouchTap && this.props.onDayTouchTap(day, modifiers, e);
  },

  handleDayMouseEnter(day, modifiers, e) {
    this.props.onDayMouseEnter && this.props.onDayMouseEnter(day, modifiers, e);
  },

  handleDayMouseLeave(day, modifiers, e) {
    this.props.onDayMouseLeave && this.props.onDayMouseLeave(day, modifiers, e);
  },

  handleDayFocus(day, modifiers, e){
    this.setState({focus: day.dayOfYear()}, () => {
      this.props.onDayFocus && this.props.onDayFocus(e);
    });
  },

  handleDayBlur(day, modifiers, e){
    this.setState({focus: null});
  },

  handleNextTouchTap(e) {
    const nextMonth = this.state.month.add(1, 'month');
    this.setState({month: nextMonth});
  },

  handlePrevTouchTap(e) {
    const prevMonth = this.state.month.subtract(1, 'month');
    this.setState({month: prevMonth});
  },

  getDayModifiers(day) {
    var dayModifiers = [];
    if (!this.props.modifiers) return dayModifiers;
    const modifiers = this.props.modifiers;
    for (let modifier in modifiers) {
      var func = modifiers[modifier];
      if (func(day)) dayModifiers.push(modifier);
    }
    return dayModifiers;
  },

  render() {
    return (

        { this.renderToolbar() }
        { this.renderWeekHeader() }
        { this.renderWeeks() }

    );
  },

  renderToolbar() {
    return (

        { this.renderToolbarButton('left') }
        { this.state.month.format('MMMM YYYY') }
        { this.renderToolbarButton('right') }

    );
  },

  renderToolbarButton(position) {
    const className = `daypicker__toolbar-button daypicker__toolbar-button--${position}`;
    const handler = position === 'left' ?  this.handlePrevTouchTap :  this.handleNextTouchTap;

    return <button ref={"btn-"+position} className={className} style={{float: position}} onTouchTap={handler} />;
  },

  renderWeeks() {
    return weeks(this.state.month).map((week, i) => {
      return (

          { this.renderDays(week) }

      );
    });
  },

  renderWeekHeader() {
    var header = [];
    for (let i = 0; i < 7; i++) {
      header.push(

          { moment().weekday(i).format('dd') }

      )
    }
    return
{ header }
;
  },

  renderDays(week) {
    const firstDay = week[0];
    const lastDay = week[week.length-1];

    var days = week.map(day => this.renderDay(day));

    // days belonging to the previous month
    for (let i = 0; i < firstDay.weekday(); i++) {
      var prevDay = firstDay.clone().subtract(i+1, 'day');
      days.unshift(this.renderDay(prevDay, true));
    }

    // days belonging to the next month
    for (let j = lastDay.weekday() + 1, count = 1; j < 7; j++, count++) {
      var nextDay = lastDay.clone().add(count, 'day');
      days.push(this.renderDay(nextDay, true));
    }

    return days;
  },

  renderDay(day, otherMonth) {
    const modifiers = this.getDayModifiers(day);

    var className = 'daypicker__day';
    if (otherMonth) className += ' daypicker__day--other-month';
    className += modifiers.map((mod) => { return ' daypicker__day--' + mod }).join('');

    return (
      <div tabIndex="0"
        ref={"d" + day.dayOfYear()}
        key={"d" + day.dayOfYear()}
        className={className}
        style={cellStyle}
        onMouseEnter={this.handleDayMouseEnter.bind(this, day, modifiers)}
        onMouseLeave={this.handleDayMouseLeave.bind(this, day, modifiers)}
        onKeyUp={this.handleDayKeyUp.bind(this, day, modifiers)}
        onTouchTap={this.handleDayTouchTap.bind(this, day, modifiers)}
        onBlur={this.handleDayBlur.bind(this, day, modifiers)}
        onFocus={this.handleDayFocus.bind(this, day, modifiers)}>
        { day.format('D') }

    );
  }

});

export default DayPicker;<commit_msg>Code cleanup<commit_after>import React from 'react';
import moment from 'moment';
import { weeks } from './utils';

const tableStyle = {display: 'table'};
const captionStyle = {display: 'table-caption', textAlign: 'center'};
const rowStyle = {display: 'table-row'};
const cellStyle = {display: 'table-cell', textAlign: 'center'};

const DayPicker = React.createClass({

  propTypes: {

    initialMonth: React.PropTypes.object, // default is current month
    modifiers: React.PropTypes.object,

    onDayTouchTap: React.PropTypes.func, // requires react-tap-event-plugin enabled
    onDayMouseEnter: React.PropTypes.func,
    onDayMouseLeave: React.PropTypes.func
  },

  getDefaultProps() {
    return { initialMonth: moment() };
  },

  componentWillReceiveProps(nextProps) {
    this.setState({ month: nextProps.initialMonth });
  },

  componentDidUpdate(prevProps, prevState) {
    if (this.state.focus) this.refs['d'+this.state.focus].getDOMNode().focus();
  },

  getInitialState() {
    return { month: this.props.initialMonth.clone(), focus: null };
  },

  handleDayTouchTap(day, modifiers, e) {
    this.props.onDayTouchTap && this.props.onDayTouchTap(day, modifiers, e);
  },

  handleDayKeyUp(day, modifiers, e) {
    if (e.keyCode === 13 || e.keyCode === 32)
      this.props.onDayTouchTap && this.props.onDayTouchTap(day, modifiers, e);
  },

  handleDayMouseEnter(day, modifiers, e) {
    this.props.onDayMouseEnter && this.props.onDayMouseEnter(day, modifiers, e);
  },

  handleDayMouseLeave(day, modifiers, e) {
    this.props.onDayMouseLeave && this.props.onDayMouseLeave(day, modifiers, e);
  },

  handleDayFocus(day, modifiers, e){
    this.setState({ focus: day.dayOfYear() });
  },

  handleDayBlur(day, modifiers, e){
    this.setState({ focus: null });
  },

  handleNextTouchTap(e) {
    this.setState({ month: this.state.month.add(1, 'month') });
  },

  handlePrevTouchTap(e) {
    this.setState({ month: this.state.month.subtract(1, 'month') });
  },

  getModifiersForDay(day) {
    var dayModifiers = [];
    if (this.props.modifiers) {
      const modifiers = this.props.modifiers;
      for (let modifier in modifiers) {
        var func = modifiers[modifier];
        if (func(day)) dayModifiers.push(modifier);
      }
    }
    return dayModifiers;
  },

  render() {
    return (

        { this.renderToolbar() }
        { this.renderWeekHeader() }
        { this.renderWeeks() }

    );
  },

  renderToolbar() {
    return (

        { this.renderToolbarButton('left') }
        { this.state.month.format('MMMM YYYY') }
        { this.renderToolbarButton('right') }

    );
  },

  renderToolbarButton(position) {
    const className = `daypicker__toolbar-button daypicker__toolbar-button--${position}`;
    const handler = position === 'left' ?  this.handlePrevTouchTap :  this.handleNextTouchTap;

    return <button ref={"btn-"+position} className={className} style={{float: position}} onTouchTap={handler} />;
  },

  renderWeeks() {
    return weeks(this.state.month).map((week, i) => {
      return (

          { this.renderDays(week) }

      );
    });
  },

  renderWeekHeader() {
    var header = [];
    for (let i = 0; i < 7; i++) {
      header.push(

          { moment().weekday(i).format('dd') }

      )
    }
    return
{ header }
;
  },

  renderDays(week) {
    const firstDay = week[0];
    const lastDay = week[week.length-1];

    var days = week.map(day => this.renderDay(day));

    // days belonging to the previous month
    for (let i = 0; i < firstDay.weekday(); i++) {
      var prevDay = firstDay.clone().subtract(i+1, 'day');
      days.unshift(this.renderDay(prevDay, true));
    }

    // days belonging to the next month
    for (let j = lastDay.weekday() + 1, count = 1; j < 7; j++, count++) {
      var nextDay = lastDay.clone().add(count, 'day');
      days.push(this.renderDay(nextDay, true));
    }

    return days;
  },

  renderDay(day, otherMonth) {
    const modifiers = this.getModifiersForDay(day);

    var className = 'daypicker__day';
    if (otherMonth) className += ' daypicker__day--other-month';
    className += modifiers.map((mod) => { return ' daypicker__day--' + mod }).join('');

    return (
      <div tabIndex="0"
        ref={"d" + day.dayOfYear()}
        key={"d" + day.dayOfYear()}
        className={className}
        style={cellStyle}
        onMouseEnter={this.handleDayMouseEnter.bind(this, day, modifiers)}
        onMouseLeave={this.handleDayMouseLeave.bind(this, day, modifiers)}
        onKeyUp={this.handleDayKeyUp.bind(this, day, modifiers)}
        onTouchTap={this.handleDayTouchTap.bind(this, day, modifiers)}
        onBlur={this.handleDayBlur.bind(this, day, modifiers)}
        onFocus={this.handleDayFocus.bind(this, day, modifiers)}>
        { day.format('D') }

    );
  }

});

export default DayPicker;<|endoftext|>


<commit_before>'use strict';

const stat = Promise.promisify(require('fs').stat);
const glob = Promise.promisify(require('glob'));

const log = logger.child({ module: 'util/get-file-size' });

/**
 * Gets the size of a regular file(s).
 *
 * @param {string|array} path The path(s)
 *
 * @return {Promise} A promise that fulfills when done
 */
function fileSize(path) {
    const paths = Array.isArray(path) ? path : [path];

    return Promise.map(paths, (path) => {
        return stat(path)
        .then((stat) => {
            if (!stat.isFile()) {
                throw Object.assign(new Error(`Path ${path} is not a regular file`), { code: 'EDIR' });
            }

            return stat.size;
        })
        // Return 0 if path does not exist
        .catch({ code: 'ENOENT' }, () => 0)
        // Ignore errors of modules that have large nested paths..
        // This was happening in some modules, e.g.: `cordova-plugin-forcetouch`
        .catch({ code: 'ENAMETOOLONG' }, (err) => {
            log.warn({ err }, `ENAMETOOLONG while getting file size of ${path}, returning 0..`);
            return 0;
        });
    }, { concurrency: 50 })
    .then((sizes) => sizes.reduce((sum, size) => sum + size, 0));
}

/**
 * Gets the size of a directory.
 *
 * @param {string} dir The directory path
 *
 * @return {Promise} A promise that fulfills when done
 */
function fileSizeDir(dir) {
    return glob(`${dir}/**/*`, {
        nodir: true,
        dot: true,
        silent: true,   // Do not print warnings
        strict: false,  // Do not crash on the first error
    })
    .then((paths) => fileSize(paths));
}

module.exports = fileSize;
module.exports.dir = fileSizeDir;
<commit_msg>Protect against ELOOP.<commit_after>'use strict';

const stat = Promise.promisify(require('fs').stat);
const glob = Promise.promisify(require('glob'));

const log = logger.child({ module: 'util/get-file-size' });

/**
 * Gets the size of a regular file(s).
 *
 * @param {string|array} path The path(s)
 *
 * @return {Promise} A promise that fulfills when done
 */
function fileSize(path) {
    const paths = Array.isArray(path) ? path : [path];

    return Promise.map(paths, (path) => {
        return stat(path)
        .then((stat) => {
            if (!stat.isFile()) {
                throw Object.assign(new Error(`Path ${path} is not a regular file`), { code: 'EDIR' });
            }

            return stat.size;
        })
        // Return 0 if path does not exist
        .catch({ code: 'ENOENT' }, () => 0)
        // Return 0 if too many symlinks are being followed, e.g.: `condensation`
        .catch({ code: 'ELOOP' }, () => 0)
        // Ignore errors of modules that have large nested paths..
        // This was happening in some modules, e.g.: `cordova-plugin-forcetouch`
        .catch({ code: 'ENAMETOOLONG' }, (err) => {
            log.warn({ err }, `ENAMETOOLONG while getting file size of ${path}, returning 0..`);
            return 0;
        });
    }, { concurrency: 50 })
    .then((sizes) => sizes.reduce((sum, size) => sum + size, 0));
}

/**
 * Gets the size of a directory.
 *
 * @param {string} dir The directory path
 *
 * @return {Promise} A promise that fulfills when done
 */
function fileSizeDir(dir) {
    return glob(`${dir}/**/*`, {
        nodir: true,
        dot: true,
        silent: true,   // Do not print warnings
        strict: false,  // Do not crash on the first error
    })
    .then((paths) => fileSize(paths));
}

module.exports = fileSize;
module.exports.dir = fileSizeDir;
<|endoftext|>


<commit_before>var
  _ = require('lodash'),
  q = require('q'),
  async = require('async'),
  vm = require('vm'),
  BadRequestError = require('../../errors/badRequestError'),
  RequestObject = require('../requestObject'),
  InternalError = require('../../errors/internalError'),
  Sandbox = require('../../sandbox'),
  internalIndex = require('rc')('kuzzle').internalIndex;

function Role () {
  this.indexes = {};
  this.closures = {};
}

function doesIndexExist(requestObject, indexes) {
  return indexes && indexes[requestObject.index] !== undefined;
}

function doesCollectionExist(requestObject, indexes) {
  return indexes && _.contains(indexes[requestObject.index], requestObject.collection);
}

/**
 * @param requestObject
 * @param context - user's context
 * @param indexes - list of existing indexes/collections
 * @param {Kuzzle} kuzzle
 *
 * @returns {Promise}
 */
Role.prototype.isActionAllowed = function (requestObject, context, indexes, kuzzle) {
  var
    indexRights,
    collectionRights,
    controllerRights,
    actionRights,
    path = [];

  if (this.indexes === undefined) {
    return q(false);
  }

  /*
   Security controller's routes are only applicable on the internal index.
   Therefore, the "index" argument is not expected in the request object.
    */
  if (requestObject.controller === 'security' && !this.indexes[internalIndex]) {
    return q(false);
  }

  if (this.indexes[requestObject.index] !== undefined) {
    indexRights = this.indexes[requestObject.index];
    path.push(requestObject.index);
  }
  // Wildcards doesn't resolve the internal index. The internal index must always be set explicitly
  else if (this.indexes['*'] !== undefined && internalIndex !== requestObject.index) {
    indexRights = this.indexes['*'];
    path.push('*');
  }
  else {
    return q(false);
  }

  if (requestObject.action === 'createIndex' &&
    this.indexes._canCreate !== undefined &&
    !this.indexes._canCreate) {
    return q(false);
  }

  if (requestObject.action === 'deleteIndex' &&
    indexRights._canDelete !== undefined &&
    !indexRights._canDelete) {
    return q(false);
  }

  if (!doesIndexExist(requestObject, indexes) &&
    this.indexes._canCreate !== undefined &&
    !this.indexes._canCreate &&
    _.contains(['import', 'create', 'createCollection', 'updateMapping', 'createOrReplace'], requestObject.action)) {
    return q(false);
  }

  if (indexRights.collections === undefined) {
    return q(false);
  }

  if (indexRights.collections[requestObject.collection] !== undefined) {
    collectionRights = indexRights.collections[requestObject.collection];
    path.push(requestObject.collection);
  }
  else if (indexRights.collections['*'] !== undefined) {
    collectionRights = indexRights.collections['*'];
    path.push('*');
  }
  else {
    return q(false);
  }
  if (requestObject.action === 'deleteCollection' &&
    collectionRights._canDelete !== undefined &&
    !collectionRights._canDelete) {
    return q(false);
  }

  if (!doesCollectionExist(requestObject, indexes) &&
    indexRights.collections._canCreate !== undefined &&
    !indexRights.collections._canCreate &&
    _.contains(['import', 'create', 'createCollection', 'updateMapping', 'createOrReplace'], requestObject.action)) {
    return q(false);
  }

  if (collectionRights.controllers === undefined) {
    return q(false);
  }

  if (collectionRights.controllers[requestObject.controller] !== undefined) {
    controllerRights = collectionRights.controllers[requestObject.controller];
    path.push(requestObject.controller);
  }
  else if (collectionRights.controllers['*'] !== undefined) {
    controllerRights = collectionRights.controllers['*'];
    path.push('*');
  }
  else {
    return q(false);
  }

  if (controllerRights.actions === undefined) {
    return q(false);
  }

  if (controllerRights.actions[requestObject.action] !== undefined) {
    actionRights = controllerRights.actions[requestObject.action];
    path.push(requestObject.action);
  }
  else if (controllerRights.actions['*'] !== undefined) {
    actionRights = controllerRights.actions['*'];
    path.push('*');
  }
  else {
    return q(false);
  }

  if (_.isBoolean(actionRights)) {
    // the role configuration is set to a static boolean. We return it as-is.
    return q(actionRights);
  }

  if (_.isObject(actionRights)) {
    // the role configuration is an object with a function in attribute `test`. We assume it can be (almost) safely run
    // as it should have previously been validated during creation.

    return executeClosure.call(this, kuzzle, path, actionRights, requestObject, context);
  }

  throw (new InternalError('Invalid rights given for role ' + this._id + '(' + path.join('/') + ') : ' + actionRights));
};

/**
 * @param {Object} context
 * @returns {*}
 */
Role.prototype.validateDefinition = function (context) {
  var
    deferred = q.defer(),
    promises = [],
    args = {};

  if (!_.isObject(this.indexes)) {
    return q.reject(new BadRequestError('The index definition must be an object'));
  }
  if (_.isEmpty(this.indexes)) {
    return q.reject(new BadRequestError('The index definition cannot be empty'));
  }

  Object.keys(this.indexes).every(indexKey => {
    var indexRights = this.indexes[indexKey];

    if (indexKey === '_canCreate') {
      if (!_.isBoolean(indexRights)) {
        deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Must be an boolean'));
        return false;
      }

      return true;
    }

    if (!_.isObject(indexRights)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Must be an object'));
      return false;
    }
    if (_.isEmpty(indexRights)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Cannot be empty'));
      return false;
    }
    if (indexRights.collections === undefined) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute missing'));
      return false;
    }
    if (!_.isObject(indexRights.collections)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute must be an object'));
      return false;
    }
    if (_.isEmpty(indexRights.collections)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute cannot be empty'));
      return false;
    }

    Object.keys(indexRights.collections).every(collectionKey => {
      var collectionRights = indexRights.collections[collectionKey];

      if (collectionKey === '_canCreate') {
        if (!_.isBoolean(collectionRights)) {
          deferred.reject(new BadRequestError('Invalid index definition for ' + [indexKey, collectionKey] + '. Must be an boolean'));
          return false;
        }

        return true;
      }

      if (!_.isObject(collectionRights)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Must be an object'));
        return false;
      }
      if (_.isEmpty(collectionRights)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Cannot be empty'));
        return false;
      }
      if (collectionRights.controllers === undefined) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute missing'));
        return false;
      }
      if (!_.isObject(collectionRights.controllers)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute must be an object'));
        return false;
      }
      if (_.isEmpty(collectionRights.controllers)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute cannot be empty'));
        return false;
      }

      Object.keys(collectionRights.controllers).every(controllerKey => {
        var controllerRights = collectionRights.controllers[controllerKey];

        if (!_.isObject(controllerRights)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. Must be an object'));
          return false;
        }
        if (_.isEmpty(controllerRights)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. Cannot be empty'));
          return false;
        }
        if (controllerRights.actions === undefined) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. `actions` attribute missing'));
          return false;
        }
        if (!_.isObject(controllerRights.actions)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. `actions` attribute must be an object'));
          return false;
        }
        if (_.isEmpty(controllerRights.actions)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. `actions` attribute cannot be empty'));
          return false;
        }

        Object.keys(controllerRights.actions).every(actionKey => {
          var actionRights = controllerRights.actions[actionKey];

          if (!_.isBoolean(actionRights) && !_.isObject(actionRights)) {
            deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey, actionKey] + '. Must be a boolean or an object'));
            return false;
          }

          if (_.isObject(actionRights) && actionRights.test) {

            promises.push((function () {
              var
                sandBox = new Sandbox();

              return sandBox.run({
                sandbox: {
                  requestObject: {
                    index: 'index',
                    collection: 'collection',
                    controller: 'controller',
                    action: 'action',
                    data: {
                      _id: -1,
                      body: {
                        a: true
                      }
                    }
                  },
                  context: {
                    connection: {type: context.connection.type},
                    token: context.token
                  },
                  args: {}
                },
                code: '(function (requestObject, context, args) { ' + actionRights.test + '\nreturn false;\n})(requestObject, context, args)'
              })
              .then(result => {
                var error;

                if (result.result !== undefined && _.isBoolean(result.result)) {
                  return q(result.result);
                }

                error = new BadRequestError('Invalid definition for '+ [indexKey, collectionKey, controllerKey, actionKey] + '. Error executing function');
                error.detail = result.err;

                return q.reject(error);
              });
            })());
          }

          return true;
        });

        return true;
      });

      return true;
    });

    return true;
  });

  if (promises.length > 0) {
    q.all(promises)
      .then(() => {
        deferred.resolve(true);
      })
      .catch(error => {
        deferred.reject(error);
      });
  }
  else {
    deferred.resolve(true);
  }

  return deferred.promise;
};

module.exports = Role;

/**
 * @param {Kuzzle} kuzzle
 * @param {Array} path
 * @param {Object} actionRights
 * @param {RequestObject} requestObject
 * @param {Object} context
 * @returns {Promise}
 */
function executeClosure (kuzzle, path, actionRights, requestObject, context) {
  var
    sandboxContext,
    sandboxScript,
    error,
    args = {},
    argsDefinitions = {};

  if (this.closures[path] === undefined) {
    this.closures[path] = {};
  }

  if (actionRights.args && !this.closures[path].getArgsDefinitions) {
    try {
      /* jshint evil: true */
      this.closures[path].getArgsDefinitions = eval('(function (requestObject) {return ' +
        JSON.stringify(actionRights.args).replace('"$currentId"', 'requestObject.data._id') +
        ';})');
      /* jshint evil: false */
    }
    catch (err) {
      error = new InternalError('Error parsing rights for role ' + this._id + ' (' + path.join('/') + ') :' + actionRights);
      error.details = err;

      throw (error);
    }
  }

  if (this.closures[path].getArgsDefinitions) {
    argsDefinitions = this.closures[path].getArgsDefinitions(requestObject);
  }

  return buildArgsForContext.call(kuzzle, argsDefinitions)
    .then(args => {
      sandboxContext = vm.createContext({ requestObject: requestObject, context: context, args: args });

      if (!this.closures[path].test) {
        try {
          sandboxScript = new vm.Script('(function (requestObject, context, args) { ' + actionRights.test + '\nreturn false;\n })(requestObject, context, args)');
          this.closures[path].test = sandboxScript;
        }
        catch (err) {
          var message = `Error parsing rights for role ${this._id} (${path.join('/')}): ${actionRights}`;

          kuzzle.pluginsManager.trigger('log:error', message);
          error = new InternalError(message);
          error.details = err;

          throw (error);
        }
      }

      return this.closures[path].test.runInContext(sandboxContext);
    })
    .catch((e) => {
      var message = `Error during executing rights action closure (${path.join('/')}): ${e.message}`;

      kuzzle.pluginsManager.trigger('log:error', message);
      error = new InternalError(message);
      error.details = err;

      throw (error);
    });
}

/**
 *
 * @param argsDefinitions
 * @returns {Promise}
 */
function buildArgsForContext (argsDefinitions) {
  var
    deferred = q.defer();

  if (!_.isEmpty(argsDefinitions)) {
    return buildClosureArgs.call(this, argsDefinitions);
  }

  deferred.resolve({});
  return deferred.promise;
}

/**
 * @param {Object} argsDefinitions
 * @returns {Promise}
 */
function buildClosureArgs (argsDefinitions) {
  var
    deferred = q.defer(),
    argsFunctions = {};

  // Build the object that will be passed to parallel
  _.forEach(argsDefinitions, (argDefinition, argName) => {
    argsFunctions[argName] = factoryFunctionClosure.call(this, argName, argDefinition);
  });

  async.parallel(argsFunctions, (error, results) => {
    if (error) {
      // In case we have an error we want to return an empty object because the error can come from readEngine
      return deferred.resolve({});
    }

    deferred.resolve(results);
  });

  return deferred.promise;
}

/**
 * Return a function built for async.parallel with the right action on readEngine
 * @param {String} argName - the argument name set by user (i.e.: document, documents...)
 * @param {Object} argDefinition - definition set by user. Like {collection: 'messages', index: 'chat', action: { get: '$currentId' }}
 * @returns {Function} the function that will execute a get/mget/search on readEngine
 */
function factoryFunctionClosure (argName, argDefinition) {
  var methodName;

  if (!argDefinition.collection || !argDefinition.index || !argDefinition.action || _.isEmpty(argDefinition.action)) {
    this.pluginsManager.trigger('log:error', `Bad format in closure rights for ${argName}`);
    return callback => callback(null, {});
  }

  methodName = Object.keys(argDefinition.action)[0];

  if (['get', 'mget', 'search'].indexOf(methodName) === -1) {
    this.pluginsManager.trigger('log:error', `Try to use an unauthorized function (${methodName}) in closure rights check`);
    return callback => callback(null, {});
  }

  return callback => {
    var requestObject = new RequestObject({
      controller: 'read',
      action: methodName,
      collection: argDefinition.collection,
      index: argDefinition.index
    });

    if (methodName === 'mget') {
      requestObject.data = {
        ids: argDefinition.action[methodName]
      };
    }
    else if (methodName === 'search') {
      requestObject.data = argDefinition.action[methodName];
    }
    else if (methodName === 'get') {
      requestObject.data = {
        id: argDefinition.action[methodName]
      };
    }

    this.services.list.readEngine[methodName](requestObject)
      .then(responseObject => {
        if (responseObject.data.hits) {
          return callback(null, responseObject.data.hits);
        }

        callback(null, {content: responseObject.data.body._source, id: responseObject.data.body._id});
      })
      .catch((e) => {
        return callback(null, {});
      });
  };
}<commit_msg>fix for using mget/search in closure<commit_after>var
  _ = require('lodash'),
  q = require('q'),
  async = require('async'),
  vm = require('vm'),
  BadRequestError = require('../../errors/badRequestError'),
  RequestObject = require('../requestObject'),
  InternalError = require('../../errors/internalError'),
  Sandbox = require('../../sandbox'),
  internalIndex = require('rc')('kuzzle').internalIndex;

function Role () {
  this.indexes = {};
  this.closures = {};
}

function doesIndexExist(requestObject, indexes) {
  return indexes && indexes[requestObject.index] !== undefined;
}

function doesCollectionExist(requestObject, indexes) {
  return indexes && _.contains(indexes[requestObject.index], requestObject.collection);
}

/**
 * @param requestObject
 * @param context - user's context
 * @param indexes - list of existing indexes/collections
 * @param {Kuzzle} kuzzle
 *
 * @returns {Promise}
 */
Role.prototype.isActionAllowed = function (requestObject, context, indexes, kuzzle) {
  var
    indexRights,
    collectionRights,
    controllerRights,
    actionRights,
    path = [];

  if (this.indexes === undefined) {
    return q(false);
  }

  /*
   Security controller's routes are only applicable on the internal index.
   Therefore, the "index" argument is not expected in the request object.
    */
  if (requestObject.controller === 'security' && !this.indexes[internalIndex]) {
    return q(false);
  }

  if (this.indexes[requestObject.index] !== undefined) {
    indexRights = this.indexes[requestObject.index];
    path.push(requestObject.index);
  }
  // Wildcards doesn't resolve the internal index. The internal index must always be set explicitly
  else if (this.indexes['*'] !== undefined && internalIndex !== requestObject.index) {
    indexRights = this.indexes['*'];
    path.push('*');
  }
  else {
    return q(false);
  }

  if (requestObject.action === 'createIndex' &&
    this.indexes._canCreate !== undefined &&
    !this.indexes._canCreate) {
    return q(false);
  }

  if (requestObject.action === 'deleteIndex' &&
    indexRights._canDelete !== undefined &&
    !indexRights._canDelete) {
    return q(false);
  }

  if (!doesIndexExist(requestObject, indexes) &&
    this.indexes._canCreate !== undefined &&
    !this.indexes._canCreate &&
    _.contains(['import', 'create', 'createCollection', 'updateMapping', 'createOrReplace'], requestObject.action)) {
    return q(false);
  }

  if (indexRights.collections === undefined) {
    return q(false);
  }

  if (indexRights.collections[requestObject.collection] !== undefined) {
    collectionRights = indexRights.collections[requestObject.collection];
    path.push(requestObject.collection);
  }
  else if (indexRights.collections['*'] !== undefined) {
    collectionRights = indexRights.collections['*'];
    path.push('*');
  }
  else {
    return q(false);
  }
  if (requestObject.action === 'deleteCollection' &&
    collectionRights._canDelete !== undefined &&
    !collectionRights._canDelete) {
    return q(false);
  }

  if (!doesCollectionExist(requestObject, indexes) &&
    indexRights.collections._canCreate !== undefined &&
    !indexRights.collections._canCreate &&
    _.contains(['import', 'create', 'createCollection', 'updateMapping', 'createOrReplace'], requestObject.action)) {
    return q(false);
  }

  if (collectionRights.controllers === undefined) {
    return q(false);
  }

  if (collectionRights.controllers[requestObject.controller] !== undefined) {
    controllerRights = collectionRights.controllers[requestObject.controller];
    path.push(requestObject.controller);
  }
  else if (collectionRights.controllers['*'] !== undefined) {
    controllerRights = collectionRights.controllers['*'];
    path.push('*');
  }
  else {
    return q(false);
  }

  if (controllerRights.actions === undefined) {
    return q(false);
  }

  if (controllerRights.actions[requestObject.action] !== undefined) {
    actionRights = controllerRights.actions[requestObject.action];
    path.push(requestObject.action);
  }
  else if (controllerRights.actions['*'] !== undefined) {
    actionRights = controllerRights.actions['*'];
    path.push('*');
  }
  else {
    return q(false);
  }

  if (_.isBoolean(actionRights)) {
    // the role configuration is set to a static boolean. We return it as-is.
    return q(actionRights);
  }

  if (_.isObject(actionRights)) {
    // the role configuration is an object with a function in attribute `test`. We assume it can be (almost) safely run
    // as it should have previously been validated during creation.

    return executeClosure.call(this, kuzzle, path, actionRights, requestObject, context);
  }

  throw (new InternalError('Invalid rights given for role ' + this._id + '(' + path.join('/') + ') : ' + actionRights));
};

/**
 * @param {Object} context
 * @returns {*}
 */
Role.prototype.validateDefinition = function (context) {
  var
    deferred = q.defer(),
    promises = [],
    args = {};

  if (!_.isObject(this.indexes)) {
    return q.reject(new BadRequestError('The index definition must be an object'));
  }
  if (_.isEmpty(this.indexes)) {
    return q.reject(new BadRequestError('The index definition cannot be empty'));
  }

  Object.keys(this.indexes).every(indexKey => {
    var indexRights = this.indexes[indexKey];

    if (indexKey === '_canCreate') {
      if (!_.isBoolean(indexRights)) {
        deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Must be an boolean'));
        return false;
      }

      return true;
    }

    if (!_.isObject(indexRights)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Must be an object'));
      return false;
    }
    if (_.isEmpty(indexRights)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Cannot be empty'));
      return false;
    }
    if (indexRights.collections === undefined) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute missing'));
      return false;
    }
    if (!_.isObject(indexRights.collections)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute must be an object'));
      return false;
    }
    if (_.isEmpty(indexRights.collections)) {
      deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute cannot be empty'));
      return false;
    }

    Object.keys(indexRights.collections).every(collectionKey => {
      var collectionRights = indexRights.collections[collectionKey];

      if (collectionKey === '_canCreate') {
        if (!_.isBoolean(collectionRights)) {
          deferred.reject(new BadRequestError('Invalid index definition for ' + [indexKey, collectionKey] + '. Must be an boolean'));
          return false;
        }

        return true;
      }

      if (!_.isObject(collectionRights)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Must be an object'));
        return false;
      }
      if (_.isEmpty(collectionRights)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Cannot be empty'));
        return false;
      }
      if (collectionRights.controllers === undefined) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute missing'));
        return false;
      }
      if (!_.isObject(collectionRights.controllers)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute must be an object'));
        return false;
      }
      if (_.isEmpty(collectionRights.controllers)) {
        deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute cannot be empty'));
        return false;
      }

      Object.keys(collectionRights.controllers).every(controllerKey => {
        var controllerRights = collectionRights.controllers[controllerKey];

        if (!_.isObject(controllerRights)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. Must be an object'));
          return false;
        }
        if (_.isEmpty(controllerRights)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. Cannot be empty'));
          return false;
        }
        if (controllerRights.actions === undefined) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. `actions` attribute missing'));
          return false;
        }
        if (!_.isObject(controllerRights.actions)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. `actions` attribute must be an object'));
          return false;
        }
        if (_.isEmpty(controllerRights.actions)) {
          deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey] + '. `actions` attribute cannot be empty'));
          return false;
        }

        Object.keys(controllerRights.actions).every(actionKey => {
          var actionRights = controllerRights.actions[actionKey];

          if (!_.isBoolean(actionRights) && !_.isObject(actionRights)) {
            deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey, controllerKey, actionKey] + '. Must be a boolean or an object'));
            return false;
          }

          if (_.isObject(actionRights) && actionRights.test) {

            promises.push((function () {
              var
                sandBox = new Sandbox();

              return sandBox.run({
                sandbox: {
                  requestObject: {
                    index: 'index',
                    collection: 'collection',
                    controller: 'controller',
                    action: 'action',
                    data: {
                      _id: -1,
                      body: {
                        a: true
                      }
                    }
                  },
                  context: {
                    connection: {type: context.connection.type},
                    token: context.token
                  },
                  $currentUserId: -1,
                  args: {}
                },
                code: '(function (requestObject, context, $currentUserId, args) { ' + actionRights.test + '\nreturn false;\n})(requestObject, context, $currentUserId, args)'
              })
              .then(result => {
                var error;

                if (result.result !== undefined && _.isBoolean(result.result)) {
                  return q(result.result);
                }

                error = new BadRequestError('Invalid definition for '+ [indexKey, collectionKey, controllerKey, actionKey] + '. Error executing function');
                error.detail = result.err;

                return q.reject(error);
              });
            })());
          }

          return true;
        });

        return true;
      });

      return true;
    });

    return true;
  });

  if (promises.length > 0) {
    q.all(promises)
      .then(() => {
        deferred.resolve(true);
      })
      .catch(error => {
        deferred.reject(error);
      });
  }
  else {
    deferred.resolve(true);
  }

  return deferred.promise;
};

module.exports = Role;

/**
 * @param {Kuzzle} kuzzle
 * @param {Array} path
 * @param {Object} actionRights
 * @param {RequestObject} requestObject
 * @param {Object} context
 * @returns {Promise}
 */
function executeClosure (kuzzle, path, actionRights, requestObject, context) {
  var
    sandboxContext,
    sandboxScript,
    error,
    args = {},
    argsDefinitions = {};

  if (this.closures[path] === undefined) {
    this.closures[path] = {};
  }

  if (actionRights.args && !this.closures[path].getArgsDefinitions) {
    try {
      /* jshint evil: true */
      this.closures[path].getArgsDefinitions = eval('(function (requestObject) {return ' +
        JSON.stringify(actionRights.args).replace('"$currentId"', 'requestObject.data._id') +
        ';})');
      /* jshint evil: false */
    }
    catch (err) {
      error = new InternalError('Error parsing rights for role ' + this._id + ' (' + path.join('/') + ') :' + actionRights);
      error.details = err;

      throw (error);
    }
  }

  if (this.closures[path].getArgsDefinitions) {
    argsDefinitions = this.closures[path].getArgsDefinitions(requestObject);
  }

  return buildArgsForContext.call(kuzzle, argsDefinitions)
    .then(args => {
      sandboxContext = vm.createContext({ requestObject: requestObject, context: context, $currentUserId: context.token.user._id, args: args });

      if (!this.closures[path].test) {
        try {
          sandboxScript = new vm.Script('(function (requestObject, context, $currentUserId, args) { ' +
            actionRights.test +
            '\nreturn false;\n })(requestObject, context, $currentUserId, args)');

          this.closures[path].test = sandboxScript;
        }
        catch (err) {
          var message = `Error parsing rights for role ${this._id} (${path.join('/')}): ${actionRights}`;

          kuzzle.pluginsManager.trigger('log:error', message);
          error = new InternalError(message);
          error.details = err;

          throw (error);
        }
      }

      return this.closures[path].test.runInContext(sandboxContext);
    })
    .catch((e) => {
      var message = `Error during executing rights action closure (${path.join('/')}): ${e.message}`;

      kuzzle.pluginsManager.trigger('log:error', message);
      error = new InternalError(message);
      error.details = err;

      throw (error);
    });
}

/**
 *
 * @param argsDefinitions
 * @returns {Promise}
 */
function buildArgsForContext (argsDefinitions) {
  var
    deferred = q.defer();

  if (!_.isEmpty(argsDefinitions)) {
    return buildClosureArgs.call(this, argsDefinitions);
  }

  deferred.resolve({});
  return deferred.promise;
}

/**
 * @param {Object} argsDefinitions
 * @returns {Promise}
 */
function buildClosureArgs (argsDefinitions) {
  var
    deferred = q.defer(),
    argsFunctions = {};

  // Build the object that will be passed to parallel
  _.forEach(argsDefinitions, (argDefinition, argName) => {
    argsFunctions[argName] = factoryFunctionClosure.call(this, argName, argDefinition);
  });

  async.parallel(argsFunctions, (error, results) => {
    if (error) {
      // In case we have an error we want to return an empty object because the error can come from readEngine
      return deferred.resolve({});
    }

    deferred.resolve(results);
  });

  return deferred.promise;
}

/**
 * Return a function built for async.parallel with the right action on readEngine
 * @param {String} argName - the argument name set by user (i.e.: document, documents...)
 * @param {Object} argDefinition - definition set by user. Like {collection: 'messages', index: 'chat', action: { get: '$currentId' }}
 * @returns {Function} the function that will execute a get/mget/search on readEngine
 */
function factoryFunctionClosure (argName, argDefinition) {
  var methodName;

  if (!argDefinition.collection || !argDefinition.index || !argDefinition.action || _.isEmpty(argDefinition.action)) {
    this.pluginsManager.trigger('log:error', `Bad format in closure rights for ${argName}`);
    return callback => callback(null, {});
  }

  methodName = Object.keys(argDefinition.action)[0];

  if (['get', 'mget', 'search'].indexOf(methodName) === -1) {
    this.pluginsManager.trigger('log:error', `Try to use an unauthorized function (${methodName}) in closure rights check`);
    return callback => callback(null, {});
  }

  return callback => {
    var requestObject = new RequestObject({
      controller: 'read',
      action: methodName,
      collection: argDefinition.collection,
      index: argDefinition.index
    });

    if (methodName === 'mget') {
      requestObject.data = {
        body: {
          ids: argDefinition.action[methodName]
        }
      };
    }
    else if (methodName === 'search') {
      requestObject.data = {
        body: argDefinition.action[methodName]
      };
    }
    else if (methodName === 'get') {
      requestObject.data = {
        id: argDefinition.action[methodName]
      };
    }

    this.services.list.readEngine[methodName](requestObject)
      .then(responseObject => {
        if (responseObject.data.body.hits) {
          return callback(null, responseObject.data.body.hits.map(document => {
            return {content: document._source, id: document._id};
          }));
        }

        callback(null, {content: responseObject.data.body._source, id: responseObject.data.body._id});
      })
      .catch((e) => {
        this.pluginsManager.trigger('log:error', `Error during readEngine execution for ${methodName} with ${JSON.stringify(argDefinition)}: ${e.message}`);
        return callback(null, {});
      });
  };
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2014, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint plusplus: true */
/*global YUI*/

/**
 * @module ResourceStoreAddon
 */

/**
 * RS addon that computes AC addon dependencies at startup to be attached
 * at runtime.
 *
 * @class RSAddonDispatchHelper
 * @extension ResourceStore.server
 */
YUI.add('addon-rs-dispatch-helper', function (Y, NAME) {

    'use strict';

    var libpath = require('path');

    function RSAddonDispatchHelper() {
        RSAddonDispatchHelper.superclass.constructor.apply(this, arguments);
    }

    RSAddonDispatchHelper.NS = 'dispatch-helper';

    Y.extend(RSAddonDispatchHelper, Y.Plugin.Base, {

        initializer: function (config) {
            this.modules = {}; // Controllers and ac addons.
            this.beforeHostMethod('addResourceVersion', this.addResourceVersion, this);
            this.onHostEvent('resolveMojitDetails', this.onResolveMojitDetails, this);
        },

        /**
         * Using AOP, this is called before the ResourceStore's version, in order
         * to keep track of all the controllers and AC addons. These modules are then
         * used by onResolveMojitDetails in order to precompute list of AC addons for
         * a mojit's controller.
         * @method addResourceVersion
         * @param {object} res resource version metadata
         * @return {nothing}
         */
        addResourceVersion: function (res) {
            if (res.type === 'controller' || (res.type === 'addon' && res.subtype === 'ac')) {
                // We only care about controllers and AC addons since a mojit's controller's addons
                // should be derived from its addons requirements, recursively including the addons of
                // the controllers and addons it requires.

                // Do not overwrite a previous module with the same name; this ensures that
                // if there are duplicate modules, only the shallowest is considered.
                if (!this.modules[res.yui.name]) {
                    this.modules[res.yui.name] = res;
                }
            }
        },

        /**
         * This is called when the ResourceStore fires this event.
         * It precomputes the list of AC addons used by the mojit's controller,
         * to be used later during onGetMojitTypeDetails.
         * @method onResolveMojitDetails
         * @param {object} evt The fired event
         * @return {nothing}
         */
        onResolveMojitDetails: function (evt) {
            var self = this,
                store = this.get('host'),
                env = evt.args.env,
                mojitType = evt.args.type,
                details = evt.mojitDetails,
                addonName,
                acAddonNames = evt.acAddonNames;

            if ('shared' === mojitType) {
                return;
            }

            if (!details.controller) {
                // It's not an error if a mojit is missing a controller, since
                // some mojits only run on the server side (or only on the
                // client side).
                return;
            }

            details.acAddons = [];
            (function getRequiredAddons(requires, seen) {
                var r,
                    module;
                for (r = 0; r < requires.length; r++) {
                    module = requires[r];
                    if (seen[module] || !self.modules[module] ||
                            (self.modules[module].affinity.affinity !== env &&
                                self.modules[module].affinity.affinity !== 'common')) {
                        continue;
                    }
                    seen[module] = true;

                    getRequiredAddons(self.modules[module].yui.meta.requires, seen);
                    if (acAddonNames[module]) {
                        details.acAddons.push(acAddonNames[module]);
                    }
                }
            }(self.modules[details.controller].yui.meta.requires, {}));
        }

    });

    Y.namespace('mojito.addons.rs')['dispatch-helper'] = RSAddonDispatchHelper;

}, '0.0.1', { requires: [
    'addon-rs-yui',
    'plugin',
    'oop'
]});<commit_msg>Dispatch fixes that takes care of corner cases such as yui modules with the same name but client/server affinities, duplicate yui names at different levels or with different selectors.<commit_after>/*
 * Copyright (c) 2012-2014, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint plusplus: true */
/*global YUI*/

/**
 * @module ResourceStoreAddon
 */

/**
 * RS addon that computes AC addon dependencies at startup to be attached
 * at runtime.
 *
 * @class RSAddonDispatchHelper
 * @extension ResourceStore.server
 */
YUI.add('addon-rs-dispatch-helper', function (Y, NAME) {

    'use strict';

    var libpath = require('path');

    function RSAddonDispatchHelper() {
        RSAddonDispatchHelper.superclass.constructor.apply(this, arguments);
    }

    RSAddonDispatchHelper.NS = 'dispatch-helper';

    Y.extend(RSAddonDispatchHelper, Y.Plugin.Base, {

        initializer: function (config) {
            this.modules = {
                client: {},
                server: {},
                common: {}
            }; // Controllers and ac addons.
            this.beforeHostMethod('addResourceVersion', this.addResourceVersion, this);
            this.onHostEvent('resolveMojitDetails', this.onResolveMojitDetails, this);
        },

        /**
         * Using AOP, this is called before the ResourceStore's version, in order
         * to keep track of all the controllers and AC addons. These modules are then
         * used by onResolveMojitDetails in order to precompute list of AC addons for
         * a mojit's controller.
         * @method addResourceVersion
         * @param {object} res resource version metadata
         * @return {nothing}
         */
        addResourceVersion: function (res) {
            if (res.type === 'controller' || (res.type === 'addon' && res.subtype === 'ac')) {
                // We only care about controllers and AC addons since a mojit's controller's addons
                // should be derived from its addons requirements, recursively including the addons of
                // the controllers and addons it requires.

                var affinity = res.affinity.toString();
                // Do not overwrite a previous module with the same name; this ensures that
                // if there are duplicate modules, only the shallowest is considered.
                if (!this.modules[affinity][res.yui.name]) {
                    this.modules[affinity][res.yui.name] = res;
                }
            }
        },

        /**
         * This is called when the ResourceStore fires this event.
         * It precomputes the list of AC addons used by the mojit's controller,
         * to be used later during onGetMojitTypeDetails.
         * @method onResolveMojitDetails
         * @param {object} evt The fired event
         * @return {nothing}
         */
        onResolveMojitDetails: function (evt) {
            var self = this,
                store = this.get('host'),
                env = evt.args.env,
                mojitType = evt.args.type,
                details = evt.mojitDetails,
                addonName,
                acAddonNames = evt.acAddonNames;

            if ('shared' === mojitType) {
                return;
            }

            if (!details.controller) {
                // It's not an error if a mojit is missing a controller, since
                // some mojits only run on the server side (or only on the
                // client side).
                return;
            }

            details.acAddons = [];
            (function getRequiredAddons(requires, seen) {
                var r,
                    module,
                    moduleName;
                for (r = 0; r < requires.length; r++) {
                    moduleName = requires[r];

                    if (seen[moduleName]) {
                        continue;
                    }
                    seen[moduleName] = true;

                    module = self.modules[env][moduleName] || self.modules.common[moduleName];
                    if (module) {
                        getRequiredAddons(module.yui.meta.requires, seen);
                        if (acAddonNames[moduleName]) {
                            details.acAddons.push(acAddonNames[moduleName]);
                        }
                    }
                }
            }([details.controller], {}));
        }

    });

    Y.namespace('mojito.addons.rs')['dispatch-helper'] = RSAddonDispatchHelper;

}, '0.0.1', { requires: [
    'addon-rs-yui',
    'plugin',
    'oop'
]});<|endoftext|>


<commit_before>/**
 * Schema fields are nested schema's
 *
 * @constructor
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  1.1.0
 */
var SchemaField = Function.inherits('Alchemy.Field', function Schema(schema, name, options) {
	Schema.super.call(this, schema, name, options);

	if (options.schema == null || typeof options.schema != 'object') {
		return;
	}

	// Field schema
	this.fieldSchema = options.schema;

	if (!this.fieldSchema.name) {
		this.fieldSchema.setName(name);
	}

	if (!this.fieldSchema.parent || schema.root_schema == this.fieldSchema.parent) {
		this.fieldSchema.setParent(schema);
	} else {
		// It's possible the given schema already had the correct parent
		// But if that's not the case, throw an error
		if (this.fieldSchema.parent != schema) {
			throw new Error('Unable to re-use a schema for field "' + name + '"');
		}
	}

	if (!this.fieldSchema.model_name) {
		this.fieldSchema.setModel(schema.model_name);
	}
});

/**
 * Schema fields are stored as objects
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    1.1.0
 * @version  1.1.0
 */
SchemaField.setDatatype('object');

/**
 * Get the subschema of this field
 *
 * @constructor
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.0
 *
 * @return   {String}
 */
SchemaField.setProperty(function root_model() {
	return this.parent_schema.root_model || this.parent_schema.model_name;
});

/**
 * Get the subschema of this field
 *
 * @constructor
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.4.0
 *
 * @param    {Object}   record
 * @param    {String}   some_path   Some path to a field in the wanted schema
 *
 * @return   {Schema}
 */
SchemaField.setMethod(function getSubschema(record, some_path) {

	var schema = this.options.schema,
	    external_field_name,
	    property_name,
	    record_value,
	    pieces,
	    field,
	    name,
	    temp;

	// If schema is a string,
	// it needs to be extracted from another field's value
	if (typeof schema == 'string') {

		// When there are 2 pieces, the second piece is the property name
		pieces = schema.split('.');

		// The first piece is the external field name
		external_field_name = pieces[0];

		// The second piece is the property name
		property_name = pieces[1] || 'schema';

		// Get that other field by its name
		field = this.schema.getField(external_field_name);

		if (!field) {
			console.error('Failed to get subschema', external_field_name, 'of', this.schema, some_path);
			return null;
		}

		// Get the values that field can have (probably an enum)
		temp = field.getValues();

		if (temp == null) {
			return null;
		}

		// Now get the actual external value from the record
		record_value = field.getRecordValue(record);

		// I'm not sure if this will help
		if (record_value == null) {
			record_value = field.getRecordValue(record, some_path);
		}

		// Get the correct field value
		temp = temp[record_value];

		if (temp != null) {
			schema = temp[property_name];

			if (schema == null && typeof temp == 'function') {
				temp = new temp();
				schema = temp[property_name] || temp.schema || temp.blueprint;

				if (schema != null) {
					schema.setModel(this.schema.model_name);
					schema.setName(this.name);
					schema.setParent(this.schema);
				}
			}
		} else {
			schema = null;
		}
	}

	return schema;
});

/**
 * Cast all the subschema values using their _toDatasource method
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.4.0
 *
 * @param    {Object}      value        Value of field, an object in this case
 * @param    {Object}      data         The data object containing `value`
 * @param    {Datasource}  datasource   The destination datasource
 *
 * @return   {Object}
 */
SchemaField.setMethod(function _toDatasource(value, data, datasource, callback) {

	var that = this,
	    sub_schema,
	    record,
	    model,
	    temp;

	record = {};

	// Recreate a record
	record[this.schema.name] = data;

	sub_schema = this.getSubschema(record);

	// If the sub schema has been found, return it now
	if (sub_schema) {
		return datasource.toDatasource(sub_schema, value, callback);
	}

	// @WARNING:
	// Doing a model.save with ONLY updates can break subschema behaviour!

	// If it has not been found, and the linked schema is actually a string,
	// it's possible the linked field was not provided
	if (typeof this.options.schema == 'string') {
		model = this.root_model;

		if (model) {
			model = Model.get(model);

			model.find('first', {document: false, fields: [this.options.schema]}, function gotRecord(err, result) {

				if (err) {
					return callback(err);
				}

				if (!result.length) {
					log.warning('Subschema was not found for', that.name, 'in', model.name, 'model');
					return datasource.toDatasource(null, value, callback);
				}

				// Add the newly found data
				temp = Object.assign({}, data);
				record = {};
				record[that.schema.name] = temp;

				Object.assign(record, result[0]);

				// Try getting the schema again
				sub_schema = that.getSubschema(record);

				datasource.toDatasource(sub_schema, value, callback);
			});

			return;
		} else {
			log.warn('Model not found for subschema', this.options.schema);
		}
	}

	log.warning('Model and subschema were not found for', that.name);
	return datasource.toDatasource(null, value, callback);
});

/**
 * Get some more subschema data
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  1.1.0
 *
 * @param    {Mixed}      value
 * @param    {Function}   callback
 */
SchemaField.setMethod(function _toApp(query, options, value, callback) {

	var that = this,
	    recursive,
	    Dummy,
	    item,
	    name;

	// Don't get schema associated records if recursive is disabled
	recursive = options.recursive;

	if (recursive == null) {
		recursive = 1;
	}

	// Get associated records if the subschema has associations defined
	if (recursive && this.fieldSchema && !Object.isEmpty(this.fieldSchema.associations)) {

		name = this.name + 'FieldModel';
		Dummy = alchemy.getModel('Model', false);
		Dummy = new Dummy({name: name});
		item = {};

		item[name] = value;

		let sub_criteria = Dummy.find();

		// Disable generating Document instances
		// if the original find did the same
		sub_criteria.setOption('document', options.document);
		sub_criteria.setOption('original_query', query);
		sub_criteria.setOption('_root_data', options._root_data);
		sub_criteria.setOption('_parent_field', that);
		sub_criteria.setOption('_parent_model', that.schema.model_name);
		sub_criteria.setOption('recursive', recursive);

		sub_criteria.setOption('associations', this.fieldSchema.associations);

		// @todo: inherit other original find options?

		Dummy.addAssociatedDataToRecord(sub_criteria, item, function gotAssociatedData(err, result) {

				var key;

				if (err) {
					return callback(err);
				}

				for (key in result) {
					if (key == name) {
						continue;
					}

					value[key] = result[key];
				}

				callback(null, value);
			}
		);

		return;
	}

	// Create new dummy record
	let record = {};

	// Recreate the record
	record[this.schema.name] = value;

	let sub_schema = this.getSubschema(record);

	// If the sub schema has been found, return it now
	if (sub_schema) {
		let tasks = {};

		Object.each(value, function eachField(field_value, field_name) {

			var field = sub_schema.get(field_name);

			if (field != null) {
				tasks[field_name] = function doToDatasource(next) {
					field.toApp({}, {}, field_value, next);
				};
			}
		});

		return Function.parallel(4, tasks, callback);
	}

	callback(null, this.cast(value));
});<commit_msg>Rename fieldSchema to field_schema<commit_after>/**
 * Schema fields are nested schema's
 *
 * @constructor
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  1.1.0
 */
var SchemaField = Function.inherits('Alchemy.Field', function Schema(schema, name, options) {
	Schema.super.call(this, schema, name, options);

	if (options.schema == null || typeof options.schema != 'object') {
		return;
	}

	// Field schema
	this.field_schema = options.schema;

	if (!this.field_schema.name) {
		this.field_schema.setName(name);
	}

	if (!this.field_schema.parent || schema.root_schema == this.field_schema.parent) {
		this.field_schema.setParent(schema);
	} else {
		// It's possible the given schema already had the correct parent
		// But if that's not the case, throw an error
		if (this.field_schema.parent != schema) {
			throw new Error('Unable to re-use a schema for field "' + name + '"');
		}
	}

	if (!this.field_schema.model_name) {
		this.field_schema.setModel(schema.model_name);
	}
});

SchemaField.setDeprecatedProperty('fieldSchema', 'field_schema');

/**
 * Schema fields are stored as objects
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    1.1.0
 * @version  1.1.0
 */
SchemaField.setDatatype('object');

/**
 * Get the subschema of this field
 *
 * @constructor
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.0
 *
 * @return   {String}
 */
SchemaField.setProperty(function root_model() {
	return this.parent_schema.root_model || this.parent_schema.model_name;
});

/**
 * Get the subschema of this field
 *
 * @constructor
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.4.0
 *
 * @param    {Object}   record
 * @param    {String}   some_path   Some path to a field in the wanted schema
 *
 * @return   {Schema}
 */
SchemaField.setMethod(function getSubschema(record, some_path) {

	var schema = this.options.schema,
	    external_field_name,
	    property_name,
	    record_value,
	    pieces,
	    field,
	    name,
	    temp;

	// If schema is a string,
	// it needs to be extracted from another field's value
	if (typeof schema == 'string') {

		// When there are 2 pieces, the second piece is the property name
		pieces = schema.split('.');

		// The first piece is the external field name
		external_field_name = pieces[0];

		// The second piece is the property name
		property_name = pieces[1] || 'schema';

		// Get that other field by its name
		field = this.schema.getField(external_field_name);

		if (!field) {
			console.error('Failed to get subschema', external_field_name, 'of', this.schema, some_path);
			return null;
		}

		// Get the values that field can have (probably an enum)
		temp = field.getValues();

		if (temp == null) {
			return null;
		}

		// Now get the actual external value from the record
		record_value = field.getRecordValue(record);

		// I'm not sure if this will help
		if (record_value == null) {
			record_value = field.getRecordValue(record, some_path);
		}

		// Get the correct field value
		temp = temp[record_value];

		if (temp != null) {
			schema = temp[property_name];

			if (schema == null && typeof temp == 'function') {
				temp = new temp();
				schema = temp[property_name] || temp.schema || temp.blueprint;

				if (schema != null) {
					schema.setModel(this.schema.model_name);
					schema.setName(this.name);
					schema.setParent(this.schema);
				}
			}
		} else {
			schema = null;
		}
	}

	return schema;
});

/**
 * Cast all the subschema values using their _toDatasource method
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.4.0
 *
 * @param    {Object}      value        Value of field, an object in this case
 * @param    {Object}      data         The data object containing `value`
 * @param    {Datasource}  datasource   The destination datasource
 *
 * @return   {Object}
 */
SchemaField.setMethod(function _toDatasource(value, data, datasource, callback) {

	var that = this,
	    sub_schema,
	    record,
	    model,
	    temp;

	record = {};

	// Recreate a record
	record[this.schema.name] = data;

	sub_schema = this.getSubschema(record);

	// If the sub schema has been found, return it now
	if (sub_schema) {
		return datasource.toDatasource(sub_schema, value, callback);
	}

	// @WARNING:
	// Doing a model.save with ONLY updates can break subschema behaviour!

	// If it has not been found, and the linked schema is actually a string,
	// it's possible the linked field was not provided
	if (typeof this.options.schema == 'string') {
		model = this.root_model;

		if (model) {
			model = Model.get(model);

			model.find('first', {document: false, fields: [this.options.schema]}, function gotRecord(err, result) {

				if (err) {
					return callback(err);
				}

				if (!result.length) {
					log.warning('Subschema was not found for', that.name, 'in', model.name, 'model');
					return datasource.toDatasource(null, value, callback);
				}

				// Add the newly found data
				temp = Object.assign({}, data);
				record = {};
				record[that.schema.name] = temp;

				Object.assign(record, result[0]);

				// Try getting the schema again
				sub_schema = that.getSubschema(record);

				datasource.toDatasource(sub_schema, value, callback);
			});

			return;
		} else {
			log.warn('Model not found for subschema', this.options.schema);
		}
	}

	log.warning('Model and subschema were not found for', that.name);
	return datasource.toDatasource(null, value, callback);
});

/**
 * Get some more subschema data
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  1.1.0
 *
 * @param    {Mixed}      value
 * @param    {Function}   callback
 */
SchemaField.setMethod(function _toApp(query, options, value, callback) {

	var that = this,
	    recursive,
	    Dummy,
	    item,
	    name;

	// Don't get schema associated records if recursive is disabled
	recursive = options.recursive;

	if (recursive == null) {
		recursive = 1;
	}

	// Get associated records if the subschema has associations defined
	if (recursive && this.field_schema && !Object.isEmpty(this.field_schema.associations)) {

		name = this.name + 'FieldModel';
		Dummy = alchemy.getModel('Model', false);
		Dummy = new Dummy({name: name});
		item = {};

		item[name] = value;

		let sub_criteria = Dummy.find();

		// Disable generating Document instances
		// if the original find did the same
		sub_criteria.setOption('document', options.document);
		sub_criteria.setOption('original_query', query);
		sub_criteria.setOption('_root_data', options._root_data);
		sub_criteria.setOption('_parent_field', that);
		sub_criteria.setOption('_parent_model', that.schema.model_name);
		sub_criteria.setOption('recursive', recursive);

		sub_criteria.setOption('associations', this.field_schema.associations);

		// @todo: inherit other original find options?

		Dummy.addAssociatedDataToRecord(sub_criteria, item, function gotAssociatedData(err, result) {

				var key;

				if (err) {
					return callback(err);
				}

				for (key in result) {
					if (key == name) {
						continue;
					}

					value[key] = result[key];
				}

				callback(null, value);
			}
		);

		return;
	}

	// Create new dummy record
	let record = {};

	// Recreate the record
	record[this.schema.name] = value;

	let sub_schema = this.getSubschema(record);

	// If the sub schema has been found, return it now
	if (sub_schema) {
		let tasks = {};

		Object.each(value, function eachField(field_value, field_name) {

			var field = sub_schema.get(field_name);

			if (field != null) {
				tasks[field_name] = function doToDatasource(next) {
					field.toApp({}, {}, field_value, next);
				};
			}
		});

		return Function.parallel(4, tasks, callback);
	}

	callback(null, this.cast(value));
});<|endoftext|>


<commit_before>// vim: ts=4 et sw=4 sts=4

$(function () {
    // Micro-plugin for positioning the cursor
    $.fn.setCursor = $.fn.setCursor || function(pos) {
        return this.each(function() {
            if (this.setSelectionRange) {
                this.focus();
                this.setSelectionRange(pos, pos);
            } else if (this.createTextRange) {
                var range = this.createTextRange();
                range.collapse(true);
                range.moveEnd('character', pos);
                range.moveStart('character', pos);
                range.select();
            }
        });
    };
    // Micro-plugin for getting the position of the cursor
    $.fn.getCursor = $.fn.getCursor || function () {
        // IE doesn't have selectionStart, and I'm too lazy to implement
        // the IE specific version of this, so we'll just assume
        // the cursor is at the end of the input for this case.
        if (typeof this[0].selectionStart === 'undefined') {
            return this.value.length;
        } else {
            return this[0].selectionStart;
        }
    };
    $(".magic_grid[data-default-ajax-handler=true]").on("magic_grid:loading", function (e) {
        $(".magic_grid_spinner", this).
            addClass("ui-icon-spinner").
                html("Loading...");
    });
    $(".magic_grid[data-remote=true]").on( "click", ".pagination a, .sorter a", function (e) {
        var $a = $(this),
            $grid = $a.closest(".magic_grid[data-remote=true]"),
            url = $a.attr('href');
        $grid.trigger("magic_grid:loading");
        $grid.load(url + ' #' + $grid.attr('id') + " > *");
        return false;
    });
    $(".magic_grid[data-searcher]").each( function () {
        var $grid = $(this),
            grid_id = this.id,
            input_id = "#" + $grid.data("searcher"),
            live = $grid.data("live-search"),
            // keydown seems to be the only reliable and portable way to capture
            // things like backspace and delete
            events = (live ? "keydown change search" : "change search"),
            timer = null,
            minLength = $(input_id).data("min-length") || 3,
            current = $(input_id).val();
        $grid.parent().on(events, input_id, function (e) {
            var is_manual = (e.type == 'search' ||
                             e.type == 'change' ||
                             (e.type == 'keydown' && e.which == '13')
                            ),
                base_url = $grid.data("current"),
                params = {'magic_grid_id' : grid_id},
                //40 wpm typists == 280ms/keystroke
                //90 wpm typists == 120ms/keystroke
                delay = is_manual ? 1 : 500;
            clearTimeout(timer);
            timer = setTimeout(function () {
                var $input = $(input_id),
                    value = $input.val(),
                    length = value.length,
                    url = base_url + "?",
                    relevant = is_manual || (value != current && (length >= minLength || length == 0)),
                    pos = $input.getCursor();
                clearTimeout(timer);
                params[grid_id + "_q"] = value;
                url += $.param(params);
                if (relevant) {
                    $grid.trigger("magic_grid:loading");
                    if ($grid.data("remote")) {
                        $(input_id).prop('disabled', true);
                        $grid.load(url + ' #' + grid_id + ' > *', function () {
                            // Move the cursor back to where it was,
                            // less surprising that way.
                            current = $(input_id).setCursor(pos).val();
                        });
                    } else {
                        window.location = url;
                    }
                }
            }, delay);
            e.stopImmediatePropagation();
            //return (!!timer);
        });
    });
    $(".magic_grid[data-listeners]").each( function () {
        var $grid = $(this),
            listeners = $grid.data("listeners"),
            grid_id = this.id
            base_url = $grid.data("current"),
            get_values = function () {
                var $input, key, params = {};
                for (var k in listeners) {
                    key = listeners[k];
                    $input = $("#" + k);
                    if ($input.is(":checkbox")) {
                        params[key] = $input.is(":checked");
                    } else if ($input.is("select")) {
                        params[key] = $input.find("option:selected").val();
                    } else {
                        params[key] = $input.val();
                    }
                }
                return params;
            },
            handler = function (change) {
                var url = base_url + "?" + $.param(get_values());
                $grid.trigger("magic_grid:loading");
                if ($grid.data("remote")) {
                    $grid.load(url + ' #' + grid_id + " > *");
                } else {
                    window.location = url;
                }
            };
        for (var k in listeners) {
            if ($("#" + k).hasClass("ready")) {
                $("#" + k).on("change", {"field": listeners[k]}, handler);
            } else {
                $("#" + k).on("ready", {"field": listeners[k]}, function (ready) {
                    $(this).on("change", ready.data, handler);
                });
            }
        }
    });
});
<commit_msg>JS cleanups<commit_after>// vim: ts=4 et sw=4 sts=4

$(function () {
    // Micro-plugin for positioning the cursor
    $.fn.setCursor = $.fn.setCursor || function(pos) {
        return this.each(function() {
            if (this.setSelectionRange) {
                this.focus();
                this.setSelectionRange(pos, pos);
            } else if (this.createTextRange) {
                var range = this.createTextRange();
                range.collapse(true);
                range.moveEnd('character', pos);
                range.moveStart('character', pos);
                range.select();
            }
        });
    };
    // Micro-plugin for getting the position of the cursor
    $.fn.getCursor = $.fn.getCursor || function () {
        // IE doesn't have selectionStart, and I'm too lazy to implement
        // the IE specific version of this, so we'll just assume
        // the cursor is at the end of the input for this case.
        if (typeof this[0].selectionStart === 'undefined') {
            return this.value.length;
        } else {
            return this[0].selectionStart;
        }
    };
    $(".magic_grid[data-default-ajax-handler=true]").on("magic_grid:loading", function (e) {
        $(".magic_grid_spinner", this).
            addClass("ui-icon-spinner").
                html("Loading...");
    });
    $(".magic_grid[data-remote=true]").on( "click", ".pagination a, .sorter a", function (e) {
        var $grid = $(e.delegateTarget),
            url = this.href;
        $grid.trigger("magic_grid:loading");
        $grid.load(url + ' #' + $grid.attr('id') + " > *");
        return false;
    });
    $(".magic_grid[data-searcher]").each( function () {
        var $grid = $(this),
            grid_id = this.id,
            input_id = "#" + $grid.data("searcher"),
            live = $grid.data("live-search"),
            // keydown seems to be the only reliable and portable way to capture
            // things like backspace and delete
            events = (live ? "keydown change search" : "change search"),
            timer = null,
            minLength = $(input_id).data("min-length") || 3,
            current = $(input_id).val();
        $grid.parent().on(events, input_id, function (e) {
            var is_manual = (e.type == 'search' ||
                             e.type == 'change' ||
                             (e.type == 'keydown' && e.which == '13')
                            ),
                base_url = $grid.data("current"),
                params = {'magic_grid_id' : grid_id},
                //40 wpm typists == 280ms/keystroke
                //90 wpm typists == 120ms/keystroke
                delay = is_manual ? 1 : 500;
            clearTimeout(timer);
            timer = setTimeout(function () {
                var $input = $(input_id),
                    value = $input.val(),
                    length = value.length,
                    url = base_url + "?",
                    relevant = is_manual || (value != current && (length >= minLength || length == 0)),
                    pos = $input.getCursor();
                clearTimeout(timer);
                params[grid_id + "_q"] = value;
                url += $.param(params);
                if (relevant) {
                    $grid.trigger("magic_grid:loading");
                    if ($grid.data("remote")) {
                        $(input_id).prop('disabled', true);
                        $grid.load(url + ' #' + grid_id + ' > *', function () {
                            // Move the cursor back to where it was,
                            // less surprising that way.
                            // EXCEPT FOR IE, that is
                            current = $(input_id).setCursor(pos).val();
                        });
                    } else {
                        window.location = url;
                    }
                }
            }, delay);
            e.stopImmediatePropagation();
        });
    });
    $(".magic_grid[data-listeners]").each( function () {
        var $grid = $(this),
            listeners = $grid.data("listeners"),
            grid_id = this.id
            base_url = $grid.data("current"),
            get_values = function () {
                var $input, key, params = {};
                for (var k in listeners) {
                    key = listeners[k];
                    $input = $("#" + k);
                    if ($input.is(":checkbox")) {
                        params[key] = $input.is(":checked");
                    } else if ($input.is("select")) {
                        params[key] = $input.find("option:selected").val();
                    } else {
                        params[key] = $input.val();
                    }
                }
                return params;
            },
            handler = function (change) {
                var url = base_url + "?" + $.param(get_values());
                $grid.trigger("magic_grid:loading");
                if ($grid.data("remote")) {
                    $grid.load(url + ' #' + grid_id + " > *");
                } else {
                    window.location = url;
                }
            };
        for (var k in listeners) {
            if ($("#" + k).hasClass("ready")) {
                $("#" + k).on("change", {"field": listeners[k]}, handler);
            } else {
                $("#" + k).on("ready", {"field": listeners[k]}, function (ready) {
                    $(this).on("change", ready.data, handler);
                });
            }
        }
    });
});
<|endoftext|>


<commit_before>/**
 * Created by Awesome on 3/7/2016.
 */

// use strict
'use strict';

// require dependencies
var acl          = require ('acl');
var redis        = require ('socket.io-redis');
var daemon       = require ('daemon');
var session      = require ('express-session');
var passport     = require ('passport.socketio');
var socketio     = require ('socket.io');
var redisStore   = require ('connect-redis') (session);
var cookieParser = require ('cookie-parser');

// require local dependencies
var user   = require ('user/models/user');
var cache  = require ('app/cache/config.json');
var config = require ('app/config');

/**
 * build socket daemon
 */
class socket extends daemon {
    /**
     * construct socket daemon
     *
     * @param  {eden} eden    eden app
     */
    constructor (eden) {
        // run super
        super (eden);

        // bind variables
        this.io      = false;
        this.users   = {};
        this.sockets = {};
        this.index   = 0;

        // bind methods
        this.build  = this.build.bind (this);
        this.socket = this.socket.bind (this);

        // bind private methods
        this._route       = this._route.bind (this);
        this._connections = this._connections.bind (this);

        // check if socket enabled;
        if (!config.socket) {
            return;
        }

        // build
        this.build ();
    }

    /**
     * build chat daemon
     */
    build () {
        // set io
        this.io = socketio (this.eden.server);

        // set redis adapter
        this.io.adapter (redis ({
            'key' : config.domain + ':socket'
        }));

        // use passport auth
        this.io.use (passport.authorize ({
            key          : 'eden.session.id',
            store        : new redisStore (),
            secret       : config.session,
            cookieParser : cookieParser,
            fail         : (data, message, critical, accept) => {
                // accept connection
                accept (null, false);
            },
            success      : (data, accept) => {
                // accept
                accept (null, true);
            }
        }));

        // listen for connection
        this.io.on ('connection', this.socket);

        // setup emiti function
        var emitFn = (data) => {
            // emit
            this.io.emit (data.type, data.data);
        };

        // setup room function
        var roomFn = (data) => {
            // emit
            this.io.to (data.room).emit (data.type, data.data);
        };

        // setup user fn
        var userFn = (data) => {
            // check data.to
            if (!data.to) {
                return;
            }

            // loop all user socket connections
            if (this.users[data.to]) {
                for (var i = 0; i < this.users[data.to].length; i++) {
                    // check socket exists
                    if (this.sockets[this.users[data.to][i]]) {
                        // emit to socket
                        this.sockets[this.users[data.to][i]].emit (data.type, data.data);
                    }
                }
            }
        };

        // listen for global event for emit
        this.eden.on ('socket:emit', emitFn);

        // listen for global event for room
        this.eden.on ('socket:room', roomFn);

        // listen for global event for user
        this.eden.on ('socket:user', userFn, true);
    }

    /**
     * set socket object
     *
     * @param socket
     */
     socket (socket) {
         // publish connections
         this._connections ();

         // check for user
         var User = (!socket.request.user || !socket.request.user.logged_in) ? false : socket.request.user;

         // set socketid
         let socketid = this.index;
         this.index++;

         // log connection
         this.logger.log ('debug', 'client ' + socketid + ' - ' + (User ? User.get ('username') : 'anonymous') + ' connected', {
             'class' : 'socketDaemon'
         });

         // add socket to sockets object
         this.sockets[socketid] = socket;

         // check if user on handshake
         if (User) {
             // create users object
             if (!this.users[User.get ('_id').toString ()]) {
                 this.users[User.get ('_id').toString ()] = [];
             }
             // add i to users object
             this.users[User.get ('_id').toString ()].push (socketid);
         }

         // disconnect socket
         socket.on ('disconnect', () => {
             // log disconnected
             this.logger.log ('debug', 'client ' + socketid + ' - ' + (User ? User.get ('username') : 'anonymous') + ' disconnected', {
                 'class' : 'socketDaemon'
             });

             // publish connections
             this._connections ();

             // remove socket id from user
             if (User) {
                 if (this.users[User.get ('_id').toString ()] && this.users[User.get ('_id').toString ()].indexOf (socketid) > -1) {
                     this.users[User.get ('_id').toString ()].splice (this.users[User.get ('_id').toString ()].indexOf (socketid), 1);
                 }
             }

             // delete socket
             delete this.sockets[socketid];
         });

         // create functions for cached config
         for (var type in cache.sockets) {
             // create listener
             this._route (cache.sockets[type], socket, User);
         }
     }

     /**
      * creates route listener
      *
      * @param  {Object} route
      * @param  {socket} socket
      * @param  {user}   User
      */
     _route (route, socket, User) {
         // create socket listener
         socket.on (route.type, async (data) => {
             // reload user
             if (User) {
                 User = await user.findById (User.get ('_id').toString ());
             }

             // check if can
             var can = await acl.test (route.acl, User);

             // return if cant
             if (can !== true) {
                 return;
             }

             // get controller
             var controller = await this.eden.controller (route.controller);

             // run function
             controller[route.socket] (socket, data, User);
         });
     }

     /**
      * publishes socket connection count
      *
      * @private
      */
     _connections () {
         // publish to eden
         this.eden.emit ('socket:connections', Object.keys (this.io.sockets.sockets).length, true);
     }
}

/**
 * construct socket daemon
 *
 * @type {socket}
 */
module.exports = socket;
<commit_msg>check all theads for user in socketio<commit_after>/**
 * Created by Awesome on 3/7/2016.
 */

// use strict
'use strict';

// require dependencies
var acl          = require ('acl');
var redis        = require ('socket.io-redis');
var daemon       = require ('daemon');
var session      = require ('express-session');
var passport     = require ('passport.socketio');
var socketio     = require ('socket.io');
var redisStore   = require ('connect-redis') (session);
var cookieParser = require ('cookie-parser');

// require local dependencies
var user   = require ('user/models/user');
var cache  = require ('app/cache/config.json');
var config = require ('app/config');

/**
 * build socket daemon
 */
class socket extends daemon {
    /**
     * construct socket daemon
     *
     * @param  {eden} eden    eden app
     */
    constructor (eden) {
        // run super
        super (eden);

        // bind variables
        this.io      = false;
        this.users   = {};
        this.sockets = {};
        this.index   = 0;

        // bind methods
        this.build  = this.build.bind (this);
        this.socket = this.socket.bind (this);

        // bind private methods
        this._route       = this._route.bind (this);
        this._connections = this._connections.bind (this);

        // check if socket enabled;
        if (!config.socket) {
            return;
        }

        // build
        this.build ();
    }

    /**
     * build chat daemon
     */
    build () {
        // set io
        this.io = socketio (this.eden.server);

        // set redis adapter
        this.io.adapter (redis ({
            'key' : config.domain + ':socket'
        }));

        // use passport auth
        this.io.use (passport.authorize ({
            key          : 'eden.session.id',
            store        : new redisStore (),
            secret       : config.session,
            cookieParser : cookieParser,
            fail         : (data, message, critical, accept) => {
                // accept connection
                accept (null, false);
            },
            success      : (data, accept) => {
                // accept
                accept (null, true);
            }
        }));

        // listen for connection
        this.io.on ('connection', this.socket);

        // setup emiti function
        var emitFn = (data) => {
            // emit
            this.io.emit (data.type, data.data);
        };

        // setup room function
        var roomFn = (data) => {
            // emit
            this.io.to (data.room).emit (data.type, data.data);
        };

        // setup user fn
        var userFn = (channel, data) => {
            // check data.to
            if (!data.to) {
                return;
            }

            // loop all user socket connections
            if (this.users[data.to]) {
                for (var i = 0; i < this.users[data.to].length; i++) {
                    // check socket exists
                    if (this.sockets[this.users[data.to][i]]) {
                        // emit to socket
                        this.sockets[this.users[data.to][i]].emit (data.type, data.data);
                    }
                }
            }
        };

        // listen for global event for emit
        this.eden.on ('socket:emit', emitFn);

        // listen for global event for room
        this.eden.on ('socket:room', roomFn);

        // listen for global event for user
        this.eden.on ('socket:user', userFn, true);
    }

    /**
     * set socket object
     *
     * @param socket
     */
     socket (socket) {
         // publish connections
         this._connections ();

         // check for user
         var User = (!socket.request.user || !socket.request.user.logged_in) ? false : socket.request.user;

         // set socketid
         let socketid = this.index;
         this.index++;

         // log connection
         this.logger.log ('debug', 'client ' + socketid + ' - ' + (User ? User.get ('username') : 'anonymous') + ' connected', {
             'class' : 'socketDaemon'
         });

         // add socket to sockets object
         this.sockets[socketid] = socket;

         // check if user on handshake
         if (User) {
             // create users object
             if (!this.users[User.get ('_id').toString ()]) {
                 this.users[User.get ('_id').toString ()] = [];
             }
             // add i to users object
             this.users[User.get ('_id').toString ()].push (socketid);
         }

         // disconnect socket
         socket.on ('disconnect', () => {
             // log disconnected
             this.logger.log ('debug', 'client ' + socketid + ' - ' + (User ? User.get ('username') : 'anonymous') + ' disconnected', {
                 'class' : 'socketDaemon'
             });

             // publish connections
             this._connections ();

             // remove socket id from user
             if (User) {
                 if (this.users[User.get ('_id').toString ()] && this.users[User.get ('_id').toString ()].indexOf (socketid) > -1) {
                     this.users[User.get ('_id').toString ()].splice (this.users[User.get ('_id').toString ()].indexOf (socketid), 1);
                 }
             }

             // delete socket
             delete this.sockets[socketid];
         });

         // create functions for cached config
         for (var type in cache.sockets) {
             // create listener
             this._route (cache.sockets[type], socket, User);
         }
     }

     /**
      * creates route listener
      *
      * @param  {Object} route
      * @param  {socket} socket
      * @param  {user}   User
      */
     _route (route, socket, User) {
         // create socket listener
         socket.on (route.type, async (data) => {
             // reload user
             if (User) {
                 User = await user.findById (User.get ('_id').toString ());
             }

             // check if can
             var can = await acl.test (route.acl, User);

             // return if cant
             if (can !== true) {
                 return;
             }

             // get controller
             var controller = await this.eden.controller (route.controller);

             // run function
             controller[route.socket] (socket, data, User);
         });
     }

     /**
      * publishes socket connection count
      *
      * @private
      */
     _connections () {
         // publish to eden
         this.eden.emit ('socket:connections', Object.keys (this.io.sockets.sockets).length, true);
     }
}

/**
 * construct socket daemon
 *
 * @type {socket}
 */
module.exports = socket;
<|endoftext|>