<commit_before>import * as actions from './actions.js';

import {
  getCurrent,
  getLast,
  getNext,
} from './selector.js';

import match from './match.js';
import createHistory from './history.js';

import {
  stringifyLocation
} from './location.js';

// keep routes outside of the function to access from middleware.
let routes = {};
let history = createHistory();
let ignoreHistory = false;

// return cloned routes.
export const getRoutes = () => Object.assign({}, routes);
export const getHistory = () => history;
export const getIgnoreHistory = () => ignoreHistory;

export const findRouteKeyByPath = (path) => {
  const routes = Object.keys(getRoutes());
  for (let i = 0; i < routes.length; i++) {
    const key = routes[ i ];
    if (key !== '*' && match(key, path)) return key; // return matched route.
  }
  return null; // not found
};

// add route/params to location.
export const enrichLocation = ({ pathname, search }) => {
  const route = findRouteKeyByPath(pathname);
  const params = match(route, pathname);
  return {
    pathname,
    search,
    route,
    params
  }
};

// check and handle locationChangeFailure
export const locationChangeFailure = (store, data) => {
  const { dispatch, getState } = store;
  const routes = getRoutes();

  // if called with falsy data(includes Error)
  if (!data || data instanceof Error) {
    dispatch(actions.locationChangeFailure(data));

    if (routes.Error) {
      // call error handler.
      routes.Error({ dispatch, state: getState() });
    } else {
      // put some messages with no onError handler.
      console.warn('You should register router.onError to handle this routing error. data =', data);
    }
    return true;
  }

  return false;
};

export const routeLeave = (store) => {
  const { dispatch, getState } = store;
  const current = getCurrent(getState());
  const currentRoute = current && routes[ current.route ];
  // call onLeave hook
  if (currentRoute && currentRoute.onLeave) {
    currentRoute.onLeave({dispatch, state: getState()});
  }
};

export const routeEnter = (store, cb = () => {}) => {
  const { dispatch, getState } = store;
  const next = getNext(getState());
  const nextRoute = next && routes[ next.route ];

  // call onEnter hook
  if (nextRoute && nextRoute.onEnter) {
    // if onEnter accepts callback.
    if (nextRoute.onEnter.length == 2) {
      nextRoute.onEnter({ dispatch, state: getState() }, cb);
      return;
    } else {
      nextRoute.onEnter({ dispatch, state: getState() });
    }
  }
};

export const createRouterAction = (store) => {
  const { dispatch, getState } = store;
  // routerAction
  return (action) => function routerAction () {
    dispatch(actions[ action ].apply(this, arguments))

    // onLeave
    routeLeave(store);

    // onEnter
    const next = getNext(getState());
    routeEnter(store, (data = true) => {
      // return if locationChangeFailure exists.
      if (locationChangeFailure(store, data)) return;
      dispatch(actions.locationChange(stringifyLocation(next)));
    });

    next && dispatch(actions.locationChange(stringifyLocation(next)));
  }
}

// router
export default (store, option = {}) => {
  const { dispatch, getState, subscribe } = store;
  // initialize routes.
  routes = {};
  // initialize history.
  history = createHistory(option.history);
  // initialize ignoreHistory.
  ignoreHistory = false;
  // initialize routerAction.
  const routerAction = createRouterAction(store);

  // call on Route enter.
  const onError = (fn) => {
    routes.Error = fn;
  };

  // bind route to pathname.
  const on = (pathname, route) => {
    let nextRoute;

    // allow pass render function directly.
    if (typeof route === 'function') {
      nextRoute = { render: route }
    } else if (route.onEnter || route.onLeave || route.render) {
      // allow register object.
      nextRoute = Object.assign({}, route);
    } else {
      // allow register jsx.
      nextRoute = { ...route.props, render: () => route }
    }
    routes[ pathname ] = nextRoute;

    // set current on initial render
    const location = history.getLocation();
    if (pathname !== '*' && match(pathname, location.pathname)) {
      dispatch(actions.locationChange(stringifyLocation(location)))
    }
  }

  // render
  const render = () => {
    const current = getCurrent(getState());
    let currentRoute = current && routes[ current.route ];
    if (!currentRoute && routes['*']) {
      currentRoute = routes['*'];
    }
    return currentRoute ? currentRoute.render() : null;
  }

  const pop = routerAction('pop');

  // handle store.update(for redux time travel)
  // use currentLocation if last state is not defined.
  let last = getLast(getState()) ? getLast(getState()) : history.getLocation();

  const unsubscribe = subscribe(() => {
    let current = getCurrent(getState());

    if (ignoreHistory) return;
    if (!current) {
      // when time travel starts.
      current = last;
    } else if (stringifyLocation(last) === stringifyLocation(current)) {
       return;
    }

    last = current;

    // prevent push to same path.
    const location = history.getLocation();
    if (location.pathname === current.pathname) return;

    history.push(stringifyLocation(current));
  });

  const unlisten = history.listen((e) => {
    const current = history.getLocation();
    if (stringifyLocation(last) === stringifyLocation(current)) return true;

    // set ignoreHistory for store subscriber.
    ignoreHistory = true;
    last = current;
    pop(stringifyLocation(current));
    ignoreHistory = false;
  });

  // destroy instance.
  const destroy = () => {
    unsubscribe();
    unlisten();
  }

  return {
    on,
    onError,
    render,
    destroy,

    // history actions.
    push: routerAction('push'),
    replace: routerAction('replace'),
    go: routerAction('go'),
    back: routerAction('back'),
    forward: routerAction('forward')
  }
}
<commit_msg>add working progress.<commit_after>import * as actions from './actions.js';

import {
  getCurrent,
  getLast,
  getNext,
} from './selector.js';

import match from './match.js';
import createHistory from './history.js';

import {
  stringifyLocation
} from './location.js';

// keep routes outside of the function to access from middleware.
let routes = {};
let history = createHistory();
let ignoreHistory = false;

// return cloned routes.
export const getRoutes = () => Object.assign({}, routes);
export const getHistory = () => history;
export const getIgnoreHistory = () => ignoreHistory;

export const findRouteKeyByPath = (path) => {
  const routes = Object.keys(getRoutes());
  for (let i = 0; i < routes.length; i++) {
    const key = routes[ i ];
    if (key !== '*' && match(key, path)) return key; // return matched route.
  }
  return null; // not found
};

// add route/params to location.
export const enrichLocation = ({ pathname, search }) => {
  const route = findRouteKeyByPath(pathname);
  const params = match(route, pathname);
  return {
    pathname,
    search,
    route,
    params
  }
};

// check and handle locationChangeFailure
export const locationChangeFailure = (store, data) => {
  const { dispatch, getState } = store;
  const routes = getRoutes();

  // if called with falsy data(includes Error)
  if (!data || data instanceof Error) {
    dispatch(actions.locationChangeFailure(data));

    if (routes.Error) {
      // call error handler.
      routes.Error({ dispatch, state: getState() });
    } else {
      // put some messages with no onError handler.
      console.warn('You should register router.onError to handle this routing error. data =', data);
    }
    return true;
  }

  return false;
};

export const routeLeave = (store) => {
  const { dispatch, getState } = store;
  const current = getCurrent(getState());
  const currentRoute = current && routes[ current.route ];
  // call onLeave hook
  if (currentRoute && currentRoute.onLeave) {
    currentRoute.onLeave({dispatch, state: getState()});
  }
};

export const routeEnter = (store, cb = () => {}) => {
  const { dispatch, getState } = store;
  const next = getNext(getState());
  const nextRoute = next && routes[ next.route ];

  // call onEnter hook
  if (nextRoute && nextRoute.onEnter) {
    // if onEnter accepts callback.
    if (nextRoute.onEnter.length == 2) {
      nextRoute.onEnter({ dispatch, state: getState() }, cb);
      return;
    } else {
      nextRoute.onEnter({ dispatch, state: getState() });
    }
  }
};

export const createRouterAction = (store) => {
  const { dispatch, getState } = store;
  // routerAction
  return (action) => function routerAction () {
    dispatch(actions[ action ].apply(this, arguments))

    // onLeave
    routeLeave(store);

    // onEnter
    const next = getNext(getState());
    routeEnter(store, (data = true) => {
      // return if locationChangeFailure exists.
      if (locationChangeFailure(store, data)) return;
      dispatch(actions.locationChange(stringifyLocation(next)));
    });

    // TODO: ここを呼ばれないようにする。
    next && dispatch(actions.locationChange(stringifyLocation(next)));
  }
}

// router
export default (store, option = {}) => {
  const { dispatch, getState, subscribe } = store;
  // initialize routes.
  routes = {};
  // initialize history.
  history = createHistory(option.history);
  // initialize ignoreHistory.
  ignoreHistory = false;
  // initialize routerAction.
  const routerAction = createRouterAction(store);

  const pop = routerAction('pop');
  const push = routerAction('push');

  // call on Route enter.
  const onError = (fn) => {
    routes.Error = fn;
  };

  // bind route to pathname.
  const on = (pathname, route) => {
    let nextRoute;

    // allow pass render function directly.
    if (typeof route === 'function') {
      nextRoute = { render: route }
    } else if (route.onEnter || route.onLeave || route.render) {
      // allow register object.
      nextRoute = Object.assign({}, route);
    } else {
      // allow register jsx.
      nextRoute = { ...route.props, render: () => route }
    }
    routes[ pathname ] = nextRoute;

    // set current on initial render
    const location = history.getLocation();
    if (pathname !== '*' && match(pathname, location.pathname)) {
      if (nextRoute.onEnter) {
        push(stringifyLocation(location))
        return;
      }
      dispatch(actions.locationChange(stringifyLocation(location)))
    }
  }

  // render
  const render = () => {
    const current = getCurrent(getState());
    let currentRoute = current && routes[ current.route ];
    if (!currentRoute && routes['*']) {
      currentRoute = routes['*'];
    }
    return currentRoute ? currentRoute.render() : null;
  }

  // handle store.update(for redux time travel)
  // use currentLocation if last state is not defined.
  let last = getLast(getState()) ? getLast(getState()) : history.getLocation();

  const unsubscribe = subscribe(() => {
    let current = getCurrent(getState());

    if (ignoreHistory) return;
    if (!current) {
      // when time travel starts.
      current = last;
    } else if (stringifyLocation(last) === stringifyLocation(current)) {
       return;
    }

    last = current;

    // prevent push to same path.
    const location = history.getLocation();
    if (location.pathname === current.pathname) return;

    history.push(stringifyLocation(current));
  });

  const unlisten = history.listen((e) => {
    const current = history.getLocation();
    if (stringifyLocation(last) === stringifyLocation(current)) return true;

    // set ignoreHistory for store subscriber.
    ignoreHistory = true;
    last = current;
    pop(stringifyLocation(current));
    ignoreHistory = false;
  });

  // destroy instance.
  const destroy = () => {
    unsubscribe();
    unlisten();
  }

  return {
    on,
    onError,
    render,
    destroy,

    // history actions.
    push: push,
    replace: routerAction('replace'),
    go: routerAction('go'),
    back: routerAction('back'),
    forward: routerAction('forward')
  }
}
<|endoftext|>


<commit_before>'use strict';

var BearerToken = require('./bearer');
var Inflector = require('inflected');
var Mutation = require('./mutation');

var REST = {
  index: { endpoint: '/', method: 'get' },
  new: { endpoint: '/', method: 'post' },
  show: { endpoint: '/:id', method: 'get' },
  update: { endpoint: '/:id', method: 'put' },
  delete: { endpoint: '/:id', method: 'delete' }
};

var MethodMatrix = {
  GET: 'read',
  POST: 'create',
  PUT: 'edit',
  DELETE: 'delete'
};

var Routes = function(express, DS) {
  var Builder = { express: express };
  var middleware = [];
  var controllers = [];
  var DS = {};
  var Scopes = {};
  var Instance = {};
  var ErrorHandler = require('./errors');

  Builder.mount = function(models, instanceObj) {
    DS = models;
    Instance = instanceObj;

    for (var i = 0; i < controllers.length; i++) {
      Builder.route(controllers[i](DS, Mutation(DS, Instance)));
    }

    express.use(ErrorHandler);
  };

  Builder.addErrorHandler = function(func) {
    ErrorHandler = func;
  };

  Builder.addController = function(controller) {
    controllers.push(controller);
  };

  Builder.registerMiddleware = function(func) {
    middleware.push(func);
  };

  Builder.mixInController = function(spec, action, rest, params) {
    return function(req, res, next) {
      var route = Inflector.singularize(Inflector.camelize(req.route.path.split('/')[1]));
      var resource = params.parent ? params.parent.resource : route;
      var locator = params.parent ? params.parent.fieldName + '._id' : '_id';

      req.controller = {
        spec: spec,
        action: action,
        permissions: Instance.scope[route],
        route: route,
        model: resource,
        locator: locator,
        _rest: rest,
        child: route !== resource
      };

      if (params.parent) req.controller.fieldName = params.parent.fieldName;

      return next();
    };
  };

  Builder.runMiddlewares = function(req, res, next) {
    if (middleware.length < 1) return next();

    for (var i = 0; i < middleware.length; i++) {
      middleware[i](req, res, next);
    }
  };

  // Sets an individual route
  Builder.route = function(controller) {
    var params = {};

    if (controller._parent) params.parent = controller._parent;

    controller._expose.forEach(function(el, i, arr) {
      if (typeof el === 'object') Builder.customRoute(controller._verb, el, controller[el.name], params);
      if (typeof el === 'string') Builder.restRoute(controller._verb, el, controller[el], params);
    });
  };

  // Handles a non-RESTful controller method
  Builder.customRoute = function(verb, spec, action, params) {
    if (!spec || !action) return;

    var method = spec.method;
    var endpoint = '/' + verb;

    if (spec.endpoint !== '/') endpoint += '/' + spec.endpoint;

    express[method](
      endpoint,
      Builder.mixInController(spec, action, false, params),
      BearerToken.serialize,
      Builder.findUser,
      Builder.resourceQuery,
      Builder.runQuery,
      Builder.catchScope,
      Builder.filterAllowedFields,
      Builder.runMiddlewares,
      action.responder
    );
  };

  // Handles a RESTful controller method
  Builder.restRoute = function(verb, name, action, params) {
    if (!REST[name] || !action) return;

    var method = REST[name].method;
    var endpoint = '/' + verb;

    if (REST[name].endpoint !== '/') endpoint += REST[name].endpoint;

    express[method](
      endpoint,
      Builder.mixInController({ verb: verb, name: name }, action, true, params),
      BearerToken.serialize,
      Builder.findUser,
      Builder.resourceQuery,
      Builder.processIncludables,
      Builder.runQuery,
      Builder.catchScope,
      Builder.filterAllowedFields,
      Builder.runMiddlewares,
      action.responder
    );
  };

  Builder.findUser = function(req, res, next) {
    if (!req.bearerToken) return next();

    var User = DS[Instance.scopable.model];
    var query = User.findOne({ _id: req.bearerToken.user });

    query.lean().exec(function(err, user) {
      if (err) return next(err);

      req.user = user;
      return next();
    });
  };

  Builder.catchScope = function(req, res, next) {
    var ownership = false;
    var permissions = false;

    if (req.controller._rest) {
      permissions = req.controller.permissions[MethodMatrix[req.method]] || req.controller.permissions;
    }

    req.caughtScope = '*';

    if (req.user) {
      var userScopes = req.user[Instance.scopable.field];
      if (['index', 'new'].indexOf(req.controller.spec.name) === -1) ownership = Builder._checkForOwnership(req);
      req.caughtScope = (ownership) ? 'owner' : Builder._highestScope(permissions, userScopes);

      if (req.caughtScope === '*' && !req.controller._rest && userScopes.length > 1) {
        for (var i = 0; i < userScopes.length; i++) {
          if (req.controller.action.scope.indexOf(userScopes[i]) > -1) req.caughtScope = userScopes[i];
        }
      }
    }

    if (permissions === true || permissions[req.caughtScope] === true) {
      req.allowedFields = true;
      return next();
    } else if (Array.isArray(permissions)) {
      req.allowedFields = JSON.parse(JSON.stringify(permissions));
      return next();
    } else if (Array.isArray(permissions[req.caughtScope])) {
      req.allowedFields = JSON.parse(JSON.stringify(permissions[req.caughtScope]));
      return next();
    } else if (!req.controller._rest && ownership) {
      req.allowedFields = true;
      return next();
    } else if (!req.controller._rest && req.controller.action.scope.indexOf(req.caughtScope) > -1) {
      req.allowedFields = true;
      return next();
    }

    // The user is not authorized to see this resource
    return next({ stack: 'auth', translation: 'api.error.auth.insufficient_privilege', message: 'Insufficient privileges for that' });
  };

  Builder.resourceQuery = function(req, res, next) {
    req.searchParams = {};

    if (!req.params.id && req.controller.permissions.searchableBy) {
      var allowed = req.controller.permissions.searchableBy;

      for (var i = 0; i < allowed.length; i++) {
        if (req.parsedQuery[allowed[i]]) req.searchParams[allowed[i]] = req.parsedQuery[allowed[i]];
      }
    }

    return next();
  };

  Builder.processIncludables = function(req, res, next) {
    req.includeRelations = [];

    if (!req.query.include || req.query.include === '') return next();

    var relations = req.query.include.split(',');

    for (var i = 0; i < relations.length; i++) {
      req.includeRelations.push(relations[i]);
    }

    return next();
  };

  Builder.runQuery = function(req, res, next) {
    var Model = DS[req.controller.model];
    req.rawResources = {};

    if (req.params.id && !req.controller.child) {
      var q = Model.findById(req.params.id);

      q.lean().exec(function(err, result) {
        if (err) return next(err);

        req.rawResources = result;
        return next();
      });
    } else if (req.params.id && req.controller.child) {
      var search = {};
      search[req.controller.locator] = req.params.id;

      var q = Model.findOne(search);

      q.lean().exec(function(err, result) {
        if (err) return next(err);
        if (result === null) return next({ message: 'not found' });

        var resource = result[req.controller.fieldName];

        if (Array.isArray(resource)) {
          for (var i = 0; i < resource.length; i++) {
            if (req.params.id === resource[i]._id.toString()) resource = resource[i];
          }
        }

        req.rawResources = resource;
        return next();
      });

    } else if (!req.params.id && req.method === 'GET' && !req.controller.child) {
      var q = Model.find(req.searchParams);
      var count = Model.count(req.searchParams);

      if (req.query.page) {
        var perPage = parseInt(req.query.perPage) || 20;
        q.skip((parseInt(req.query.page) * perPage) - perPage);
        q.limit(perPage);
      } else {
        q.limit(req.query.limit || 20);
      }

      if (req.includeRelations) {
        for (var i = 0; i < req.includeRelations.length; i++) {
          q.populate(req.includeRelations[i]);
        }
      }

      if (req.query.sort) q.sort(req.query.sort);

      q.lean().exec(function(err, result) {
        if (err) return next(err);

        count.lean().exec(function(err, counter) {
          if (err) return next(err);

          req.rawResources = result;
          req.rawResourcesCount = counter;
          return next();
        });
      });
    } else if (!req.params.id && req.method === 'GET' && req.controller.child) {
      var projection = {};
      var fields = Object.keys(DS[req.controller.route].schema.paths);

      for (var i = 0; i < fields.length; i++) {
        projection[fields[i]] = '$' + req.controller.fieldName + '.' + fields[i];
      }

      var query = [
        { $match: req.searchParams },
        { $unwind: '$' + req.controller.fieldName },
        { $match: req.searchParams },
        { $project: projection }
      ];

      var agg = Model.aggregate(query);

      agg.exec(function(err, result) {
        if (err) return next(err);

        var resource = [];

        if (req.query.page) {
          var perPage = parseInt(req.query.perPage) || 20;
          var skip = (parseInt(req.query.page) * perPage) - perPage;
          resource = result.slice(skip, skip + perPage);
        } else {
          resource = result.slice(0, 19);
        }

        req.rawResources = resource;
        req.rawResourcesCount = result.length;
        return next();
      });
    } else {
      return next();
    }
  };

  Builder.filterAllowedFields = function(req, res, next) {
    req.resource = [];

    if (Object.keys(req.rawResources).length === 0) return next();
    if (req.allowedFields !== true) req.allowedFields.push('_id');

    if (Array.isArray(req.rawResources)) {
      for (var i = 0; i < req.rawResources.length; i++) {
        req.resource.push(Builder.filterFromResource(req.rawResources[i], req.allowedFields));
      }
    } else {
      req.resource = Builder.filterFromResource(req.rawResources, req.allowedFields);
    }

    return next();
  };

  Builder.filterFromResource = function(resource, allowed) {
    if (allowed === true) return resource;
    var safeResource = {};

    for (var i = 0; i < allowed.length; i++) {
      safeResource[allowed[i]] = resource[allowed[i]];
    }

    return safeResource;
  };

  Builder._checkForOwnership = function(req) {
    if (Object.keys(req.rawResources).length === 0) return false;

    var model = req.controller.model;
    var permissions = req.controller.permissions;

    if (!permissions.owners || permissions.owners.length < 1) return false;
    if (Array.isArray(req.rawResources)) return false;
    if (!req.user) return false;

    for (var i = 0; i < permissions.owners.length; i++) {
      if (typeof req.rawResources[permissions.owners[i]] === 'undefined') return false;
      if (typeof req.user._id === 'undefined') return false;

      if (req.rawResources[permissions.owners[i]].toString() === req.user._id.toString()) return true;
    }

    return false;
  };

  Builder._highestScope = function(permissions, scopes) {
    if (typeof permissions === 'undefined') return '*';

    var highest = 0;
    var fieldCount = 0;

    scopes.unshift('*');

    for (var i = 0; i < scopes.length; i++) {
      if (permissions === true || Array.isArray(permissions)) {
        return '*';
      } else if (permissions[scopes[i]] === true) {
        return scopes[i];
      } else if (Array.isArray(permissions[scopes[i]]) && (fieldCount < permissions[scopes[i]].length)) {
        highest = i;
        fieldCount = permissions[scopes[i]].length;
      }
    }

    return scopes[highest];
  };

  return Builder;

};

module.exports = Routes;
<commit_msg>fixing a 500 returned if resource empty<commit_after>'use strict';

var BearerToken = require('./bearer');
var Inflector = require('inflected');
var Mutation = require('./mutation');

var REST = {
  index: { endpoint: '/', method: 'get' },
  new: { endpoint: '/', method: 'post' },
  show: { endpoint: '/:id', method: 'get' },
  update: { endpoint: '/:id', method: 'put' },
  delete: { endpoint: '/:id', method: 'delete' }
};

var MethodMatrix = {
  GET: 'read',
  POST: 'create',
  PUT: 'edit',
  DELETE: 'delete'
};

var Routes = function(express, DS) {
  var Builder = { express: express };
  var middleware = [];
  var controllers = [];
  var DS = {};
  var Scopes = {};
  var Instance = {};
  var ErrorHandler = require('./errors');

  Builder.mount = function(models, instanceObj) {
    DS = models;
    Instance = instanceObj;

    for (var i = 0; i < controllers.length; i++) {
      Builder.route(controllers[i](DS, Mutation(DS, Instance)));
    }

    express.use(ErrorHandler);
  };

  Builder.addErrorHandler = function(func) {
    ErrorHandler = func;
  };

  Builder.addController = function(controller) {
    controllers.push(controller);
  };

  Builder.registerMiddleware = function(func) {
    middleware.push(func);
  };

  Builder.mixInController = function(spec, action, rest, params) {
    return function(req, res, next) {
      var route = Inflector.singularize(Inflector.camelize(req.route.path.split('/')[1]));
      var resource = params.parent ? params.parent.resource : route;
      var locator = params.parent ? params.parent.fieldName + '._id' : '_id';

      req.controller = {
        spec: spec,
        action: action,
        permissions: Instance.scope[route],
        route: route,
        model: resource,
        locator: locator,
        _rest: rest,
        child: route !== resource
      };

      if (params.parent) req.controller.fieldName = params.parent.fieldName;

      return next();
    };
  };

  Builder.runMiddlewares = function(req, res, next) {
    if (middleware.length < 1) return next();

    for (var i = 0; i < middleware.length; i++) {
      middleware[i](req, res, next);
    }
  };

  // Sets an individual route
  Builder.route = function(controller) {
    var params = {};

    if (controller._parent) params.parent = controller._parent;

    controller._expose.forEach(function(el, i, arr) {
      if (typeof el === 'object') Builder.customRoute(controller._verb, el, controller[el.name], params);
      if (typeof el === 'string') Builder.restRoute(controller._verb, el, controller[el], params);
    });
  };

  // Handles a non-RESTful controller method
  Builder.customRoute = function(verb, spec, action, params) {
    if (!spec || !action) return;

    var method = spec.method;
    var endpoint = '/' + verb;

    if (spec.endpoint !== '/') endpoint += '/' + spec.endpoint;

    express[method](
      endpoint,
      Builder.mixInController(spec, action, false, params),
      BearerToken.serialize,
      Builder.findUser,
      Builder.resourceQuery,
      Builder.runQuery,
      Builder.catchScope,
      Builder.filterAllowedFields,
      Builder.runMiddlewares,
      action.responder
    );
  };

  // Handles a RESTful controller method
  Builder.restRoute = function(verb, name, action, params) {
    if (!REST[name] || !action) return;

    var method = REST[name].method;
    var endpoint = '/' + verb;

    if (REST[name].endpoint !== '/') endpoint += REST[name].endpoint;

    express[method](
      endpoint,
      Builder.mixInController({ verb: verb, name: name }, action, true, params),
      BearerToken.serialize,
      Builder.findUser,
      Builder.resourceQuery,
      Builder.processIncludables,
      Builder.runQuery,
      Builder.catchScope,
      Builder.filterAllowedFields,
      Builder.runMiddlewares,
      action.responder
    );
  };

  Builder.findUser = function(req, res, next) {
    if (!req.bearerToken) return next();

    var User = DS[Instance.scopable.model];
    var query = User.findOne({ _id: req.bearerToken.user });

    query.lean().exec(function(err, user) {
      if (err) return next(err);

      req.user = user;
      return next();
    });
  };

  Builder.catchScope = function(req, res, next) {
    var ownership = false;
    var permissions = false;

    if (req.controller._rest) {
      permissions = req.controller.permissions[MethodMatrix[req.method]] || req.controller.permissions;
    }

    req.caughtScope = '*';

    if (req.user) {
      var userScopes = req.user[Instance.scopable.field];
      if (['index', 'new'].indexOf(req.controller.spec.name) === -1) ownership = Builder._checkForOwnership(req);
      req.caughtScope = (ownership) ? 'owner' : Builder._highestScope(permissions, userScopes);

      if (req.caughtScope === '*' && !req.controller._rest && userScopes.length > 1) {
        for (var i = 0; i < userScopes.length; i++) {
          if (req.controller.action.scope.indexOf(userScopes[i]) > -1) req.caughtScope = userScopes[i];
        }
      }
    }

    if (permissions === true || permissions[req.caughtScope] === true) {
      req.allowedFields = true;
      return next();
    } else if (Array.isArray(permissions)) {
      req.allowedFields = JSON.parse(JSON.stringify(permissions));
      return next();
    } else if (Array.isArray(permissions[req.caughtScope])) {
      req.allowedFields = JSON.parse(JSON.stringify(permissions[req.caughtScope]));
      return next();
    } else if (!req.controller._rest && ownership) {
      req.allowedFields = true;
      return next();
    } else if (!req.controller._rest && req.controller.action.scope.indexOf(req.caughtScope) > -1) {
      req.allowedFields = true;
      return next();
    }

    // The user is not authorized to see this resource
    return next({ stack: 'auth', translation: 'api.error.auth.insufficient_privilege', message: 'Insufficient privileges for that' });
  };

  Builder.resourceQuery = function(req, res, next) {
    req.searchParams = {};

    if (!req.params.id && req.controller.permissions.searchableBy) {
      var allowed = req.controller.permissions.searchableBy;

      for (var i = 0; i < allowed.length; i++) {
        if (req.parsedQuery[allowed[i]]) req.searchParams[allowed[i]] = req.parsedQuery[allowed[i]];
      }
    }

    return next();
  };

  Builder.processIncludables = function(req, res, next) {
    req.includeRelations = [];

    if (!req.query.include || req.query.include === '') return next();

    var relations = req.query.include.split(',');

    for (var i = 0; i < relations.length; i++) {
      req.includeRelations.push(relations[i]);
    }

    return next();
  };

  Builder.runQuery = function(req, res, next) {
    var Model = DS[req.controller.model];
    req.rawResources = {};

    if (req.params.id && !req.controller.child) {
      var q = Model.findById(req.params.id);

      q.lean().exec(function(err, result) {
        if (err) return next(err);
        if (!result) return next();

        req.rawResources = result;
        return next();
      });
    } else if (req.params.id && req.controller.child) {
      var search = {};
      search[req.controller.locator] = req.params.id;

      var q = Model.findOne(search);

      q.lean().exec(function(err, result) {
        if (err) return next(err);
        if (result === null) return next({ message: 'not found' });

        var resource = result[req.controller.fieldName];

        if (Array.isArray(resource)) {
          for (var i = 0; i < resource.length; i++) {
            if (req.params.id === resource[i]._id.toString()) resource = resource[i];
          }
        }

        req.rawResources = resource;
        return next();
      });

    } else if (!req.params.id && req.method === 'GET' && !req.controller.child) {
      var q = Model.find(req.searchParams);
      var count = Model.count(req.searchParams);

      if (req.query.page) {
        var perPage = parseInt(req.query.perPage) || 20;
        q.skip((parseInt(req.query.page) * perPage) - perPage);
        q.limit(perPage);
      } else {
        q.limit(req.query.limit || 20);
      }

      if (req.includeRelations) {
        for (var i = 0; i < req.includeRelations.length; i++) {
          q.populate(req.includeRelations[i]);
        }
      }

      if (req.query.sort) q.sort(req.query.sort);

      q.lean().exec(function(err, result) {
        if (err) return next(err);

        count.lean().exec(function(err, counter) {
          if (err) return next(err);

          req.rawResources = result;
          req.rawResourcesCount = counter;
          return next();
        });
      });
    } else if (!req.params.id && req.method === 'GET' && req.controller.child) {
      var projection = {};
      var fields = Object.keys(DS[req.controller.route].schema.paths);

      for (var i = 0; i < fields.length; i++) {
        projection[fields[i]] = '$' + req.controller.fieldName + '.' + fields[i];
      }

      var query = [
        { $match: req.searchParams },
        { $unwind: '$' + req.controller.fieldName },
        { $match: req.searchParams },
        { $project: projection }
      ];

      var agg = Model.aggregate(query);

      agg.exec(function(err, result) {
        if (err) return next(err);

        var resource = [];

        if (req.query.page) {
          var perPage = parseInt(req.query.perPage) || 20;
          var skip = (parseInt(req.query.page) * perPage) - perPage;
          resource = result.slice(skip, skip + perPage);
        } else {
          resource = result.slice(0, 19);
        }

        req.rawResources = resource;
        req.rawResourcesCount = result.length;
        return next();
      });
    } else {
      return next();
    }
  };

  Builder.filterAllowedFields = function(req, res, next) {
    if (!req.rawResources || Object.keys(req.rawResources).length === 0) {
      return next();
    }

    req.resource = [];

    if (req.allowedFields !== true) req.allowedFields.push('_id');

    if (Array.isArray(req.rawResources)) {
      for (var i = 0; i < req.rawResources.length; i++) {
        req.resource.push(Builder.filterFromResource(req.rawResources[i], req.allowedFields));
      }
    } else {
      req.resource = Builder.filterFromResource(req.rawResources, req.allowedFields);
    }

    return next();
  };

  Builder.filterFromResource = function(resource, allowed) {
    if (allowed === true) return resource;
    var safeResource = {};

    for (var i = 0; i < allowed.length; i++) {
      safeResource[allowed[i]] = resource[allowed[i]];
    }

    return safeResource;
  };

  Builder._checkForOwnership = function(req) {
    if (Object.keys(req.rawResources).length === 0) return false;

    var model = req.controller.model;
    var permissions = req.controller.permissions;

    if (!permissions.owners || permissions.owners.length < 1) return false;
    if (Array.isArray(req.rawResources)) return false;
    if (!req.user) return false;

    for (var i = 0; i < permissions.owners.length; i++) {
      if (typeof req.rawResources[permissions.owners[i]] === 'undefined') return false;
      if (typeof req.user._id === 'undefined') return false;

      if (req.rawResources[permissions.owners[i]].toString() === req.user._id.toString()) return true;
    }

    return false;
  };

  Builder._highestScope = function(permissions, scopes) {
    if (typeof permissions === 'undefined') return '*';

    var highest = 0;
    var fieldCount = 0;

    scopes.unshift('*');

    for (var i = 0; i < scopes.length; i++) {
      if (permissions === true || Array.isArray(permissions)) {
        return '*';
      } else if (permissions[scopes[i]] === true) {
        return scopes[i];
      } else if (Array.isArray(permissions[scopes[i]]) && (fieldCount < permissions[scopes[i]].length)) {
        highest = i;
        fieldCount = permissions[scopes[i]].length;
      }
    }

    return scopes[highest];
  };

  return Builder;

};

module.exports = Routes;
<|endoftext|>


<commit_before>Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() {
    return Meteor.subscribe('notifications');
  }
});

Router.route('/posts/:_id', {
  name: 'postPage',
  waitOn: function() {
    return [Meteor.subscribe('comments', this.params._id),
            Meteor.subscribe('singlePost', this.params._id)]
  },

  data: function() {
    return Posts.findOne(this.params._id);
  }
});

Router.route('posts/:_id/edit', {
  name: 'postEdit',
  waitOn: function() {
    return Meteor.subscribe('singlePost', this.params._id);
  },
  data: function() {
    return Posts.findOne(this.params._id);
  }
});

Router.route('/submit', { name: 'postSubmit' });

PostsListController = RouteController.extend({
  template: 'postsList',
  increment: 5,
  postsLimit: function() {
    return parseInt(this.params.postsLimit) || this.increment;
  },
  findOptions: function() {
    return { sort: { submitte: -1 }, limit: this.postsLimit() }
  },
  subscriptions: function() {
    this.postsSub = Meteor.subscribe('posts', this.findOptions());
  },
  posts: function() {
    return Posts.find({}, this.findOptions());
  },
  data: function() {
    var hasMore = this.posts().count() === this.postsLimit();
    var nextPath = this.route.path({ postsLimit: this.postsLimit() + this.increment });

    return {
      posts: this.posts(),
      ready: this.postsSub.ready,
      nextPath: hasMore ? nextPath : null
    };
  }
});

Router.route('/:postsLimit?', {
  name: 'postsList'
});

var requireLogin = function() {
  if (!Meteor.user()) {
    if (Meteor.loggingIn()) {
      this.render(this.loadingTemplate);
    } else {
      this.render('accessDenied');
    }
  } else {
    this.next();
  }
}

Router.onBeforeAction('dataNotFound', { only: 'postPage' });
Router.onBeforeAction(requireLogin, { only: 'postSubmit' });
<commit_msg>Add best and new post controllers<commit_after>Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() {
    return Meteor.subscribe('notifications');
  }
});

Router.route('/posts/:_id', {
  name: 'postPage',
  waitOn: function() {
    return [Meteor.subscribe('comments', this.params._id),
            Meteor.subscribe('singlePost', this.params._id)]
  },

  data: function() {
    return Posts.findOne(this.params._id);
  }
});

Router.route('posts/:_id/edit', {
  name: 'postEdit',
  waitOn: function() {
    return Meteor.subscribe('singlePost', this.params._id);
  },
  data: function() {
    return Posts.findOne(this.params._id);
  }
});

Router.route('/submit', { name: 'postSubmit' });

PostsListController = RouteController.extend({
  template: 'postsList',
  increment: 5,
  postsLimit: function() {
    return parseInt(this.params.postsLimit) || this.increment;
  },
  findOptions: function() {
    return { sort: this.sort, limit: this.postsLimit() }
  },
  subscriptions: function() {
    this.postsSub = Meteor.subscribe('posts', this.findOptions());
  },
  posts: function() {
    return Posts.find({}, this.findOptions());
  },
  data: function() {
    var hasMore = this.posts().count() === this.postsLimit();
    var nextPath = this.route.path({ postsLimit: this.postsLimit() + this.increment });

    return {
      posts: this.posts(),
      ready: this.postsSub.ready,
      nextPath: hasMore ? nextPath : null
    };
  }
});

NewPostsController = PostsListController.extend({
  sort: { submitted: -1, _id: -1 },
  nextPath: function() {
    return Router.routes.newPosts.path({ postsLimit: this.Limit() + this.increment });
  }
});

BestPostsController = PostsListController.extend({
  sort: { votes: -1, submitted: -1, _id: -1 },
  nextPath: function() {
    return Router.routes.bestPosts.path({ postsLimit: this.postsLimit() + this.increment });
  }
});

Router.route('/', {
  name: 'home',
  controller: NewPostsController
});

Router.route('/new/:postsLimit?', { name: 'newPosts' });
Router.route('/best/:postsLimit?', { name: 'bestPosts' });

var requireLogin = function() {
  if (!Meteor.user()) {
    if (Meteor.loggingIn()) {
      this.render(this.loadingTemplate);
    } else {
      this.render('accessDenied');
    }
  } else {
    this.next();
  }
}

Router.onBeforeAction('dataNotFound', { only: 'postPage' });
Router.onBeforeAction(requireLogin, { only: 'postSubmit' });
<|endoftext|>


<commit_before>const bodyParser = require('body-parser');
const controller = require('./controller');

module.exports = (config) => {
  var session = require('./controller/library/session')(config);
  var cookie = require('./controller/library/cookie')(config);

  var bodyParserUrlEncodedOpts = {extended: true};

  if (config.cfg.requestSizeLimit != undefined) {
    bodyParserUrlEncodedOpts.limit = config.cfg.requestSizeLimit;
  }

  var bodyParserUrlEncoded = bodyParser.urlencoded(bodyParserUrlEncodedOpts);

  var handler = (delegate, req, res) => {
    return async (() => {
      var ctrl = controller.delegate(config, req, res, session, cookie);

      try {
        await (delegate(ctrl))
      }
      catch (err) {
        controller.handler.serverError(ctrl, req, res, config.handler.error, err);
      }
    })()
  }

  var delegates = {
    route: (method, path, delegate, replaceAll, replaceMethod, suffix, middlewares) => {
      path = replaceAll || replaceMethod || path;
      path = path + (suffix ? suffix : '');

      var params = [path];
      var fn = undefined;

      if (method == 'get') {
        fn = config.app.get;
      }
      else if (method == 'post') {
        fn = config.app.post;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'delete') {
        fn = config.app.delete;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'put') {
        fn = config.app.put;
        params.push(bodyParserUrlEncoded);
      }

      // Add middlewares
      if (middlewares && typeof(middlewares) === 'object') {
        middlewares.forEach((middleware) => {
          params.push(middleware);
        });
      }

      // Add action
      params.push((req, res) => {
        handler(delegate, req, res)
      });

      if (fn) {
        fn.apply(config.app, params);
      }
    }
  }

  if (config.cfg.controller) {
    var fs = require('fs');

    var traverse = (path, dirPath) => {
      try {
        if (!fs.statSync(dirPath).isDirectory()) {
          return;
        }
      } catch(_) {
        console.error('Controller path load error, ' + dirPath);
        return;
      }

      fs.readdirSync(dirPath).forEach(function(name) {
        var fullPath = dirPath + '/' + name

        if (name.startsWith('.')) { return }

        if (fs.statSync(fullPath).isDirectory()) {
          traverse(path + '/' + name, fullPath)
        }
        else {
          include(
            name != 'index.js' ? path + '/' + name.substr(0, name.lastIndexOf('.')) : path,
            fullPath
          )
        }
      })
    }

    var include = (path, filePath) => {
      if (!fs.statSync(filePath).isFile()) { return false }

      var routes = require(filePath)
      var methods = ['get', 'post', 'delete', 'put']

      methods.forEach((method) => {
        if (typeof routes[method] == 'function') {
          var replaceAll = routes.path;
          var replaceMethod = routes['_' + method];
          var suffix = routes[method + '_'];
          var middlewares = routes['_' + method + '_'];

          if (middlewares && typeof(middlewares) === 'function') {
            middlewares = [middlewares];
          }

          delegates.route(method, path, routes[method], replaceAll, replaceMethod, suffix, middlewares);
        }
      })
    }

    traverse('', config.cfg.controller)
  }

  return delegates
}
<commit_msg>add dp handler for middleware.<commit_after>const bodyParser = require('body-parser');
const controller = require('./controller');

module.exports = (config) => {
  var session = require('./controller/library/session')(config);
  var cookie = require('./controller/library/cookie')(config);

  var bodyParserUrlEncodedOpts = {extended: true};

  if (config.cfg.requestSizeLimit != undefined) {
    bodyParserUrlEncodedOpts.limit = config.cfg.requestSizeLimit;
  }

  var bodyParserUrlEncoded = bodyParser.urlencoded(bodyParserUrlEncodedOpts);

  var handler = (delegate, req, res) => {
    return async (() => {
      var ctrl = controller.delegate(config, req, res, session, cookie);

      try {
        await (delegate(ctrl))
      }
      catch (err) {
        controller.handler.serverError(ctrl, req, res, config.handler.error, err);
      }
    })()
  }

  var delegates = {
    route: (method, path, delegate, replaceAll, replaceMethod, suffix, middlewares) => {
      path = replaceAll || replaceMethod || path;
      path = path + (suffix ? suffix : '');

      var params = [path];
      var fn = undefined;

      if (method == 'get') {
        fn = config.app.get;
      }
      else if (method == 'post') {
        fn = config.app.post;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'delete') {
        fn = config.app.delete;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'put') {
        fn = config.app.put;
        params.push(bodyParserUrlEncoded);
      }

      // Add middlewares
      if (middlewares && typeof(middlewares) === 'object') {
        params.push((req, res, next) => {
          var dp = (callback) => {
            return async(() => {
              var ctrl = controller.delegate(config, req, res, session, cookie);
              var handler = {
                controller: ctrl,
                helper: config.helper,
                model: config.model
              };

              try {
                await (callback(handler))
              }
              catch (err) {
                controller.handler.serverError(ctrl, req, res, config.handler.error, err);
              }
            })();
          };

          req.async = dp;
          res.async = dp;

          next();
        });

        middlewares.forEach((middleware) => {
          params.push(middleware);
        });
      }

      // Add action
      params.push((req, res) => {
        handler(delegate, req, res)
      });

      if (fn) {
        fn.apply(config.app, params);
      }
    }
  }

  if (config.cfg.controller) {
    var fs = require('fs');

    var traverse = (path, dirPath) => {
      try {
        if (!fs.statSync(dirPath).isDirectory()) {
          return;
        }
      } catch(_) {
        console.error('Controller path load error, ' + dirPath);
        return;
      }

      fs.readdirSync(dirPath).forEach(function(name) {
        var fullPath = dirPath + '/' + name

        if (name.startsWith('.')) { return }

        if (fs.statSync(fullPath).isDirectory()) {
          traverse(path + '/' + name, fullPath)
        }
        else {
          include(
            name != 'index.js' ? path + '/' + name.substr(0, name.lastIndexOf('.')) : path,
            fullPath
          )
        }
      })
    }

    var include = (path, filePath) => {
      if (!fs.statSync(filePath).isFile()) { return false }

      var routes = require(filePath)
      var methods = ['get', 'post', 'delete', 'put']

      methods.forEach((method) => {
        if (typeof routes[method] == 'function') {
          var replaceAll = routes.path;
          var replaceMethod = routes['_' + method];
          var suffix = routes[method + '_'];
          var middlewares = routes['_' + method + '_'];

          if (middlewares && typeof(middlewares) === 'function') {
            middlewares = [middlewares];
          }

          delegates.route(method, path, routes[method], replaceAll, replaceMethod, suffix, middlewares);
        }
      })
    }

    traverse('', config.cfg.controller)
  }

  return delegates
}
<|endoftext|>


<commit_before>"use strict";

const P = require('bluebird');
const HyperSwitch = require('hyperswitch');
const URI = HyperSwitch.URI;

const mwUtil = require('../lib/mwUtil');

const spec = HyperSwitch.utils.loadSpec(`${__dirname}/parsoid.yaml`);

class ParsoidProxy {
    constructor(options) {
        this.options = options || {};
        this.options.backends = this.options.backends || { default: 'old' };

        // Set up operations
        this.operations = {
            getPageBundle: this.pagebundle.bind(this),
            // Revision retrieval per format
            getWikitext: this.getFormat.bind(this, 'wikitext'),
            getHtml: this.getFormat.bind(this, 'html'),
            getDataParsoid: this.getFormat.bind(this, 'data-parsoid'),
            // Listings
            listWikitextRevisions: this.listRevisions.bind(this, 'wikitext'),
            listHtmlRevisions: this.listRevisions.bind(this, 'html'),
            listDataParsoidRevisions: this.listRevisions.bind(this, 'data-parsoid'),
            // Transforms
            transformHtmlToHtml: this.makeTransform('html', 'html'),
            transformHtmlToWikitext: this.makeTransform('html', 'wikitext'),
            transformWikitextToHtml: this.makeTransform('wikitext', 'html'),
            transformWikitextToLint: this.makeTransform('wikitext', 'lint'),
            transformChangesToWikitext: this.makeTransform('changes', 'wikitext')
        };
    }

    _chooseBackend(req) {
        return this.options.backends[req.params.domain] || this.options.backends.default;
    }

    _buildRequest(prefix, req, listing) {
        const rp = req.params;
        const path = [rp.domain, 'sys'].concat(prefix);
        if (rp.title) {
            path.push(rp.title);
            if (rp.revision) {
                path.push(rp.revision);
                if (rp.tid) {
                    path.push(rp.tid);
                }
            }
        }
        if (listing) {
            path.push('');
        }
        return {
            method: req.method,
            uri: new URI(path),
            query: req.query,
            headers: req.headers,
            body: req.body
        };
    }

    _createDoubleProcessingRequest(hyper, req, path, listing) {
        const oldRequest = this._buildRequest(['parsoid_old'].concat(path), req, listing);
        const newRequest = this._buildRequest(['parsoid_new'].concat(path), req, listing);

        const backend = this._chooseBackend(req);
        if (backend === 'old') {
            return hyper.request(oldRequest);
        }
        if (backend === 'new') {
            return hyper.request(newRequest);
        }
        if (backend === 'both') {
            return P.join(
                hyper.request(oldRequest)
                .catch((e) => {
                    if (e.status !== 412) {
                        hyper.log('error/parsoid', {
                            message: 'Error fetching old parsoid content',
                            error: e
                        });
                        throw e;
                    }
                }),
                hyper.request(newRequest)
                .catch((e) => {
                    if (e.status !== 412) {
                        hyper.log('error/parsoid', {
                            message: 'Error fetching new parsoid content',
                            error: e
                        });
                    }
                })
            )
            .then((results) => {
                const oldContent = results[0];
                const newContent = results[1];

                if (oldContent && newContent && mwUtil.isNoCacheRequest(req)) {
                    if (oldContent.body !== newContent.body) {
                        hyper.log('error/parsoid', {
                            message: 'Content mismatch between old and new bucket',
                            old_etag: oldContent.headers.etag,
                            new_etag:  newContent.headers.etag
                        });
                    }
                }

                return oldContent;
            });
        }
        throw new Error(`Unrecognised parsoid backend ${backend}`);
    }

    pagebundle(hyper, req) {
        return this._createDoubleProcessingRequest(
            hyper,
            req,
            ['pagebundle']
        );
    }

    getFormat(format, hyper, req) {
        return this._createDoubleProcessingRequest(
            hyper,
            req,
            [format]
        );
    }

    listRevisions(format, hyper, req) {
        return this._createDoubleProcessingRequest(
            hyper,
            req,
            [format],
            true
        );

    }

    makeTransform(from, to) {
        return (hyper, req) => this._createDoubleProcessingRequest(
            hyper,
            req,
            ['transform', from, 'to', to]
        );

    }
}

module.exports = (options) => {
    options = options || {};
    const ps = new ParsoidProxy(options);

    return {
        spec,
        operations: ps.operations
    };
};
<commit_msg>Fix the 412 test failure<commit_after>"use strict";

const P = require('bluebird');
const HyperSwitch = require('hyperswitch');
const URI = HyperSwitch.URI;

const mwUtil = require('../lib/mwUtil');

const spec = HyperSwitch.utils.loadSpec(`${__dirname}/parsoid.yaml`);

class ParsoidProxy {
    constructor(options) {
        this.options = options || {};
        this.options.backends = this.options.backends || { default: 'old' };

        // Set up operations
        this.operations = {
            getPageBundle: this.pagebundle.bind(this),
            // Revision retrieval per format
            getWikitext: this.getFormat.bind(this, 'wikitext'),
            getHtml: this.getFormat.bind(this, 'html'),
            getDataParsoid: this.getFormat.bind(this, 'data-parsoid'),
            // Listings
            listWikitextRevisions: this.listRevisions.bind(this, 'wikitext'),
            listHtmlRevisions: this.listRevisions.bind(this, 'html'),
            listDataParsoidRevisions: this.listRevisions.bind(this, 'data-parsoid'),
            // Transforms
            transformHtmlToHtml: this.makeTransform('html', 'html'),
            transformHtmlToWikitext: this.makeTransform('html', 'wikitext'),
            transformWikitextToHtml: this.makeTransform('wikitext', 'html'),
            transformWikitextToLint: this.makeTransform('wikitext', 'lint'),
            transformChangesToWikitext: this.makeTransform('changes', 'wikitext')
        };
    }

    _chooseBackend(req) {
        return this.options.backends[req.params.domain] || this.options.backends.default;
    }

    _buildRequest(prefix, req, listing) {
        const rp = req.params;
        const path = [rp.domain, 'sys'].concat(prefix);
        if (rp.title) {
            path.push(rp.title);
            if (rp.revision) {
                path.push(rp.revision);
                if (rp.tid) {
                    path.push(rp.tid);
                }
            }
        }
        if (listing) {
            path.push('');
        }
        return {
            method: req.method,
            uri: new URI(path),
            query: req.query,
            headers: req.headers,
            body: req.body
        };
    }

    _createDoubleProcessingRequest(hyper, req, path, listing) {
        const oldRequest = this._buildRequest(['parsoid_old'].concat(path), req, listing);
        const newRequest = this._buildRequest(['parsoid_new'].concat(path), req, listing);

        const backend = this._chooseBackend(req);
        if (backend === 'old') {
            return hyper.request(oldRequest);
        }
        if (backend === 'new') {
            return hyper.request(newRequest);
        }
        if (backend === 'both') {
            return P.join(
                hyper.request(oldRequest)
                .catch((e) => {
                    if (e.status !== 412) {
                        hyper.log('error/parsoid', {
                            message: 'Error fetching old parsoid content',
                            error: e
                        });
                    }
                    throw e;
                }),
                hyper.request(newRequest)
                .catch((e) => {
                    if (e.status !== 412) {
                        hyper.log('error/parsoid', {
                            message: 'Error fetching new parsoid content',
                            error: e
                        });
                    }
                })
            )
            .then((results) => {
                const oldContent = results[0];
                const newContent = results[1];

                if (oldContent && newContent && mwUtil.isNoCacheRequest(req)) {
                    if (oldContent.body !== newContent.body) {
                        hyper.log('error/parsoid', {
                            message: 'Content mismatch between old and new bucket',
                            old_etag: oldContent.headers.etag,
                            new_etag:  newContent.headers.etag
                        });
                    }
                }

                return oldContent;
            });
        }
        throw new Error(`Unrecognised parsoid backend ${backend}`);
    }

    pagebundle(hyper, req) {
        return this._createDoubleProcessingRequest(
            hyper,
            req,
            ['pagebundle']
        );
    }

    getFormat(format, hyper, req) {
        return this._createDoubleProcessingRequest(
            hyper,
            req,
            [format]
        );
    }

    listRevisions(format, hyper, req) {
        return this._createDoubleProcessingRequest(
            hyper,
            req,
            [format],
            true
        );

    }

    makeTransform(from, to) {
        return (hyper, req) => this._createDoubleProcessingRequest(
            hyper,
            req,
            ['transform', from, 'to', to]
        );

    }
}

module.exports = (options) => {
    options = options || {};
    const ps = new ParsoidProxy(options);

    return {
        spec,
        operations: ps.operations
    };
};
<|endoftext|>


<commit_before>/*****************************************************************************/
/* Imports */
/*****************************************************************************/
var MiddlewareStack = Iron.MiddlewareStack;
var Url = Iron.Url;
var Layout = Iron.Layout;
var warn = Iron.utils.warn;
var assert = Iron.utils.assert;

Router = function (options) {
  // keep the same api throughout which is:
  // fn(url, context, done);
  function router (req, res, next) {
    //XXX this assumes no other routers on the parent stack which we should probably fix
    router.dispatch(req.url, {
      request: req,
      response: res
    }, next);
  }

  // the main router stack
  router._stack = new MiddlewareStack;

  // for storing global hooks like before, after, etc.
  router._globalHooks = {};

  // backward compat and quicker lookup of Route handlers vs. regular function
  // handlers.
  router.routes = [];

  // to make sure we don't have more than one route per path
  router.routes._byPath = {};

  // always good to have options
  this.configure.call(router, options);

  // add proto properties to the router function
  _.extend(router, this);

  // let client and server side routing doing different things here
  this.init.call(router, options);

  Meteor.startup(function () {
    Meteor.defer(function () {
      if (router.options.autoStart !== false)
        router.start();
    });
  });

  return router;
};

Router.prototype.init = function (options) {};

Router.prototype.configure = function (options) {
  var self = this;

  options = options || {};

  var toArray = function (value) {
    if (!value)
      return [];

    if (_.isArray(value))
      return value;

    return [value];
  };

  // e.g. before: fn OR before: [fn1, fn2]
  _.each(Iron.Router.HOOK_TYPES, function eachHookType (type) {
    if (options[type]) {
      _.each(toArray(options[type]), function eachHook (hook) {
        self.addHook(type, hook);
      });

      delete options[type];
    }
  });

  this.options = this.options || {};
  _.extend(this.options, options);

  return this;
};

/**
 * Just to support legacy calling. Doesn't really serve much purpose.
 */
Router.prototype.map = function (fn) {
  return fn.call(this);
};

/*
 * XXX removing for now until this is thought about more carefully.
Router.prototype.use = function (path, fn, opts) {
  if (typeof path === 'function') {
    opts = fn || {};
    opts.mount = true;
    opts.where = opts.where || 'server';
    this._stack.push(path, opts);
  } else {
    opts = opts || {};
    opts.mount = true;
    opts.where = opts.where || 'server';
    this._stack.push(path, fn, opts);
  }

  return this;
};
*/

//XXX seems like we could put a params method on the route directly and make it reactive
Router.prototype.route = function (path, fn, opts) {
  var typeOf = function (val) { return Object.prototype.toString.call(val); };
  assert(typeOf(path) === '[object String]' || typeOf(path) === '[object RegExp]', "Router.route requires a path that is a string or regular expression.");

  if (typeof fn === 'object') {
    opts = fn;
    fn = opts.action;
  }

  var route = new Route(path, fn, opts);

  opts = opts || {};

  // don't mount the route
  opts.mount = false;

  // stack expects a function which is exactly what a new Route returns!
  var handler = this._stack.push(path, route, opts);

  handler.route = route;
  route.handler = handler;
  route.router = this;

  assert(!this.routes._byPath[handler.path],
    "A route for the path " + JSON.stringify(handler.path) + " already exists by the name of " + JSON.stringify(handler.name) + ".");
  this.routes._byPath[handler.path] = route;

  this.routes.push(route);

  if (typeof handler.name === 'string')
    this.routes[handler.name] = route;

  return route;
};

/**
 * Find the first route for the given url and options.
 */
Router.prototype.findFirstRoute = function (url) {
  for (var i = 0; i < this.routes.length; i++) {
    if (this.routes[i].handler.test(url, {}))
      return this.routes[i];
  }

  return null;
};

Router.prototype.path = function (routeName, params, options) {
  var route = this.routes[routeName];
  warn(route, "You called Router.path for a route named " + JSON.stringify(routeName) + " but that route doesn't seem to exist. Are you sure you created it?");
  return route && route.path(params, options);
};

Router.prototype.url = function (routeName, params, options) {
  var route = this.routes[routeName];
  warn(route, "You called Router.url for a route named " + JSON.stringify(routeName) + " but that route doesn't seem to exist. Are you sure you created it?");
  return route && route.url(params, options);
};

/**
 * Create a new controller for a dispatch.
 */
Router.prototype.createController = function (url, context) {
  // see if there's a route for this url
  var route = this.findFirstRoute(url);
  var controller;

  context = context || {};

  if (route)
    // let the route decide what controller to use
    controller = route.createController({layout: this._layout});
  else
    // create an anonymous controller
    controller = new RouteController({layout: this._layout});

  controller.router = this;
  controller.configureFromUrl(url, context, {reactive: false});
  return controller;
};

Router.prototype.setTemplateNameConverter = function (fn) {
  this._templateNameConverter = fn;
  return this;
};

Router.prototype.setControllerNameConverter = function (fn) {
  this._controllerNameConverter = fn;
  return this;
};

Router.prototype.toTemplateName = function (str) {
  if (this._templateNameConverter)
    return this._templateNameConverter(str);
  else
    return str;
};

Router.prototype.toControllerName = function (str) {
  if (this._controllerNameConverter)
    return this._controllerNameConverter(str);
  else
    return Iron.utils.classCase(str) + 'Controller';
};

/**
 *
 * Add a hook to all routes. The hooks will apply to all routes,
 * unless you name routes to include or exclude via `only` and `except` options
 *
 * @param {String} [type] one of 'load', 'unload', 'before' or 'after'
 * @param {Object} [options] Options to controll the hooks [optional]
 * @param {Function} [hook] Callback to run
 * @return {IronRouter}
 * @api public
 *
 */

Router.prototype.addHook = function(type, hook, options) {
  var self = this;

  options = options || {};

  var toArray = function (input) {
    if (!input)
      return [];
    else if (_.isArray(input))
      return input;
    else
      return [input];
  }

  if (options.only)
    options.only = toArray(options.only);
  if (options.except)
    options.except = toArray(options.except);

  var hooks = this._globalHooks[type] = this._globalHooks[type] || [];

  var hookWithOptions = function () {
    var thisArg = this;
    var args = arguments;
    // this allows us to bind hooks to options that get looked up when you call
    // this.lookupOption from within the hook. And it looks better to keep
    // plugin/hook related options close to their definitions instead of
    // Router.configure. But we use a dynamic variable so we don't have to
    // pass the options explicitly as an argument and plugin creators can
    // just use this.lookupOption which will follow the proper lookup chain from
    // "this", local options, dynamic variable options, route, router, etc.
    return CurrentOptions.withValue(options, function () {
      return self.lookupHook(hook).apply(thisArg, args);
    });
  };

  hooks.push({options: options, hook: hookWithOptions});
  return this;
};

/**
 * If the argument is a function return it directly. If it's a string, see if
 * there is a function in the Iron.Router.hooks namespace. Throw an error if we
 * can't find the hook.
 */
Router.prototype.lookupHook = function (nameOrFn) {
  var fn = nameOrFn;

  // if we already have a func just return it
  if (_.isFunction(fn))
    return fn;

  // look up one of the out-of-box hooks like
  // 'loaded or 'dataNotFound' if the nameOrFn is a
  // string
  if (_.isString(fn)) {
    if (_.isFunction(Iron.Router.hooks[fn]))
      return Iron.Router.hooks[fn];
  }

  // we couldn't find it so throw an error
  throw new Error("No hook found named: " + nameOrFn);
};

/**
 *
 * Fetch the list of global hooks that apply to the given route name.
 * Hooks are defined by the .addHook() function above.
 *
 * @param {String} [type] one of IronRouter.HOOK_TYPES
 * @param {String} [name] the name of the route we are interested in
 * @return {[Function]} [hooks] an array of hooks to run
 * @api public
 *
 */

Router.prototype.getHooks = function(type, name) {
  var self = this;
  var hooks = [];

  _.each(this._globalHooks[type], function(hook) {
    var options = hook.options;

    if (options.except && _.include(options.except, name))
      return [];

    if (options.only && ! _.include(options.only, name))
      return [];

    hooks.push(hook.hook);
  });

  return hooks;
};

Router.HOOK_TYPES = [
  'onRun',
  'onRerun',
  'onBeforeAction',
  'onAfterAction',
  'onStop',

  // not technically a hook but we'll use it
  // in a similar way. This will cause waitOn
  // to be added as a method to the Router and then
  // it can be selectively applied to specific routes
  'waitOn',

  // legacy hook types but we'll let them slide
  'load', // onRun
  'before', // onBeforeAction
  'after', // onAfterAction
  'unload' // onStop
];

/**
 * A namespace for hooks keyed by name.
 */
Router.hooks = {};

/**
 * A namespace for plugin functions keyed by name.
 */
Router.plugins = {};

/**
 * Auto add helper mtehods for all the hooks.
 */

Router.HOOK_TYPES.forEach(function (type) {
  Router.prototype[type] = function (hook, options) {
    this.addHook(type, hook, options);
  };
});

/**
 * Add a plugin to the router instance.
 */
Router.prototype.plugin = function (nameOrFn, options) {
  var func;

  if (typeof nameOrFn === 'function')
    func = nameOrFn;
  else if (typeof nameOrFn === 'string')
    func = Iron.Router.plugins[nameOrFn];

  if (!func)
    throw new Error("No plugin found named " + JSON.stringify(nameOrFn));

  // fn(router, options)
  func.call(this, this, options);

  return this;
};

Iron.Router = Router;
<commit_msg>reverting default template name converter to class case...<commit_after>/*****************************************************************************/
/* Imports */
/*****************************************************************************/
var MiddlewareStack = Iron.MiddlewareStack;
var Url = Iron.Url;
var Layout = Iron.Layout;
var warn = Iron.utils.warn;
var assert = Iron.utils.assert;

Router = function (options) {
  // keep the same api throughout which is:
  // fn(url, context, done);
  function router (req, res, next) {
    //XXX this assumes no other routers on the parent stack which we should probably fix
    router.dispatch(req.url, {
      request: req,
      response: res
    }, next);
  }

  // the main router stack
  router._stack = new MiddlewareStack;

  // for storing global hooks like before, after, etc.
  router._globalHooks = {};

  // backward compat and quicker lookup of Route handlers vs. regular function
  // handlers.
  router.routes = [];

  // to make sure we don't have more than one route per path
  router.routes._byPath = {};

  // always good to have options
  this.configure.call(router, options);

  // add proto properties to the router function
  _.extend(router, this);

  // let client and server side routing doing different things here
  this.init.call(router, options);

  Meteor.startup(function () {
    Meteor.defer(function () {
      if (router.options.autoStart !== false)
        router.start();
    });
  });

  return router;
};

Router.prototype.init = function (options) {};

Router.prototype.configure = function (options) {
  var self = this;

  options = options || {};

  var toArray = function (value) {
    if (!value)
      return [];

    if (_.isArray(value))
      return value;

    return [value];
  };

  // e.g. before: fn OR before: [fn1, fn2]
  _.each(Iron.Router.HOOK_TYPES, function eachHookType (type) {
    if (options[type]) {
      _.each(toArray(options[type]), function eachHook (hook) {
        self.addHook(type, hook);
      });

      delete options[type];
    }
  });

  this.options = this.options || {};
  _.extend(this.options, options);

  return this;
};

/**
 * Just to support legacy calling. Doesn't really serve much purpose.
 */
Router.prototype.map = function (fn) {
  return fn.call(this);
};

/*
 * XXX removing for now until this is thought about more carefully.
Router.prototype.use = function (path, fn, opts) {
  if (typeof path === 'function') {
    opts = fn || {};
    opts.mount = true;
    opts.where = opts.where || 'server';
    this._stack.push(path, opts);
  } else {
    opts = opts || {};
    opts.mount = true;
    opts.where = opts.where || 'server';
    this._stack.push(path, fn, opts);
  }

  return this;
};
*/

//XXX seems like we could put a params method on the route directly and make it reactive
Router.prototype.route = function (path, fn, opts) {
  var typeOf = function (val) { return Object.prototype.toString.call(val); };
  assert(typeOf(path) === '[object String]' || typeOf(path) === '[object RegExp]', "Router.route requires a path that is a string or regular expression.");

  if (typeof fn === 'object') {
    opts = fn;
    fn = opts.action;
  }

  var route = new Route(path, fn, opts);

  opts = opts || {};

  // don't mount the route
  opts.mount = false;

  // stack expects a function which is exactly what a new Route returns!
  var handler = this._stack.push(path, route, opts);

  handler.route = route;
  route.handler = handler;
  route.router = this;

  assert(!this.routes._byPath[handler.path],
    "A route for the path " + JSON.stringify(handler.path) + " already exists by the name of " + JSON.stringify(handler.name) + ".");
  this.routes._byPath[handler.path] = route;

  this.routes.push(route);

  if (typeof handler.name === 'string')
    this.routes[handler.name] = route;

  return route;
};

/**
 * Find the first route for the given url and options.
 */
Router.prototype.findFirstRoute = function (url) {
  for (var i = 0; i < this.routes.length; i++) {
    if (this.routes[i].handler.test(url, {}))
      return this.routes[i];
  }

  return null;
};

Router.prototype.path = function (routeName, params, options) {
  var route = this.routes[routeName];
  warn(route, "You called Router.path for a route named " + JSON.stringify(routeName) + " but that route doesn't seem to exist. Are you sure you created it?");
  return route && route.path(params, options);
};

Router.prototype.url = function (routeName, params, options) {
  var route = this.routes[routeName];
  warn(route, "You called Router.url for a route named " + JSON.stringify(routeName) + " but that route doesn't seem to exist. Are you sure you created it?");
  return route && route.url(params, options);
};

/**
 * Create a new controller for a dispatch.
 */
Router.prototype.createController = function (url, context) {
  // see if there's a route for this url
  var route = this.findFirstRoute(url);
  var controller;

  context = context || {};

  if (route)
    // let the route decide what controller to use
    controller = route.createController({layout: this._layout});
  else
    // create an anonymous controller
    controller = new RouteController({layout: this._layout});

  controller.router = this;
  controller.configureFromUrl(url, context, {reactive: false});
  return controller;
};

Router.prototype.setTemplateNameConverter = function (fn) {
  this._templateNameConverter = fn;
  return this;
};

Router.prototype.setControllerNameConverter = function (fn) {
  this._controllerNameConverter = fn;
  return this;
};

Router.prototype.toTemplateName = function (str) {
  if (this._templateNameConverter)
    return this._templateNameConverter(str);
  else
    return Iron.utils.classCase(str);
};

Router.prototype.toControllerName = function (str) {
  if (this._controllerNameConverter)
    return this._controllerNameConverter(str);
  else
    return Iron.utils.classCase(str) + 'Controller';
};

/**
 *
 * Add a hook to all routes. The hooks will apply to all routes,
 * unless you name routes to include or exclude via `only` and `except` options
 *
 * @param {String} [type] one of 'load', 'unload', 'before' or 'after'
 * @param {Object} [options] Options to controll the hooks [optional]
 * @param {Function} [hook] Callback to run
 * @return {IronRouter}
 * @api public
 *
 */

Router.prototype.addHook = function(type, hook, options) {
  var self = this;

  options = options || {};

  var toArray = function (input) {
    if (!input)
      return [];
    else if (_.isArray(input))
      return input;
    else
      return [input];
  }

  if (options.only)
    options.only = toArray(options.only);
  if (options.except)
    options.except = toArray(options.except);

  var hooks = this._globalHooks[type] = this._globalHooks[type] || [];

  var hookWithOptions = function () {
    var thisArg = this;
    var args = arguments;
    // this allows us to bind hooks to options that get looked up when you call
    // this.lookupOption from within the hook. And it looks better to keep
    // plugin/hook related options close to their definitions instead of
    // Router.configure. But we use a dynamic variable so we don't have to
    // pass the options explicitly as an argument and plugin creators can
    // just use this.lookupOption which will follow the proper lookup chain from
    // "this", local options, dynamic variable options, route, router, etc.
    return CurrentOptions.withValue(options, function () {
      return self.lookupHook(hook).apply(thisArg, args);
    });
  };

  hooks.push({options: options, hook: hookWithOptions});
  return this;
};

/**
 * If the argument is a function return it directly. If it's a string, see if
 * there is a function in the Iron.Router.hooks namespace. Throw an error if we
 * can't find the hook.
 */
Router.prototype.lookupHook = function (nameOrFn) {
  var fn = nameOrFn;

  // if we already have a func just return it
  if (_.isFunction(fn))
    return fn;

  // look up one of the out-of-box hooks like
  // 'loaded or 'dataNotFound' if the nameOrFn is a
  // string
  if (_.isString(fn)) {
    if (_.isFunction(Iron.Router.hooks[fn]))
      return Iron.Router.hooks[fn];
  }

  // we couldn't find it so throw an error
  throw new Error("No hook found named: " + nameOrFn);
};

/**
 *
 * Fetch the list of global hooks that apply to the given route name.
 * Hooks are defined by the .addHook() function above.
 *
 * @param {String} [type] one of IronRouter.HOOK_TYPES
 * @param {String} [name] the name of the route we are interested in
 * @return {[Function]} [hooks] an array of hooks to run
 * @api public
 *
 */

Router.prototype.getHooks = function(type, name) {
  var self = this;
  var hooks = [];

  _.each(this._globalHooks[type], function(hook) {
    var options = hook.options;

    if (options.except && _.include(options.except, name))
      return [];

    if (options.only && ! _.include(options.only, name))
      return [];

    hooks.push(hook.hook);
  });

  return hooks;
};

Router.HOOK_TYPES = [
  'onRun',
  'onRerun',
  'onBeforeAction',
  'onAfterAction',
  'onStop',

  // not technically a hook but we'll use it
  // in a similar way. This will cause waitOn
  // to be added as a method to the Router and then
  // it can be selectively applied to specific routes
  'waitOn',

  // legacy hook types but we'll let them slide
  'load', // onRun
  'before', // onBeforeAction
  'after', // onAfterAction
  'unload' // onStop
];

/**
 * A namespace for hooks keyed by name.
 */
Router.hooks = {};

/**
 * A namespace for plugin functions keyed by name.
 */
Router.plugins = {};

/**
 * Auto add helper mtehods for all the hooks.
 */

Router.HOOK_TYPES.forEach(function (type) {
  Router.prototype[type] = function (hook, options) {
    this.addHook(type, hook, options);
  };
});

/**
 * Add a plugin to the router instance.
 */
Router.prototype.plugin = function (nameOrFn, options) {
  var func;

  if (typeof nameOrFn === 'function')
    func = nameOrFn;
  else if (typeof nameOrFn === 'string')
    func = Iron.Router.plugins[nameOrFn];

  if (!func)
    throw new Error("No plugin found named " + JSON.stringify(nameOrFn));

  // fn(router, options)
  func.call(this, this, options);

  return this;
};

Iron.Router = Router;
<|endoftext|>


<commit_before>var page = require('page');
var localforage = require('localforage');
var firstRun = true;

module.exports = function (app) {
    page(function (e, next) {
        // don't save the state on first run
        if (firstRun) {
            firstRun = false;
            return next();
        }

        // clear the state for home page
        if (e.path == '/') {
            localforage.setItem('state', null);
        } else {
            localforage.setItem('state', {
                title: e.title,
                path: e.path
            });
        }

        next();
    });

    page('/', function (c) {
        app.currentView = 'templates';
        app.title = 'Templates';
    });

    page('/apps', function () {
        app.currentView = 'apps';
        app.title = 'Apps';
    });

    page('/profile', function () {
        app.currentView = 'profile';
        app.title = 'Me';
    });

    page('*', function () {
        console.log('404');
    });

    page({
        click: true,
        popstate: true
    });

    // check for saved state to load
    localforage.getItem('state', function (loc) {
        if (loc) {
            page(loc.path);
        }
    });
};
<commit_msg>Move router state restoration to use new model module<commit_after>var page = require('page');
var model = require('./model');

module.exports = function (app) {
    page(function (e, next) {
        // Don't save the state on first run
        if (model.history.ftu) {
            model.history.ftu = false;
            return next();
        }

        // Update history with current path (if not root)
        if (e.path === '/') {
            model.history.path = null;
        } else {
            model.history.path = e.path;
        }

        // Continue
        next();
    });

    page('/', function (c) {
        app.currentView = 'templates';
        app.title = 'Templates';
    });

    page('/apps', function () {
        app.currentView = 'apps';
        app.title = 'Apps';
    });

    page('/profile', function () {
        app.currentView = 'profile';
        app.title = 'Me';
    });

    page('*', function () {
        console.log('404');
    });

    page({
        click: true,
        popstate: true
    });

    // Restore saved state
    page(model.history.path);
};
<|endoftext|>


<commit_before>var loggedInAction = function loggedInAction() {
  var routeToBe = Router.current().route.getName();

  if (!Meteor.userId()) {
    this.redirect('login');
  }
  if (Meteor.userId() && (routeToBe === 'login' || routeToBe === 'signup')) {
    goBack();
  }
  this.next();
};

goBack = function goBack() {
  var previousRoute = Session.get('previousRoute');
  var previousRouteParams = Session.get('previousRouteParams');

  Router.go(previousRoute || 'initiatives', previousRouteParams || {});
};

Router.configure({
  layoutTemplate: '_cherishLayout',
  loadingTemplate: '_loading',
  progressDelay: 100,
  progressSpinner: false
});

Router.onStop(function routerOnStop() {
  var currentRoute = Router.current().route.getName();
  var currentRouteParams = Router.current().params;

  if (currentRoute === 'login' || currentRoute === 'signup') {
    return;
  }

  Session.set({
    previousRoute: currentRoute,
    previousRouteParams: _.extend({}, currentRouteParams)
  });
});

Router.route('/profile', {
  name: 'profile',
  template: 'profileSelf',
  data: function data() {
    return {
      user: Meteor.user()
    };
  }
});

Router.route('/profile/notifications', {
  name: 'profile.notifications',
  template: 'profileNotifications',
  data: function data() {
    return {
      user: Meteor.user()
    };
  }
});

Router.route('/signup', {
  name: 'signup',
  template: 'publicSignup'
});

Router.route('/login', {
  name: 'login',
  template: 'publicLogin'
});

Router.route('/about', {
  name: 'about',
  template: 'about'
});

Router.onBeforeAction(loggedInAction, {except: ['initiatives', 'initiative', 'category', 'about']});<commit_msg>Fix signup route restriction<commit_after>var loggedInAction = function loggedInAction() {
  var routeToBe = Router.current().route.getName();

  if (!Meteor.userId()) {
    this.redirect('login');
  }
  if (Meteor.userId() && (routeToBe === 'login' || routeToBe === 'signup')) {
    goBack();
  }
  this.next();
};

goBack = function goBack() {
  var previousRoute = Session.get('previousRoute');
  var previousRouteParams = Session.get('previousRouteParams');

  Router.go(previousRoute || 'initiatives', previousRouteParams || {});
};

Router.configure({
  layoutTemplate: '_cherishLayout',
  loadingTemplate: '_loading',
  progressDelay: 100,
  progressSpinner: false
});

Router.onStop(function routerOnStop() {
  var currentRoute = Router.current().route.getName();
  var currentRouteParams = Router.current().params;

  if (currentRoute === 'login' || currentRoute === 'signup') {
    return;
  }

  Session.set({
    previousRoute: currentRoute,
    previousRouteParams: _.extend({}, currentRouteParams)
  });
});

Router.route('/profile', {
  name: 'profile',
  template: 'profileSelf',
  data: function data() {
    return {
      user: Meteor.user()
    };
  }
});

Router.route('/profile/notifications', {
  name: 'profile.notifications',
  template: 'profileNotifications',
  data: function data() {
    return {
      user: Meteor.user()
    };
  }
});

Router.route('/signup', {
  name: 'signup',
  template: 'publicSignup'
});

Router.route('/login', {
  name: 'login',
  template: 'publicLogin'
});

Router.route('/about', {
  name: 'about',
  template: 'about'
});

Router.onBeforeAction(loggedInAction, {except: ['initiatives', 'initiative', 'category', 'about', 'signup']});<|endoftext|>