<commit_before>var OAuth = require('oauth').OAuth,
	step = require('step'),
	readline = require('readline'),
	createOAuthWrapper,
	// Regular expressions for parsing the XML OAuth response
	tokenPattern = /<oauth_token\>([^<]*)<\/oauth_token\>/,
	secretPattern = /<oauth_token_secret\>([^<]*)<\/oauth_token_secret\>/;

// Creates an OAuth v1 wrapper configured with the supplied key and
// secret and the necessary options for authenticating with the
// 7digital API.
//
// - @param {String} oauthkey
// - @param {String} oauthsecret
//
// - @return {OAuth}
module.exports.createOAuthWrapper = createOAuthWrapper =
	function (oauthkey, oauthsecret) {
	var oAuth = new OAuth(
				'http://api.7digital.com/1.2/oauth/requesttoken',
				'http://api.7digital.com/1.2/oauth/accesstoken',
				oauthkey, oauthsecret, '1.0', null, 'HMAC-SHA1');

	oAuth.setClientOptions({
		requestTokenHttpMethod: "GET",
		accessTokenHttpMethod: "GET"
	});

	return oAuth;
};

// Gets a request token from the 7digital API and generates an authorise
// url for the user to grant the application access to their locker and
// make purchases on their behalf.
//
//
//     var oauthHelper = require('oauth-helper');
//     oauthHelper.getRequestToken({
//             oauthkey: 'YOUR_KEY_HERE',
//             oauthsecret: 'YOUR_SECRET',
//             callbackUrl: ''
//         }, function authorise(err, requestToken, requestSecret,
//                               authoriseUrl) {
//              // Get an access token
//     });
//
//
// The options parameter should contain the following
//
//  - `oauthkey` your app's oauth consumer key
//  - `oauthsecret` your app's oauth consumer secret
//  - `callbackUrl` (optional) the url to return once the user has authorised
//                  your app
//
// - @param {Object} options
// - @param {Function} callback
module.exports.getRequestToken = function (options, callback) {
	var wrapperFactory = createOAuthWrapper;

	step(
		function getRequestToken() {
			var oAuth = wrapperFactory(options.oauthkey, options.oauthsecret);

			oAuth.setClientOptions({
				requestTokenHttpMethod: "GET",
				accessTokenHttpMethod: "GET"
			});

			oAuth.getOAuthRequestToken(this);
		},
		function parseResponse(err, oauth_token, oauth_token_secret, results) {
			var mangledResponse, requestToken, requestSecret, authoriseUrl,
				returnUrl, consoleInterface;

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

			// The OAuth endpoints on the 7digital API return XML instead of
			// an encoded parameter response.
			mangledResponse = results['<?xml version'];

			// Parse the token and secret from the XML
			requestToken = tokenPattern.exec(mangledResponse)[1];
			requestSecret  = secretPattern.exec(mangledResponse)[1];

			// re-encode the token and secret as the node oauth client
			// decodes them thinking they are parameter encoded as per
			// the spec.
			requestToken = requestToken.replace(' ', '+');
			requestSecret = requestSecret.replace(' ', '+');

			authoriseUrl = 'https://account.7digital.com/' +
							options.oauthkey + '/oauth/authorise?' +
						'oauth_token=' + requestToken + '&oauth_callback=' +
						encodeURIComponent(options.callbackUrl);

			callback(null, requestToken, requestSecret, authoriseUrl);
		}
	);
};

// Gets an access token from the 7digital API for signing requests to
// access a user's 7digital account on their behalf.
//
// You must generate your access token only after the user has authorised
// the request token.
//
// You can use this token and secret for signing requests to the 3-legged
// OAuth secured API endpoints.
//
//
//     var oauthHelper = require('oauth-helper');
//     oauthHelper.getAccessToken({
//             oauthkey: 'YOUR_KEY_HERE',
//             oauthsecret: 'YOUR_SECRET_HERE',
//             requesttoken: 'YOUR_TOKEN_HERE',
//             requestsecret: 'TOKEN_SECRET_HERE'
//         }, function accessSignedEndpoints(err, accesstoken, accesssecret) {
//             // Access the user's locker etc
//     });
//
//
// The options parameter should contain the following
//
//  - `requesttoken` an authorised OAuth request token
//  - `requestsecret` the corresponding OAuth request secret
//
// - @param {Object} options
// - @param {Function} callback
module.exports.getAccessToken = function getAccessToken(options, callback) {
	var wrapperFactory = createOAuthWrapper;

	step(
		function getAccessToken() {
			var oAuth = wrapperFactory(options.oauthkey, options.oauthsecret);

			oAuth.getOAuthAccessToken(options.requesttoken,
										options.requestsecret, this);
		},
		function parseResponse(err, oauth_access_token,
								oauth_access_token_secret, results) {
			var mangledResponse, accessToken, accessSecret;

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

			// The OAuth endpoints on the 7digital API return XML instead of
			// an encoded parameter response.
			mangledResponse = results['<?xml version'];

			// The OAuth endpoints on the 7digital API return XML instead of an
			// encoded parameter response so we need to parse the token and
			// secret from the XML
			accessToken = tokenPattern.exec(mangledResponse)[1];
			accessSecret = secretPattern.exec(mangledResponse)[1];

			accessToken = encodeURIComponent(accessToken);
			accessSecret = encodeURIComponent(accessSecret);

			callback(null, accessToken, accessSecret);
		}
	);
};
<commit_msg>Rencode less aggressively (access token)<commit_after>var OAuth = require('oauth').OAuth,
	step = require('step'),
	readline = require('readline'),
	createOAuthWrapper,
	// Regular expressions for parsing the XML OAuth response
	tokenPattern = /<oauth_token\>([^<]*)<\/oauth_token\>/,
	secretPattern = /<oauth_token_secret\>([^<]*)<\/oauth_token_secret\>/;

// Creates an OAuth v1 wrapper configured with the supplied key and
// secret and the necessary options for authenticating with the
// 7digital API.
//
// - @param {String} oauthkey
// - @param {String} oauthsecret
//
// - @return {OAuth}
module.exports.createOAuthWrapper = createOAuthWrapper =
	function (oauthkey, oauthsecret) {
	var oAuth = new OAuth(
				'http://api.7digital.com/1.2/oauth/requesttoken',
				'http://api.7digital.com/1.2/oauth/accesstoken',
				oauthkey, oauthsecret, '1.0', null, 'HMAC-SHA1');

	oAuth.setClientOptions({
		requestTokenHttpMethod: "GET",
		accessTokenHttpMethod: "GET"
	});

	return oAuth;
};

// Gets a request token from the 7digital API and generates an authorise
// url for the user to grant the application access to their locker and
// make purchases on their behalf.
//
//
//     var oauthHelper = require('oauth-helper');
//     oauthHelper.getRequestToken({
//             oauthkey: 'YOUR_KEY_HERE',
//             oauthsecret: 'YOUR_SECRET',
//             callbackUrl: ''
//         }, function authorise(err, requestToken, requestSecret,
//                               authoriseUrl) {
//              // Get an access token
//     });
//
//
// The options parameter should contain the following
//
//  - `oauthkey` your app's oauth consumer key
//  - `oauthsecret` your app's oauth consumer secret
//  - `callbackUrl` (optional) the url to return once the user has authorised
//                  your app
//
// - @param {Object} options
// - @param {Function} callback
module.exports.getRequestToken = function (options, callback) {
	var wrapperFactory = createOAuthWrapper;

	step(
		function getRequestToken() {
			var oAuth = wrapperFactory(options.oauthkey, options.oauthsecret);

			oAuth.getOAuthRequestToken(this);
		},
		function parseResponse(err, oauth_token, oauth_token_secret, results) {
			var mangledResponse, requestToken, requestSecret, authoriseUrl,
				returnUrl, consoleInterface;

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

			// The OAuth endpoints on the 7digital API return XML instead of
			// an encoded parameter response.
			mangledResponse = results['<?xml version'];

			// Parse the token and secret from the XML
			requestToken = tokenPattern.exec(mangledResponse)[1];
			requestSecret  = secretPattern.exec(mangledResponse)[1];

			// re-encode the token and secret as the node oauth client
			// decodes them thinking they are parameter encoded as per
			// the spec.
			requestToken = requestToken.replace(' ', '+');
			requestSecret = requestSecret.replace(' ', '+');

			authoriseUrl = 'https://account.7digital.com/' +
							options.oauthkey + '/oauth/authorise?' +
						'oauth_token=' + requestToken + '&oauth_callback=' +
						encodeURIComponent(options.callbackUrl);

			callback(null, requestToken, requestSecret, authoriseUrl);
		}
	);
};

// Gets an access token from the 7digital API for signing requests to
// access a user's 7digital account on their behalf.
//
// You must generate your access token only after the user has authorised
// the request token.
//
// You can use this token and secret for signing requests to the 3-legged
// OAuth secured API endpoints.
//
//
//     var oauthHelper = require('oauth-helper');
//     oauthHelper.getAccessToken({
//             oauthkey: 'YOUR_KEY_HERE',
//             oauthsecret: 'YOUR_SECRET_HERE',
//             requesttoken: 'YOUR_TOKEN_HERE',
//             requestsecret: 'TOKEN_SECRET_HERE'
//         }, function accessSignedEndpoints(err, accesstoken, accesssecret) {
//             // Access the user's locker etc
//     });
//
//
// The options parameter should contain the following
//
//  - `requesttoken` an authorised OAuth request token
//  - `requestsecret` the corresponding OAuth request secret
//
// - @param {Object} options
// - @param {Function} callback
module.exports.getAccessToken = function getAccessToken(options, callback) {
	var wrapperFactory = createOAuthWrapper;

	step(
		function getAccessToken() {
			var oAuth = wrapperFactory(options.oauthkey, options.oauthsecret);

			oAuth.getOAuthAccessToken(options.requesttoken,
										options.requestsecret, this);
		},
		function parseResponse(err, oauth_access_token,
								oauth_access_token_secret, results) {
			var mangledResponse, accessToken, accessSecret;

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

			// The OAuth endpoints on the 7digital API return XML instead of
			// an encoded parameter response.
			mangledResponse = results['<?xml version'];

			// The OAuth endpoints on the 7digital API return XML instead of an
			// encoded parameter response so we need to parse the token and
			// secret from the XML
			accessToken = tokenPattern.exec(mangledResponse)[1];
			accessSecret = secretPattern.exec(mangledResponse)[1];

			// Parse the token and secret from the XML
			accessToken = accessToken.replace(' ', '+');
			accessSecret = accessSecret.replace(' ', '+');

			callback(null, accessToken, accessSecret);
		}
	);
};
<|endoftext|>


<commit_before>/**
 * Copyright 2013-present NightWorld.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var error = require('./error'),
  AuthCodeGrant = require('./authCodeGrant'),
  Authorise = require('./authorise'),
  Grant = require('./grant'),
  Scope = require('./scope'),
  ImplicitGrant = require('./implicit');

module.exports = OAuth2Server;

/**
 * Constructor
 *
 * @param {Object} config Configuration object
 */
function OAuth2Server (config) {

  if (!(this instanceof OAuth2Server)) return new OAuth2Server(config);

  config = config || {};

  if (!config.model) throw new Error('No model supplied to OAuth2Server');
  this.model = config.model;

  this.grants = config.grants || [];
  this.debug = config.debug || function () {};
  if (typeof this.debug !== 'function') {
      this.debug = console.log;
  }
  this.passthroughErrors = config.passthroughErrors;
  this.continueAfterResponse = config.continueAfterResponse;

  this.accessTokenLifetime = config.accessTokenLifetime !== undefined ?
    config.accessTokenLifetime : 3600;
  this.refreshTokenLifetime = config.refreshTokenLifetime !== undefined ?
    config.refreshTokenLifetime : 1209600;
  this.authCodeLifetime = config.authCodeLifetime || 30;

  this.regex = {
    clientId: config.clientIdRegex || /^[a-z0-9-_]{3,40}$/i,
    grantType: new RegExp('^(' + this.grants.join('|') + ')$', 'i')
  };
}

/**
 * Authorisation Middleware
 *
 * Returns middleware that will authorise the request using oauth,
 * if successful it will allow the request to proceed to the next handler
 *
 * @return {Function} middleware
 */
OAuth2Server.prototype.authorise = function (options) {
  var self = this;

  return function (req, res, next) {
    return new Authorise(self, req, res, options, next);
  };
};

/**
 * Grant Middleware
 *
 * Returns middleware that will grant tokens to valid requests.
 * This would normally be mounted at '/oauth/token' e.g.
 *
 * `app.all('/oauth/token', oauth.grant());`
 *
 * @return {Function} middleware
 */
OAuth2Server.prototype.grant = function (options) {
  var self = this;

  return function (req, res, next) {
    return new Grant(self, options, req, res, next);
  };
};

/**
 * Code Auth Grant Middleware
 *
 * @param  {Function} check Function will be called with req to check if the
 *                          user has authorised the request.
 * @return {Function}       middleware
 */
OAuth2Server.prototype.authCodeGrant = function (check) {
  var self = this;

  return function (req, res, next) {
    new AuthCodeGrant(self, req, res, next, check);
  };
};

/**
 * Implicit Auth Grant Middleware
 * @param  {Function} check   Function will be called with req to check if the
 *                            user has authorised the request.
 * @param  {Function} handler middleware
 * @return {Function}         middleware
 */
OAuth2Server.prototype.implicit = function(check) {
  var self = this;
  return function (req, res, next) {
    new ImplicitGrant(self, req, res, next, check);
  };
};

OAuth2Server.prototype.implicitRedirect = function() {
  var errfunc = function (err, req, res, next) {
    if (res.oauthRedirect && res.redirectUri) {
      return res.redirect(res.redirectUri + '?error=' + err.error +
          '&error_description=' + err.error_description + '&code=' + err.code);
    }
    next(err);
  };

  var goodfunc = function (req, res, next) {
    if (res.oauthRedirect && res.redirectUri) {
      return res.redirect(res.redirectUri + '#token=' + res.accessToken +
          (req.query.state ? '&state=' + req.query.state : ''));
    }
    next();
  };

  return [errfunc, goodfunc];
};

/**
 * Scope Check Middleware
 *
 * Returns middleware that allows the specification of required
 * scope(s) for routers and/or routes, which is validated by the model.
 *
 * @param  {Mixed}    requiredScope String or list of scope keys
 *                                  required to access the route.
 * @return {Function}
 */
OAuth2Server.prototype.scope = function(requiredScope) {
  var self = this;

  return function(req, res, next) {
    return new Scope(self, req, next, requiredScope);
  };
};

/**
 * OAuth Error Middleware
 *
 * Returns middleware that will catch OAuth errors and ensure an OAuth
 * complaint response
 *
 * @return {Function} middleware
 */
OAuth2Server.prototype.errorHandler = function () {
  var self = this;

  return function (err, req, res, next) {
    if (!(err instanceof error) || self.passthroughErrors) return next(err);

    delete err.name;
    delete err.message;

    self.debug(err.stack || err);
    delete err.stack;

    if (err.headers) res.set(err.headers);
    delete err.headers;

    res.status(err.code).send(err);
  };
};

/**
 * Lockdown
 *
 * When using the lockdown patter, this function should be called after
 * all routes have been declared.
 * It will search through each route and if it has not been explitly bypassed
 * (by passing oauth.bypass) then authorise will be inserted.
 * If oauth.grant has been passed it will replace it with the proper grant
 * middleware
 * NOTE: When using this method, you must PASS the method not CALL the method,
 * e.g.:
 *
 * `
 * app.all('/oauth/token', app.oauth.grant);
 *
 * app.get('/secrets', function (req, res) {
 *   res.send('secrets');
 * });
 *
 * app.get('/public', app.oauth.bypass, function (req, res) {
 *   res.send('publci');
 * });
 *
 * app.oauth.lockdown(app);
 * `
 *
 * @param  {Object} app Express app
 */
OAuth2Server.prototype.lockdown = function (app) {
  var self = this;

  var lockdownExpress3 = function (stack) {
    // Check if it's a grant route
    var pos = stack.indexOf(self.grant);
    if (pos !== -1) {
      stack[pos] = self.grant();
      return;
    }

    // Check it's not been explitly bypassed
    pos = stack.indexOf(self.bypass);
    if (pos === -1) {
      stack.unshift(self.authorise());
    } else {
      stack.splice(pos, 1);
    }
  };

  var lockdownExpress4 = function (layer) {
    if (!layer.route)
      return;

    var stack = layer.route.stack;
    var handlers = stack.map(function (item) {
      return item.handle;
    });

    // Check if it's a grant route
    var pos = handlers.indexOf(self.grant);
    if (pos !== -1) {
      stack[pos].handle = self.grant();
      return;
    }

    // Check it's not been explitly bypassed
    pos = handlers.indexOf(self.bypass);
    if (pos === -1) {
      // Add authorise another route (could do it properly with express.route?)
      var copy = {};
      var first = stack[0];
      for (var key in first) {
        copy[key] = first[key];
      }
      copy.handle = self.authorise();
      stack.unshift(copy);
    } else {
      stack.splice(pos, 1);
    }
  };

  if (app.routes) {
    for (var method in app.routes) {
      app.routes[method].forEach(function (route) {
        lockdownExpress3(route.callbacks);
      });
    }
  } else {
    app._router.stack.forEach(lockdownExpress4);
  }
};

/**
 * Bypass
 *
 * This is used as placeholder for when using the lockdown pattern
 *
 * @return {Function} noop
 */
OAuth2Server.prototype.bypass = function () {};
<commit_msg>Properly handle nested routers in express 4<commit_after>/**
 * Copyright 2013-present NightWorld.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var error = require('./error'),
  AuthCodeGrant = require('./authCodeGrant'),
  Authorise = require('./authorise'),
  Grant = require('./grant'),
  Scope = require('./scope'),
  ImplicitGrant = require('./implicit');

module.exports = OAuth2Server;

/**
 * Constructor
 *
 * @param {Object} config Configuration object
 */
function OAuth2Server (config) {

  if (!(this instanceof OAuth2Server)) return new OAuth2Server(config);

  config = config || {};

  if (!config.model) throw new Error('No model supplied to OAuth2Server');
  this.model = config.model;

  this.grants = config.grants || [];
  this.debug = config.debug || function () {};
  if (typeof this.debug !== 'function') {
      this.debug = console.log;
  }
  this.passthroughErrors = config.passthroughErrors;
  this.continueAfterResponse = config.continueAfterResponse;

  this.accessTokenLifetime = config.accessTokenLifetime !== undefined ?
    config.accessTokenLifetime : 3600;
  this.refreshTokenLifetime = config.refreshTokenLifetime !== undefined ?
    config.refreshTokenLifetime : 1209600;
  this.authCodeLifetime = config.authCodeLifetime || 30;

  this.regex = {
    clientId: config.clientIdRegex || /^[a-z0-9-_]{3,40}$/i,
    grantType: new RegExp('^(' + this.grants.join('|') + ')$', 'i')
  };
}

/**
 * Authorisation Middleware
 *
 * Returns middleware that will authorise the request using oauth,
 * if successful it will allow the request to proceed to the next handler
 *
 * @return {Function} middleware
 */
OAuth2Server.prototype.authorise = function (options) {
  var self = this;

  return function (req, res, next) {
    return new Authorise(self, req, res, options, next);
  };
};

/**
 * Grant Middleware
 *
 * Returns middleware that will grant tokens to valid requests.
 * This would normally be mounted at '/oauth/token' e.g.
 *
 * `app.all('/oauth/token', oauth.grant());`
 *
 * @return {Function} middleware
 */
OAuth2Server.prototype.grant = function (options) {
  var self = this;

  return function (req, res, next) {
    return new Grant(self, options, req, res, next);
  };
};

/**
 * Code Auth Grant Middleware
 *
 * @param  {Function} check Function will be called with req to check if the
 *                          user has authorised the request.
 * @return {Function}       middleware
 */
OAuth2Server.prototype.authCodeGrant = function (check) {
  var self = this;

  return function (req, res, next) {
    new AuthCodeGrant(self, req, res, next, check);
  };
};

/**
 * Implicit Auth Grant Middleware
 * @param  {Function} check   Function will be called with req to check if the
 *                            user has authorised the request.
 * @param  {Function} handler middleware
 * @return {Function}         middleware
 */
OAuth2Server.prototype.implicit = function(check) {
  var self = this;
  return function (req, res, next) {
    new ImplicitGrant(self, req, res, next, check);
  };
};

OAuth2Server.prototype.implicitRedirect = function() {
  var errfunc = function (err, req, res, next) {
    if (res.oauthRedirect && res.redirectUri) {
      return res.redirect(res.redirectUri + '?error=' + err.error +
          '&error_description=' + err.error_description + '&code=' + err.code);
    }
    next(err);
  };

  var goodfunc = function (req, res, next) {
    if (res.oauthRedirect && res.redirectUri) {
      return res.redirect(res.redirectUri + '#token=' + res.accessToken +
          (req.query.state ? '&state=' + req.query.state : ''));
    }
    next();
  };

  return [errfunc, goodfunc];
};

/**
 * Scope Check Middleware
 *
 * Returns middleware that allows the specification of required
 * scope(s) for routers and/or routes, which is validated by the model.
 *
 * @param  {Mixed}    requiredScope String or list of scope keys
 *                                  required to access the route.
 * @return {Function}
 */
OAuth2Server.prototype.scope = function(requiredScope) {
  var self = this;

  return function(req, res, next) {
    return new Scope(self, req, next, requiredScope);
  };
};

/**
 * OAuth Error Middleware
 *
 * Returns middleware that will catch OAuth errors and ensure an OAuth
 * complaint response
 *
 * @return {Function} middleware
 */
OAuth2Server.prototype.errorHandler = function () {
  var self = this;

  return function (err, req, res, next) {
    if (!(err instanceof error) || self.passthroughErrors) return next(err);

    delete err.name;
    delete err.message;

    self.debug(err.stack || err);
    delete err.stack;

    if (err.headers) res.set(err.headers);
    delete err.headers;

    res.status(err.code).send(err);
  };
};

/**
 * Lockdown
 *
 * When using the lockdown patter, this function should be called after
 * all routes have been declared.
 * It will search through each route and if it has not been explitly bypassed
 * (by passing oauth.bypass) then authorise will be inserted.
 * If oauth.grant has been passed it will replace it with the proper grant
 * middleware
 * NOTE: When using this method, you must PASS the method not CALL the method,
 * e.g.:
 *
 * `
 * app.all('/oauth/token', app.oauth.grant);
 *
 * app.get('/secrets', function (req, res) {
 *   res.send('secrets');
 * });
 *
 * app.get('/public', app.oauth.bypass, function (req, res) {
 *   res.send('publci');
 * });
 *
 * app.oauth.lockdown(app);
 * `
 *
 * @param  {Object} app Express app
 */
OAuth2Server.prototype.lockdown = function (app) {
  var self = this;

  var lockdownExpress3 = function (stack) {
    // Check if it's a grant route
    var pos = stack.indexOf(self.grant);
    if (pos !== -1) {
      stack[pos] = self.grant();
      return;
    }

    // Check it's not been explitly bypassed
    pos = stack.indexOf(self.bypass);
    if (pos === -1) {
      stack.unshift(self.authorise());
    } else {
      stack.splice(pos, 1);
    }
  };

  var lockdownExpress4 = function (layer) {
    if (layer.name === 'router') {
      layer.handle.stack.forEach(lockdownExpress4);
      return;
    }

    if (!layer.route)
      return;

    var stack = layer.route.stack;
    var handlers = stack.map(function (item) {
      return item.handle;
    });

    // Check if it's a grant route
    var pos = handlers.indexOf(self.grant);
    if (pos !== -1) {
      stack[pos].handle = self.grant();
      return;
    }

    // Check it's not been explitly bypassed
    pos = handlers.indexOf(self.bypass);
    if (pos === -1) {
      // Add authorise another route (could do it properly with express.route?)
      var copy = {};
      var first = stack[0];
      for (var key in first) {
        copy[key] = first[key];
      }
      copy.handle = self.authorise();
      stack.unshift(copy);
    } else {
      stack.splice(pos, 1);
    }
  };

  if (app.routes) {
    for (var method in app.routes) {
      app.routes[method].forEach(function (route) {
        lockdownExpress3(route.callbacks);
      });
    }
  } else {
    app._router.stack.forEach(lockdownExpress4);
  }
};

/**
 * Bypass
 *
 * This is used as placeholder for when using the lockdown pattern
 *
 * @return {Function} noop
 */
OAuth2Server.prototype.bypass = function () {};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Actor LLC. <https://actor.im>
 */

import _ from 'lodash';
import React from 'react';
import ReactMixin from 'react-mixin';
import { IntlMixin, FormattedMessage } from 'react-intl';
import classnames from 'classnames';

import ActorClient from 'utils/ActorClient';
import confirm from 'utils/confirm'

import ContactActionCreators from 'actions/ContactActionCreators';
import DialogActionCreators from 'actions/DialogActionCreators';

import PeerStore from 'stores/PeerStore';
import DialogStore from 'stores/DialogStore';

import AvatarItem from 'components/common/AvatarItem.react';
import Fold from 'components/common/Fold.React';

const getStateFromStores = (userId) => {
  const thisPeer = PeerStore.getUserPeer(userId);
  return {
    thisPeer: thisPeer,
    isNotificationsEnabled: DialogStore.isNotificationsEnabled(thisPeer)
  };
};

@ReactMixin.decorate(IntlMixin)
class UserProfile extends React.Component {
  static propTypes = {
    user: React.PropTypes.object.isRequired
  };

  constructor(props) {
    super(props);

    this.state = _.assign({
      isActionsDropdownOpen: false
    }, getStateFromStores(props.user.id));

    DialogStore.addNotificationsListener(this.onChange);
  }

  componentWillUnmount() {
    DialogStore.removeNotificationsListener(this.onChange);
  }

  componentWillReceiveProps(newProps) {
    this.setState(getStateFromStores(newProps.user.id));
  }

  addToContacts = () => {
    ContactActionCreators.addContact(this.props.user.id);
  };

  removeFromContacts = () => {
    const { user } = this.props;
    const confirmText = 'You really want to remove ' + user.name + ' from your contacts?';

    confirm(confirmText).then(
      () => ContactActionCreators.removeContact(user.id)
    );
  };

  onNotificationChange = (event) => {
    const { thisPeer } = this.state;
    DialogActionCreators.changeNotificationsEnabled(thisPeer, event.target.checked);
  };

  onChange = () => {
    const { user } = this.props;
    this.setState(getStateFromStores(user.id));
  };

  toggleActionsDropdown = () => {
    const { isActionsDropdownOpen } = this.state;

    if (!isActionsDropdownOpen) {
      this.setState({isActionsDropdownOpen: true});
      document.addEventListener('click', this.closeActionsDropdown, false);
    } else {
      this.closeActionsDropdown();
    }
  };

  closeActionsDropdown = () => {
    this.setState({isActionsDropdownOpen: false});
    document.removeEventListener('click', this.closeActionsDropdown, false);
  };

  clearChat = (uid) => {
    confirm('Do you really want to delete this conversation?').then(
      () => {
        const peer = ActorClient.getUserPeer(uid);
        DialogActionCreators.clearChat(peer);
      },
      () => {}
    );
  };

  deleteChat = (uid) => {
    confirm('Do you really want to delete this conversation?').then(
      () => {
        const peer = ActorClient.getUserPeer(uid);
        DialogActionCreators.deleteChat(peer);
      },
      () => {}
    );
  };

  render() {
    const { user } = this.props;
    const { isNotificationsEnabled, isActionsDropdownOpen } = this.state;

    const actions = (user.isContact === false) ? (

        <FormattedMessage message={this.getIntlMessage('addToContacts')}/>

    ) : (

        <FormattedMessage message={this.getIntlMessage('removeFromContacts')}/>

    );

    const dropdownClassNames = classnames('dropdown pull-left', {
      'dropdown--opened': isActionsDropdownOpen
    });

    const about = user.about ? (

    ) : null;

    const nickname = user.nick ? (

        <svg className="icon icon--pink"
             dangerouslySetInnerHTML={{__html: '<use xlink:href="assets/sprite/icons.svg#username"/>'}}/>
        {user.nick}
        nickname

    ) : null;

    const email = user.email ? (

        mail
        {user.email}
        email

    ) : null;

    const phone = user.phones[0] ? (

        call
        {'+' + user.phones[0].number}
        mobile

    ) : null;

    return (

              <AvatarItem image={user.bigAvatar}
                          placeholder={user.placeholder}
                          size="large"
                          title={user.name}/>

{user.presence}

            {about}

                <button className="dropdown__button button button--flat" onClick={this.toggleActionsDropdown}>
                  more_horiz
                  <FormattedMessage message={this.getIntlMessage('actions')}/>
                </button>

                  {actions}

 this.clearChat(user.id)}>
                    <FormattedMessage message={this.getIntlMessage('clearConversation')}/>

 this.deleteChat(user.id)}>
                    <FormattedMessage message={this.getIntlMessage('deleteConversation')}/>

              {nickname}
              {phone}
              {email}

            <Fold icon="attach_file" iconClassName="icon--gray" title={this.getIntlMessage('sharedMedia')}>

230 Shared Photos and Videos

49 Shared Links

49 Shared Files

            </Fold>

              notifications_none
              <FormattedMessage message={this.getIntlMessage('notifications')}/>

                <input checked={isNotificationsEnabled}
                       id="notifications"
                       onChange={this.onNotificationChange}
                       type="checkbox"/>

    );
  }
}

export default UserProfile;
<commit_msg>fix(web): add forgotten import<commit_after>/*
 * Copyright (C) 2015 Actor LLC. <https://actor.im>
 */

import _ from 'lodash';
import React from 'react';
import ReactMixin from 'react-mixin';
import { IntlMixin, FormattedMessage } from 'react-intl';
import classnames from 'classnames';

import ActorClient from 'utils/ActorClient';
import confirm from 'utils/confirm'
import { escapeWithEmoji } from 'utils/EmojiUtils';

import ContactActionCreators from 'actions/ContactActionCreators';
import DialogActionCreators from 'actions/DialogActionCreators';

import PeerStore from 'stores/PeerStore';
import DialogStore from 'stores/DialogStore';

import AvatarItem from 'components/common/AvatarItem.react';
import Fold from 'components/common/Fold.React';

const getStateFromStores = (userId) => {
  const thisPeer = PeerStore.getUserPeer(userId);
  return {
    thisPeer: thisPeer,
    isNotificationsEnabled: DialogStore.isNotificationsEnabled(thisPeer)
  };
};

@ReactMixin.decorate(IntlMixin)
class UserProfile extends React.Component {
  static propTypes = {
    user: React.PropTypes.object.isRequired
  };

  constructor(props) {
    super(props);

    this.state = _.assign({
      isActionsDropdownOpen: false
    }, getStateFromStores(props.user.id));

    DialogStore.addNotificationsListener(this.onChange);
  }

  componentWillUnmount() {
    DialogStore.removeNotificationsListener(this.onChange);
  }

  componentWillReceiveProps(newProps) {
    this.setState(getStateFromStores(newProps.user.id));
  }

  addToContacts = () => {
    ContactActionCreators.addContact(this.props.user.id);
  };

  removeFromContacts = () => {
    const { user } = this.props;
    const confirmText = 'You really want to remove ' + user.name + ' from your contacts?';

    confirm(confirmText).then(
      () => ContactActionCreators.removeContact(user.id)
    );
  };

  onNotificationChange = (event) => {
    const { thisPeer } = this.state;
    DialogActionCreators.changeNotificationsEnabled(thisPeer, event.target.checked);
  };

  onChange = () => {
    const { user } = this.props;
    this.setState(getStateFromStores(user.id));
  };

  toggleActionsDropdown = () => {
    const { isActionsDropdownOpen } = this.state;

    if (!isActionsDropdownOpen) {
      this.setState({isActionsDropdownOpen: true});
      document.addEventListener('click', this.closeActionsDropdown, false);
    } else {
      this.closeActionsDropdown();
    }
  };

  closeActionsDropdown = () => {
    this.setState({isActionsDropdownOpen: false});
    document.removeEventListener('click', this.closeActionsDropdown, false);
  };

  clearChat = (uid) => {
    confirm('Do you really want to delete this conversation?').then(
      () => {
        const peer = ActorClient.getUserPeer(uid);
        DialogActionCreators.clearChat(peer);
      },
      () => {}
    );
  };

  deleteChat = (uid) => {
    confirm('Do you really want to delete this conversation?').then(
      () => {
        const peer = ActorClient.getUserPeer(uid);
        DialogActionCreators.deleteChat(peer);
      },
      () => {}
    );
  };

  render() {
    const { user } = this.props;
    const { isNotificationsEnabled, isActionsDropdownOpen } = this.state;

    const actions = (user.isContact === false) ? (

        <FormattedMessage message={this.getIntlMessage('addToContacts')}/>

    ) : (

        <FormattedMessage message={this.getIntlMessage('removeFromContacts')}/>

    );

    const dropdownClassNames = classnames('dropdown pull-left', {
      'dropdown--opened': isActionsDropdownOpen
    });

    const about = user.about ? (

    ) : null;

    const nickname = user.nick ? (

        <svg className="icon icon--pink"
             dangerouslySetInnerHTML={{__html: '<use xlink:href="assets/sprite/icons.svg#username"/>'}}/>
        {user.nick}
        nickname

    ) : null;

    const email = user.email ? (

        mail
        {user.email}
        email

    ) : null;

    const phone = user.phones[0] ? (

        call
        {'+' + user.phones[0].number}
        mobile

    ) : null;

    return (

              <AvatarItem image={user.bigAvatar}
                          placeholder={user.placeholder}
                          size="large"
                          title={user.name}/>

{user.presence}

            {about}

                <button className="dropdown__button button button--flat" onClick={this.toggleActionsDropdown}>
                  more_horiz
                  <FormattedMessage message={this.getIntlMessage('actions')}/>
                </button>

                  {actions}

 this.clearChat(user.id)}>
                    <FormattedMessage message={this.getIntlMessage('clearConversation')}/>

 this.deleteChat(user.id)}>
                    <FormattedMessage message={this.getIntlMessage('deleteConversation')}/>

              {nickname}
              {phone}
              {email}

            <Fold icon="attach_file" iconClassName="icon--gray" title={this.getIntlMessage('sharedMedia')}>

230 Shared Photos and Videos

49 Shared Links

49 Shared Files

            </Fold>

              notifications_none
              <FormattedMessage message={this.getIntlMessage('notifications')}/>

                <input checked={isNotificationsEnabled}
                       id="notifications"
                       onChange={this.onNotificationChange}
                       type="checkbox"/>

    );
  }
}

export default UserProfile;
<|endoftext|>


<commit_before>/* globals EmberENV */
import { get } from '@ember/object';
import { nextTickPromise } from './-utils';
import { getContext } from './setup-context';
import hasEmberVersion from './has-ember-version';
import settled from './settled';

/**
  Navigate the application to the provided URL.

  @public
  @returns {Promise<void>} resolves when settled
*/
export function visit() {
  let context = getContext();
  let { owner } = context;

  return nextTickPromise()
    .then(() => {
      return owner.visit(...arguments);
    })
    .then(() => {
      if (EmberENV._APPLICATION_TEMPLATE_WRAPPER !== false) {
        context.element = document.querySelector('#ember-testing > .ember-view');
      } else {
        context.element = document.querySelector('#ember-testing');
      }
    })
    .then(settled);
}

/**
  @public
  @returns {string} the currently active route name
*/
export function currentRouteName() {
  let { owner } = getContext();
  let router = owner.lookup('router:main');
  return get(router, 'currentRouteName');
}

const HAS_CURRENT_URL_ON_ROUTER = hasEmberVersion(2, 13);

/**
  @public
  @returns {string} the applications current url
*/
export function currentURL() {
  let { owner } = getContext();
  let router = owner.lookup('router:main');

  if (HAS_CURRENT_URL_ON_ROUTER) {
    return get(router, 'currentURL');
  } else {
    return get(router, 'location').getURL();
  }
}

/**
  Used by test framework addons to setup the provided context for working with
  an application (e.g. routing).

  `setupContext` must have been ran on the provided context prior to calling
  `setupApplicatinContext`.

  Sets up the basic framework used by application tests.

  @public
  @param {Object} context the context to setup
  @returns {Promise<Object>} resolves with the context that was setup
*/
export default function setupApplicationContext() {
  return nextTickPromise();
}
<commit_msg>Update setup-application-context.js<commit_after>/* globals EmberENV */
import { get } from '@ember/object';
import { nextTickPromise } from './-utils';
import { getContext } from './setup-context';
import hasEmberVersion from './has-ember-version';
import settled from './settled';

/**
  Navigate the application to the provided URL.

  @public
  @returns {Promise<void>} resolves when settled
*/
export function visit() {
  let context = getContext();
  let { owner } = context;

  return nextTickPromise()
    .then(() => {
      return owner.visit(...arguments);
    })
    .then(() => {
      if (EmberENV._APPLICATION_TEMPLATE_WRAPPER !== false) {
        context.element = document.querySelector('#ember-testing > .ember-view');
      } else {
        context.element = document.querySelector('#ember-testing');
      }
    })
    .then(settled);
}

/**
  @public
  @returns {string} the currently active route name
*/
export function currentRouteName() {
  let { owner } = getContext();
  let router = owner.lookup('router:main');
  return get(router, 'currentRouteName');
}

const HAS_CURRENT_URL_ON_ROUTER = hasEmberVersion(2, 13);

/**
  @public
  @returns {string} the applications current url
*/
export function currentURL() {
  let { owner } = getContext();
  let router = owner.lookup('router:main');

  if (HAS_CURRENT_URL_ON_ROUTER) {
    return get(router, 'currentURL');
  } else {
    return get(router, 'location').getURL();
  }
}

/**
  Used by test framework addons to setup the provided context for working with
  an application (e.g. routing).

  `setupContext` must have been run on the provided context prior to calling
  `setupApplicationContext`.

  Sets up the basic framework used by application tests.

  @public
  @param {Object} context the context to setup
  @returns {Promise<Object>} resolves with the context that was setup
*/
export default function setupApplicationContext() {
  return nextTickPromise();
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var App = require('app');
var credentialsUtils = require('utils/credentials');

App.KerberosWizardController = App.WizardController.extend(App.InstallComponent, {

  exceptionsOnSkipClient: [{'KDC': 'realm'}, {'KDC': 'kdc_type'}, {'Advanced kerberos-env': 'executable_search_paths'}],

  name: 'kerberosWizardController',

  totalSteps: 8,

  /**
   * @type {string}
   */
  displayName: Em.I18n.t('admin.kerberos.wizard.header'),

  isKerberosWizard: true,

  stackConfigsLoaded: false,
  /**
   * Used for hiding back button in wizard
   */
  hideBackButton: true,

  /**
   * Check if Kerberos Clients should be installed.
   * Clients installation will be skipped if Manual Kerberization was selected.
   *
   * @type {Boolean}
   */
  skipClientInstall: function() {
    if (this.get('content.kerberosOption')) {
      return this.get('content.kerberosOption') === Em.I18n.t('admin.kerberos.wizard.step1.option.manual');
    }
    return false;
  }.property('content.kerberosOption'),

  kerberosDescriptorConfigs: null,

  content: Em.Object.create({
    controllerName: 'kerberosWizardController',
    serviceName: 'KERBEROS',
    kerberosOption: null,
    cluster: null,
    services: [],
    advancedServiceConfig: null,
    serviceConfigProperties: [],
    failedTask: null,
    secureStoragePersisted: null
  }),

  /**
   * set current step
   * @param {string} currentStep
   * @param {boolean} completed
   * @param {boolean} skipStateSave
   */
  setCurrentStep: function (currentStep, completed, skipStateSave) {
    this._super(currentStep, completed);
    if (App.get('testMode') || skipStateSave) {
      return;
    }
    App.clusterStatus.setClusterStatus({
      clusterName: this.get('content.cluster.name'),
      clusterState: 'KERBEROS_DEPLOY',
      wizardControllerName: 'kerberosWizardController',
      localdb: App.db.data
    });
  },

  setStepsEnable: function () {
    for (var i = 1; i <= this.get('totalSteps'); i++) {
      var step = this.get('isStepDisabled').findProperty('step', i);
      if (i <= this.get('currentStep') && App.get('router.clusterController.isLoaded')) {
        step.set('value', false);
      } else {
        step.set('value', i != this.get('currentStep'));
      }
    }
  }.observes('currentStep', 'App.router.clusterController.isLoaded'),

  /**
   * return new object extended from clusterStatusTemplate
   * @return Object
   */
  getCluster: function () {
    return jQuery.extend({}, this.get('clusterStatusTemplate'), {name: App.get('router').getClusterName()});
  },

  updateClusterEnvData: function (configs) {
    var kerberosDescriptor = this.kerberosDescriptorConfigs;
    configs['security_enabled'] = true;
    configs['kerberos_domain'] = kerberosDescriptor.properties.realm;
    return configs;
  },

  /**
   * save status of the cluster.
   * @param clusterStatus object with status,requestId fields.
   */
  saveClusterStatus: function (clusterStatus) {
    var oldStatus = this.toObject(this.get('content.cluster'));
    clusterStatus = jQuery.extend(oldStatus, clusterStatus);
    if (clusterStatus.requestId) {
      clusterStatus.requestId.forEach(function (requestId) {
        if (clusterStatus.oldRequestsId.indexOf(requestId) === -1) {
          clusterStatus.oldRequestsId.push(requestId)
        }
      }, this);
    }
    this.set('content.cluster', clusterStatus);
    this.save('cluster');
  },

  saveConfigTag: function (tag) {
    App.db.setKerberosWizardConfigTag(tag);
    this.set('content.' + [tag.name], tag.value);
  },

  saveKerberosOption: function (stepController) {
    this.setDBProperty('kerberosOption', stepController.get('selectedItem'));
    this.set('content.kerberosOption', stepController.get('selectedItem'));
  },

  /**
   * Load serviceConfigProperties to model
   */
  loadServiceConfigProperties: function () {
    var serviceConfigProperties = this.getDBProperty('serviceConfigProperties');
    this.set('content.serviceConfigProperties', serviceConfigProperties);
  },

  loadKerberosDescriptorConfigs: function () {
    var kerberosDescriptorConfigs = this.getDBProperty('kerberosDescriptorConfigs');
    this.set('kerberosDescriptorConfigs', kerberosDescriptorConfigs);
  },

  /**
   * Override the visibility of a list of form items with a new value
   *
   * @param {Array} itemsArray
   * @param newValue
   */
  overrideVisibility: function (itemsArray, newValue, exceptions) {
    newValue = newValue || false;

    for (var i = 0, len = itemsArray.length; i < len; i += 1) {
      if (!Ember.$.isEmptyObject(itemsArray[i])) {
        var isException = exceptions.filterProperty(itemsArray[i].category, itemsArray[i].name);
        if (!isException.length) {
          itemsArray[i].isVisible = newValue;
        }
      }
    }
  },

  loadKerberosOption: function () {
    this.set('content.kerberosOption', this.getDBProperty('kerberosOption'));
  },

  saveKerberosDescriptorConfigs: function (kerberosDescriptorConfigs) {
    this.setDBProperty('kerberosDescriptorConfigs',kerberosDescriptorConfigs);
    this.set('kerberosDescriptorConfigs', kerberosDescriptorConfigs);
  },

  createKerberosResources: function (callback) {
    var self = this;
    this.createKerberosService().done(function () {
      self.updateAndCreateServiceComponent('KERBEROS_CLIENT').done(function () {
        self.createKerberosHostComponents().done(callback);
      });
    });
  },

  createKerberosService: function () {
    return App.ajax.send({
      name: 'wizard.step8.create_selected_services',
      sender: this,
      data: {
        data: '{"ServiceInfo": { "service_name": "KERBEROS"}}',
        cluster: App.get('clusterName') || App.clusterStatus.get('clusterName')
      }
    });
  },

  createKerberosHostComponents: function () {
    var hostNames = App.get('allHostNames');
    var queryStr = '';
    hostNames.forEach(function (hostName) {
      queryStr += 'Hosts/host_name=' + hostName + '|';
    });
    //slice off last symbol '|'
    queryStr = queryStr.slice(0, -1);

    var data = {
      "RequestInfo": {
        "query": queryStr
      },
      "Body": {
        "host_components": [
          {
            "HostRoles": {
              "component_name": 'KERBEROS_CLIENT'
            }
          }
        ]
      }
    };

    return App.ajax.send({
      name: 'wizard.step8.register_host_to_component',
      sender: this,
      data: {
        cluster: App.router.getClusterName(),
        data: JSON.stringify(data)
      }
    });
  },

  loadMap: {
    '1': [
      {
        type: 'sync',
        callback: function () {
          var self = this;
          this.loadKerberosOption();
          if (App.get('supports.storeKDCCredentials')) {
            credentialsUtils.isStorePersisted(App.get('clusterName')).then(function(isPersisted) {
              self.set('content.secureStoragePersisted', isPersisted);
            });
          }
        }
      }
    ],
    '2': [
      {
        type: 'sync',
        callback: function () {
          var self = this;
          this.loadServiceConfigProperties();
          if (!this.get('stackConfigsLoaded')) {
            App.config.loadConfigsFromStack(['KERBEROS']).complete(function() {
              self.set('stackConfigsLoaded', true);
            }, this);
          }
        }
      },
      {
        type: 'async',
        callback: function() {
          var self = this;
          var dfd = $.Deferred();
          if (App.get('supports.storeKDCCredentials')) {
            credentialsUtils.isStorePersisted(App.get('clusterName')).then(function(isPersisted) {
              self.set('content.secureStoragePersisted', isPersisted);
              dfd.resolve();
            });
          } else {
            dfd.resolve();
          }
          return dfd.promise();
        }
      }
    ],
    '3': [
      {
        type: 'sync',
        callback: function () {
          this.loadTasksStatuses();
          this.loadTasksRequestIds();
          this.loadRequestIds();
        }
      }
    ],
    '4': [
      {
        type: 'sync',
        callback: function () {
          this.loadKerberosDescriptorConfigs();
        }
      }
    ],
    '6': [
      {
        type: 'sync',
        callback: function () {
          this.loadKerberosDescriptorConfigs();
        }
      }
    ]
  },

  /**
   * Remove all loaded data.
   * Created as copy for App.router.clearAllSteps
   */
  clearAllSteps: function () {
    this.clearInstallOptions();
    // clear temporary information stored during the install
    this.set('content.cluster', this.getCluster());
  },

  clearTasksData: function () {
    this.saveTasksStatuses(undefined);
    this.saveRequestIds(undefined);
    this.saveTasksRequestIds(undefined);
  },
  /**
   * shows popup with to warn user
   * @param primary
   * @param isCritical
   */
  warnBeforeExitPopup: function(primary, isCritical) {
    var primaryText = Em.I18n.t('common.exitAnyway');
    var msg = isCritical ? Em.I18n.t('admin.kerberos.wizard.exit.critical.msg')
      : Em.I18n.t('admin.kerberos.wizard.exit.warning.msg');
    return App.showConfirmationPopup(primary, msg, null, null, primaryText, isCritical);
  },
  /**
   * Clear all temporary data
   */
  finish: function () {
    // The in-memory variable for current step should be reset to 1st step.
    this.setCurrentStep('1', false, true);
    // kerberos wizard namespace in the localStorage should be emptied
    this.resetDbNamespace();
    App.get('router.updateController').updateAll();
  }
});
<commit_msg>AMBARI-13455. Ambari QE Deploy Test - Kerberos service check fails. Additional changes. (akovalenko)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var App = require('app');
var credentialsUtils = require('utils/credentials');

App.KerberosWizardController = App.WizardController.extend(App.InstallComponent, {

  exceptionsOnSkipClient: [{'KDC': 'realm'}, {'KDC': 'kdc_type'}, {'Advanced kerberos-env': 'executable_search_paths'}],

  name: 'kerberosWizardController',

  totalSteps: 8,

  /**
   * @type {string}
   */
  displayName: Em.I18n.t('admin.kerberos.wizard.header'),

  isKerberosWizard: true,

  stackConfigsLoaded: false,
  /**
   * Used for hiding back button in wizard
   */
  hideBackButton: true,

  /**
   * Check if Kerberos Clients should be installed.
   * Clients installation will be skipped if Manual Kerberization was selected.
   *
   * @type {Boolean}
   */
  skipClientInstall: function() {
    if (this.get('content.kerberosOption')) {
      return this.get('content.kerberosOption') === Em.I18n.t('admin.kerberos.wizard.step1.option.manual');
    }
    return false;
  }.property('content.kerberosOption'),

  kerberosDescriptorConfigs: null,

  content: Em.Object.create({
    controllerName: 'kerberosWizardController',
    serviceName: 'KERBEROS',
    kerberosOption: null,
    cluster: null,
    services: [],
    advancedServiceConfig: null,
    serviceConfigProperties: [],
    failedTask: null,
    secureStoragePersisted: null
  }),

  /**
   * set current step
   * @param {string} currentStep
   * @param {boolean} completed
   * @param {boolean} skipStateSave
   */
  setCurrentStep: function (currentStep, completed, skipStateSave) {
    this._super(currentStep, completed);
    if (App.get('testMode') || skipStateSave) {
      return;
    }
    App.clusterStatus.setClusterStatus({
      clusterName: this.get('content.cluster.name'),
      clusterState: 'KERBEROS_DEPLOY',
      wizardControllerName: 'kerberosWizardController',
      localdb: App.db.data
    });
  },

  setStepsEnable: function () {
    for (var i = 1; i <= this.get('totalSteps'); i++) {
      var step = this.get('isStepDisabled').findProperty('step', i);
      if (i <= this.get('currentStep') && App.get('router.clusterController.isLoaded')) {
        step.set('value', false);
      } else {
        step.set('value', i != this.get('currentStep'));
      }
    }
  }.observes('currentStep', 'App.router.clusterController.isLoaded'),

  /**
   * return new object extended from clusterStatusTemplate
   * @return Object
   */
  getCluster: function () {
    return jQuery.extend({}, this.get('clusterStatusTemplate'), {name: App.get('router').getClusterName()});
  },

  updateClusterEnvData: function (configs) {
    var kerberosDescriptor = this.kerberosDescriptorConfigs;
    configs['security_enabled'] = true;
    configs['kerberos_domain'] = kerberosDescriptor.properties.realm;
    return configs;
  },

  /**
   * save status of the cluster.
   * @param clusterStatus object with status,requestId fields.
   */
  saveClusterStatus: function (clusterStatus) {
    var oldStatus = this.toObject(this.get('content.cluster'));
    clusterStatus = jQuery.extend(oldStatus, clusterStatus);
    if (clusterStatus.requestId) {
      clusterStatus.requestId.forEach(function (requestId) {
        if (clusterStatus.oldRequestsId.indexOf(requestId) === -1) {
          clusterStatus.oldRequestsId.push(requestId)
        }
      }, this);
    }
    this.set('content.cluster', clusterStatus);
    this.save('cluster');
  },

  saveConfigTag: function (tag) {
    App.db.setKerberosWizardConfigTag(tag);
    this.set('content.' + [tag.name], tag.value);
  },

  saveKerberosOption: function (stepController) {
    this.setDBProperty('kerberosOption', stepController.get('selectedItem'));
    this.set('content.kerberosOption', stepController.get('selectedItem'));
  },

  /**
   * Load serviceConfigProperties to model
   */
  loadServiceConfigProperties: function () {
    var serviceConfigProperties = this.getDBProperty('serviceConfigProperties');
    this.set('content.serviceConfigProperties', serviceConfigProperties);
  },

  loadKerberosDescriptorConfigs: function () {
    var kerberosDescriptorConfigs = this.getDBProperty('kerberosDescriptorConfigs');
    this.set('kerberosDescriptorConfigs', kerberosDescriptorConfigs);
  },

  /**
   * Override the visibility of a list of form items with a new value
   *
   * @param {Array} itemsArray
   * @param newValue
   */
  overrideVisibility: function (itemsArray, newValue, exceptions) {
    newValue = newValue || false;

    for (var i = 0, len = itemsArray.length; i < len; i += 1) {
      if (!Ember.$.isEmptyObject(itemsArray[i])) {
        var isException = exceptions.filterProperty(itemsArray[i].category, itemsArray[i].name);
        if (!isException.length) {
          itemsArray[i].isVisible = newValue;
        }
      }
    }
  },

  loadKerberosOption: function () {
    this.set('content.kerberosOption', this.getDBProperty('kerberosOption'));
  },

  saveKerberosDescriptorConfigs: function (kerberosDescriptorConfigs) {
    this.setDBProperty('kerberosDescriptorConfigs',kerberosDescriptorConfigs);
    this.set('kerberosDescriptorConfigs', kerberosDescriptorConfigs);
  },

  createKerberosResources: function (callback) {
    var self = this;
    this.createKerberosService().done(function () {
      self.updateAndCreateServiceComponent('KERBEROS_CLIENT').done(function () {
        self.createKerberosHostComponents().done(callback);
      });
    });
  },

  createKerberosService: function () {
    return App.ajax.send({
      name: 'wizard.step8.create_selected_services',
      sender: this,
      data: {
        data: '{"ServiceInfo": { "service_name": "KERBEROS"}}',
        cluster: App.get('clusterName') || App.clusterStatus.get('clusterName')
      }
    });
  },

  createKerberosHostComponents: function () {
    var hostNames = App.get('allHostNames');
    var queryStr = '';
    hostNames.forEach(function (hostName) {
      queryStr += 'Hosts/host_name=' + hostName + '|';
    });
    //slice off last symbol '|'
    queryStr = queryStr.slice(0, -1);

    var data = {
      "RequestInfo": {
        "query": queryStr
      },
      "Body": {
        "host_components": [
          {
            "HostRoles": {
              "component_name": 'KERBEROS_CLIENT'
            }
          }
        ]
      }
    };

    return App.ajax.send({
      name: 'wizard.step8.register_host_to_component',
      sender: this,
      data: {
        cluster: App.router.getClusterName(),
        data: JSON.stringify(data)
      }
    });
  },

  loadMap: {
    '1': [
      {
        type: 'sync',
        callback: function () {
          var self = this;
          this.loadKerberosOption();
          if (App.get('supports.storeKDCCredentials')) {
            credentialsUtils.isStorePersisted(App.get('clusterName')).then(function(isPersisted) {
              self.set('content.secureStoragePersisted', isPersisted);
            });
          }
        }
      }
    ],
    '2': [
      {
        type: 'sync',
        callback: function () {
          var self = this;
          this.loadServiceConfigProperties();
          if (!this.get('stackConfigsLoaded')) {
            App.config.loadConfigsFromStack(['KERBEROS']).complete(function() {
              self.set('stackConfigsLoaded', true);
            }, this);
          }
        }
      }
    ],
    '3': [
      {
        type: 'sync',
        callback: function () {
          this.loadTasksStatuses();
          this.loadTasksRequestIds();
          this.loadRequestIds();
        }
      }
    ],
    '4': [
      {
        type: 'sync',
        callback: function () {
          this.loadKerberosDescriptorConfigs();
        }
      }
    ],
    '6': [
      {
        type: 'sync',
        callback: function () {
          this.loadKerberosDescriptorConfigs();
        }
      }
    ]
  },

  /**
   * Remove all loaded data.
   * Created as copy for App.router.clearAllSteps
   */
  clearAllSteps: function () {
    this.clearInstallOptions();
    // clear temporary information stored during the install
    this.set('content.cluster', this.getCluster());
  },

  clearTasksData: function () {
    this.saveTasksStatuses(undefined);
    this.saveRequestIds(undefined);
    this.saveTasksRequestIds(undefined);
  },
  /**
   * shows popup with to warn user
   * @param primary
   * @param isCritical
   */
  warnBeforeExitPopup: function(primary, isCritical) {
    var primaryText = Em.I18n.t('common.exitAnyway');
    var msg = isCritical ? Em.I18n.t('admin.kerberos.wizard.exit.critical.msg')
      : Em.I18n.t('admin.kerberos.wizard.exit.warning.msg');
    return App.showConfirmationPopup(primary, msg, null, null, primaryText, isCritical);
  },
  /**
   * Clear all temporary data
   */
  finish: function () {
    // The in-memory variable for current step should be reset to 1st step.
    this.setCurrentStep('1', false, true);
    // kerberos wizard namespace in the localStorage should be emptied
    this.resetDbNamespace();
    App.get('router.updateController').updateAll();
  }
});
<|endoftext|>


<commit_before>'use strict';

var node_path   = require('path');
var option_cwd  = require('../util/cwd');
var fs = require('fs');

exports.shorthands = {
  c: 'cwd',
};

exports.options = {
  // cortex build abc
  cwd: option_cwd,

  preinstall: {
    enumerable: false,
    type: Boolean,
    default: false
  },

  prebuild: {
    enumerable: false,
    type: Boolean,
    default: true
  },

  dest: {
    type: node_path,

    // default to be profile.built_root
    info: 'the destination directory to be built into',
    setter: function (dest) {
      var done = this.async();

      function cb (err) {
        // '/abc/' -> '/abc'
        done(err || null, node_path.resolve(dest));
      }

      if (!dest) {
        dest = node_path.join(this.get('cwd'), 'neurons');
        // legacy
        // If `neurons` exists and is not a directory, try to unlink it.
        fs.lstat(dest, function (err, stat) {
          if (err) {
            if (err.code === 'ENOENT') {
              return cb();
            }
            return cb(err);
          }

          if (stat.isDirectory()) {
            return cb();
          }

          fs.unlink(dest, cb);
        });
        return;
      }

      cb();
    }
  },

  extra: {
    enumerable: false,
    type: Boolean,
    info: 'Extra jobs, including creating symlink, creating engines, and generating configurations.',
    default: true
  }
};

exports.info = 'Build module wrapper, and publish to cortex server root.';

exports.usage = [
  '{{name}} build [options]'
];
<commit_msg>option/build: renames option.extra -> !option[install-build]; #410<commit_after>'use strict';

var node_path   = require('path');
var option_cwd  = require('../util/cwd');
var fs = require('fs');

exports.shorthands = {
  c: 'cwd',
};

exports.options = {
  // cortex build abc
  cwd: option_cwd,

  preinstall: {
    enumerable: false,
    type: Boolean,
    default: false
  },

  prebuild: {
    enumerable: false,
    type: Boolean,
    default: true
  },

  dest: {
    type: node_path,

    // default to be profile.built_root
    info: 'the destination directory to be built into',
    setter: function (dest) {
      var done = this.async();

      function cb (err) {
        // '/abc/' -> '/abc'
        done(err || null, node_path.resolve(dest));
      }

      if (!dest) {
        dest = node_path.join(this.get('cwd'), 'neurons');
        // legacy
        // If `neurons` exists and is not a directory, try to unlink it.
        fs.lstat(dest, function (err, stat) {
          if (err) {
            if (err.code === 'ENOENT') {
              return cb();
            }
            return cb(err);
          }

          if (stat.isDirectory()) {
            return cb();
          }

          fs.unlink(dest, cb);
        });
        return;
      }

      cb();
    }
  },

  'install-build': {
    enumerable: false,
    type: Boolean,
    info: 'install build will skip creating symlink, creating engines, generating configurations, etc.',
    default: false
  }
};

exports.info = 'Build module wrapper, and publish to cortex server root.';

exports.usage = [
  '{{name}} build [options]'
];
<|endoftext|>


<commit_before>var _ = require('lodash');
var util = require('util');
var juice = require('juice');

var command = require('../utils/command');
var fs = require('../utils/fs');
var Promise = require('../utils/promise');
var WebsiteOutput = require('./website');
var assetsInliner = require('./assets-inliner');

function _EbookOutput() {
    WebsiteOutput.apply(this, arguments);

    // ebook-convert does not support link like "./"
    this.opts.directoryIndex = false;
}
util.inherits(_EbookOutput, WebsiteOutput);

var EbookOutput = assetsInliner(_EbookOutput);

EbookOutput.prototype.name = 'ebook';

// Finish generation, create ebook using ebook-convert
EbookOutput.prototype.finish = function() {
    var that = this;
    if (that.book.isMultilingual()) {
        return EbookOutput.super_.prototype.finish.apply(that);
    }

    return Promise()
    .then(function() {
        return EbookOutput.super_.prototype.finish.apply(that);
    })

    // Generate SUMMARY.html
    .then(function() {
        return that.render('summary', that.getContext())
        .then(function(html) {
            return that.writeFile(
                'SUMMARY.html',
                html
            );
        });
    })

    // Start ebook-convert
    .then(function() {
        return that.ebookConvertOption();
    })

    .then(function(options) {
        if (!that.opts.format) return;

        var cmd = [
            'ebook-convert',
            that.resolve('SUMMARY.html'),
            that.resolve('index.'+that.opts.format),
            command.optionsToShellArgs(options)
        ].join(' ');

        return command.exec(cmd)
        .fail(function(err) {
            if (err.code == 127) {
                err = new Error('Need to install ebook-convert from Calibre');
            }

            throw err;
        });
    });
};

// Generate header/footer for PDF
EbookOutput.prototype.getPDFTemplate = function(tpl) {
    var that = this;
    var context = _.extend(
        {
            // Nunjucks context mapping to ebook-convert templating
            page: {
                num: '_PAGENUM_',
                title: '_TITLE_',
                section: '_SECTION_'
            }
        },
        this.getContext()
    );

    return this.render('pdf_'+tpl, context)

    // Inline css, include css relative to the output folder
    .then(function(output) {
        return Promise.nfcall(juice.juiceResources, output, {
            webResources: {
                relativeTo: that.root()
            }
        });
    });
};

// Generate options for ebook-convert
EbookOutput.prototype.ebookConvertOption = function() {
    var that = this;
    var cover = this.book.config.get('cover');

    if (!cover && fs.existsSync(this.resolve('cover.jpg'))) {
        cover = this.resolve('cover.jpg');
    }

    var options = {
        '--cover': cover,
        '--title': this.book.config.get('title'),
        '--comments': this.book.config.get('description'),
        '--isbn': this.book.config.get('isbn'),
        '--authors': this.book.config.get('author'),
        '--language': this.book.config.get('language'),
        '--book-producer': 'GitBook',
        '--publisher': 'GitBook',
        '--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]',
        '--level1-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-1 \')]',
        '--level2-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-2 \')]',
        '--level3-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-3 \')]',
        '--no-chapters-in-toc': true,
        '--max-levels': '1',
        '--breadth-first': true
    };

    if (this.opts.format == 'epub') {
        options = _.extend(options, {
            '--dont-split-on-page-breaks': true
        });
    }

    if (this.opts.format != 'pdf') return Promise(options);

    var pdfOptions = this.book.config.get('pdf');

    options = _.extend(options, {
        '--chapter-mark': String(pdfOptions.chapterMark),
        '--page-breaks-before': String(pdfOptions.pageBreaksBefore),
        '--margin-left': String(pdfOptions.margin.left),
        '--margin-right': String(pdfOptions.margin.right),
        '--margin-top': String(pdfOptions.margin.top),
        '--margin-bottom': String(pdfOptions.margin.bottom),
        '--pdf-default-font-size': String(pdfOptions.fontSize),
        '--pdf-mono-font-size': String(pdfOptions.fontSize),
        '--paper-size': String(pdfOptions.paperSize),
        '--pdf-page-numbers': Boolean(pdfOptions.pageNumbers),
        '--pdf-header-template': that.getPDFTemplate('header'),
        '--pdf-footer-template': that.getPDFTemplate('footer'),
        '--pdf-sans-family': String(pdfOptions.fontFamily)
    });

    return this.getPDFTemplate('header')
    .then(function(tpl) {
        options['--pdf-header-template'] = tpl;

        return that.getPDFTemplate('footer');
    })
    .then(function(tpl) {
        options['--pdf-footer-template'] = tpl;

        return options;
    });
};

// Don't write multi-lingual index for wbook
EbookOutput.prototype.outputMultilingualIndex = function() {

};

module.exports = EbookOutput;
<commit_msg>Use cover file from main book in multilingual book (when no cover is set for language)<commit_after>var _ = require('lodash');
var util = require('util');
var juice = require('juice');

var command = require('../utils/command');
var fs = require('../utils/fs');
var Promise = require('../utils/promise');
var WebsiteOutput = require('./website');
var assetsInliner = require('./assets-inliner');

function _EbookOutput() {
    WebsiteOutput.apply(this, arguments);

    // ebook-convert does not support link like "./"
    this.opts.directoryIndex = false;
}
util.inherits(_EbookOutput, WebsiteOutput);

var EbookOutput = assetsInliner(_EbookOutput);

EbookOutput.prototype.name = 'ebook';

// Finish generation, create ebook using ebook-convert
EbookOutput.prototype.finish = function() {
    var that = this;
    if (that.book.isMultilingual()) {
        return EbookOutput.super_.prototype.finish.apply(that);
    }

    return Promise()
    .then(function() {
        return EbookOutput.super_.prototype.finish.apply(that);
    })

    // Generate SUMMARY.html
    .then(function() {
        return that.render('summary', that.getContext())
        .then(function(html) {
            return that.writeFile(
                'SUMMARY.html',
                html
            );
        });
    })

    // Start ebook-convert
    .then(function() {
        return that.ebookConvertOption();
    })

    .then(function(options) {
        if (!that.opts.format) return;

        var cmd = [
            'ebook-convert',
            that.resolve('SUMMARY.html'),
            that.resolve('index.'+that.opts.format),
            command.optionsToShellArgs(options)
        ].join(' ');

        return command.exec(cmd)
        .fail(function(err) {
            if (err.code == 127) {
                err = new Error('Need to install ebook-convert from Calibre');
            }

            throw err;
        });
    });
};

// Generate header/footer for PDF
EbookOutput.prototype.getPDFTemplate = function(tpl) {
    var that = this;
    var context = _.extend(
        {
            // Nunjucks context mapping to ebook-convert templating
            page: {
                num: '_PAGENUM_',
                title: '_TITLE_',
                section: '_SECTION_'
            }
        },
        this.getContext()
    );

    return this.render('pdf_'+tpl, context)

    // Inline css, include css relative to the output folder
    .then(function(output) {
        return Promise.nfcall(juice.juiceResources, output, {
            webResources: {
                relativeTo: that.root()
            }
        });
    });
};

// Locate the cover file to use
// Use configuration or search a "cover.jpg" file
// For multi-lingual book, it can use the one from the main book
EbookOutput.prototype.locateCover = function() {
    var cover = this.book.config.get('cover', 'cover.jpg');

    // Resolve to absolute
    cover = this.resolve(cover);

    // Cover doesn't exist and multilingual?
    if (!fs.existsSync(cover) && this.parent) {
        return this.parent.locateCover();
    }

    return cover;
};

// Generate options for ebook-convert
EbookOutput.prototype.ebookConvertOption = function() {
    var that = this;

    var options = {
        '--cover': this.locateCover(),
        '--title': that.book.config.get('title'),
        '--comments': that.book.config.get('description'),
        '--isbn': that.book.config.get('isbn'),
        '--authors': that.book.config.get('author'),
        '--language': that.book.config.get('language'),
        '--book-producer': 'GitBook',
        '--publisher': 'GitBook',
        '--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]',
        '--level1-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-1 \')]',
        '--level2-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-2 \')]',
        '--level3-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-3 \')]',
        '--no-chapters-in-toc': true,
        '--max-levels': '1',
        '--breadth-first': true
    };

    if (that.opts.format == 'epub') {
        options = _.extend(options, {
            '--dont-split-on-page-breaks': true
        });
    }

    if (that.opts.format != 'pdf') return Promise(options);

    var pdfOptions = that.book.config.get('pdf');

    options = _.extend(options, {
        '--chapter-mark': String(pdfOptions.chapterMark),
        '--page-breaks-before': String(pdfOptions.pageBreaksBefore),
        '--margin-left': String(pdfOptions.margin.left),
        '--margin-right': String(pdfOptions.margin.right),
        '--margin-top': String(pdfOptions.margin.top),
        '--margin-bottom': String(pdfOptions.margin.bottom),
        '--pdf-default-font-size': String(pdfOptions.fontSize),
        '--pdf-mono-font-size': String(pdfOptions.fontSize),
        '--paper-size': String(pdfOptions.paperSize),
        '--pdf-page-numbers': Boolean(pdfOptions.pageNumbers),
        '--pdf-header-template': that.getPDFTemplate('header'),
        '--pdf-footer-template': that.getPDFTemplate('footer'),
        '--pdf-sans-family': String(pdfOptions.fontFamily)
    });

    return that.getPDFTemplate('header')
    .then(function(tpl) {
        options['--pdf-header-template'] = tpl;

        return that.getPDFTemplate('footer');
    })
    .then(function(tpl) {
        options['--pdf-footer-template'] = tpl;

        return options;
    });
};

// Don't write multi-lingual index for wbook
EbookOutput.prototype.outputMultilingualIndex = function() {

};

module.exports = EbookOutput;
<|endoftext|>


<commit_before>(function() {

	"use strict";

	angular
		.module('app')
		.controller('SignedInController', ['$rootScope', '$http', '$state', function SignedInController($rootScope, $http, $state) {
			var signedIn = this;

			signedIn.signOut = function singedInSignOut($event) {
				$event.preventDefault();
				$http({
					method: "DELETE",
					url: '/sessions/' + $rootScope.currentUser.id
				}).then(function onSuccessfulSignOut(response) {
					$rootScope.currentUser = undefined;
					$state.go('index.visitor.welcome');
				}, function onFailedSignOut(response) {
					console.log(response);
				});
			}

		}]);

})();<commit_msg>signed in states now redirect a visitor to the welcome page when not signed in<commit_after>(function() {

	"use strict";

	angular
		.module('app')
		.controller('SignedInController', ['$rootScope', '$http', '$state', function SignedInController($rootScope, $http, $state) {

			if (!$rootScope.currentUser) {
				$state.go('index.visitor.welcome');
			}

			var signedIn = this;

			signedIn.signOut = function singedInSignOut($event) {
				$event.preventDefault();
				$http({
					method: "DELETE",
					url: '/sessions/' + $rootScope.currentUser.id
				}).then(function onSuccessfulSignOut(response) {
					$rootScope.currentUser = undefined;
					$state.go('index.visitor.welcome');
				}, function onFailedSignOut(response) {
					console.log(response);
				});
			}

		}]);

})();<|endoftext|>


<commit_before>define('mas-editor', [
  'editor',
  'editor-plugin-sticky-toolbar',
  'eventsWithPromises'
], function (
  Editor,
  editorPluginStickyToolbar,
  events
) {
  return {
    /**
     * Setups options and instantiates Editor instance
     * @param  {Object} options Options
     * @return {Object} this
     */
    init: function(options) {
      if(typeof options !== 'object') return;

      this.options = options;
      this.cmsFormNode = this.options.cmsFormNode;
      this.toolbarNode = this.options.toolbarNode;
      this.editorContainer = this.options.editorContainer;
      this.htmlEditorNode = this.options.htmlEditorNode;
      this.htmlEditorContentNode = this.options.htmlEditorContentNode;
      this.markdownEditorNode = this.options.markdownEditorNode;
      this.markdownEditorContentNode = this.options.markdownEditorContentNode;
      this.switchModeButtonNodes = this.options.switchModeButtonNodes;
      this.editor = new Editor(
        this.htmlEditorContentNode,
        this.markdownEditorContentNode,
        this.toolbarNode
      );
      this.editor.use(editorPluginStickyToolbar(this.toolbarNode));
      this.classActive = this.options.classActive || this.editor.constants.CLASSES.ACTIVE;
      this.mode = this.options.mode || this.editor.config.defaultEditingMode;

      this.bindEvents();
    },

    /**
     * Bind DOM events
     * @return {Object} this
     */
    bindEvents: function() {
      var _this = this;

      // Catches form submit and delegates to a handler function
      this.cmsFormNode.addEventListener('submit', function(event) {
        event.preventDefault();
        _this.handleSubmit();
      });

      this.setupModeButton();

      return this;
    },

    /**
     * Setups mode switching buttons
     * @return {Object} this
     */
    setupModeButton: function() {
      var i = this.switchModeButtonNodes.length,
          _this = this;

      while(i--) {
        (function(button) {
          button.addEventListener('click', function() {
            _this.handleModeButtonClick(button);
          });
        })(this.switchModeButtonNodes[i]);
      }
      return this;
    },

    /**
     * Handles Mode button click
     * @param  {Object} button Button DOM node
     * @return {[type]}        [description]
     */
    handleModeButtonClick: function(buttonNode) {
      var i = this.switchModeButtonNodes.length;

      while(i--) {
        this.switchModeButtonNodes[i].classList.remove(this.classActive);
      }
      buttonNode.classList.add(this.classActive);
      this.changeMode(buttonNode.dataset.mode);
    },

    /**
     * Converts HTML input into Markdown then submits form
     * @return {Object} this
     */
    handleSubmit: function() {
      if(this.mode === this.editor.constants.MODES.HTML) {
        this.editor.changeEditingMode(this.editor.constants.MODES.MARKDOWN);
      }
      this.cmsFormNode.submit();
      return this;
    },

    /**
     * Handles switch between HTML and Markdown mode
     * @param  {String} mode html|markdown
     * @return {Object} this
     */
    changeMode: function(mode) {
      if(mode === this.mode) return;

      this.mode = mode;

      switch(mode) {
        case this.editor.constants.MODES.HTML:
          this.show(this.htmlEditorNode).hide(this.markdownEditorNode);
          break;
        case this.editor.constants.MODES.MARKDOWN:
          this.show(this.markdownEditorNode).hide(this.htmlEditorNode);
          break;
        default:
          this.show(this.htmlEditorNode).hide(this.markdownEditorNode);
          break;
      }

      this.editor.changeEditingMode(this.mode);

      return this;
    },

    /**
     * Adds an active class to a DOM node
     * @param  {Object} node Target DOM node
     * @return {Object} this
     */
    show: function(node) {
      node.classList.add(this.classActive);
      return this;
    },

    /**
     * Removes active class from a DOM node
     * @param  {Object} node Target DOM node
     * @return {Object} this
     */
    hide: function(node) {
      node.classList.remove(this.classActive);
      return this;
    }
  };
});
<commit_msg>Remove unused events param<commit_after>define('mas-editor', [
  'editor',
  'editor-plugin-sticky-toolbar',
  'eventsWithPromises'
], function (
  Editor,
  editorPluginStickyToolbar
) {
  return {
    /**
     * Setups options and instantiates Editor instance
     * @param  {Object} options Options
     * @return {Object} this
     */
    init: function(options) {
      if(typeof options !== 'object') return;

      this.options = options;
      this.cmsFormNode = this.options.cmsFormNode;
      this.toolbarNode = this.options.toolbarNode;
      this.editorContainer = this.options.editorContainer;
      this.htmlEditorNode = this.options.htmlEditorNode;
      this.htmlEditorContentNode = this.options.htmlEditorContentNode;
      this.markdownEditorNode = this.options.markdownEditorNode;
      this.markdownEditorContentNode = this.options.markdownEditorContentNode;
      this.switchModeButtonNodes = this.options.switchModeButtonNodes;
      this.editor = new Editor(
        this.htmlEditorContentNode,
        this.markdownEditorContentNode,
        this.toolbarNode
      );
      this.editor.use(editorPluginStickyToolbar(this.toolbarNode));
      this.classActive = this.options.classActive || this.editor.constants.CLASSES.ACTIVE;
      this.mode = this.options.mode || this.editor.config.defaultEditingMode;

      this.bindEvents();
    },

    /**
     * Bind DOM events
     * @return {Object} this
     */
    bindEvents: function() {
      var _this = this;

      // Catches form submit and delegates to a handler function
      this.cmsFormNode.addEventListener('submit', function(event) {
        event.preventDefault();
        _this.handleSubmit();
      });

      this.setupModeButton();

      return this;
    },

    /**
     * Setups mode switching buttons
     * @return {Object} this
     */
    setupModeButton: function() {
      var i = this.switchModeButtonNodes.length,
          _this = this;

      while(i--) {
        (function(button) {
          button.addEventListener('click', function() {
            _this.handleModeButtonClick(button);
          });
        })(this.switchModeButtonNodes[i]);
      }
      return this;
    },

    /**
     * Handles Mode button click
     * @param  {Object} button Button DOM node
     * @return {[type]}        [description]
     */
    handleModeButtonClick: function(buttonNode) {
      var i = this.switchModeButtonNodes.length;

      while(i--) {
        this.switchModeButtonNodes[i].classList.remove(this.classActive);
      }
      buttonNode.classList.add(this.classActive);
      this.changeMode(buttonNode.dataset.mode);
    },

    /**
     * Converts HTML input into Markdown then submits form
     * @return {Object} this
     */
    handleSubmit: function() {
      if(this.mode === this.editor.constants.MODES.HTML) {
        this.editor.changeEditingMode(this.editor.constants.MODES.MARKDOWN);
      }
      this.cmsFormNode.submit();
      return this;
    },

    /**
     * Handles switch between HTML and Markdown mode
     * @param  {String} mode html|markdown
     * @return {Object} this
     */
    changeMode: function(mode) {
      if(mode === this.mode) return;

      this.mode = mode;

      switch(mode) {
        case this.editor.constants.MODES.HTML:
          this.show(this.htmlEditorNode).hide(this.markdownEditorNode);
          break;
        case this.editor.constants.MODES.MARKDOWN:
          this.show(this.markdownEditorNode).hide(this.htmlEditorNode);
          break;
        default:
          this.show(this.htmlEditorNode).hide(this.markdownEditorNode);
          break;
      }

      this.editor.changeEditingMode(this.mode);

      return this;
    },

    /**
     * Adds an active class to a DOM node
     * @param  {Object} node Target DOM node
     * @return {Object} this
     */
    show: function(node) {
      node.classList.add(this.classActive);
      return this;
    },

    /**
     * Removes active class from a DOM node
     * @param  {Object} node Target DOM node
     * @return {Object} this
     */
    hide: function(node) {
      node.classList.remove(this.classActive);
      return this;
    }
  };
});
<|endoftext|>


<commit_before>/**
  A builder to create routes for topic discovery.

  @function buildTopicRoute
  @param {String} filter to create route for
**/
function buildTopicRoute(filter) {
  return Discourse.Route.extend({
    queryParams: {
      sort: { replace: true },
      ascending: { replace: true }
    },

    beforeModel: function() {
      this.controllerFor('navigation/default').set('filterMode', filter);
    },

    model: function(data, transaction) {

      var params = transaction.queryParams;

      // attempt to stop early cause we need this to be called before .sync
      Discourse.ScreenTrack.current().stop();

      var findOpts = {};
      if (params && params.order) { findOpts.order = params.order; }
      if (params && params.ascending) { findOpts.ascending = params.ascending; }

      return Discourse.TopicList.list(filter, findOpts).then(function(list) {
        var tracking = Discourse.TopicTrackingState.current();
        if (tracking) {
          tracking.sync(list, filter);
          tracking.trackIncoming(filter);
        }
        return list;
      });
    },

    setupController: function(controller, model, trans) {

      controller.setProperties({
        order: Em.get(trans, 'queryParams.order'),
        ascending: Em.get(trans, 'queryParams.ascending')
      });

      var period = filter.indexOf('/') > 0 ? filter.split('/')[1] : '',
          filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0});

      if (filter === Discourse.Utilities.defaultHomepage()) {
        Discourse.set('title', '');
      } else {
        Discourse.set('title', I18n.t('filters.with_topics', {filter: filterText}));
      }

      this.controllerFor('discovery/topics').setProperties({
        model: model,
        category: null,
        period: period,
        selected: []
      });

      // If there's a draft, open the create topic composer
      if (model.draft) {
        var composer = this.controllerFor('composer');
        if (!composer.get('model.viewOpen')) {
          composer.open({
            action: Discourse.Composer.CREATE_TOPIC,
            draft: model.draft,
            draftKey: model.draft_key,
            draftSequence: model.draft_sequence
          });
        }
      }

      this.controllerFor('navigation/default').set('canCreateTopic', model.get('can_create_topic'));
    },

    renderTemplate: function() {
      this.render('navigation/default', { outlet: 'navigation-bar' });
      this.render('discovery/topics', { controller: 'discovery/topics', outlet: 'list-container' });
    }
  });
}

/**
  A builder to create routes for topic discovery within a category.

  @function buildTopicRoute
  @param {String} filter to create route for
  @param {Object} params with additional options
**/
function buildCategoryRoute(filter, params) {
  return Discourse.Route.extend({
    model: function(params) {
      return Discourse.Category.findBySlug(params.slug, params.parentSlug);
    },

    afterModel: function(model) {
      var self = this,
          noSubcategories = params && !!params.no_subcategories,
          filterMode = "category/" + Discourse.Category.slugFor(model) + (noSubcategories ? "/none" : "") + "/l/" + filter,
          listFilter = "category/" + Discourse.Category.slugFor(model) + "/l/" + filter;

      this.controllerFor('search').set('searchContext', model.get('searchContext'));

      var opts = { category: model, filterMode: filterMode };
      opts.noSubcategories = params && params.no_subcategories;
      opts.canEditCategory = Discourse.User.currentProp('staff');
      opts.canChangeCategoryNotificationLevel = Discourse.User.current();
      this.controllerFor('navigation/category').setProperties(opts);

      return Discourse.TopicList.list(listFilter, params).then(function(list) {
        var tracking = Discourse.TopicTrackingState.current();
        if (tracking) {
          tracking.sync(list, listFilter);
          tracking.trackIncoming(listFilter);
        }

        // If all the categories are the same, we can hide them
        var hideCategory = !list.get('topics').find(function (t) { return t.get('category') !== model; });
        list.set('hideCategory', hideCategory);
        self.set('topics', list);
      });
    },

    setupController: function(controller, model) {
      var topics = this.get('topics'),
          period = filter.indexOf('/') > 0 ? filter.split('/')[1] : '',
          filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0});

      Discourse.set('title', I18n.t('filters.with_category', { filter: filterText, category: model.get('name').capitalize() }));

      this.controllerFor('navigation/category').set('canCreateTopic', topics.get('can_create_topic'));
      this.controllerFor('discovery/topics').setProperties({
        model: topics,
        category: model,
        period: period,
        selected: [],
        noSubcategories: params && !!params.no_subcategories
      });

      this.set('topics', null);
    },

    renderTemplate: function() {
      this.render('navigation/category', { outlet: 'navigation-bar' });
      this.render('discovery/topics', { controller: 'discovery/topics', outlet: 'list-container' });
    },

    deactivate: function() {
      this._super();
      this.controllerFor('search').set('searchContext', null);
    },

    actions: {
      setNotification: function(notification_level){
        this.currentModel.setNotification(notification_level);
      }
    }
  });
}

// Finally, build all the routes with the helpers we created
Discourse.addInitializer(function() {
  Discourse.DiscoveryCategoryRoute = buildCategoryRoute('latest');
  Discourse.DiscoveryCategoryNoneRoute = buildCategoryRoute('latest', {no_subcategories: true});

  Discourse.Site.currentProp('filters').forEach(function(filter) {
    Discourse["Discovery" + filter.capitalize() + "Controller"] = Discourse.DiscoverySortableController.extend();
    Discourse["Discovery" + filter.capitalize() + "Route"] = buildTopicRoute(filter);
    Discourse["Discovery" + filter.capitalize() + "CategoryRoute"] = buildCategoryRoute(filter);
    Discourse["Discovery" + filter.capitalize() + "CategoryNoneRoute"] = buildCategoryRoute(filter, {no_subcategories: true});
  });

  Discourse.Site.currentProp('periods').forEach(function(period) {
    Discourse["DiscoveryTop" + period.capitalize() + "Controller"] = Discourse.DiscoverySortableController.extend();
    Discourse["DiscoveryTop" + period.capitalize() + "Route"] = buildTopicRoute('top/' + period);
    Discourse["DiscoveryTop" + period.capitalize() + "CategoryRoute"] = buildCategoryRoute('top/' + period);
    Discourse["DiscoveryTop" + period.capitalize() + "CategoryNoneRoute"] = buildCategoryRoute('top/' + period, {no_subcategories: true});
  });
}, true);
<commit_msg>Comment out category notification button, it is breaking qunit<commit_after>/**
  A builder to create routes for topic discovery.

  @function buildTopicRoute
  @param {String} filter to create route for
**/
function buildTopicRoute(filter) {
  return Discourse.Route.extend({
    queryParams: {
      sort: { replace: true },
      ascending: { replace: true }
    },

    beforeModel: function() {
      this.controllerFor('navigation/default').set('filterMode', filter);
    },

    model: function(data, transaction) {

      var params = transaction.queryParams;

      // attempt to stop early cause we need this to be called before .sync
      Discourse.ScreenTrack.current().stop();

      var findOpts = {};
      if (params && params.order) { findOpts.order = params.order; }
      if (params && params.ascending) { findOpts.ascending = params.ascending; }

      return Discourse.TopicList.list(filter, findOpts).then(function(list) {
        var tracking = Discourse.TopicTrackingState.current();
        if (tracking) {
          tracking.sync(list, filter);
          tracking.trackIncoming(filter);
        }
        return list;
      });
    },

    setupController: function(controller, model, trans) {

      controller.setProperties({
        order: Em.get(trans, 'queryParams.order'),
        ascending: Em.get(trans, 'queryParams.ascending')
      });

      var period = filter.indexOf('/') > 0 ? filter.split('/')[1] : '',
          filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0});

      if (filter === Discourse.Utilities.defaultHomepage()) {
        Discourse.set('title', '');
      } else {
        Discourse.set('title', I18n.t('filters.with_topics', {filter: filterText}));
      }

      this.controllerFor('discovery/topics').setProperties({
        model: model,
        category: null,
        period: period,
        selected: []
      });

      // If there's a draft, open the create topic composer
      if (model.draft) {
        var composer = this.controllerFor('composer');
        if (!composer.get('model.viewOpen')) {
          composer.open({
            action: Discourse.Composer.CREATE_TOPIC,
            draft: model.draft,
            draftKey: model.draft_key,
            draftSequence: model.draft_sequence
          });
        }
      }

      this.controllerFor('navigation/default').set('canCreateTopic', model.get('can_create_topic'));
    },

    renderTemplate: function() {
      this.render('navigation/default', { outlet: 'navigation-bar' });
      this.render('discovery/topics', { controller: 'discovery/topics', outlet: 'list-container' });
    }
  });
}

/**
  A builder to create routes for topic discovery within a category.

  @function buildTopicRoute
  @param {String} filter to create route for
  @param {Object} params with additional options
**/
function buildCategoryRoute(filter, params) {
  return Discourse.Route.extend({
    model: function(params) {
      return Discourse.Category.findBySlug(params.slug, params.parentSlug);
    },

    afterModel: function(model) {
      var self = this,
          noSubcategories = params && !!params.no_subcategories,
          filterMode = "category/" + Discourse.Category.slugFor(model) + (noSubcategories ? "/none" : "") + "/l/" + filter,
          listFilter = "category/" + Discourse.Category.slugFor(model) + "/l/" + filter;

      this.controllerFor('search').set('searchContext', model.get('searchContext'));

      var opts = { category: model, filterMode: filterMode };
      opts.noSubcategories = params && params.no_subcategories;
      opts.canEditCategory = Discourse.User.currentProp('staff');

      // TODO: @Neil FIXME, this breaks qunit!
      // opts.canChangeCategoryNotificationLevel = Discourse.User.current();
      this.controllerFor('navigation/category').setProperties(opts);

      return Discourse.TopicList.list(listFilter, params).then(function(list) {
        var tracking = Discourse.TopicTrackingState.current();
        if (tracking) {
          tracking.sync(list, listFilter);
          tracking.trackIncoming(listFilter);
        }

        // If all the categories are the same, we can hide them
        var hideCategory = !list.get('topics').find(function (t) { return t.get('category') !== model; });
        list.set('hideCategory', hideCategory);
        self.set('topics', list);
      });
    },

    setupController: function(controller, model) {
      var topics = this.get('topics'),
          period = filter.indexOf('/') > 0 ? filter.split('/')[1] : '',
          filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0});

      Discourse.set('title', I18n.t('filters.with_category', { filter: filterText, category: model.get('name').capitalize() }));

      this.controllerFor('navigation/category').set('canCreateTopic', topics.get('can_create_topic'));
      this.controllerFor('discovery/topics').setProperties({
        model: topics,
        category: model,
        period: period,
        selected: [],
        noSubcategories: params && !!params.no_subcategories
      });

      this.set('topics', null);
    },

    renderTemplate: function() {
      this.render('navigation/category', { outlet: 'navigation-bar' });
      this.render('discovery/topics', { controller: 'discovery/topics', outlet: 'list-container' });
    },

    deactivate: function() {
      this._super();
      this.controllerFor('search').set('searchContext', null);
    },

    actions: {
      setNotification: function(notification_level){
        this.currentModel.setNotification(notification_level);
      }
    }
  });
}

// Finally, build all the routes with the helpers we created
Discourse.addInitializer(function() {
  Discourse.DiscoveryCategoryRoute = buildCategoryRoute('latest');
  Discourse.DiscoveryCategoryNoneRoute = buildCategoryRoute('latest', {no_subcategories: true});

  Discourse.Site.currentProp('filters').forEach(function(filter) {
    Discourse["Discovery" + filter.capitalize() + "Controller"] = Discourse.DiscoverySortableController.extend();
    Discourse["Discovery" + filter.capitalize() + "Route"] = buildTopicRoute(filter);
    Discourse["Discovery" + filter.capitalize() + "CategoryRoute"] = buildCategoryRoute(filter);
    Discourse["Discovery" + filter.capitalize() + "CategoryNoneRoute"] = buildCategoryRoute(filter, {no_subcategories: true});
  });

  Discourse.Site.currentProp('periods').forEach(function(period) {
    Discourse["DiscoveryTop" + period.capitalize() + "Controller"] = Discourse.DiscoverySortableController.extend();
    Discourse["DiscoveryTop" + period.capitalize() + "Route"] = buildTopicRoute('top/' + period);
    Discourse["DiscoveryTop" + period.capitalize() + "CategoryRoute"] = buildCategoryRoute('top/' + period);
    Discourse["DiscoveryTop" + period.capitalize() + "CategoryNoneRoute"] = buildCategoryRoute('top/' + period, {no_subcategories: true});
  });
}, true);
<|endoftext|>