<commit_before>/**
 * @fileOverview 图分析模版
 * @author huangtonger@aliyun.com
 * 保留字段:
 * node.vx, node.vy, node.x, node.y
 * node.to, node.from
 * node.visited, node.edges, node.links
 * edge.isTreeEdge、edge.lineWidth
 * 可配置字段:
 * node.rank  分层权重
 * node.label 节点标签
 */
const G6 = require('@antv/g6');
const maxSpanningForest = require('./maxSpanningForest');
const Layout = require('../layout.forceAtlas2/layout');
// const d3 = require('d3');
// const {
//   forceSimulation,
//   forceLink,
//   forceManyBody,
//   forceCenter,
//   forceCollide
// } = d3;
const Util = G6.Util;

class Plugin {
  constructor(options) {
    Util.mix(this, {
      layout(ns, es, g) {

        // force atlas 2
        const params = {
          kr: 50,
          kg: 1.0,
          mode: 'common',
          prev_overlapping: true,
          dissuade_hubs: false,
          max_iteration: 500,
          barnes_hut: false,
          ks: 0.1,
          ksmax: 10,
          tao: 0.1,
          graph: g,
          nodes: ns,
          edges: es
        };
        const forceAtlas2 = new Layout(params);
        forceAtlas2.execute();

        // d3 force directed
        // const width = graph.getWidth();
        // const height = graph.getHeight();
        // const simulation = forceSimulation(nodes)
        //   .force('charge', forceManyBody().distanceMax(width * 3))
        //   .force('link', forceLink(Util.cloneDeep(edges))
        //     .id(model => {
        //       return model.id;
        //     })
        //     .strength(1)
        //   )
        //   .force('center', forceCenter(width / 2, height / 2))
        //   .force('collision', forceCollide().radius(model => {
        //     return model.width / 2 * 1.2;
        //   }));
        // simulation.stop();
        // for (let i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
        //   simulation.tick();
        // }
        // nodes.forEach(node => {
        //   delete node.vx;
        //   delete node.vy;
        // });
      },
      ...options
    });
  }
  init() {
    const graph = this.graph;
    graph.on('beforeinit', () => {
      const layout = graph.get('layout');
      if (!layout) {
        graph.set('layout', this.layout);
      }
    });
    graph.on('beforerender', () => {
      const data = graph.getSource();
      const {
        nodes,
        edges
      } = data;
      const forest = maxSpanningForest(nodes, edges);
      // const params = { kr: 0.1, kg: 0.1, cons_step: 0.1, mode: 'common', prev_overlapping: 'false', dissuade_hubs: 'false' };
      // const forest = forceAtlas2(graph, nodes, edges, params);
      forest.edges.forEach(edge => {
        edge.isTreeEdge = true;
      });
      graph.addFilter(item => {
        return !item.isEdge || item.getModel().isTreeEdge;
      });
    });
  }
}

G6.Plugins['template.maxSpanningForest'] = Plugin;
// G6.Plugins['template.forceAtlas2'] = Plugin;

module.exports = Plugin;
<commit_msg>style: change the calling way of forceAtlas2 on template.maxSpanningForest<commit_after>/**
 * @fileOverview 图分析模版
 * @author huangtonger@aliyun.com
 * 保留字段:
 * node.vx, node.vy, node.x, node.y
 * node.to, node.from
 * node.visited, node.edges, node.links
 * edge.isTreeEdge、edge.lineWidth
 * 可配置字段:
 * node.rank  分层权重
 * node.label 节点标签
 */
const G6 = require('@antv/g6');
const maxSpanningForest = require('./maxSpanningForest');
const Layout = require('../layout.forceAtlas2/layout');
const Util = G6.Util;

class Plugin {
  constructor(options) {
    Util.mix(this, {
      layout: new Layout({
        kr: 50,
        kg: 1.0,
        mode: 'common',
        prev_overlapping: true,
        dissuade_hubs: false,
        max_iteration: 500,
        barnes_hut: false,
        ks: 0.1,
        ksmax: 10,
        tao: 0.1
      }),
      ...options
    });
  }
  init() {
    const graph = this.graph;
    graph.on('beforeinit', () => {
      const layout = graph.get('layout');
      if (!layout) {
        graph.set('layout', this.layout);
      }
    });
    graph.on('beforerender', () => {
      const data = graph.getSource();
      const {
        nodes,
        edges
      } = data;
      const forest = maxSpanningForest(nodes, edges);
      forest.edges.forEach(edge => {
        edge.isTreeEdge = true;
      });
      graph.addFilter(item => {
        return !item.isEdge || item.getModel().isTreeEdge;
      });
    });
  }
}

G6.Plugins['template.maxSpanningForest'] = Plugin;

module.exports = Plugin;
<|endoftext|>


<commit_before>import React from 'react'
import ReactDOM from 'react-dom'
import { Card, CardTitle, CardText } from 'material-ui/Card'
import AppBarContainer from './appbarContainer'
import Content from './content'
import Error from './error'
import RaisedButton from 'material-ui/RaisedButton'
import { Link } from 'react-router'
import moment from 'moment'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
import TextField from 'material-ui/TextField'
import Loading from './loading'
import { red500 } from 'material-ui/styles/colors'

const STATUS = {
  0: 'none',
  1: 'heating',
  2: 'cooling'
}

class Status extends React.Component {
  constructor(props) {
    super(props)
    this.props.getAuthentication()
    this.props.getStatus()
    this.state = {
      updatedAt: Date.now(),
      settingTargetTemperature: false
    }
  }
  componentWillMount() {
    this.forceRenderInterval = setInterval(() => {
      this.setState({ updateAt: Date.now() })
    }, 1000)
  }
  componentWillUnmount() {
    clearInterval(this.forceRenderInterval)
  }
  setTargetTemperature(event) {
    if (!event || !event.keyCode || event.keyCode === 13) {
      this.setState({ settingTargetTemperature: false })
      this.props.setTargetTemperature(this.refs.targetTemperature.getValue())
    }
  }
  renderControlButtons() {
    if (!this.props.authentication.particleDeviceName || !this.props.status.transmitterPaired) {
      return null
    }

    let turnLabel
    if (this.props.status.controlled) {
      turnLabel = this.props.controlled.loading ? 'Turning off' : 'Turn off'
    }
    else {
      turnLabel = this.props.controlled.loading ? 'Turning on' : 'Turn on'
    }

    return (

        <RaisedButton
          primary
          label='Set target temperature'
          style={{ width: '100%' }}
          style={{ width: '100%' }}
          onClick={() => this.setState({ settingTargetTemperature: true })}
          />

        <RaisedButton
          primary
          disabled={this.props.controlled.loading}
          label={turnLabel}
          style={{ width: '100%' }}
          onClick={() => this.props.setControlled(!this.props.status.controlled)}
          />

    )
  }
  renderSetupButtons() {
    return (

        {!this.props.authentication.particleDeviceName ?

            <Link to='/particle'>
              <RaisedButton
                primary
                label='Setup device'
                style={{ width: '100%' }}
                />
            </Link>

        : null}
        {this.props.authentication.particleDeviceName && !this.props.status.transmitterPaired ?

            <Link to='/transmitter'>
              <RaisedButton
                primary
                label='Setup transmitter'
                style={{ width: '100%' }}
                />
            </Link>

        : null}

    )
  }
  renderContent() {
    if (this.props.authentication.loading || this.props.status.loading) {
      return <Loading />
    }

    if (this.props.authentication.error) {
      return <Error message='Failed to retrieve config' />
    }

    if (!this.props.authentication.particleDeviceName) {
      return (
        <Error
          message='Particle device not connected'
          showTitle={false}
          reload={false}
          >
          {this.renderSetupButtons()}
        </Error>
      )
    }

    if (this.props.status.error) {
      return <Error message='Failed connect to device' />
    }

    const now = Date.now()
    const createdAt = this.props.status.createdAt
    let targetTemperature
    // TODO to function
    if (isNaN(this.props.status.targetTemperature) || parseInt(this.props.status.targetTemperature, 10) === -127) {
      targetTemperature = '-'
    }
    else {
      targetTemperature = `${this.props.status.targetTemperature} °C`
    }
    // TODO to function
    let temperature
    if (isNaN(this.props.status.temperature) || parseInt(this.props.status.temperature, 10) === -127) {
      temperature = '-'
    }
    else {
      temperature = `${this.props.status.temperature} °C`
    }

    const isStatusStale = createdAt < (now - 20 * 1000)
    let title
    let subtitle
    if (this.props.status.loading) {
      title = 'Loading...'
      subtitle = '\u00a0'
    }
    else if (this.props.status.error) {
      title = 'Error loading status'
      subtitle = '\u00a0'
    }
    else {
      if (isStatusStale) {
        title = 'Waiting for up to date data..'
        subtitle = moment(createdAt).format('YYYY-MM-DD HH:mm:ss')
      }
      else {
        title = `Status: ${STATUS[this.props.status.status]}`
        if (this.props.status.tuning) {
          title += ' (autotuning)'
        }
        const diff = now - createdAt
        if (diff < 10000) {
          subtitle = 'Just now'
        }
        else {
          subtitle = `${parseInt(diff / 1000, 10)} seconds ago`
        }
      }
    }

    const actions = []
    actions.push(
      <FlatButton
        label='Cancel'
        onTouchTap={() => this.setState({ settingTargetTemperature: false })}
        />
    )
    actions.push(
      <FlatButton
        label={this.props.targetTemperature.loading ? 'Saving...' : 'Save'}
        disabled={this.props.targetTemperature.loading}
        primary
        onTouchTap={(event) => this.setTargetTemperature(event) }
        />
    )

    return (

        {this.renderSetupButtons()}
        <Card>
          <CardTitle
            title={title}
            subtitle={subtitle}
            titleColor={isStatusStale ? red500 : null}
            />
          <CardText style={{ textAlign: 'center' }}>
            Current temperature

              {temperature}

            Target temperature

              {targetTemperature}

          </CardText>
        </Card>

        {this.renderControlButtons()}
        <Dialog
          title='Set target temperature'
          open={!!(this.state.settingTargetTemperature || this.props.targetTemperature.loading)}
          onRequestClose={() => this.setState({ settingTargetTemperature: false })}
          actions={actions}
          >
          <TextField
            autoFocus
            onFocus={() => {
              // move caret to end after focus
              setTimeout(() => {
                const nodes = ReactDOM.findDOMNode(this.refs.targetTemperature).getElementsByTagName('input')
                if (nodes.length > 0) {
                  const node = nodes[0]
                  window.foo = node
                  const length = node.value.length
                  node.setSelectionRange(length, length)
                }
              }, 0)
            }}
            ref='targetTemperature'
            defaultValue={this.props.status.targetTemperature}
            style={{ width: '100%' }}
            onKeyDown={event => this.setTargetTemperature(event) }
            />
        </Dialog>

    )
  }
  render() {
    return (

        <AppBarContainer />
        <Content>
          {this.renderContent()}
        </Content>

    )
  }
}

Status.propTypes = {
  getStatus: React.PropTypes.func,
  status: React.PropTypes.shape({
    loading: React.PropTypes.bool,
    error: React.PropTypes.any,
    status: React.PropTypes.number,
    tuning: React.PropTypes.tuning,
    temperature: React.PropTypes.string,
    createdAt: React.PropTypes.number,
    targetTemperature: React.PropTypes.string,
    transmitterPaired: React.PropTypes.bool,
    controlled: React.PropTypes.bool
  }),
  getAuthentication: React.PropTypes.func,
  authentication: React.PropTypes.shape({
    particleDeviceName: React.PropTypes.string,
    loading: React.PropTypes.bool,
    error: React.PropTypes.any
  }),
  controlled: React.PropTypes.shape({
    loading: React.PropTypes.bool,
    error: React.PropTypes.any
  }),
  setControlled: React.PropTypes.func,
  targetTemperature: React.PropTypes.shape({
    loading: React.PropTypes.bool,
    error: React.PropTypes.any
  }),
  setTargetTemperature: React.PropTypes.func
}

export default Status
<commit_msg>remove duplicate style<commit_after>import React from 'react'
import ReactDOM from 'react-dom'
import { Card, CardTitle, CardText } from 'material-ui/Card'
import AppBarContainer from './appbarContainer'
import Content from './content'
import Error from './error'
import RaisedButton from 'material-ui/RaisedButton'
import { Link } from 'react-router'
import moment from 'moment'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
import TextField from 'material-ui/TextField'
import Loading from './loading'
import { red500 } from 'material-ui/styles/colors'

const STATUS = {
  0: 'none',
  1: 'heating',
  2: 'cooling'
}

class Status extends React.Component {
  constructor(props) {
    super(props)
    this.props.getAuthentication()
    this.props.getStatus()
    this.state = {
      updatedAt: Date.now(),
      settingTargetTemperature: false
    }
  }
  componentWillMount() {
    this.forceRenderInterval = setInterval(() => {
      this.setState({ updateAt: Date.now() })
    }, 1000)
  }
  componentWillUnmount() {
    clearInterval(this.forceRenderInterval)
  }
  setTargetTemperature(event) {
    if (!event || !event.keyCode || event.keyCode === 13) {
      this.setState({ settingTargetTemperature: false })
      this.props.setTargetTemperature(this.refs.targetTemperature.getValue())
    }
  }
  renderControlButtons() {
    if (!this.props.authentication.particleDeviceName || !this.props.status.transmitterPaired) {
      return null
    }

    let turnLabel
    if (this.props.status.controlled) {
      turnLabel = this.props.controlled.loading ? 'Turning off' : 'Turn off'
    }
    else {
      turnLabel = this.props.controlled.loading ? 'Turning on' : 'Turn on'
    }

    return (

        <RaisedButton
          primary
          label='Set target temperature'
          style={{ width: '100%' }}
          onClick={() => this.setState({ settingTargetTemperature: true })}
          />

        <RaisedButton
          primary
          disabled={this.props.controlled.loading}
          label={turnLabel}
          style={{ width: '100%' }}
          onClick={() => this.props.setControlled(!this.props.status.controlled)}
          />

    )
  }
  renderSetupButtons() {
    return (

        {!this.props.authentication.particleDeviceName ?

            <Link to='/particle'>
              <RaisedButton
                primary
                label='Setup device'
                style={{ width: '100%' }}
                />
            </Link>

        : null}
        {this.props.authentication.particleDeviceName && !this.props.status.transmitterPaired ?

            <Link to='/transmitter'>
              <RaisedButton
                primary
                label='Setup transmitter'
                style={{ width: '100%' }}
                />
            </Link>

        : null}

    )
  }
  renderContent() {
    if (this.props.authentication.loading || this.props.status.loading) {
      return <Loading />
    }

    if (this.props.authentication.error) {
      return <Error message='Failed to retrieve config' />
    }

    if (!this.props.authentication.particleDeviceName) {
      return (
        <Error
          message='Particle device not connected'
          showTitle={false}
          reload={false}
          >
          {this.renderSetupButtons()}
        </Error>
      )
    }

    if (this.props.status.error) {
      return <Error message='Failed connect to device' />
    }

    const now = Date.now()
    const createdAt = this.props.status.createdAt
    let targetTemperature
    // TODO to function
    if (isNaN(this.props.status.targetTemperature) || parseInt(this.props.status.targetTemperature, 10) === -127) {
      targetTemperature = '-'
    }
    else {
      targetTemperature = `${this.props.status.targetTemperature} °C`
    }
    // TODO to function
    let temperature
    if (isNaN(this.props.status.temperature) || parseInt(this.props.status.temperature, 10) === -127) {
      temperature = '-'
    }
    else {
      temperature = `${this.props.status.temperature} °C`
    }

    const isStatusStale = createdAt < (now - 20 * 1000)
    let title
    let subtitle
    if (this.props.status.loading) {
      title = 'Loading...'
      subtitle = '\u00a0'
    }
    else if (this.props.status.error) {
      title = 'Error loading status'
      subtitle = '\u00a0'
    }
    else {
      if (isStatusStale) {
        title = 'Waiting for up to date data..'
        subtitle = moment(createdAt).format('YYYY-MM-DD HH:mm:ss')
      }
      else {
        title = `Status: ${STATUS[this.props.status.status]}`
        if (this.props.status.tuning) {
          title += ' (autotuning)'
        }
        const diff = now - createdAt
        if (diff < 10000) {
          subtitle = 'Just now'
        }
        else {
          subtitle = `${parseInt(diff / 1000, 10)} seconds ago`
        }
      }
    }

    const actions = []
    actions.push(
      <FlatButton
        label='Cancel'
        onTouchTap={() => this.setState({ settingTargetTemperature: false })}
        />
    )
    actions.push(
      <FlatButton
        label={this.props.targetTemperature.loading ? 'Saving...' : 'Save'}
        disabled={this.props.targetTemperature.loading}
        primary
        onTouchTap={(event) => this.setTargetTemperature(event) }
        />
    )

    return (

        {this.renderSetupButtons()}
        <Card>
          <CardTitle
            title={title}
            subtitle={subtitle}
            titleColor={isStatusStale ? red500 : null}
            />
          <CardText style={{ textAlign: 'center' }}>
            Current temperature

              {temperature}

            Target temperature

              {targetTemperature}

          </CardText>
        </Card>

        {this.renderControlButtons()}
        <Dialog
          title='Set target temperature'
          open={!!(this.state.settingTargetTemperature || this.props.targetTemperature.loading)}
          onRequestClose={() => this.setState({ settingTargetTemperature: false })}
          actions={actions}
          >
          <TextField
            autoFocus
            onFocus={() => {
              // move caret to end after focus
              setTimeout(() => {
                const nodes = ReactDOM.findDOMNode(this.refs.targetTemperature).getElementsByTagName('input')
                if (nodes.length > 0) {
                  const node = nodes[0]
                  window.foo = node
                  const length = node.value.length
                  node.setSelectionRange(length, length)
                }
              }, 0)
            }}
            ref='targetTemperature'
            defaultValue={this.props.status.targetTemperature}
            style={{ width: '100%' }}
            onKeyDown={event => this.setTargetTemperature(event) }
            />
        </Dialog>

    )
  }
  render() {
    return (

        <AppBarContainer />
        <Content>
          {this.renderContent()}
        </Content>

    )
  }
}

Status.propTypes = {
  getStatus: React.PropTypes.func,
  status: React.PropTypes.shape({
    loading: React.PropTypes.bool,
    error: React.PropTypes.any,
    status: React.PropTypes.number,
    tuning: React.PropTypes.tuning,
    temperature: React.PropTypes.string,
    createdAt: React.PropTypes.number,
    targetTemperature: React.PropTypes.string,
    transmitterPaired: React.PropTypes.bool,
    controlled: React.PropTypes.bool
  }),
  getAuthentication: React.PropTypes.func,
  authentication: React.PropTypes.shape({
    particleDeviceName: React.PropTypes.string,
    loading: React.PropTypes.bool,
    error: React.PropTypes.any
  }),
  controlled: React.PropTypes.shape({
    loading: React.PropTypes.bool,
    error: React.PropTypes.any
  }),
  setControlled: React.PropTypes.func,
  targetTemperature: React.PropTypes.shape({
    loading: React.PropTypes.bool,
    error: React.PropTypes.any
  }),
  setTargetTemperature: React.PropTypes.func
}

export default Status
<|endoftext|>


<commit_before><commit_msg>Enabled loading of AppEnsembles via get-paramter<commit_after><|endoftext|>


<commit_before><commit_msg>added typo handler<commit_after><|endoftext|>


<commit_before><commit_msg>Restore gameMenu for developers, as a convenience.<commit_after><|endoftext|>


<commit_before><commit_msg>improvement(browsers): change browserslist query to support a wider variety<commit_after><|endoftext|>


<commit_before><commit_msg>fix error handling for pattern downloads<commit_after><|endoftext|>


<commit_before><commit_msg>typo and isArray fix<commit_after><|endoftext|>


<commit_before><commit_msg>Fix Firefox new tab preferences to correct value<commit_after><|endoftext|>


<commit_before><commit_msg>intermediate saving<commit_after><|endoftext|>


<commit_before><commit_msg>Refs #12368. Copy IE selection test code from original TextBox.js to fix a failure in robot/ValidationTextBox.html<commit_after><|endoftext|>


<commit_before>/**
 * Created by rerobins on 9/29/15.
 */
var CashFlowDirectiveGenerator = function() {
    return {
        scope: {
            reportData: '&'
        },
        templateUrl: 'src/reports/cash_flow_chart/cashFlowDirective.html',
        link: function($scope) {
            var data = $scope.reportData();

            $scope.options = {
                chart: {
                    type: 'multiBarChart',
                    height: 450,
                    margin : {
                        top: 20,
                        right: 20,
                        bottom: 70,
                        left: 75
                    },
                    x: function(d){return d[0];},
                    y: function(d){return d[1];},
                    showValues: true,
                    valueFormat: function(d){
                        return d3.format(',.2f')(d);
                    },
                    stacked: true,
                    transitionDuration: 500,
                    xAxis: {
                        axisLabel: 'X Axis',
                        tickFormat: function(d) {
                            return d3.time.format('%x')(new Date(d));
                        },
                        rotateLabels: 50,
                        showMaxMin: false
                    },
                    yAxis: {
                        axisLabel: 'Y Axis',
                        axisLabelDistance: 35,
                        tickFormat: function(d){
                            return d3.format(',.2f')(d);
                        }
                    }
                }
            };

            var creditValues = [];
            var debitValues = [];
            var grossValues = [];
            data.credits.forEach(function(dataValue) {
                creditValues.push([dataValue.date * 1000, dataValue.value]);
            });

            data.debits.forEach(function(dataValue) {
                debitValues.push([dataValue.date * 1000, dataValue.value]);
            });

            data.gross.forEach(function(dataValue) {
                grossValues.push([dataValue.date * 1000, dataValue.value]);
            });

            $scope.data = [
                {
                    "key" : "Credits" ,
                    "bar": true,
                    "color": "#007700",
                    "values" : creditValues
                },
                {
                    "key" : "Debits" ,
                    "bar": true,
                    "color": "#770000",
                    "values" : debitValues
                }
            ];
        }
    };
};

angular.module('gnucash-reports-view.reports.cash_flow')
    .directive('cashFlow', [CashFlowDirectiveGenerator]);<commit_msg>Update axis titles.<commit_after>/**
 * Created by rerobins on 9/29/15.
 */
var CashFlowDirectiveGenerator = function() {
    return {
        scope: {
            reportData: '&'
        },
        templateUrl: 'src/reports/cash_flow_chart/cashFlowDirective.html',
        link: function($scope) {
            var data = $scope.reportData();

            $scope.options = {
                chart: {
                    type: 'multiBarChart',
                    height: 450,
                    margin : {
                        top: 20,
                        right: 20,
                        bottom: 100,
                        left: 100
                    },
                    x: function(d){return d[0];},
                    y: function(d){return d[1];},
                    showControls: false,
                    showValues: true,
                    valueFormat: function(d){
                        return d3.format(',.2f')(d);
                    },
                    stacked: true,
                    transitionDuration: 500,
                    xAxis: {
                        axisLabel: 'Date',
                        tickFormat: function(d) {
                            return d3.time.format('%x')(new Date(d));
                        },
                        rotateLabels: 50,
                        showMaxMin: false
                    },
                    yAxis: {
                        axisLabel: 'USD',
                        axisLabelDistance: 35,
                        tickFormat: function(d){
                            return d3.format(',.2f')(d);
                        }
                    }
                }
            };

            var creditValues = [];
            var debitValues = [];
            var grossValues = [];
            data.credits.forEach(function(dataValue) {
                creditValues.push([dataValue.date * 1000, dataValue.value]);
            });

            data.debits.forEach(function(dataValue) {
                debitValues.push([dataValue.date * 1000, dataValue.value]);
            });

            data.gross.forEach(function(dataValue) {
                grossValues.push([dataValue.date * 1000, dataValue.value]);
            });

            $scope.data = [
                {
                    "key" : "Credits" ,
                    "bar": true,
                    "color": "#007700",
                    "values" : creditValues
                },
                {
                    "key" : "Debits" ,
                    "bar": true,
                    "color": "#770000",
                    "values" : debitValues
                }
            ];
        }
    };
};

angular.module('gnucash-reports-view.reports.cash_flow')
    .directive('cashFlow', [CashFlowDirectiveGenerator]);<|endoftext|>


<commit_before>var assert = require("assert");
var expect = require("expect.js");

var config = require("../server/config.js");

var setup = require("../test/setup");
var waitUntil = require("../test/waitUntil");

var testBlock =
    setup.usingPhantomPage(
            setup.given3rdPartyRssServer(
                setup.whenRunningTheServer(
                    setup.givenCleanDatabase(exports))));

setup.qtest(testBlock, "can upload rss", function() {

    var that = this;

    var page = this.page;

    that.rssServer.posts = [];

    for(var i = 0; i < 30; i++) {
        that.rssServer.posts.push({
            postName: "post name " + i,
            postUrl: "http://trimtest.com/" + i,
            postDate: new Date()
        });
    }

    return page.promise.open(config.urlFor("/feeds"))
        .then(function() {
            return require("../test/login.js").doLogin(page);
        })
        .then(function() {
            return require("../test/uploadRss.js")(page, {
                feeds: [{
                    name: "ignored",
                    rssUrl: that.rssServer.urlFor("/rss/whatever"),
                    htmlUrl: "http://ignored.com/"
                }
            ]});
        })
        .then(function() {
            return waitUntil("rss import completes", function() {
                return page.promise.get("content")
                .then(function(content) {
                    return content.indexOf("Upload complete") > -1;
                });
            });
        })
        .then(function() {
            return page.promise.open(config.urlFor("/"));
        })
        .then(function() {
            return waitUntil("the trim posts form appear", function() {
                return page.promise.evaluate(function() {
                    return document.querySelector("form[name='trimPosts']") !== null;
                });
            });
        })
        .then(function() {
            return page.promise.clickElement("input[name='submitTrimPosts']");
        })
        .then(function() {
            return waitUntil("the page loads with the trimmed posts", function() {
                return page.promise.evaluate(function() {
                    return $(".js-post").length == 12;
                });
            });
        })
        .fail(function(err) {
            var path = config.tempPathFor("screenshot.png");
            console.log("saving screenshot to " + path);
            page.render(path);
            throw err;
        });
});<commit_msg>make test not need jquery loaded<commit_after>var assert = require("assert");
var expect = require("expect.js");

var config = require("../server/config.js");

var setup = require("../test/setup");
var waitUntil = require("../test/waitUntil");

var testBlock =
    setup.usingPhantomPage(
            setup.given3rdPartyRssServer(
                setup.whenRunningTheServer(
                    setup.givenCleanDatabase(exports))));

setup.qtest(testBlock, "can upload rss", function() {

    var that = this;

    var page = this.page;

    that.rssServer.posts = [];

    for(var i = 0; i < 30; i++) {
        that.rssServer.posts.push({
            postName: "post name " + i,
            postUrl: "http://trimtest.com/" + i,
            postDate: new Date()
        });
    }

    return page.promise.open(config.urlFor("/feeds"))
        .then(function() {
            return require("../test/login.js").doLogin(page);
        })
        .then(function() {
            return require("../test/uploadRss.js")(page, {
                feeds: [{
                    name: "ignored",
                    rssUrl: that.rssServer.urlFor("/rss/whatever"),
                    htmlUrl: "http://ignored.com/"
                }
            ]});
        })
        .then(function() {
            return waitUntil("rss import completes", function() {
                return page.promise.get("content")
                .then(function(content) {
                    return content.indexOf("Upload complete") > -1;
                });
            });
        })
        .then(function() {
            return page.promise.open(config.urlFor("/"));
        })
        .then(function() {
            return waitUntil("the trim posts form appear", function() {
                return page.promise.evaluate(function() {
                    return document.querySelector("form[name='trimPosts']") !== null;
                });
            });
        })
        .then(function() {
            return page.promise.clickElement("input[name='submitTrimPosts']");
        })
        .then(function() {
            return waitUntil("the page loads with the trimmed posts", function() {
                return page.promise.evaluate(function() {
                    return document.querySelectorAll(".js-post").length == 12;
                });
            });
        })
        .fail(function(err) {
            var path = config.tempPathFor("screenshot.png");
            console.log("saving screenshot to " + path);
            page.render(path);
            throw err;
        });
});<|endoftext|>


<commit_before>import { Connection, Story, Storymetadata } from '../../models'
import { config } from '../../../config'
import jszip from 'jszip'
import { getHeaderMetadata, getUserId, jsforce, metadata, streamClose, streamData, streamError } from '../../utils'
const metadataZip = require('../../../metadata-zip').metadataZip //bit hacky to reference the path ..
import mongoose from 'mongoose'

const packageHeader = '<?xml version="1.0" encoding="UTF-8"?>\n' +
  '<Package xmlns="http://soap.sforce.com/2006/04/metadata">'

const packageFooter = '\t<version>' + config.apiVersion + '</version>\n' +
'</Package>'

export async function cancel(ctx, data) {
  console.log('going to cancel', data.targetId, data.deploymentId)
  const connection = await Connection.findById(data.targetId)
  const sfConn = jsforce(connection)
  await sfConn.metadata._invoke('cancelDeploy', {String: data.deploymentId})
  ctx.socket.emit('deployment', {
    message: {
      state: 'Canceling'
    }
  })
  sfConn._destroy()
}

export async function deploy(ctx, data) {
  try {
    let zipBase64

    if(!data.deleteMetadata) {
      if(data.storyIds && data.storyIds.length) {
        zipBase64 = await getMetadataFromStories(ctx, data)
      } else {
        let res = await getMetadataFromSalesforce(ctx, data)
        zipBase64 = res.zipFile
      }

      ctx.socket.emit('deployment', {
        message: {
          state: 'Package retrieved'
        }
      })
    } else {
      let zip = new jszip()
      zip.file('destructiveChanges.xml', buildPackage(data.metadata))
      zip.file('package.xml', packageHeader + packageFooter)

      zipBase64 = await zip.generateAsync({
        type: 'base64'
      })
    }

    const deploymentResult = await deployMetadataToSalesforce(zipBase64, ctx, data)
    console.log('result of deployment: ', deploymentResult)
    ctx.socket.emit('deployment', {
      deploymentResult
    })
  } catch(e) {
    console.log('error deploy ==>', e)
    ctx.socket.emit('deployment', {
      message: {
        state: e.message
      }
    })
    ctx.socket.emit('deployment', {
      deploymentResult: {
        status: 'Failed'
      }
    })
  }
}

export async function deployMetadataToSalesforce(zip, ctx, data) {
  const connection = await Connection.findById( data.deleteMetadata? data.sourceId : data.targetId )
  const sfConn = jsforce(connection)
  sfConn.metadata.pollTimeout = 60 * 1000 * 1000 // 1 hour
  sfConn.metadata.pollInterval = 15 * 1000 // 15 seconds
  const deploy = sfConn.metadata.deploy(zip, {
    checkOnly: data.checkOnly,
    rollbackOnError: connection.issandbox?data.rollbackOnError:true,
    testLevel: data.testLevel,
    singlePackage: data.deleteMetadata || !!data.storyIds.length
  })

  if(ctx) {
    ctx.socket.emit('deployment', {
      message: {
        state: `Starting deployment to ${connection.folder}`
      }
    })

    deploy.on('progress', async (event) => {
      console.log('progressing ...', event)
      ctx.socket.emit('deployment', {
        deploymentId: event.id
      })

      const deploymentResult = await sfConn.metadata.checkDeployStatus(event.id, true)
      ctx.socket.emit('deployment', {
        deploymentResult
      })
      console.log('intermediaryStatus', deploymentResult)
    })
  }

  const res = await deploy.complete(true)
  sfConn._destroy()
  return res
}

function getMetadataFromStories(ctx, data) {
  return new Promise(async (resolve, reject) => {
    try {
      const userId = await getUserId(ctx)

      let subMetadatas = {
        CustomObject: metadata.customObjectMetadata,
        SharingRules: metadata.sharingRulesMetadata,
        Workflow: metadata.workflowMetadata
      }
      let storyMetadataStream,
        storyMetadatas = []

      let request = {
        session: {
          passport: {
            user: {
              _id: userId
            }
          }
        }
      }

      await getDescribeMetadata(request)
      let describeMetadata = request.body

      let stories = await Story.find({
        _id: {
          $in: data.storyIds.map(storyId => mongoose.Types.ObjectId(storyId))
        },
        $or: [{
          sharedWith: userId
        }, {
          user: userId
        }]
      }).lean()

      ctx.socket.emit('deployment', {
        message: {
          state: `Deploying ${stories.length > 1?'stories': 'story'} ${stories.map(story => story.name).join(', ')}`
        }
      })

      let query = {
        story: {
          $in: stories.map(story => story._id)
        }
      }

      if (data.metadata.length) {
        //we must query additionally the paths to restrict to them
        query._id = {
          $in: data.metadata.map(metadata => mongoose.Types.ObjectId(metadata))
        }
      }

      let callback = function() {
        console.log('storyMetadatas', storyMetadatas)
        let zip = metadataZip(describeMetadata, storyMetadatas, subMetadatas)
        zip.generateAsync({
          type: 'base64'
        }).then((myZip) => {
          resolve(myZip)
        })
      }

      storyMetadataStream = Storymetadata.find(query).sort({
        fullPath: 1
      }).lean().batchSize(10000).cursor()
      storyMetadataStream.on('data', streamData(ctx, null, storyMetadatas, 10000))
      storyMetadataStream.on('error', streamError)
      storyMetadataStream.on('end', streamClose(ctx, null, storyMetadatas, callback))
    } catch(e) {
      reject(e)
    }
  })
}

async function getMetadataFromSalesforce(ctx, data) {
  const pkg = extractObjectMetadata(data.metadata, false, true, true)
  const connection = await Connection.findById(data.sourceId)
  const sfConn = jsforce(connection)
  sfConn.metadata.pollTimeout = 60 * 1000 * 1000 // 1 hour
  sfConn.metadata.pollInterval = 10 * 1000 // 10 seconds

  const retrieve = sfConn.metadata.retrieve({
    singlePackage: true,
    apiVersion: config.apiVersion,
    unpackaged: pkg
  })

  ctx.socket.emit('deployment', {
    message: {
      state: `Starting package retrieval from ${connection.folder}`
    }
  })

  retrieve.on('progress', event => {
    console.log('progressing ...', event)
    ctx.socket.emit('deployment', {
      message: event
    })
  })

  const zip = await retrieve.complete()

  sfConn._destroy()
  return zip
}

export async function getDescribeMetadata(ctx) {
  console.log('Get Describe Metadata!')
  // We fetch all the connections and get the list metadata for all them as a story could be assigned
  // to multiple connections... (and each connection may have different header metadata, e.g. Territory)
  const listMetadataTmp = await getHeaderMetadata(null, await Connection.find({user: ctx.session.passport.user._id}))

  let describeMetadata = {}
  if(listMetadataTmp) {
    for(let connectionId of Object.keys(listMetadataTmp)) {
      let headerMetadata = listMetadataTmp[connectionId]

      for (let metadata of headerMetadata) {
        describeMetadata[metadata.directoryName] = metadata
      }
    }
  }

  ctx.body = describeMetadata
}

export async function getStoryReleaseMetadata(ctx, data) {
  const userId = await getUserId(ctx)
  console.log('userid/storyId=>', userId, data.storyIds)

  const storyMetadatas = [],
    metadataIds = []

  let stories = await Story.find({
    _id: {
      $in: data.storyIds.map(storyId => mongoose.Types.ObjectId(storyId))
    },
    $or: [{
      sharedWith: userId
    }, {
      user: userId
    }]
  }).lean()

  console.log('stories', stories)
  if(!stories || !stories.length) {
    return
  }

  stories = stories.map(story => story._id)
  const storyMetadataStream = Storymetadata.find({
    story: {
      $in: stories
    }
  }, '_id fullPath isDeleted').lean().batchSize(10000).cursor()

  storyMetadataStream.on('data', streamData(ctx, 'storyMetadatas', storyMetadatas, 10000, metadataIds, 'metadata'))
  storyMetadataStream.on('error', streamError)
  storyMetadataStream.on('end', streamClose(ctx, 'storyMetadatas', storyMetadatas))

}

export function getPackage(ctx) {
  ctx.set('Content-Disposition', 'attachment')
  ctx.body = buildPackage(ctx.request.body)
}

function buildPackage(metadata) {
  return packageHeader + extractObjectMetadata(metadata, false, true, false) + packageFooter
}

export async function getZip(ctx, data) {
  console.log('getZip ...')
  try {
    const zip = await getMetadataFromSalesforce(ctx, data)

    ctx.socket.emit('deployment', zip)
  } catch(e) {
    console.log('error getZip ==>', e)
  }
}

function extractObjectMetadata(metadata, withDomainName, withFormating, isObject) {
  const objectMetadata = new Map()
  let ret

  for (let i = 0; i < metadata.length; i++) {
    if (metadata[i].indexOf('/') !== -1) {
      let path = metadata[i].split('/')
      if (path.length === 4) {
        //CustomObject ...
        path[0] = path[2]
        path[1] = path[1] + '.' + path[3]
      }

      if (path.length !== 3 || (path[0] !== 'CustomObject' && path[0] !== 'Workflow')) {
        if (!objectMetadata.has(path[0])) {
          objectMetadata.set(path[0], new Set())
        }
        objectMetadata.get(path[0]).add(path[1])
      }

      if (path.length === 3) {
        if (path[0] === 'CustomObject' || path[0] === 'Workflow') {
          //otherwise, we gonna mess up the deployment, we don't want anyway this value.
          continue
        }
        objectMetadata.get(path[0]).add(path[1] + '/' + path[2])
      }
    }
  }

  if (isObject) {
    ret = {types: [], version: config.apiVersion}
  } else {
    ret = ''
  }

  objectMetadata.forEach(function(members, key) {
    if (members.size) {
      if (isObject) {
        ret.types.push({
          name: key,
          members: [...members]
        })
      } else {

        ret += (withFormating ? '\n\t' : '') + '<' + (withDomainName ? '' : '') + 'types>' + (withFormating ? '\n' : '')

        for (let member of members) {
          ret += (withFormating ? '\t\t' : '') + '<' + (withDomainName ? '' : '') + 'members>' + member + '</' + (withDomainName ? '' : '') + 'members>' + (withFormating ? '\n' : '')
        }
        ret += (withFormating ? '\t\t' : '') + '<' + (withDomainName ? '' : '') + 'name>' + key + '</' + (withDomainName ? '' : '') + 'name>' + (withFormating ? '\n' : '')
        ret += (withFormating ? '\t' : '') + '</' + (withDomainName ? '' : '') + 'types>' + (withFormating ? '\n' : '')
      }
    }
  })

  return ret
}
<commit_msg>singlePackage always true on deployment<commit_after>import { Connection, Story, Storymetadata } from '../../models'
import { config } from '../../../config'
import jszip from 'jszip'
import { getHeaderMetadata, getUserId, jsforce, metadata, streamClose, streamData, streamError } from '../../utils'
const metadataZip = require('../../../metadata-zip').metadataZip //bit hacky to reference the path ..
import mongoose from 'mongoose'

const packageHeader = '<?xml version="1.0" encoding="UTF-8"?>\n' +
  '<Package xmlns="http://soap.sforce.com/2006/04/metadata">'

const packageFooter = '\t<version>' + config.apiVersion + '</version>\n' +
'</Package>'

export async function cancel(ctx, data) {
  console.log('going to cancel', data.targetId, data.deploymentId)
  const connection = await Connection.findById(data.targetId)
  const sfConn = jsforce(connection)
  await sfConn.metadata._invoke('cancelDeploy', {String: data.deploymentId})
  ctx.socket.emit('deployment', {
    message: {
      state: 'Canceling'
    }
  })
  sfConn._destroy()
}

export async function deploy(ctx, data) {
  try {
    let zipBase64

    if(!data.deleteMetadata) {
      if(data.storyIds && data.storyIds.length) {
        zipBase64 = await getMetadataFromStories(ctx, data)
      } else {
        let res = await getMetadataFromSalesforce(ctx, data)
        zipBase64 = res.zipFile
      }

      ctx.socket.emit('deployment', {
        message: {
          state: 'Package retrieved'
        }
      })
    } else {
      let zip = new jszip()
      zip.file('destructiveChanges.xml', buildPackage(data.metadata))
      zip.file('package.xml', packageHeader + packageFooter)

      zipBase64 = await zip.generateAsync({
        type: 'base64'
      })
    }

    const deploymentResult = await deployMetadataToSalesforce(zipBase64, ctx, data)
    console.log('result of deployment: ', deploymentResult)
    ctx.socket.emit('deployment', {
      deploymentResult
    })
  } catch(e) {
    console.log('error deploy ==>', e)
    ctx.socket.emit('deployment', {
      message: {
        state: e.message
      }
    })
    ctx.socket.emit('deployment', {
      deploymentResult: {
        status: 'Failed'
      }
    })
  }
}

export async function deployMetadataToSalesforce(zip, ctx, data) {
  const connection = await Connection.findById( data.deleteMetadata? data.sourceId : data.targetId )
  const sfConn = jsforce(connection)
  sfConn.metadata.pollTimeout = 60 * 1000 * 1000 // 1 hour
  sfConn.metadata.pollInterval = 15 * 1000 // 15 seconds
  const deploy = sfConn.metadata.deploy(zip, {
    checkOnly: data.checkOnly,
    rollbackOnError: connection.issandbox?data.rollbackOnError:true,
    testLevel: data.testLevel,
    singlePackage: true
  })

  if(ctx) {
    ctx.socket.emit('deployment', {
      message: {
        state: `Starting deployment to ${connection.folder}`
      }
    })

    deploy.on('progress', async (event) => {
      console.log('progressing ...', event)
      ctx.socket.emit('deployment', {
        deploymentId: event.id
      })

      const deploymentResult = await sfConn.metadata.checkDeployStatus(event.id, true)
      ctx.socket.emit('deployment', {
        deploymentResult
      })
      console.log('intermediaryStatus', deploymentResult)
    })
  }

  const res = await deploy.complete(true)
  sfConn._destroy()
  return res
}

function getMetadataFromStories(ctx, data) {
  return new Promise(async (resolve, reject) => {
    try {
      const userId = await getUserId(ctx)

      let subMetadatas = {
        CustomObject: metadata.customObjectMetadata,
        SharingRules: metadata.sharingRulesMetadata,
        Workflow: metadata.workflowMetadata
      }
      let storyMetadataStream,
        storyMetadatas = []

      let request = {
        session: {
          passport: {
            user: {
              _id: userId
            }
          }
        }
      }

      await getDescribeMetadata(request)
      let describeMetadata = request.body

      let stories = await Story.find({
        _id: {
          $in: data.storyIds.map(storyId => mongoose.Types.ObjectId(storyId))
        },
        $or: [{
          sharedWith: userId
        }, {
          user: userId
        }]
      }).lean()

      ctx.socket.emit('deployment', {
        message: {
          state: `Deploying ${stories.length > 1?'stories': 'story'} ${stories.map(story => story.name).join(', ')}`
        }
      })

      let query = {
        story: {
          $in: stories.map(story => story._id)
        }
      }

      if (data.metadata.length) {
        //we must query additionally the paths to restrict to them
        query._id = {
          $in: data.metadata.map(metadata => mongoose.Types.ObjectId(metadata))
        }
      }

      let callback = function() {
        console.log('storyMetadatas', storyMetadatas)
        let zip = metadataZip(describeMetadata, storyMetadatas, subMetadatas)
        zip.generateAsync({
          type: 'base64'
        }).then((myZip) => {
          resolve(myZip)
        })
      }

      storyMetadataStream = Storymetadata.find(query).sort({
        fullPath: 1
      }).lean().batchSize(10000).cursor()
      storyMetadataStream.on('data', streamData(ctx, null, storyMetadatas, 10000))
      storyMetadataStream.on('error', streamError)
      storyMetadataStream.on('end', streamClose(ctx, null, storyMetadatas, callback))
    } catch(e) {
      reject(e)
    }
  })
}

async function getMetadataFromSalesforce(ctx, data) {
  const pkg = extractObjectMetadata(data.metadata, false, true, true)
  const connection = await Connection.findById(data.sourceId)
  const sfConn = jsforce(connection)
  sfConn.metadata.pollTimeout = 60 * 1000 * 1000 // 1 hour
  sfConn.metadata.pollInterval = 10 * 1000 // 10 seconds

  const retrieve = sfConn.metadata.retrieve({
    singlePackage: true,
    apiVersion: config.apiVersion,
    unpackaged: pkg
  })

  ctx.socket.emit('deployment', {
    message: {
      state: `Starting package retrieval from ${connection.folder}`
    }
  })

  retrieve.on('progress', event => {
    console.log('progressing ...', event)
    ctx.socket.emit('deployment', {
      message: event
    })
  })

  const zip = await retrieve.complete()

  sfConn._destroy()
  return zip
}

export async function getDescribeMetadata(ctx) {
  console.log('Get Describe Metadata!')
  // We fetch all the connections and get the list metadata for all them as a story could be assigned
  // to multiple connections... (and each connection may have different header metadata, e.g. Territory)
  const listMetadataTmp = await getHeaderMetadata(null, await Connection.find({user: ctx.session.passport.user._id}))

  let describeMetadata = {}
  if(listMetadataTmp) {
    for(let connectionId of Object.keys(listMetadataTmp)) {
      let headerMetadata = listMetadataTmp[connectionId]

      for (let metadata of headerMetadata) {
        describeMetadata[metadata.directoryName] = metadata
      }
    }
  }

  ctx.body = describeMetadata
}

export async function getStoryReleaseMetadata(ctx, data) {
  const userId = await getUserId(ctx)
  console.log('userid/storyId=>', userId, data.storyIds)

  const storyMetadatas = [],
    metadataIds = []

  let stories = await Story.find({
    _id: {
      $in: data.storyIds.map(storyId => mongoose.Types.ObjectId(storyId))
    },
    $or: [{
      sharedWith: userId
    }, {
      user: userId
    }]
  }).lean()

  console.log('stories', stories)
  if(!stories || !stories.length) {
    return
  }

  stories = stories.map(story => story._id)
  const storyMetadataStream = Storymetadata.find({
    story: {
      $in: stories
    }
  }, '_id fullPath isDeleted').lean().batchSize(10000).cursor()

  storyMetadataStream.on('data', streamData(ctx, 'storyMetadatas', storyMetadatas, 10000, metadataIds, 'metadata'))
  storyMetadataStream.on('error', streamError)
  storyMetadataStream.on('end', streamClose(ctx, 'storyMetadatas', storyMetadatas))

}

export function getPackage(ctx) {
  ctx.set('Content-Disposition', 'attachment')
  ctx.body = buildPackage(ctx.request.body)
}

function buildPackage(metadata) {
  return packageHeader + extractObjectMetadata(metadata, false, true, false) + packageFooter
}

export async function getZip(ctx, data) {
  console.log('getZip ...')
  try {
    const zip = await getMetadataFromSalesforce(ctx, data)

    ctx.socket.emit('deployment', zip)
  } catch(e) {
    console.log('error getZip ==>', e)
  }
}

function extractObjectMetadata(metadata, withDomainName, withFormating, isObject) {
  const objectMetadata = new Map()
  let ret

  for (let i = 0; i < metadata.length; i++) {
    if (metadata[i].indexOf('/') !== -1) {
      let path = metadata[i].split('/')
      if (path.length === 4) {
        //CustomObject ...
        path[0] = path[2]
        path[1] = path[1] + '.' + path[3]
      }

      if (path.length !== 3 || (path[0] !== 'CustomObject' && path[0] !== 'Workflow')) {
        if (!objectMetadata.has(path[0])) {
          objectMetadata.set(path[0], new Set())
        }
        objectMetadata.get(path[0]).add(path[1])
      }

      if (path.length === 3) {
        if (path[0] === 'CustomObject' || path[0] === 'Workflow') {
          //otherwise, we gonna mess up the deployment, we don't want anyway this value.
          continue
        }
        objectMetadata.get(path[0]).add(path[1] + '/' + path[2])
      }
    }
  }

  if (isObject) {
    ret = {types: [], version: config.apiVersion}
  } else {
    ret = ''
  }

  objectMetadata.forEach(function(members, key) {
    if (members.size) {
      if (isObject) {
        ret.types.push({
          name: key,
          members: [...members]
        })
      } else {

        ret += (withFormating ? '\n\t' : '') + '<' + (withDomainName ? '' : '') + 'types>' + (withFormating ? '\n' : '')

        for (let member of members) {
          ret += (withFormating ? '\t\t' : '') + '<' + (withDomainName ? '' : '') + 'members>' + member + '</' + (withDomainName ? '' : '') + 'members>' + (withFormating ? '\n' : '')
        }
        ret += (withFormating ? '\t\t' : '') + '<' + (withDomainName ? '' : '') + 'name>' + key + '</' + (withDomainName ? '' : '') + 'name>' + (withFormating ? '\n' : '')
        ret += (withFormating ? '\t' : '') + '</' + (withDomainName ? '' : '') + 'types>' + (withFormating ? '\n' : '')
      }
    }
  })

  return ret
}
<|endoftext|>


<commit_before>import React from 'react'
import graphqlApi from '../../graphql-api'
import './flag-list.scss'
import Modal from '../shared/modal'
import UnknownUserForm from './unknown-user-form'
import NewIndividualForm from './new-individual-form'
import GroupSelectionForm from './group-selection-form'
import IndividualAccessRulesForm from './individual-access-rules-form'
import LinkIndividualForm from './link-individual-form'
import MessagesContainer from '../shared/messages-container'
import lodash from 'lodash'

export default class FlagList extends React.Component {
  state = {
    flags: [],
    showModal: false,
    currentFlag: null
  }

  flagQueryResponse = `{
    individual {
      id
      fullName
      primaryEmail
      serviceUserIdentities {
        serviceId
        userIdentity {
          email
          userId
        }
      }
      accessRules {
        service {
          id
        }
        accessRules {
          asset
          role
        }
      }
      groups
    }
    serviceId
    userIdentity {
      email
      userId
      fullName
    }
    assets {
      name
      role
    }
  }`

  componentWillMount = async () => {
    await this.performAudit()
  }

  showModal = (flag) => {
    this.setState({
      showModal: true,
      currentFlag: flag,
      modalTitle: flag.individual
        ? 'Set Individual Access Rules'
        : `Unknown User: ${flag.userIdentity.email || flag.userIdentity.userId}`,
      modalContents: flag.individual
        ? <IndividualAccessRulesForm service={this.serviceLookup[flag.serviceId]} assets={flag.assets} onAccessRuleSelection={this.setIndividualAccessRules} />
        : <UnknownUserForm flag={flag} onNewIndividualSelected={this.onNewIndividualSelected} onLinkToIndividualSelected={this.onLinkToIndividualSelected} />
    })
  }

  closeModal = (event) => {
    if (event) { event.preventDefault() }

    this.setState({
      showModal: false
    })
  }

  onNewIndividualSelected = () => {
    const flag = this.state.currentFlag
    this.setState({
      modalTitle: `Manage ${flag.userIdentity.email || flag.userIdentity.userId || 'blah'}`,
      modalContents: <NewIndividualForm flag={flag} onNewIndividualFormComplete={this.onNewIndividualFormComplete} onNewIndividualSelected={this.onNewIndividualSelected} />
    })
  }

  onNewIndividualFormComplete = (fullName, primaryEmail) => {
    if (!fullName || fullName.trim() === '') {
      this.messagesContainer.push({
        title: 'Invalid Name',
        body: "Please fill out the individual's name."
      })
      return
    }

    this.pendingNewIndividual = {
      fullName,
      primaryEmail
    }
    this.setState({
      modalTitle: `Select groups`,
      modalContents: <GroupSelectionForm groups={this.groups} onGroupFormComplete={this.onGroupFormComplete} individual={this.pendingNewIndividual} />
    })
  }

  onLinkToIndividualSelected = () => {
    this.setState({
      modalTitle: 'Link to an individual',
      modalContents: <LinkIndividualForm onIndividualSelected={this.onIndividualSelectedToLink} />
    })
  }

  onIndividualSelectedToLink = async (individual) => {
    const flag = this.state.currentFlag

    const query = `mutation {
      linkServiceToIndividual(
        serviceId: "${flag.serviceId}",
        individualId:"${individual.id}",
        ${flag.userIdentity.fullName ? `fullName: "${flag.userIdentity.fullName}"` : ''},
        ${flag.userIdentity.email ? `email: "${flag.userIdentity.email}"` : ''},
        ${flag.userIdentity.userId ? `userId: "${flag.userIdentity.userId}"` : ''}
      )
    }`
    const response = await graphqlApi.request(query)
    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to link to existing individual',
        body: response.error.message
      })
    } else {
      const newFlag = await this.reCheckFlag(flag)
      const flags = [...this.state.flags]
      const flagIndex = lodash.findIndex(flags, (f) => f.key === flag.key)
      if (newFlag) {
        flags[flagIndex] = newFlag
        this.setState({
          flags,
          currentFlag: newFlag,
          modalTitle: 'Set Individual Access Rules',
          modalContents: <IndividualAccessRulesForm service={this.serviceLookup[newFlag.serviceId]} assets={newFlag.assets} onAccessRuleSelection={this.setIndividualAccessRules} />
        })
      } else {
        flags.splice(flagIndex, 1)
        this.setState({
          flags,
          showModal: false
        })
      }
    }
  }

  setIndividualAccessRules = async (selectedAccessRules) => {
    const flag = this.state.currentFlag
    const query = `mutation {
      addIndividualAccessRules(
        individualId: "${flag.individual.id}",
        serviceId: "${flag.serviceId}",
        accessRules: [${selectedAccessRules.map((rule) => `{
          asset: "${rule.asset}",
          role: "${rule.role}"
        }`).join(',')}])
    }`
    const response = await graphqlApi.request(query)
    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to add selected access rules',
        body: response.error.message
      })
    } else {
      const newFlag = await this.reCheckFlag(flag)
      const flags = this.state.flags
      const flagIndex = lodash.findIndex(flags, (f) => f.key === flag.key)
      if (newFlag) {
        flags[flagIndex] = newFlag
      } else {
        flags.splice(flagIndex, 1)
      }

      this.setState({
        showModal: false,
        flags
      })
    }
  }

  reCheckFlag = async (flag) => {
    const secondParameter = flag.userIdentity.email ? `email: "${flag.userIdentity.email}"` : `userId: "${flag.userIdentity.userId}"`

    const query = `{
      auditServiceUserAccount(serviceId: "${flag.serviceId}", ${secondParameter}) ${this.flagQueryResponse}
    }`
    const response = await graphqlApi.request(query)

    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to check flag',
        body: response.error.message
      })
    } else {
      const newFlag = response.data.auditServiceUserAccount
      if (newFlag) {
        newFlag.key = flag.key
        return newFlag
      }
    }
  }

  performAudit = async () => {
    // x perform audit is doing more than just an audit, update the graphql query.
    const query = `{
      auditAll ${this.flagQueryResponse}
      groups {
        name
      }
      services(isConfigured:true){
        id
        displayName
        roles
      }
    }`

    const response = await graphqlApi.request(query)
    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to run audit',
        body: response.error.message
      })
      return
    }
    response.data.auditAll.forEach((flag) => {
      flag.key = `${flag.serviceId}${flag.userIdentity.email || flag.userIdentity.userId || new Date().valueOf()}`
    })
    this.groups = response.data.groups.map((g) => g.name)
    this.serviceLookup = {}
    response.data.services.forEach((s) => { this.serviceLookup[s.id] = s })
    this.setState({
      flags: response.data.auditAll
    })
  }

  onGroupFormComplete = async (selectedGroups) => {
    this.pendingNewIndividual.groups = selectedGroups
    const flag = this.state.currentFlag

    const query = `mutation {
      createIndividual(individual: {
        fullName: "${this.pendingNewIndividual.fullName}"
        ${this.pendingNewIndividual.primaryEmail ? `primaryEmail: "${this.pendingNewIndividual.primaryEmail}"` : ''}
        groups: [${selectedGroups.map((g) => `"${g}"`).join(',')}]
      })
    }`

    const response = await graphqlApi.request(query)
    const individualId = response.data.createIndividual

    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to Save New Individual',
        body: response.error.message
      })
    } else {
      const query = `mutation {
        linkServiceToIndividual(serviceId: "${flag.serviceId}",
          individualId:"${individualId}",
          ${flag.userIdentity.fullName ? `fullName: "${flag.userIdentity.fullName}"` : ''},
          ${flag.userIdentity.email ? `email: "${flag.userIdentity.email}"` : ''},
          ${flag.userIdentity.userId ? `userId: "${flag.userIdentity.userId}"` : ''}
        )
      }`
      const linkResponse = await graphqlApi.request(query)
      if (linkResponse.error) {
        this.messagesContainer.push({
          title: 'Failed to Link Service Account to New Individual',
          body: response.error.message
        })
      }
      const newFlag = await this.reCheckFlag(flag)
      const flags = [...this.state.flags]
      const flagIndex = lodash.findIndex(flags, (f) => f.key === flag.key)
      if (newFlag) {
        flags[flagIndex] = newFlag
        this.setState({
          flags,
          currentFlag: newFlag,
          modalTitle: 'Set Individual Access Rules',
          modalContents: <IndividualAccessRulesForm service={this.serviceLookup[newFlag.serviceId]} assets={newFlag.assets} onAccessRuleSelection={this.setIndividualAccessRules} />
        })
      } else {
        flags.splice(flagIndex, 1)
        this.setState({
          showModal: false,
          flags
        })
      }
      await this.performAudit()
    }
  }

  render () {
    const flags = this.state.flags
    return (

        { flags.length > 0 &&

            {flags.length} SERVICE ACCOUNTS

        }

            {flags.map((flag) => (

 this.showModal(flag)}>

                  { this.serviceLookup[flag.serviceId].displayName } Username:
                  { flag.userIdentity.email || flag.userIdentity.userId }
                  { flag.userIdentity.fullName || (flag.individual && flag.individual.fullName)}

            )
            )}

        { this.state.showModal &&
          <Modal title={this.state.modalTitle} closeHandler={this.closeModal}>
            { this.state.modalContents }
          </Modal>
        }

        <MessagesContainer ref={(container) => { this.messagesContainer = container }} />

    )
  }
}
<commit_msg>Refactor flagList<commit_after>import React from 'react'
import graphqlApi from '../../graphql-api'
import './flag-list.scss'
import Modal from '../shared/modal'
import UnknownUserForm from './unknown-user-form'
import NewIndividualForm from './new-individual-form'
import GroupSelectionForm from './group-selection-form'
import IndividualAccessRulesForm from './individual-access-rules-form'
import LinkIndividualForm from './link-individual-form'
import MessagesContainer from '../shared/messages-container'
import lodash from 'lodash'

export default class FlagList extends React.Component {
  state = {
    flags: [],
    showModal: false,
    currentFlag: null
  }

  flagQueryResponse = `{
    individual {
      id
      fullName
      primaryEmail
      serviceUserIdentities {
        serviceId
        userIdentity {
          email
          userId
        }
      }
      accessRules {
        service {
          id
        }
        accessRules {
          asset
          role
        }
      }
      groups
    }
    serviceId
    userIdentity {
      email
      userId
      fullName
    }
    assets {
      name
      role
    }
  }`

  componentWillMount = async () => {
    const query = `{
      groups {
        name
      }
      services(isConfigured:true){
        id
        displayName
        roles
      }
    }`

    const response = await graphqlApi.request(query)
    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to load group and service info',
        body: response.error.message
      })
      return
    }
    this.groups = response.data.groups.map((g) => g.name)
    this.serviceLookup = {}
    response.data.services.forEach((s) => { this.serviceLookup[s.id] = s })
    await this.performAudit()
  }

  showModal = (flag) => {
    this.setState({
      showModal: true,
      currentFlag: flag,
      modalTitle: flag.individual
        ? 'Set Individual Access Rules'
        : `Unknown User: ${flag.userIdentity.email || flag.userIdentity.userId}`,
      modalContents: flag.individual
        ? <IndividualAccessRulesForm service={this.serviceLookup[flag.serviceId]} assets={flag.assets} onAccessRuleSelection={this.setIndividualAccessRules} />
        : <UnknownUserForm flag={flag} onNewIndividualSelected={this.onNewIndividualSelected} onLinkToIndividualSelected={this.onLinkToIndividualSelected} />
    })
  }

  closeModal = (event) => {
    if (event) { event.preventDefault() }

    this.setState({
      showModal: false
    })
  }

  onNewIndividualSelected = () => {
    const flag = this.state.currentFlag
    this.setState({
      modalTitle: `Manage ${flag.userIdentity.email || flag.userIdentity.userId || 'blah'}`,
      modalContents: <NewIndividualForm flag={flag} onNewIndividualFormComplete={this.onNewIndividualFormComplete} onNewIndividualSelected={this.onNewIndividualSelected} />
    })
  }

  onNewIndividualFormComplete = (fullName, primaryEmail) => {
    if (!fullName || fullName.trim() === '') {
      this.messagesContainer.push({
        title: 'Invalid Name',
        body: "Please fill out the individual's name."
      })
      return
    }

    this.pendingNewIndividual = {
      fullName,
      primaryEmail
    }
    this.setState({
      modalTitle: `Select groups`,
      modalContents: <GroupSelectionForm groups={this.groups} onGroupFormComplete={this.onGroupFormComplete} individual={this.pendingNewIndividual} />
    })
  }

  onLinkToIndividualSelected = () => {
    this.setState({
      modalTitle: 'Link to an individual',
      modalContents: <LinkIndividualForm onIndividualSelected={this.onIndividualSelectedToLink} />
    })
  }

  onIndividualSelectedToLink = (individual) => {
    this.linkIndividual(individual.id)
  }

  linkIndividual = async (individualId) => {
    const flag = this.state.currentFlag

    const query = `mutation {
      linkServiceToIndividual(
        serviceId: "${flag.serviceId}",
        individualId:"${individualId}",
        ${flag.userIdentity.fullName ? `fullName: "${flag.userIdentity.fullName}"` : ''},
        ${flag.userIdentity.email ? `email: "${flag.userIdentity.email}"` : ''},
        ${flag.userIdentity.userId ? `userId: "${flag.userIdentity.userId}"` : ''}
      )
    }`
    const response = await graphqlApi.request(query)
    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to link to existing individual',
        body: response.error.message
      })
    }
    const newFlag = await this.reCheckFlag(flag)
    const flags = [...this.state.flags]
    const flagIndex = lodash.findIndex(flags, (f) => f.key === flag.key)
    if (newFlag) {
      flags[flagIndex] = newFlag
      this.setState({
        flags,
        currentFlag: newFlag,
        modalTitle: 'Set Individual Access Rules',
        modalContents: <IndividualAccessRulesForm service={this.serviceLookup[newFlag.serviceId]} assets={newFlag.assets} onAccessRuleSelection={this.setIndividualAccessRules} />
      })
    } else {
      flags.splice(flagIndex, 1)
      this.setState({
        flags,
        showModal: false
      })
    }
  }

  setIndividualAccessRules = async (selectedAccessRules) => {
    const flag = this.state.currentFlag
    const query = `mutation {
      addIndividualAccessRules(
        individualId: "${flag.individual.id}",
        serviceId: "${flag.serviceId}",
        accessRules: [${selectedAccessRules.map((rule) => `{
          asset: "${rule.asset}",
          role: "${rule.role}"
        }`).join(',')}])
    }`
    const response = await graphqlApi.request(query)
    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to add selected access rules',
        body: response.error.message
      })
    } else {
      const newFlag = await this.reCheckFlag(flag)
      const flags = this.state.flags
      const flagIndex = lodash.findIndex(flags, (f) => f.key === flag.key)
      if (newFlag) {
        flags[flagIndex] = newFlag
      } else {
        flags.splice(flagIndex, 1)
      }

      this.setState({
        showModal: false,
        flags
      })
    }
  }

  reCheckFlag = async (flag) => {
    const secondParameter = flag.userIdentity.email ? `email: "${flag.userIdentity.email}"` : `userId: "${flag.userIdentity.userId}"`

    const query = `{
      auditServiceUserAccount(serviceId: "${flag.serviceId}", ${secondParameter}) ${this.flagQueryResponse}
    }`
    const response = await graphqlApi.request(query)

    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to check flag',
        body: response.error.message
      })
    } else {
      const newFlag = response.data.auditServiceUserAccount
      if (newFlag) {
        newFlag.key = flag.key
        return newFlag
      }
    }
  }

  performAudit = async () => {
    const query = `{
      auditAll ${this.flagQueryResponse}
    }`

    const response = await graphqlApi.request(query)
    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to run audit',
        body: response.error.message
      })
      return
    }
    response.data.auditAll.forEach((flag) => {
      flag.key = `${flag.serviceId}${flag.userIdentity.email || flag.userIdentity.userId || new Date().valueOf()}`
    })
    this.setState({
      flags: response.data.auditAll
    })
  }

  onGroupFormComplete = async (selectedGroups) => {
    this.pendingNewIndividual.groups = selectedGroups
    const flag = this.state.currentFlag

    const query = `mutation {
      createIndividual(individual: {
        fullName: "${this.pendingNewIndividual.fullName}"
        ${this.pendingNewIndividual.primaryEmail ? `primaryEmail: "${this.pendingNewIndividual.primaryEmail}"` : ''}
        groups: [${selectedGroups.map((g) => `"${g}"`).join(',')}]
      })
    }`

    const response = await graphqlApi.request(query)
    const individualId = response.data.createIndividual

    if (response.error) {
      this.messagesContainer.push({
        title: 'Failed to Save New Individual',
        body: response.error.message
      })
    } else {
      await this.linkIndividual(individualId)
      await this.performAudit()
    }
  }

  render () {
    const flags = this.state.flags
    return (

        { flags.length > 0 &&

            {flags.length} SERVICE ACCOUNTS

        }

            {flags.map((flag) => (

 this.showModal(flag)}>

                  { this.serviceLookup[flag.serviceId].displayName } Username:
                  { flag.userIdentity.email || flag.userIdentity.userId }
                  { flag.userIdentity.fullName || (flag.individual && flag.individual.fullName)}

            )
            )}

        { this.state.showModal &&
          <Modal title={this.state.modalTitle} closeHandler={this.closeModal}>
            { this.state.modalContents }
          </Modal>
        }

        <MessagesContainer ref={(container) => { this.messagesContainer = container }} />

    )
  }
}
<|endoftext|>


<commit_before>import browserify from 'browserify'
import rebundler from 'rebundler'
import SpawnStream from 'spawn-stream'

import config from '../config'
import collapser from 'bundle-collapser/plugin'

import babelify from 'babelify'

function createBundle(entries) {

  return rebundler({noop: config.env !== 'development'}, (cache, pkgCache) => {
    return browserify(entries, {
      cache: cache,
      packageCache: pkgCache,
      extensions: ['.jsx'],
      debug: config.env == 'development',
      fullPaths: config.env == 'development'
    })
  })
}

const UGLIFY_CMD = require.resolve('uglify-js/bin/uglifyjs')

function uglify() {
  return SpawnStream(UGLIFY_CMD, [
    '--compress',
    '--mangle',
    '--screw-ie8'
  ])
}

const site = createBundle([
  //config.env === 'development' && require.resolve('../lib/react-a11y'),
  require.resolve('../bundles/site/entry.jsx')
].filter(Boolean))

const loader = createBundle([
  require.resolve('../bundles/loader.js')
].filter(Boolean))

const embeds = createBundle([
  require.resolve('../bundles/embeds/entry.jsx')
].filter(Boolean))

const embedsDebug = createBundle([
  require.resolve('../bundles/debug/embeds.jsx')
].filter(Boolean))

const renderDebug = createBundle([
  require.resolve('../bundles/debug/render.jsx')
].filter(Boolean))

const test = createBundle(require.resolve('../docsite/bundle.jsx'))

export default {

  '/build/js/loader.js'() {
    console.time(';// Bundle') // eslint-disable-line no-console
    const bundle = loader()
      .transform(babelify)

    if (config.env !== 'development') {
      bundle.plugin(collapser)
    }

    const stream = bundle.bundle()

    if (config.env !== 'development') {
      return stream.pipe(uglify())
    }

    stream.on('end', () => {
      console.timeEnd(';// Bundle') // eslint-disable-line no-console
    })
    return stream
  },

  '/build/js/embeds.js'() {
    console.time(';// Bundle') // eslint-disable-line no-console
    const bundle = embeds()
      .transform(babelify)

    if (config.env !== 'development') {
      bundle.plugin(collapser)
    }

    const stream = bundle.bundle()

    if (config.env !== 'development') {
      return stream.pipe(uglify())
    }

    stream.on('end', () => {
      console.timeEnd(';// Bundle') // eslint-disable-line no-console
    })
    return stream
  },

  '/build/js/site.js'() {
    console.time(';// Bundle') // eslint-disable-line no-console
    const bundle = site()
      .transform(babelify)

    if (config.env !== 'development') {
      //bundle.plugin(collapser)
    }

    const stream = bundle.bundle()

    if (config.env !== 'development') {
      return stream.pipe(uglify())
    }

    stream.on('end', () => {
      console.timeEnd(';// Bundle') // eslint-disable-line no-console
    })
    return stream
  },

  '/build/js/test.js'() {
    if (config.env !== 'development') {
      return '// only development'
    }
    return test()
      .transform('redocify')
      .transform(babelify)
      .bundle()
  },

  '/build/js/embeds-debug.js'() {
    console.time(';// Bundle') // eslint-disable-line no-console
    const bundle = embedsDebug()
      .transform(babelify)

    if (config.env !== 'development') {
      bundle.plugin(collapser)
    }

    const stream = bundle.bundle()

    if (config.env !== 'development') {
      return stream.pipe(uglify())
    }

    stream.on('end', () => {
      console.timeEnd(';// Bundle') // eslint-disable-line no-console
    })
    return stream
  },

  '/build/js/render-debug.js'() {
    console.time(';// Bundle') // eslint-disable-line no-console
    const bundle = renderDebug()
      .transform(babelify)

    if (config.env !== 'development') {
      bundle.plugin(collapser)
    }

    const stream = bundle.bundle()

    if (config.env !== 'development') {
      return stream.pipe(uglify())
    }

    stream.on('end', () => {
      console.timeEnd(';// Bundle') // eslint-disable-line no-console
    })
    return stream
  }

}
<commit_msg>Remove some code duplication when declaring browserify bundles<commit_after>import browserify from 'browserify'
import rebundler from 'rebundler'
import SpawnStream from 'spawn-stream'

import config from '../config'
import collapser from 'bundle-collapser/plugin'

import babelify from 'babelify'

const UGLIFY_CMD = require.resolve('uglify-js/bin/uglifyjs')

function uglify() {
  return SpawnStream(UGLIFY_CMD, [
    '--compress',
    '--mangle',
    '--screw-ie8'
  ])
}

function createBundle(entries, addTransforms) {
  if (!Array.isArray(entries)) {
    return createBundle([entries])
  }

  const rebundle = rebundler({noop: config.env !== 'development'}, (cache, pkgCache) => {
    const bundle = browserify(entries.filter(Boolean).map(require.resolve), {
      cache: cache,
      packageCache: pkgCache,
      extensions: ['.jsx'],
      debug: config.env == 'development',
      fullPaths: config.env == 'development'
    })
      .transform(babelify)

    if (addTransforms) {
      addTransforms(bundle)
    }

    if (config.env !== 'development') {
      bundle.plugin(collapser)
    }
    return bundle
  })

  return function build() {
    console.time(';// Bundle') // eslint-disable-line no-console

    const bundle = rebundle()

    const stream = bundle.bundle()

    if (config.env !== 'development') {
      return stream.pipe(uglify())
    }

    stream.on('end', () => {
      console.timeEnd(';// Bundle') // eslint-disable-line no-console
    })
    return stream
  }
}

function define(route, factory) {
  return {route, factory}
}

export default ([
  define('/build/js/loader.js', createBundle('../bundles/loader.js')),
  define('/build/js/embeds.js', createBundle('../bundles/embeds/entry.jsx')),
  define('/build/js/site.js', createBundle([
    //config.env === 'development' && '../lib/react-a11y',
    '../bundles/site/entry.jsx'
  ])),
  config.env !== 'production' && define('/build/js/test.js', createBundle('../docsite/bundle.jsx', bundle => bundle.transform('redocify'))),
  config.env !== 'production' && define('/build/js/embeds-debug.js', createBundle('../bundles/debug/embeds.jsx')),
  config.env !== 'production' && define('/build/js/render-debug.js', createBundle('../bundles/debug/render.jsx'))
])
  .filter(Boolean)
  .reduce((acc, def) => {
    acc[def.route] = def.factory
    return acc
  }, {})
<|endoftext|>


<commit_before>import React from 'react';
import Button from 'react-mdl/lib/Button';
import Grid, { Cell } from 'react-mdl/lib/Grid';

import ProfileTab from "../util/ProfileTab";

class PersonalTab extends ProfileTab {
  constructor(props) {
    super(props);
  }
  nextUrl = "/profile/education";

  static propTypes = {
    profile:        React.PropTypes.object,
    errors:         React.PropTypes.object,
    saveProfile:    React.PropTypes.func,
    updateProfile:  React.PropTypes.func,
  };

  static defaultProps = {
    requiredFields: [
      ['first_name'],
      ['last_name'],
      ['preferred_name'],
      ['gender'],
      ['preferred_language'],
      ['city'],
      ['state_or_territory'],
      ['country'],
      ['birth_city'],
      ['birth_state_or_territory'],
      ['birth_country'],
      ['date_of_birth'],
    ],
    validationMessages: {
      'first_name': "Given name",
      'last_name': "Family name",
      'preferred_name': "Preferred name",
      'gender': "Gender",
      'preferred_language': "Preferred language",
      'city': "City",
      'state_or_territory': 'State or Territory',
      'country': "Country",
      'birth_city': 'City',
      'birth_state_or_territory': 'State or Territory',
      'birth_country': "Country",
      'date_of_birth': "Date of birth"
    }
  }

  render() {
    return

      <Grid className="profile-splash">
        <Cell col={12}>
          Please tell us more about yourself so you can participate in the MicroMaster's
          community and qualify for your MicroMaster's certificate.
        </Cell>
      </Grid>
      <Grid className="profile-tab-grid">
        <Cell col={1} />
        <Cell col={10}>
          <Grid className="profile-tab-card-grid">
            <Cell col={6}>
              {this.boundTextField(["first_name"], "Given name")}
            </Cell>
            <Cell col={6}>
              {this.boundTextField(["last_name"], "Family name")}
            </Cell>
            <Cell col={12}>
              {this.boundTextField(["preferred_name"], "Preferred name")}
            </Cell>
            <Cell col={12}>
              {this.boundDateField(['date_of_birth'], 'Date of birth')}
            </Cell>
            <Cell col={12} className="profile-gender-group">
              {this.boundRadioGroupField(['gender'], 'Gender', this.genderOptions)}
            </Cell>
            <Cell col={12}>
              {this.boundSelectField(
                ['preferred_language'],
                'Preferred language',
                this.languageOptions
              )}
            </Cell>
            <Cell col={12}>

Currently Living

            </Cell>
            <Cell col={4}>
              {this.boundCountrySelectField(['state_or_territory'], ['country'], 'Country')}
            </Cell>
            <Cell col={4}>
              {this.boundStateSelectField(['state_or_territory'], ['country'], 'State or Territory')}
            </Cell>
            <Cell col={4}>
              {this.boundTextField(['city'], 'City')}
            </Cell>
            <Cell col={12}>

Where were you born?

            </Cell>
            <Cell col={4}>
              {this.boundCountrySelectField(
                ['birth_state_or_territory'],
                ['birth_country'],
                'Country'
              )}</Cell>
            <Cell col={4}>
              {this.boundStateSelectField(['birth_state_or_territory'], ['birth_country'], 'State or Territory')}
            </Cell>
            <Cell col={4}>
              {this.boundTextField(['birth_city'], 'City')}
            </Cell>
          </Grid>
        </Cell>
        <Cell col={1} />
        <Cell col={1} />
        <Cell col={10}>
          <Button
            raised
            colored
            className="profile-save-and-continue"
            onClick={this.saveAndContinue}
          >
            Save and Continue
          </Button>
        </Cell>
        <Cell col={1} />
      </Grid>

;
  }
}

export default PersonalTab;
<commit_msg>Replace MicroMaster's certificate text with MicroMaster's credential in app<commit_after>import React from 'react';
import Button from 'react-mdl/lib/Button';
import Grid, { Cell } from 'react-mdl/lib/Grid';

import ProfileTab from "../util/ProfileTab";

class PersonalTab extends ProfileTab {
  constructor(props) {
    super(props);
  }
  nextUrl = "/profile/education";

  static propTypes = {
    profile:        React.PropTypes.object,
    errors:         React.PropTypes.object,
    saveProfile:    React.PropTypes.func,
    updateProfile:  React.PropTypes.func,
  };

  static defaultProps = {
    requiredFields: [
      ['first_name'],
      ['last_name'],
      ['preferred_name'],
      ['gender'],
      ['preferred_language'],
      ['city'],
      ['state_or_territory'],
      ['country'],
      ['birth_city'],
      ['birth_state_or_territory'],
      ['birth_country'],
      ['date_of_birth'],
    ],
    validationMessages: {
      'first_name': "Given name",
      'last_name': "Family name",
      'preferred_name': "Preferred name",
      'gender': "Gender",
      'preferred_language': "Preferred language",
      'city': "City",
      'state_or_territory': 'State or Territory',
      'country': "Country",
      'birth_city': 'City',
      'birth_state_or_territory': 'State or Territory',
      'birth_country': "Country",
      'date_of_birth': "Date of birth"
    }
  }

  render() {
    return

      <Grid className="profile-splash">
        <Cell col={12}>
          Please tell us more about yourself so you can participate in the MicroMaster’s
          community and qualify for your MicroMaster’s credential.
        </Cell>
      </Grid>
      <Grid className="profile-tab-grid">
        <Cell col={1} />
        <Cell col={10}>
          <Grid className="profile-tab-card-grid">
            <Cell col={6}>
              {this.boundTextField(["first_name"], "Given name")}
            </Cell>
            <Cell col={6}>
              {this.boundTextField(["last_name"], "Family name")}
            </Cell>
            <Cell col={12}>
              {this.boundTextField(["preferred_name"], "Preferred name")}
            </Cell>
            <Cell col={12}>
              {this.boundDateField(['date_of_birth'], 'Date of birth')}
            </Cell>
            <Cell col={12} className="profile-gender-group">
              {this.boundRadioGroupField(['gender'], 'Gender', this.genderOptions)}
            </Cell>
            <Cell col={12}>
              {this.boundSelectField(
                ['preferred_language'],
                'Preferred language',
                this.languageOptions
              )}
            </Cell>
            <Cell col={12}>

Currently Living

            </Cell>
            <Cell col={4}>
              {this.boundCountrySelectField(['state_or_territory'], ['country'], 'Country')}
            </Cell>
            <Cell col={4}>
              {this.boundStateSelectField(['state_or_territory'], ['country'], 'State or Territory')}
            </Cell>
            <Cell col={4}>
              {this.boundTextField(['city'], 'City')}
            </Cell>
            <Cell col={12}>

Where were you born?

            </Cell>
            <Cell col={4}>
              {this.boundCountrySelectField(
                ['birth_state_or_territory'],
                ['birth_country'],
                'Country'
              )}</Cell>
            <Cell col={4}>
              {this.boundStateSelectField(['birth_state_or_territory'], ['birth_country'], 'State or Territory')}
            </Cell>
            <Cell col={4}>
              {this.boundTextField(['birth_city'], 'City')}
            </Cell>
          </Grid>
        </Cell>
        <Cell col={1} />
        <Cell col={1} />
        <Cell col={10}>
          <Button
            raised
            colored
            className="profile-save-and-continue"
            onClick={this.saveAndContinue}
          >
            Save and Continue
          </Button>
        </Cell>
        <Cell col={1} />
      </Grid>

;
  }
}

export default PersonalTab;
<|endoftext|>


<commit_before>function qid(id) {
  return id.replace(/([.:])/g, "\\$1");
}

function beforeComment(formData, jqForm, options) {
    var form = jqForm[0];
    clearStatus(options.target);
    if (!form.comment.value) {
        setError(options.target, "Your comment is empty");
        return false;
    }
    if (!form.name.value) {
        setError(options.target, "Please provide a name");
        return false;
    }
    $(options.target + " span.comment_error").after(
        "");
    $(options.target + " input[@name=submit]").attr("disabled", true);
    return true;
}

function clearStatus(target) {
    $(target + " input[@name=submit]").attr("disabled", false);
    $(target + " span.comment_error").empty().nextAll().remove();
}

function setError(target, errStr) {
    $(target + " span.comment_error").append(errStr);
}

function ajaxifyForm(id) {
  var q = qid(id);
  var target = "#comments_" + q;
  $("#form_" + q).ajaxForm({
      beforeSubmit: beforeComment,

      success: function() { ajaxifyForm(id); },

      error: function (_req, typ, exc) {
          clearStatus(target);
          switch (typ) {
          case 'error':
              setError(target, "Server error. Try again soon.");
              break;
          case 'timeout':
              setError(target, 'Request timed out. Try again in a few seconds.');
              break;
          default:
              setError(target, 'An unknown error occurred.');
              break;
          }
          return false;
      },

      target: target
  });
}

function toggleComment(id) {
  $("#toggle_" + qid(id)).nextAll().toggle();
  return false;
}

function loadComments(id) {
  $("#comments_" + qid(id)).load(location.protocol + "//" + location.host +
				 "/comments/single/" + id + "/", function() {
    ajaxifyForm(id);
  });
  return false;
}

function loadAllComments() {
  $("a.commenttoggle").each(function() {
    var id = $(this).attr("pid");
    if (id) {
      loadComments(id);
    }
  });
}

function getChapterId() {
    return $("div.preface, div.chapter, div.appendix, div.bibliography").attr("id");
}

$(document).ready(function() {
  function loading(id) {
    return " " +
      "Loading..." +
      "";
  }
  $("div.toc>p")
    .after("
" +
	   "Load all comments (slow)
")
    .toggle(function() { $(this).nextAll().show("normal"); },
	    function() { $(this).nextAll().hide("normal"); })
    .hover(function() { $(this).fadeTo("normal", 0.8); },
	   function() { $(this).fadeTo("normal", 0.35); });
  $(".chapter p[@id]").each(function() {
    $(this).append(loading($(this).attr("id")));
  });
  $(".chapter table[@id].equation").each(function() {
    id = $(this).attr("id");
    $("#" + id + " tr").after('
' + loading($(this).attr("id")) + '
');
  });
  $(".chapter pre[@id]").each(function() {
    $(this).after(loading($(this).attr("id")));
  });
  var chapid = getChapterId();
  $("#chapterfeed").attr("href",
			 $("#chapterfeed").attr("href") + chapid + "/");
  $.getJSON(location.protocol + "//" + location.host + "/comments/chapter/" +
	    chapid + "/count/", function(data) {
    $.each(data, function(id, item) {
      var s = item == 1 ? "" : "s";
      $("#comments_" + qid(id) + " span.commenttoggle").replaceWith(
        "" + item + " comment" + s + "");
    });
    $("span.commenttoggle").each(function() {
      var id = $(this).attr("pid");
      $(this).replaceWith("No comments");
    });
  });
});
<commit_msg>Factor out the function that creates a new comment link<commit_after>function qid(id) {
  return id.replace(/([.:])/g, "\\$1");
}

function beforeComment(formData, jqForm, options) {
    var form = jqForm[0];
    clearStatus(options.target);
    if (!form.comment.value) {
        setError(options.target, "Your comment is empty");
        return false;
    }
    if (!form.name.value) {
        setError(options.target, "Please provide a name");
        return false;
    }
    $(options.target + " span.comment_error").after(
        "");
    $(options.target + " input[@name=submit]").attr("disabled", true);
    return true;
}

function clearStatus(target) {
    $(target + " input[@name=submit]").attr("disabled", false);
    $(target + " span.comment_error").empty().nextAll().remove();
}

function setError(target, errStr) {
    $(target + " span.comment_error").append(errStr);
}

function ajaxifyForm(id) {
  var q = qid(id);
  var target = "#comments_" + q;
  $("#form_" + q).ajaxForm({
      beforeSubmit: beforeComment,

      success: function() { ajaxifyForm(id); },

      error: function (_req, typ, exc) {
          clearStatus(target);
          switch (typ) {
          case 'error':
              setError(target, "Server error. Try again soon.");
              break;
          case 'timeout':
              setError(target, 'Request timed out. Try again in a few seconds.');
              break;
          default:
              setError(target, 'An unknown error occurred.');
              break;
          }
          return false;
      },

      target: target
  });
}

function toggleComment(id) {
  $("#toggle_" + qid(id)).nextAll().toggle();
  return false;
}

function loadComments(id) {
  $("#comments_" + qid(id)).load(location.protocol + "//" + location.host +
				 "/comments/single/" + id + "/", function() {
    ajaxifyForm(id);
  });
  return false;
}

function loadAllComments() {
  $("a.commenttoggle").each(function() {
    var id = $(this).attr("pid");
    if (id) {
      loadComments(id);
    }
  });
}

function getChapterId() {
    return $("div.preface, div.chapter, div.appendix, div.bibliography").attr("id");
}

$(document).ready(function() {
  function loading(id) {
    return " " +
      "Loading..." +
      "";
  }
  $("div.toc>p")
    .after("
" +
	   "Load all comments (slow)
")
    .toggle(function() { $(this).nextAll().show("normal"); },
	    function() { $(this).nextAll().hide("normal"); })
    .hover(function() { $(this).fadeTo("normal", 0.8); },
	   function() { $(this).fadeTo("normal", 0.35); });
  $(".chapter p[@id]").each(function() {
    $(this).append(loading($(this).attr("id")));
  });
  $(".chapter table[@id].equation").each(function() {
    id = $(this).attr("id");
    $("#" + id + " tr").after('
' + loading($(this).attr("id")) + '
');
  });
  $(".chapter pre[@id]").each(function() {
    $(this).after(loading($(this).attr("id")));
  });
  var chapid = getChapterId();
  $("#chapterfeed").attr("href",
			 $("#chapterfeed").attr("href") + chapid + "/");
  $.getJSON(location.protocol + "//" + location.host + "/comments/chapter/" +
	    chapid + "/count/", function(data) {
    $.each(data, function(id, item) {
      $("#comments_" + qid(id) + " span.commenttoggle").replaceWith(
          commentLink(id, item);
      );
    });
    $("span.commenttoggle").each(function() {
      var id = $(this).attr("pid");
      $(this).replaceWith(commentLink(id, 0));
    });
  });
});

function commentLink(commentId, numComments) {
    var desc, act;
    if (numComments == 0) {
        desc = "No comments";
        act = "comment: add";
    } else if (numComments == 1) {
        desc = "1 comment";
        act = "comments: show/hide";
    } else {
        desc = numComments + " comments";
        act = "comments: show/hide";
    }
    return "" + desc + "";
}
<|endoftext|>