diff options
62 files changed, 2062 insertions, 287 deletions
| diff --git a/.gitignore b/.gitignore index b6a61fd27..762c156eb 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ coverage  .idea  bin/ +# Ignore node modules +/node_modules @@ -10,6 +10,9 @@ gem 'uglifier', '~> 2.7.2'  # Use CoffeeScript for .js.coffee assets and views  gem 'coffee-rails', '~> 4.0.0' +# ES6 powa +gem 'browserify-rails' +  # Use jquery as the JavaScript library  gem 'jquery-rails', '~> 3.1.4' # Update to v4 for Rails 4.2  # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks @@ -125,6 +128,7 @@ gem 'letter_opener'  group :development do    gem 'capistrano', '2.13.5'    gem 'capistrano-ext' +  gem 'capistrano-npm', require: false    gem 'guard'    gem 'guard-rspec'    gem 'rails-erd' diff --git a/Gemfile.lock b/Gemfile.lock index 631c0bfbe..a3ede77d1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -90,6 +90,8 @@ GEM      binding_of_caller (0.7.2)        debug_inspector (>= 0.0.1)      breadcrumbs_on_rails (2.3.0) +    browserify-rails (1.1.0) +      railties (>= 4.0.0, < 5.0)      builder (3.2.2)      calendar_helper (0.2.5)        open4 @@ -101,6 +103,8 @@ GEM        net-ssh-gateway (>= 1.1.0)      capistrano-ext (1.2.1)        capistrano (>= 1.0.0) +    capistrano-npm (0.0.2) +      capistrano (>= 2.5.5)      capybara (2.4.4)        mime-types (>= 1.16)        nokogiri (>= 1.3.3) @@ -574,9 +578,11 @@ DEPENDENCIES    better_errors    binding_of_caller    breadcrumbs_on_rails +  browserify-rails    calendar_helper (= 0.2.5)    capistrano (= 2.13.5)    capistrano-ext +  capistrano-npm    capybara (~> 2.4.0)    cocoon    codifligne! @@ -674,4 +680,4 @@ DEPENDENCIES    will_paginate-bootstrap (~> 1.0.1)  BUNDLED WITH -   1.12.5 +   1.13.6 diff --git a/app/assets/javascripts/es6_browserified/actions/index.js b/app/assets/javascripts/es6_browserified/actions/index.js new file mode 100644 index 000000000..8967c0322 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/actions/index.js @@ -0,0 +1,33 @@ +module.exports = { +  addStop : () => { +    return { +      type: 'ADD_STOP' +    } +  }, +  moveStopUp : (index) => { +    return { +      type: 'MOVE_STOP_UP', +      index +    } +  }, +  moveStopDown : (index) => { +    return { +      type: 'MOVE_STOP_DOWN', +      index +    } +  }, +  deleteStop: (index) => { +    return { +      type: 'DELETE_STOP', +      index +    } +  }, +  updateInputValue: (index, text) => { +    console.log('action',index, text) +    return { +      type : "UPDATE_INPUT_VALUE", +      index, +      text +    } +  } +} diff --git a/app/assets/javascripts/es6_browserified/components/App.js b/app/assets/javascripts/es6_browserified/components/App.js new file mode 100644 index 000000000..7488b0b39 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/components/App.js @@ -0,0 +1,12 @@ +var React = require('react') +var AddTodo = require('../containers/AddTodo') +var VisibleTodoList = require('../containers/VisibleTodoList') + +const App = () => ( +  <div> +    <AddTodo /> +    <VisibleTodoList /> +  </div> +) + +module.exports = App diff --git a/app/assets/javascripts/es6_browserified/components/BSelect2.js b/app/assets/javascripts/es6_browserified/components/BSelect2.js new file mode 100644 index 000000000..7e4782563 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/components/BSelect2.js @@ -0,0 +1,96 @@ +var React = require('react') +var PropTypes = require('react').PropTypes +var Select2 = require('react-select2') + +// get JSON full path +var origin = window.location.origin +var path = window.location.pathname.split('/', 3).join('/') + +class BSelect3 extends React.Component{ +  constructor(props) { +    super(props) +    this.state = { +      edit: false +    } +  } +  onToggleEdit(e) { +    e.preventDefault() +    this.setState({edit: !this.state.edit}) +  } +  onChange(e) { +    console.log(e.currentTarget.value, e.currentTarget.textContent) +    this.props.onChange(this.props.index, {text: e.currentTarget.textContent, id: e.currentTarget.value}) +    this.setState({edit: false}) +  } +  render() { +    if(this.state.edit) +      return ( +        <div> +          <BSelect2 {...this.props} onSelect={ this.onChange.bind(this) }/> +        </div> +      ) +    else +      return ( +        <div> +          <span +            title="Cliquez pour changer l'arrêt" +            style={{cursor: 'pointer', display: 'block'}} +            onClick={this.onToggleEdit.bind(this)} +          > +            {this.props.value.text} +          </span> +        </div> +      ) +  } +} + +const BSelect2 = (props) => { +  return ( +    <Select2 +      value={props.value.id} +      onSelect={ props.onSelect } +      options={{ +        placeholder: 'Sélectionnez un arrêt existant...', +        theme: 'bootstrap', +        ajax: { +          url: origin + path + '/autocomplete_stop_areas.json', +          dataType: 'json', +          delay: '500', +          data: function(params) { +            return { +              q: params.term +            }; +          }, +          processResults: function(data, params) { +            return { +              results: data.map( +                item => Object.assign( +                  {}, +                  item, +                  { text: item.name + ", " + item.zip_code + " " + item.short_city_name } +                ) +              ) +            }; +          }, +          cache: true +        }, +        minimumInputLength: 3, +        templateResult: formatRepo +      }} +    /> +  ) +} + +// to fix: this is for custom results return +const formatRepo = (props) => { +  if(props.text) return props.text +  // console.log(props) +  // return ( +  //   <div> +  //     {props.short_name} +  //     <small><em>{props.zip_code} {props.short_city_name}</em></small> +  //   </div> +  // ) +} + +module.exports = BSelect3 diff --git a/app/assets/javascripts/es6_browserified/components/Todo.js b/app/assets/javascripts/es6_browserified/components/Todo.js new file mode 100644 index 000000000..0e74e728f --- /dev/null +++ b/app/assets/javascripts/es6_browserified/components/Todo.js @@ -0,0 +1,62 @@ +var React = require('react') +var PropTypes = require('react').PropTypes +var BSelect2 = require('./BSelect2') + +const Container = {display: 'table',  width: '100%'} +const firstBlock = {display: 'table-cell', verticalAlign: 'middle'} +const secondBlock = {display: 'table-cell', verticalAlign: 'middle', width: '150px', textAlign: 'right'} + +const Todo = (props) => { +  return ( +    <div className='list-group-item' style={Container}> +      <div style={firstBlock}> +        <div style={{display: 'inline-block', width: '9%', verticalAlign: 'middle'}}> +          <span className='strong'>#{props.index}</span> +        </div> + +        <div style={{display: 'inline-block', width: '91%', verticalAlign: 'middle'}}> +          <BSelect2 id={'route_stop_points_' + props.id} value={props.value} onChange={props.onChange} index={props.index} defaultData={props.defaultData}/> +        </div> +      </div> + +      <div style={secondBlock}> +        <div className='btn-group btn-group-sm'> +          <div className='btn btn-default'> +            <span className='fa fa-times'></span> +          </div> +          <div +            className={'btn btn-primary' + (props.first ? ' disabled' : '')} +            onClick={props.onMoveUpClick} +          > +            <span className='fa fa-arrow-up'></span> +          </div> +          <div +            className={'btn btn-primary' + (props.last ? ' disabled' : '')} +            onClick={props.onMoveDownClick} +          > +            <span className='fa fa-arrow-down'></span> +          </div> +          <div +            className='btn btn-danger' +            onClick={props.onDeleteClick} +          > +            <span className='fa fa-trash'></span> +          </div> +        </div> +      </div> +    </div> +  ) +} + +Todo.propTypes = { +  onDeleteClick: PropTypes.func.isRequired, +  onMoveUpClick: PropTypes.func.isRequired, +  onMoveDownClick: PropTypes.func.isRequired, +  onChange: PropTypes.func.isRequired, +  first: PropTypes.bool, +  last: PropTypes.bool, +  index: PropTypes.number, +  defaultData: PropTypes.array +} + +module.exports = Todo diff --git a/app/assets/javascripts/es6_browserified/components/TodoList.js b/app/assets/javascripts/es6_browserified/components/TodoList.js new file mode 100644 index 000000000..367cfc256 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/components/TodoList.js @@ -0,0 +1,37 @@ +var React = require('react') +var PropTypes = require('react').PropTypes +var Todo = require('./Todo') + +const TodoList = ({ todos, onDeleteClick, onMoveUpClick, onMoveDownClick, onChange }) => { +  console.log(todos) +  return ( +    <div className='list-group'> +      {todos.map((todo, index) => +        <Todo +          key={'item-' + index} +          onDeleteClick={() => onDeleteClick(index)} +          onMoveUpClick={() => { +            console.log(index, todos) +            onMoveUpClick(index)} +          } +          onMoveDownClick={() => onMoveDownClick(index)} +          onChange={ onChange } +          first={ index === 0 } +          last={ index === (todos.length - 1) } +          index={ index } +          defaultData={ todos } +          value={ todo } +        /> +      )} +    </div> +  ) +} + +TodoList.propTypes = { +  todos: PropTypes.array.isRequired, +  onDeleteClick: PropTypes.func.isRequired, +  onMoveUpClick: PropTypes.func.isRequired, +  onMoveDownClick: PropTypes.func.isRequired +} + +module.exports = TodoList diff --git a/app/assets/javascripts/es6_browserified/containers/AddTodo.js b/app/assets/javascripts/es6_browserified/containers/AddTodo.js new file mode 100644 index 000000000..539b6f78e --- /dev/null +++ b/app/assets/javascripts/es6_browserified/containers/AddTodo.js @@ -0,0 +1,21 @@ +var React = require('react') +var connect = require('react-redux').connect +var addTodo = require('../actions').addStop + +let AddTodo = ({ dispatch }) => { +  return ( +    <div className="clearfix" style={{marginBottom: 10}}> +      <form onSubmit={e => { +        e.preventDefault() +        dispatch(addTodo()) +      }}> +        <button type="submit" className="btn btn-primary btn-xs pull-right"> +          <span className="fa fa-plus"></span> Ajouter un arrêt +        </button> +      </form> +    </div> +  ) +} +AddTodo = connect()(AddTodo) + +module.exports = AddTodo diff --git a/app/assets/javascripts/es6_browserified/containers/VisibleTodoList.js b/app/assets/javascripts/es6_browserified/containers/VisibleTodoList.js new file mode 100644 index 000000000..09da36060 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/containers/VisibleTodoList.js @@ -0,0 +1,37 @@ +var connect = require('react-redux').connect +var toggleTodo = require('../actions').toggleTodo +var deleteStop = require('../actions').deleteStop +var moveStopUp = require('../actions').moveStopUp +var moveStopDown = require('../actions').moveStopDown +var handleChange = require('../actions').updateInputValue +var TodoList = require('../components/TodoList') + +const mapStateToProps = (state) => { +  return { +    todos: state.todos +  } +} + +const mapDispatchToProps = (dispatch) => { +  return { +    onDeleteClick: (index) =>{ +      dispatch(deleteStop(index)) +    }, +    onMoveUpClick: (index) =>{ +      dispatch(moveStopUp(index)) +    }, +    onMoveDownClick: (index) =>{ +      dispatch(moveStopDown(index)) +    }, +    onChange: (index, text) =>{ +      dispatch(handleChange(index, text)) +    } +  } +} + +const VisibleTodoList = connect( +  mapStateToProps, +  mapDispatchToProps +)(TodoList) + +module.exports = VisibleTodoList diff --git a/app/assets/javascripts/es6_browserified/reducers/index.js b/app/assets/javascripts/es6_browserified/reducers/index.js new file mode 100644 index 000000000..ae8423673 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/reducers/index.js @@ -0,0 +1,8 @@ +var  combineReducers  = require('redux').combineReducers +var todos = require('./todos') + +const todoApp = combineReducers({ +  todos +}) + +module.exports = todoApp diff --git a/app/assets/javascripts/es6_browserified/reducers/todos.js b/app/assets/javascripts/es6_browserified/reducers/todos.js new file mode 100644 index 000000000..bf64632bd --- /dev/null +++ b/app/assets/javascripts/es6_browserified/reducers/todos.js @@ -0,0 +1,63 @@ +const todo = (state = {}, action, length) => { +  switch (action.type) { +    case 'ADD_STOP': +      return { +        text: '', +        index: length +      } +    case 'UPDATE_INPUT_VALUE': +      console.log('reducer', action) +      if (state.index !== action.index) { +        return state +      } + +      return Object.assign( +        {}, +        state, +        {text: action.text.text, id: action.text.id} +      ) +    default: +      return state +  } +} + +const todos = (state = [], action) => { +  switch (action.type) { +    case 'ADD_STOP': +      return [ +        ...state, +        todo(undefined, action, state.length) +      ] +    case 'MOVE_STOP_UP': +      return [ +        ...state.slice(0, action.index - 1), +        state[action.index], +        state[action.index - 1], +        ...state.slice(action.index + 1) +      ] +    case 'MOVE_STOP_DOWN': +      return [ +        ...state.slice(0, action.index), +        state[action.index + 1], +        state[action.index], +        ...state.slice(action.index + 2) +      ] +    case 'DELETE_STOP': +      return [ +        ...state.slice(0, action.index), +        ...state.slice(action.index + 1).map((todo)=>{ +          todo.index-- +          return todo +        }) +      ] +    case 'UPDATE_INPUT_VALUE': +      return state.map((t, i) => (i === action.index) ? action.text : t) +      // return state.map(t => +      //   todo(t, action) +      // ) +    default: +      return state +  } +} + +module.exports = todos diff --git a/app/assets/javascripts/es6_browserified/stop_points.js b/app/assets/javascripts/es6_browserified/stop_points.js new file mode 100644 index 000000000..9c0195f2b --- /dev/null +++ b/app/assets/javascripts/es6_browserified/stop_points.js @@ -0,0 +1,48 @@ +var React = require('react') +var render = require('react-dom').render +var Provider = require('react-redux').Provider +var createStore = require('redux').createStore +// var applyMiddleware = require('redux').applyMiddleware +var todoApp = require('./reducers') +var App = require('./components/App') +// var createLogger = require('redux-logger').default +// var thunkMiddleware = require('redux-thunk').default +// var promise = require('redux-promise') + +const getInitialState = () => { +  let state = [] +  let datas = JSON.parse(decodeURIComponent(window.itinerary_stop)) +  for (let [index, value] of datas.entries()){ + +    let fancyText = value.name +    if(value.zip_code && value.city_name) +      fancyText += ", " + value.zip_code + " " + value.city_name + +    state.push({ +      id: value.id, +      name: value.name, +      city_name: value.city_name, +      zip_code: value.zip_code, +      text: fancyText +    }) +  } +  // console.log(state) +  return state +} + +var initialState = {todos: getInitialState()} +// const loggerMiddleware = createLogger() +let store = createStore( +  todoApp, +  initialState +  // applyMiddleware(thunkMiddleware, promise, loggerMiddleware) +) + +// console.log(store.getState()) + +render( +  <Provider store={store}> +    <App /> +  </Provider>, +  document.getElementById('stop_points') +) diff --git a/app/assets/stylesheets/application.sass.erb b/app/assets/stylesheets/application.sass.erb index 53b767384..c59a07d94 100644 --- a/app/assets/stylesheets/application.sass.erb +++ b/app/assets/stylesheets/application.sass.erb @@ -42,6 +42,9 @@ $body-bg: #eee  @import 'vendor/typeahead'  @import 'vendor/bootstrap_changes'  @import 'vendor/simple_form' +// Select2 +@import 'vendor/select2' +@import 'vendor/select2-bootstrap'  // Main css  @import 'main/*' diff --git a/app/assets/stylesheets/vendor/select2-bootstrap.css b/app/assets/stylesheets/vendor/select2-bootstrap.css new file mode 100644 index 000000000..65f772696 --- /dev/null +++ b/app/assets/stylesheets/vendor/select2-bootstrap.css @@ -0,0 +1,721 @@ +/*! + * Select2 Bootstrap Theme v0.1.0-beta.9 (https://select2.github.io/select2-bootstrap-theme) + * Copyright 2015-2016 Florian Kissling and contributors (https://github.com/select2/select2-bootstrap-theme/graphs/contributors) + * Licensed under MIT (https://github.com/select2/select2-bootstrap-theme/blob/master/LICENSE) + */ + +.select2-container--bootstrap { +  display: block; +  width: 100% !important +  /*------------------------------------*      #COMMON STYLES +  \*------------------------------------*/ +  /** +   * Search field in the Select2 dropdown. +   */ +  /** +   * No outline for all search fields - in the dropdown +   * and inline in multi Select2s. +   */ +  /** +   * Adjust Select2's choices hover and selected styles to match +   * Bootstrap 3's default dropdown styles. +   * +   * @see http://getbootstrap.com/components/#dropdowns +   */ +  /** +   * Clear the selection. +   */ +  /** +   * Address disabled Select2 styles. +   * +   * @see https://select2.github.io/examples.html#disabled +   * @see http://getbootstrap.com/css/#forms-control-disabled +   */ +  /*------------------------------------*      #DROPDOWN +  \*------------------------------------*/ +  /** +   * Dropdown border color and box-shadow. +   */ +  /** +   * Limit the dropdown height. +   */ +  /*------------------------------------*      #SINGLE SELECT2 +  \*------------------------------------*/ +  /*------------------------------------*    #MULTIPLE SELECT2 +  \*------------------------------------*/ +  /** +   * Address Bootstrap control sizing classes +   * +   * 1. Reset Bootstrap defaults. +   * 2. Adjust the dropdown arrow button icon position. +   * +   * @see http://getbootstrap.com/css/#forms-control-sizes +   */ +  /* 1 */ +  /*------------------------------------*    #RTL SUPPORT +  \*------------------------------------*/ +} + +.select2-container--bootstrap .select2-selection { +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +  background-color: #fff; +  border: 1px solid #ccc; +  border-radius: 4px; +  color: #555555; +  font-size: 14px; +  outline: 0; +} + +.select2-container--bootstrap .select2-selection.form-control { +  border-radius: 4px; +} + +.select2-container--bootstrap .select2-search--dropdown .select2-search__field { +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +  background-color: #fff; +  border: 1px solid #ccc; +  border-radius: 4px; +  color: #555555; +  font-size: 14px; +} + +.select2-container--bootstrap .select2-search__field { +  outline: 0; +  /* Firefox 18- */ +  /** +     * Firefox 19+ +     * +     * @see http://stackoverflow.com/questions/24236240/color-for-styled-placeholder-text-is-muted-in-firefox +     */ +} + +.select2-container--bootstrap .select2-search__field::-webkit-input-placeholder { +  color: #999; +} + +.select2-container--bootstrap .select2-search__field:-moz-placeholder { +  color: #999; +} + +.select2-container--bootstrap .select2-search__field::-moz-placeholder { +  color: #999; +  opacity: 1; +} + +.select2-container--bootstrap .select2-search__field:-ms-input-placeholder { +  color: #999; +} + +.select2-container--bootstrap .select2-results__option { +  padding: 6px 12px; +  /** +     * Disabled results. +     * +     * @see https://select2.github.io/examples.html#disabled-results +     */ +  /** +     * Hover state. +     */ +  /** +     * Selected state. +     */ +} + +.select2-container--bootstrap .select2-results__option[role=group] { +  padding: 0; +} + +.select2-container--bootstrap .select2-results__option[aria-disabled=true] { +  color: #777777; +  cursor: not-allowed; +} + +.select2-container--bootstrap .select2-results__option[aria-selected=true] { +  background-color: #f5f5f5; +  color: #262626; +} + +.select2-container--bootstrap .select2-results__option--highlighted[aria-selected] { +  background-color: #337ab7; +  color: #fff; +} + +.select2-container--bootstrap .select2-results__option .select2-results__option { +  padding: 6px 12px; +} + +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__group { +  padding-left: 0; +} + +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option { +  margin-left: -12px; +  padding-left: 24px; +} + +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +  margin-left: -24px; +  padding-left: 36px; +} + +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +  margin-left: -36px; +  padding-left: 48px; +} + +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +  margin-left: -48px; +  padding-left: 60px; +} + +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +  margin-left: -60px; +  padding-left: 72px; +} + +.select2-container--bootstrap .select2-results__group { +  color: #777777; +  display: block; +  padding: 6px 12px; +  font-size: 12px; +  line-height: 1.42857143; +  white-space: nowrap; +} + +.select2-container--bootstrap.select2-container--focus .select2-selection, .select2-container--bootstrap.select2-container--open .select2-selection { +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +  -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +  -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +  -webkit-transition: border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; +  transition: border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; +  transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +  transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; +  border-color: #66afe9; +} + +.select2-container--bootstrap.select2-container--open { +  /** +     * Make the dropdown arrow point up while the dropdown is visible. +     */ +  /** +     * Handle border radii of the container when the dropdown is showing. +     */ +} + +.select2-container--bootstrap.select2-container--open .select2-selection .select2-selection__arrow b { +  border-color: transparent transparent #999 transparent; +  border-width: 0 4px 4px 4px; +} + +.select2-container--bootstrap.select2-container--open.select2-container--below .select2-selection { +  border-bottom-right-radius: 0; +  border-bottom-left-radius: 0; +  border-bottom-color: transparent; +} + +.select2-container--bootstrap.select2-container--open.select2-container--above .select2-selection { +  border-top-right-radius: 0; +  border-top-left-radius: 0; +  border-top-color: transparent; +} + +.select2-container--bootstrap .select2-selection__clear { +  color: #999; +  cursor: pointer; +  float: right; +  font-weight: bold; +  margin-right: 10px; +} + +.select2-container--bootstrap .select2-selection__clear:hover { +  color: #333; +} + +.select2-container--bootstrap.select2-container--disabled .select2-selection { +  border-color: #ccc; +  -webkit-box-shadow: none; +  box-shadow: none; +} + +.select2-container--bootstrap.select2-container--disabled .select2-selection, +.select2-container--bootstrap.select2-container--disabled .select2-search__field { +  cursor: not-allowed; +} + +.select2-container--bootstrap.select2-container--disabled .select2-selection, +.select2-container--bootstrap.select2-container--disabled .select2-selection--multiple .select2-selection__choice { +  background-color: #eeeeee; +} + +.select2-container--bootstrap.select2-container--disabled .select2-selection__clear, +.select2-container--bootstrap.select2-container--disabled .select2-selection--multiple .select2-selection__choice__remove { +  display: none; +} + +.select2-container--bootstrap .select2-dropdown { +  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +  border-color: #66afe9; +  overflow-x: hidden; +  margin-top: -1px; +} + +.select2-container--bootstrap .select2-dropdown--above { +  -webkit-box-shadow: 0px -6px 12px rgba(0, 0, 0, 0.175); +  box-shadow: 0px -6px 12px rgba(0, 0, 0, 0.175); +  margin-top: 1px; +} + +.select2-container--bootstrap .select2-results > .select2-results__options { +  max-height: 200px; +  overflow-y: auto; +} + +.select2-container--bootstrap .select2-selection--single { +  height: 34px; +  line-height: 1.42857143; +  padding: 6px 24px 6px 12px; +  /** +     * Adjust the single Select2's dropdown arrow button appearance. +     */ +} + +.select2-container--bootstrap .select2-selection--single .select2-selection__arrow { +  position: absolute; +  bottom: 0; +  right: 12px; +  top: 0; +  width: 4px; +} + +.select2-container--bootstrap .select2-selection--single .select2-selection__arrow b { +  border-color: #999 transparent transparent transparent; +  border-style: solid; +  border-width: 4px 4px 0 4px; +  height: 0; +  left: 0; +  margin-left: -4px; +  margin-top: -2px; +  position: absolute; +  top: 50%; +  width: 0; +} + +.select2-container--bootstrap .select2-selection--single .select2-selection__rendered { +  color: #555555; +  padding: 0; +} + +.select2-container--bootstrap .select2-selection--single .select2-selection__placeholder { +  color: #999; +} + +.select2-container--bootstrap .select2-selection--multiple { +  min-height: 34px; +  padding: 0; +  height: auto; +  /** +     * Make Multi Select2's choices match Bootstrap 3's default button styles. +     */ +  /** +     * Minus 2px borders. +     */ +  /** +     * Clear the selection. +     */ +} + +.select2-container--bootstrap .select2-selection--multiple .select2-selection__rendered { +  -webkit-box-sizing: border-box; +     -moz-box-sizing: border-box; +          box-sizing: border-box; +  display: block; +  line-height: 1.42857143; +  list-style: none; +  margin: 0; +  overflow: hidden; +  padding: 0; +  width: 100%; +  text-overflow: ellipsis; +  white-space: nowrap; +} + +.select2-container--bootstrap .select2-selection--multiple .select2-selection__placeholder { +  color: #999; +  float: left; +  margin-top: 5px; +} + +.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice { +  color: #555555; +  background: #fff; +  border: 1px solid #ccc; +  border-radius: 4px; +  cursor: default; +  float: left; +  margin: 5px 0 0 6px; +  padding: 0 6px; +} + +.select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field { +  background: transparent; +  padding: 0 12px; +  height: 32px; +  line-height: 1.42857143; +  margin-top: 0; +  min-width: 5em; +} + +.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice__remove { +  color: #999; +  cursor: pointer; +  display: inline-block; +  font-weight: bold; +  margin-right: 3px; +} + +.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice__remove:hover { +  color: #333; +} + +.select2-container--bootstrap .select2-selection--multiple .select2-selection__clear { +  margin-top: 6px; +} + +.select2-container--bootstrap .select2-selection--single.input-sm, +.input-group-sm .select2-container--bootstrap .select2-selection--single, +.form-group-sm .select2-container--bootstrap .select2-selection--single { +  border-radius: 3px; +  font-size: 12px; +  height: 30px; +  line-height: 1.5; +  padding: 5px 22px 5px 10px; +  /* 2 */ +} + +.select2-container--bootstrap .select2-selection--single.input-sm .select2-selection__arrow b, +.input-group-sm .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b, +.form-group-sm .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b { +  margin-left: -5px; +} + +.select2-container--bootstrap .select2-selection--multiple.input-sm, +.input-group-sm .select2-container--bootstrap .select2-selection--multiple, +.form-group-sm .select2-container--bootstrap .select2-selection--multiple { +  min-height: 30px; +  border-radius: 3px; +} + +.select2-container--bootstrap .select2-selection--multiple.input-sm .select2-selection__choice, +.input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice, +.form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice { +  font-size: 12px; +  line-height: 1.5; +  margin: 4px 0 0 5px; +  padding: 0 5px; +} + +.select2-container--bootstrap .select2-selection--multiple.input-sm .select2-search--inline .select2-search__field, +.input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field, +.form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field { +  padding: 0 10px; +  font-size: 12px; +  height: 28px; +  line-height: 1.5; +} + +.select2-container--bootstrap .select2-selection--multiple.input-sm .select2-selection__clear, +.input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear, +.form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear { +  margin-top: 5px; +} + +.select2-container--bootstrap .select2-selection--single.input-lg, +.input-group-lg .select2-container--bootstrap .select2-selection--single, +.form-group-lg .select2-container--bootstrap .select2-selection--single { +  border-radius: 6px; +  font-size: 18px; +  height: 46px; +  line-height: 1.3333333; +  padding: 10px 31px 10px 16px; +  /* 1 */ +} + +.select2-container--bootstrap .select2-selection--single.input-lg .select2-selection__arrow, +.input-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow, +.form-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow { +  width: 5px; +} + +.select2-container--bootstrap .select2-selection--single.input-lg .select2-selection__arrow b, +.input-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b, +.form-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b { +  border-width: 5px 5px 0 5px; +  margin-left: -5px; +  margin-left: -10px; +  margin-top: -2.5px; +} + +.select2-container--bootstrap .select2-selection--multiple.input-lg, +.input-group-lg .select2-container--bootstrap .select2-selection--multiple, +.form-group-lg .select2-container--bootstrap .select2-selection--multiple { +  min-height: 46px; +  border-radius: 6px; +} + +.select2-container--bootstrap .select2-selection--multiple.input-lg .select2-selection__choice, +.input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice, +.form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice { +  font-size: 18px; +  line-height: 1.3333333; +  border-radius: 4px; +  margin: 9px 0 0 8px; +  padding: 0 10px; +} + +.select2-container--bootstrap .select2-selection--multiple.input-lg .select2-search--inline .select2-search__field, +.input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field, +.form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field { +  padding: 0 16px; +  font-size: 18px; +  height: 44px; +  line-height: 1.3333333; +} + +.select2-container--bootstrap .select2-selection--multiple.input-lg .select2-selection__clear, +.input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear, +.form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear { +  margin-top: 10px; +} + +.select2-container--bootstrap .select2-selection.input-lg.select2-container--open .select2-selection--single { +  /** +     * Make the dropdown arrow point up while the dropdown is visible. +     */ +} + +.select2-container--bootstrap .select2-selection.input-lg.select2-container--open .select2-selection--single .select2-selection__arrow b { +  border-color: transparent transparent #999 transparent; +  border-width: 0 5px 5px 5px; +} + +.input-group-lg .select2-container--bootstrap .select2-selection.select2-container--open .select2-selection--single { +  /** +     * Make the dropdown arrow point up while the dropdown is visible. +     */ +} + +.input-group-lg .select2-container--bootstrap .select2-selection.select2-container--open .select2-selection--single .select2-selection__arrow b { +  border-color: transparent transparent #999 transparent; +  border-width: 0 5px 5px 5px; +} + +.select2-container--bootstrap[dir="rtl"] { +  /** +     * Single Select2 +     * +     * 1. Makes sure that .select2-selection__placeholder is positioned +     *    correctly. +     */ +  /** +     * Multiple Select2 +     */ +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--single { +  padding-left: 24px; +  padding-right: 12px; +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__rendered { +  padding-right: 0; +  padding-left: 0; +  text-align: right; +  /* 1 */ +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__clear { +  float: left; +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__arrow { +  left: 12px; +  right: auto; +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__arrow b { +  margin-left: 0; +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice, +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder { +  float: right; +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice { +  margin-left: 0; +  margin-right: 6px; +} + +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { +  margin-left: 2px; +  margin-right: auto; +} + +/*------------------------------------*  #ADDITIONAL GOODIES +\*------------------------------------*/ +/** + * Address Bootstrap's validation states + * + * If a Select2 widget parent has one of Bootstrap's validation state modifier + * classes, adjust Select2's border colors and focus states accordingly. + * You may apply said classes to the Select2 dropdown (body > .select2-container) + * via JavaScript match Bootstraps' to make its styles match. + * + * @see http://getbootstrap.com/css/#forms-control-validation + */ +.has-warning .select2-dropdown, +.has-warning .select2-selection { +  border-color: #8a6d3b; +} + +.has-warning .select2-container--focus .select2-selection, +.has-warning .select2-container--open .select2-selection { +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +  border-color: #66512c; +} + +.has-warning.select2-drop-active { +  border-color: #66512c; +} + +.has-warning.select2-drop-active.select2-drop.select2-drop-above { +  border-top-color: #66512c; +} + +.has-error .select2-dropdown, +.has-error .select2-selection { +  border-color: #a94442; +} + +.has-error .select2-container--focus .select2-selection, +.has-error .select2-container--open .select2-selection { +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +  border-color: #843534; +} + +.has-error.select2-drop-active { +  border-color: #843534; +} + +.has-error.select2-drop-active.select2-drop.select2-drop-above { +  border-top-color: #843534; +} + +.has-success .select2-dropdown, +.has-success .select2-selection { +  border-color: #3c763d; +} + +.has-success .select2-container--focus .select2-selection, +.has-success .select2-container--open .select2-selection { +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +  border-color: #2b542c; +} + +.has-success.select2-drop-active { +  border-color: #2b542c; +} + +.has-success.select2-drop-active.select2-drop.select2-drop-above { +  border-top-color: #2b542c; +} + +/** + * Select2 widgets in Bootstrap Input Groups + * + * When Select2 widgets are combined with other elements using Bootstraps + * "Input Group" component, we don't want specific edges of the Select2 + * container to have a border-radius. + * + * Use .select2-bootstrap-prepend and .select2-bootstrap-append on + * a Bootstrap 3 .input-group to let the contained Select2 widget know which + * edges should not be rounded as they are directly followed by another element. + * + * @see http://getbootstrap.com/components/#input-groups + */ +/** + * Mimick Bootstraps .input-group .form-control styles. + * + * @see https://github.com/twbs/bootstrap/blob/master/less/input-groups.less + */ +.input-group .select2-container--bootstrap { +  display: table; +  table-layout: fixed; +  position: relative; +  z-index: 2; +  float: left; +  width: 100%; +  margin-bottom: 0; +  /** +   * Adjust z-index like Bootstrap does to show the focus-box-shadow +   * above appended buttons in .input-group and .form-group. +   */ +} + +.input-group .select2-container--bootstrap.select2-container--open, .input-group .select2-container--bootstrap.select2-container--focus { +  z-index: 3; +} + +.input-group.select2-bootstrap-prepend .select2-container--bootstrap .select2-selection { +  border-bottom-left-radius: 0; +  border-top-left-radius: 0; +} + +.input-group.select2-bootstrap-append .select2-container--bootstrap .select2-selection { +  border-bottom-right-radius: 0; +  border-top-right-radius: 0; +} + +/** + * Adjust alignment of Bootstrap buttons in Bootstrap Input Groups to address + * Multi Select2's height which - depending on how many elements have been selected - + * may grow taller than its initial size. + * + * @see http://getbootstrap.com/components/#input-groups + */ +.select2-bootstrap-append .select2-container--bootstrap, +.select2-bootstrap-append .input-group-btn, +.select2-bootstrap-append .input-group-btn .btn, +.select2-bootstrap-prepend .select2-container--bootstrap, +.select2-bootstrap-prepend .input-group-btn, +.select2-bootstrap-prepend .input-group-btn .btn { +  vertical-align: top; +} + +/** + * Temporary fix for https://github.com/select2/select2-bootstrap-theme/issues/9 + * + * Provides `!important` for certain properties of the class applied to the + * original `<select>` element to hide it. + * + * @see https://github.com/select2/select2/pull/3301 + * @see https://github.com/fk/select2/commit/31830c7b32cb3d8e1b12d5b434dee40a6e753ada + */ +.form-control.select2-hidden-accessible { +  position: absolute !important; +  width: 1px !important; +} + +/** + * Display override for inline forms + */ +.form-inline .select2-container--bootstrap { +  display: inline-block; +} diff --git a/app/assets/stylesheets/vendor/select2.css b/app/assets/stylesheets/vendor/select2.css new file mode 100644 index 000000000..447b2b86c --- /dev/null +++ b/app/assets/stylesheets/vendor/select2.css @@ -0,0 +1,484 @@ +.select2-container { +  box-sizing: border-box; +  display: inline-block; +  margin: 0; +  position: relative; +  vertical-align: middle; } +  .select2-container .select2-selection--single { +    box-sizing: border-box; +    cursor: pointer; +    display: block; +    height: 28px; +    user-select: none; +    -webkit-user-select: none; } +    .select2-container .select2-selection--single .select2-selection__rendered { +      display: block; +      padding-left: 8px; +      padding-right: 20px; +      overflow: hidden; +      text-overflow: ellipsis; +      white-space: nowrap; } +    .select2-container .select2-selection--single .select2-selection__clear { +      position: relative; } +  .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered { +    padding-right: 8px; +    padding-left: 20px; } +  .select2-container .select2-selection--multiple { +    box-sizing: border-box; +    cursor: pointer; +    display: block; +    min-height: 32px; +    user-select: none; +    -webkit-user-select: none; } +    .select2-container .select2-selection--multiple .select2-selection__rendered { +      display: inline-block; +      overflow: hidden; +      padding-left: 8px; +      text-overflow: ellipsis; +      white-space: nowrap; } +  .select2-container .select2-search--inline { +    float: left; } +    .select2-container .select2-search--inline .select2-search__field { +      box-sizing: border-box; +      border: none; +      font-size: 100%; +      margin-top: 5px; +      padding: 0; } +      .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button { +        -webkit-appearance: none; } + +.select2-dropdown { +  background-color: white; +  border: 1px solid #aaa; +  border-radius: 4px; +  box-sizing: border-box; +  display: block; +  position: absolute; +  left: -100000px; +  width: 100%; +  z-index: 1051; } + +.select2-results { +  display: block; } + +.select2-results__options { +  list-style: none; +  margin: 0; +  padding: 0; } + +.select2-results__option { +  padding: 6px; +  user-select: none; +  -webkit-user-select: none; } +  .select2-results__option[aria-selected] { +    cursor: pointer; } + +.select2-container--open .select2-dropdown { +  left: 0; } + +.select2-container--open .select2-dropdown--above { +  border-bottom: none; +  border-bottom-left-radius: 0; +  border-bottom-right-radius: 0; } + +.select2-container--open .select2-dropdown--below { +  border-top: none; +  border-top-left-radius: 0; +  border-top-right-radius: 0; } + +.select2-search--dropdown { +  display: block; +  padding: 4px; } +  .select2-search--dropdown .select2-search__field { +    padding: 4px; +    width: 100%; +    box-sizing: border-box; } +    .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button { +      -webkit-appearance: none; } +  .select2-search--dropdown.select2-search--hide { +    display: none; } + +.select2-close-mask { +  border: 0; +  margin: 0; +  padding: 0; +  display: block; +  position: fixed; +  left: 0; +  top: 0; +  min-height: 100%; +  min-width: 100%; +  height: auto; +  width: auto; +  opacity: 0; +  z-index: 99; +  background-color: #fff; +  filter: alpha(opacity=0); } + +.select2-hidden-accessible { +  border: 0 !important; +  clip: rect(0 0 0 0) !important; +  height: 1px !important; +  margin: -1px !important; +  overflow: hidden !important; +  padding: 0 !important; +  position: absolute !important; +  width: 1px !important; } + +.select2-container--default .select2-selection--single { +  background-color: #fff; +  border: 1px solid #aaa; +  border-radius: 4px; } +  .select2-container--default .select2-selection--single .select2-selection__rendered { +    color: #444; +    line-height: 28px; } +  .select2-container--default .select2-selection--single .select2-selection__clear { +    cursor: pointer; +    float: right; +    font-weight: bold; } +  .select2-container--default .select2-selection--single .select2-selection__placeholder { +    color: #999; } +  .select2-container--default .select2-selection--single .select2-selection__arrow { +    height: 26px; +    position: absolute; +    top: 1px; +    right: 1px; +    width: 20px; } +    .select2-container--default .select2-selection--single .select2-selection__arrow b { +      border-color: #888 transparent transparent transparent; +      border-style: solid; +      border-width: 5px 4px 0 4px; +      height: 0; +      left: 50%; +      margin-left: -4px; +      margin-top: -2px; +      position: absolute; +      top: 50%; +      width: 0; } + +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear { +  float: left; } + +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow { +  left: 1px; +  right: auto; } + +.select2-container--default.select2-container--disabled .select2-selection--single { +  background-color: #eee; +  cursor: default; } +  .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear { +    display: none; } + +.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { +  border-color: transparent transparent #888 transparent; +  border-width: 0 4px 5px 4px; } + +.select2-container--default .select2-selection--multiple { +  background-color: white; +  border: 1px solid #aaa; +  border-radius: 4px; +  cursor: text; } +  .select2-container--default .select2-selection--multiple .select2-selection__rendered { +    box-sizing: border-box; +    list-style: none; +    margin: 0; +    padding: 0 5px; +    width: 100%; } +    .select2-container--default .select2-selection--multiple .select2-selection__rendered li { +      list-style: none; } +  .select2-container--default .select2-selection--multiple .select2-selection__placeholder { +    color: #999; +    margin-top: 5px; +    float: left; } +  .select2-container--default .select2-selection--multiple .select2-selection__clear { +    cursor: pointer; +    float: right; +    font-weight: bold; +    margin-top: 5px; +    margin-right: 10px; } +  .select2-container--default .select2-selection--multiple .select2-selection__choice { +    background-color: #e4e4e4; +    border: 1px solid #aaa; +    border-radius: 4px; +    cursor: default; +    float: left; +    margin-right: 5px; +    margin-top: 5px; +    padding: 0 5px; } +  .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { +    color: #999; +    cursor: pointer; +    display: inline-block; +    font-weight: bold; +    margin-right: 2px; } +    .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { +      color: #333; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline { +  float: right; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice { +  margin-left: 5px; +  margin-right: auto; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { +  margin-left: 2px; +  margin-right: auto; } + +.select2-container--default.select2-container--focus .select2-selection--multiple { +  border: solid black 1px; +  outline: 0; } + +.select2-container--default.select2-container--disabled .select2-selection--multiple { +  background-color: #eee; +  cursor: default; } + +.select2-container--default.select2-container--disabled .select2-selection__choice__remove { +  display: none; } + +.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple { +  border-top-left-radius: 0; +  border-top-right-radius: 0; } + +.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple { +  border-bottom-left-radius: 0; +  border-bottom-right-radius: 0; } + +.select2-container--default .select2-search--dropdown .select2-search__field { +  border: 1px solid #aaa; } + +.select2-container--default .select2-search--inline .select2-search__field { +  background: transparent; +  border: none; +  outline: 0; +  box-shadow: none; +  -webkit-appearance: textfield; } + +.select2-container--default .select2-results > .select2-results__options { +  max-height: 200px; +  overflow-y: auto; } + +.select2-container--default .select2-results__option[role=group] { +  padding: 0; } + +.select2-container--default .select2-results__option[aria-disabled=true] { +  color: #999; } + +.select2-container--default .select2-results__option[aria-selected=true] { +  background-color: #ddd; } + +.select2-container--default .select2-results__option .select2-results__option { +  padding-left: 1em; } +  .select2-container--default .select2-results__option .select2-results__option .select2-results__group { +    padding-left: 0; } +  .select2-container--default .select2-results__option .select2-results__option .select2-results__option { +    margin-left: -1em; +    padding-left: 2em; } +    .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +      margin-left: -2em; +      padding-left: 3em; } +      .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +        margin-left: -3em; +        padding-left: 4em; } +        .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +          margin-left: -4em; +          padding-left: 5em; } +          .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { +            margin-left: -5em; +            padding-left: 6em; } + +.select2-container--default .select2-results__option--highlighted[aria-selected] { +  background-color: #5897fb; +  color: white; } + +.select2-container--default .select2-results__group { +  cursor: default; +  display: block; +  padding: 6px; } + +.select2-container--classic .select2-selection--single { +  background-color: #f7f7f7; +  border: 1px solid #aaa; +  border-radius: 4px; +  outline: 0; +  background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%); +  background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%); +  background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%); +  background-repeat: repeat-x; +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } +  .select2-container--classic .select2-selection--single:focus { +    border: 1px solid #5897fb; } +  .select2-container--classic .select2-selection--single .select2-selection__rendered { +    color: #444; +    line-height: 28px; } +  .select2-container--classic .select2-selection--single .select2-selection__clear { +    cursor: pointer; +    float: right; +    font-weight: bold; +    margin-right: 10px; } +  .select2-container--classic .select2-selection--single .select2-selection__placeholder { +    color: #999; } +  .select2-container--classic .select2-selection--single .select2-selection__arrow { +    background-color: #ddd; +    border: none; +    border-left: 1px solid #aaa; +    border-top-right-radius: 4px; +    border-bottom-right-radius: 4px; +    height: 26px; +    position: absolute; +    top: 1px; +    right: 1px; +    width: 20px; +    background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%); +    background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%); +    background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%); +    background-repeat: repeat-x; +    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); } +    .select2-container--classic .select2-selection--single .select2-selection__arrow b { +      border-color: #888 transparent transparent transparent; +      border-style: solid; +      border-width: 5px 4px 0 4px; +      height: 0; +      left: 50%; +      margin-left: -4px; +      margin-top: -2px; +      position: absolute; +      top: 50%; +      width: 0; } + +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear { +  float: left; } + +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow { +  border: none; +  border-right: 1px solid #aaa; +  border-radius: 0; +  border-top-left-radius: 4px; +  border-bottom-left-radius: 4px; +  left: 1px; +  right: auto; } + +.select2-container--classic.select2-container--open .select2-selection--single { +  border: 1px solid #5897fb; } +  .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow { +    background: transparent; +    border: none; } +    .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b { +      border-color: transparent transparent #888 transparent; +      border-width: 0 4px 5px 4px; } + +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single { +  border-top: none; +  border-top-left-radius: 0; +  border-top-right-radius: 0; +  background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%); +  background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%); +  background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%); +  background-repeat: repeat-x; +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } + +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single { +  border-bottom: none; +  border-bottom-left-radius: 0; +  border-bottom-right-radius: 0; +  background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%); +  background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%); +  background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%); +  background-repeat: repeat-x; +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); } + +.select2-container--classic .select2-selection--multiple { +  background-color: white; +  border: 1px solid #aaa; +  border-radius: 4px; +  cursor: text; +  outline: 0; } +  .select2-container--classic .select2-selection--multiple:focus { +    border: 1px solid #5897fb; } +  .select2-container--classic .select2-selection--multiple .select2-selection__rendered { +    list-style: none; +    margin: 0; +    padding: 0 5px; } +  .select2-container--classic .select2-selection--multiple .select2-selection__clear { +    display: none; } +  .select2-container--classic .select2-selection--multiple .select2-selection__choice { +    background-color: #e4e4e4; +    border: 1px solid #aaa; +    border-radius: 4px; +    cursor: default; +    float: left; +    margin-right: 5px; +    margin-top: 5px; +    padding: 0 5px; } +  .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove { +    color: #888; +    cursor: pointer; +    display: inline-block; +    font-weight: bold; +    margin-right: 2px; } +    .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover { +      color: #555; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { +  float: right; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { +  margin-left: 5px; +  margin-right: auto; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { +  margin-left: 2px; +  margin-right: auto; } + +.select2-container--classic.select2-container--open .select2-selection--multiple { +  border: 1px solid #5897fb; } + +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple { +  border-top: none; +  border-top-left-radius: 0; +  border-top-right-radius: 0; } + +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple { +  border-bottom: none; +  border-bottom-left-radius: 0; +  border-bottom-right-radius: 0; } + +.select2-container--classic .select2-search--dropdown .select2-search__field { +  border: 1px solid #aaa; +  outline: 0; } + +.select2-container--classic .select2-search--inline .select2-search__field { +  outline: 0; +  box-shadow: none; } + +.select2-container--classic .select2-dropdown { +  background-color: white; +  border: 1px solid transparent; } + +.select2-container--classic .select2-dropdown--above { +  border-bottom: none; } + +.select2-container--classic .select2-dropdown--below { +  border-top: none; } + +.select2-container--classic .select2-results > .select2-results__options { +  max-height: 200px; +  overflow-y: auto; } + +.select2-container--classic .select2-results__option[role=group] { +  padding: 0; } + +.select2-container--classic .select2-results__option[aria-disabled=true] { +  color: grey; } + +.select2-container--classic .select2-results__option--highlighted[aria-selected] { +  background-color: #3875d7; +  color: white; } + +.select2-container--classic .select2-results__group { +  cursor: default; +  display: block; +  padding: 6px; } + +.select2-container--classic.select2-container--open .select2-dropdown { +  border-color: #5897fb; } diff --git a/app/controllers/referential_companies_controller.rb b/app/controllers/referential_companies_controller.rb index 104deba9f..a369488ba 100644 --- a/app/controllers/referential_companies_controller.rb +++ b/app/controllers/referential_companies_controller.rb @@ -27,7 +27,7 @@ class ReferentialCompaniesController < ChouetteController    end    def collection -    @q = referential.companies.search(params[:q]) +    @q = referential.workbench.companies.search(params[:q])      @companies ||= @q.result(:distinct => true).order(:name).paginate(:page => params[:page])    end diff --git a/app/controllers/referential_group_of_lines_controller.rb b/app/controllers/referential_group_of_lines_controller.rb index d40fd6090..4decef558 100644 --- a/app/controllers/referential_group_of_lines_controller.rb +++ b/app/controllers/referential_group_of_lines_controller.rb @@ -55,7 +55,7 @@ class ReferentialGroupOfLinesController < ChouetteController    end    def collection -    @q = referential.group_of_lines.search(params[:q]) +    @q = referential.workbench.group_of_lines.search(params[:q])      @group_of_lines ||= @q.result(:distinct => true).order(:name).paginate(:page => params[:page])    end diff --git a/app/controllers/referential_networks_controller.rb b/app/controllers/referential_networks_controller.rb index 2d3f4ad6b..00c680c65 100644 --- a/app/controllers/referential_networks_controller.rb +++ b/app/controllers/referential_networks_controller.rb @@ -35,7 +35,7 @@ class ReferentialNetworksController < ChouetteController    end    def collection -    @q = referential.networks.search(params[:q]) +    @q = referential.workbench.networks.search(params[:q])      @networks ||= @q.result(:distinct => true).order(:name).paginate(:page => params[:page])    end diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 3ae59f975..003c85d3a 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -57,11 +57,7 @@ class ReferentialsController < BreadcrumbController    end    def create_resource(referential) -    if referential.created_from -      referential.clone_association referential.created_from -    else -      referential.organisation = current_organisation -    end +    referential.organisation = current_organisation unless referential.created_from      super    end diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb index 8f6a1565a..1f55b1de8 100644 --- a/app/controllers/stop_areas_controller.rb +++ b/app/controllers/stop_areas_controller.rb @@ -26,6 +26,7 @@ class StopAreasController < BreadcrumbController    end    def add_children +    authorize stop_area      @stop_area = stop_area      @children = stop_area.children      build_breadcrumb :edit diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 86386772c..31443d1c7 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -39,20 +39,22 @@ class Organisation < ActiveRecord::Base      end    end +  def self.sync_update code, name, scope +    org = Organisation.find_or_initialize_by(code: code) +    if scope +      org.sso_attributes ||= {} +      org.sso_attributes[:functional_scope] = scope +    end +    org.name      = name +    org.synced_at = Time.now +    org.save +    org +  end +    def self.portail_sync      self.portail_api_request.each do |el| -      Organisation.find_or_create_by(code: el['code']).tap do |org| -        org.name = el['name'] -        if el['functional_scope'] -          org.sso_attributes ||= {} -          org.sso_attributes[:functional_scope] = el['functional_scope'].delete('\\"') -        end -        if org.changed? -          org.synced_at = Time.now -          org.save -          puts "✓ Organisation #{org.name} has been updated" unless Rails.env.test? -        end -      end +      org = self.sync_update el['code'], el['name'], el['functional_scope'] +      puts "✓ Organisation #{org.name} has been updated" unless Rails.env.test?      end    end  end diff --git a/app/models/referential.rb b/app/models/referential.rb index afadf5edd..8b8df3294 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -30,7 +30,7 @@ class Referential < ActiveRecord::Base    validates_presence_of :line_referential    belongs_to :created_from, class_name: 'Referential' -  has_many :lines, through: :line_referential +  has_many :associated_lines, through: :line_referential, source: :lines    has_many :companies, through: :line_referential    has_many :group_of_lines, through: :line_referential    has_many :networks, through: :line_referential @@ -43,6 +43,10 @@ class Referential < ActiveRecord::Base    has_many :stop_areas, through: :stop_area_referential    belongs_to :workbench +  def lines +    workbench ? workbench.lines : associated_lines +  end +    def slug_excluded_values      if ! slug.nil?        if slug.start_with? "pg_" @@ -128,13 +132,6 @@ class Referential < ActiveRecord::Base      })    end -  def clone_association from -    self.organisation          = from.organisation -    self.line_referential      = from.line_referential -    self.stop_area_referential = from.stop_area_referential -    self.workbench             = from.workbench -  end -    def self.available_srids      [        [ "RGF 93 Lambert 93 (2154)", 2154 ], @@ -171,7 +168,8 @@ class Referential < ActiveRecord::Base      projection_type || ""    end -  before_validation :assign_line_and_stop_area_referential, :on => :create, if: :workbench +  before_validation :assign_line_and_stop_area_referential, :on => :create, if: :workbench, unless: :created_from +  before_validation :clone_associations, :on => :create, if: :created_from    before_create :create_schema    after_create :create_referential_metadata, if: :workbench, unless: :created_from @@ -185,6 +183,13 @@ class Referential < ActiveRecord::Base      self.referential_metadatas.create    end +  def clone_associations +    self.organisation          = created_from.organisation +    self.line_referential      = created_from.line_referential +    self.stop_area_referential = created_from.stop_area_referential +    self.workbench             = created_from.workbench +  end +    def clone_referential_metadatas      self.created_from.referential_metadatas.each do |meta|        self.referential_metadatas << ReferentialMetadata.new_from(meta) diff --git a/app/models/user.rb b/app/models/user.rb index 8f0c32c54..5cfdf0605 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -29,16 +29,10 @@ class User < ActiveRecord::Base    after_destroy :check_destroy_organisation    def cas_extra_attributes=(extra_attributes) -    extra         = extra_attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} -    self.name     = extra[:full_name] -    self.email    = extra[:email] - -    self.organisation = Organisation.find_or_create_by(code: extra[:organisation_code]).tap do |org| -      org.name = extra[:organisation_name] -      org.sso_attributes ||= {} -      org.sso_attributes[:functional_scope] = extra[:functional_scope].delete('\\"') if extra[:functional_scope] -      org.synced_at = Time.now -    end +    extra             = extra_attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} +    self.name         = extra[:full_name] +    self.email        = extra[:email] +    self.organisation = Organisation.sync_update extra[:organisation_code], extra[:organisation_name], extra[:functional_scope]    end    def self.portail_api_request @@ -60,25 +54,14 @@ class User < ActiveRecord::Base    def self.portail_sync      self.portail_api_request.each do |el| -      User.find_or_create_by(username: el['username']).tap do |user| -        user.name         = "#{el['firstname']} #{el['lastname']}" -        user.email        = el['email'] -        user.locked_at    = el['locked_at'] - -        # Set organisation -        user.organisation = Organisation.find_or_create_by(code: el['organization_code']).tap do |org| -          org.name = el['organization_name'] -          org.sso_attributes ||= {} -          org.sso_attributes[:functional_scope] = el['functional_scope'].delete('\\"') if el['functional_scope'] -          org.synced_at = Time.now -        end - -        if user.changed? -          user.synced_at = Time.now -          user.save -          puts "✓ user #{user.username} has been updated" unless Rails.env.test? -        end -      end +      user              = User.find_or_initialize_by(username: el['username']) +      user.name         = "#{el['firstname']} #{el['lastname']}" +      user.email        = el['email'] +      user.locked_at    = el['locked_at'] +      user.organisation = Organisation.sync_update el['organization_code'], el['organization_name'], el['functional_scope'] +      user.synced_at    = Time.now +      user.save +      puts "✓ user #{user.username} has been updated" unless Rails.env.test?      end    end diff --git a/app/models/workbench.rb b/app/models/workbench.rb index 3525fe55e..a83fea70d 100644 --- a/app/models/workbench.rb +++ b/app/models/workbench.rb @@ -3,6 +3,12 @@ class Workbench < ActiveRecord::Base    belongs_to :line_referential    belongs_to :stop_area_referential +  has_many :lines, -> (workbench) { Stif::MyWorkbenchScopes.new(workbench).line_scope(self) }, through: :line_referential +  has_many :networks, through: :line_referential +  has_many :companies, through: :line_referential +  has_many :group_of_lines, through: :line_referential +  has_many :stop_areas, through: :stop_area_referential +    validates :name, presence: true    validates :organisation, presence: true diff --git a/app/views/referential_companies/_company.html.slim b/app/views/referential_companies/_company.html.slim index 06e13764e..e6090540d 100644 --- a/app/views/referential_companies/_company.html.slim +++ b/app/views/referential_companies/_company.html.slim @@ -2,10 +2,12 @@    .panel-heading      .panel-title.clearfix        span.pull-right -        = link_to edit_referential_company_path(@referential, company), class: 'btn btn-default btn-sm' do -          span.fa.fa-pencil -        = link_to referential_company_path(@referential, company), :method => :delete, :data => {:confirm => t('companies.actions.destroy_confirm')}, class: 'btn btn-danger btn-sm' do -          span.fa.fa-trash-o +        - if policy(company).update? +          = link_to edit_referential_company_path(@referential, company), class: 'btn btn-default btn-sm' do +            span.fa.fa-pencil +        - if policy(company).destroy? +          = link_to referential_company_path(@referential, company), :method => :delete, :data => {:confirm => t('companies.actions.destroy_confirm')}, class: 'btn btn-danger btn-sm' do +            span.fa.fa-trash-o        h5          = link_to [@referential, company], class: 'preview', title: "#{Chouette::Company.model_name.human.capitalize} #{company.name}" do            span.name @@ -13,4 +15,4 @@    .panel-body      p        = company.human_attribute_name('code') -      = company.code
\ No newline at end of file +      = company.code diff --git a/app/views/referential_companies/index.html.slim b/app/views/referential_companies/index.html.slim index b32f206ca..6af6d7fc8 100644 --- a/app/views/referential_companies/index.html.slim +++ b/app/views/referential_companies/index.html.slim @@ -19,6 +19,7 @@  - content_for :sidebar do    ul.actions -    li -      = link_to t('companies.actions.new'), new_referential_company_path(@referential), class: 'add' -  br
\ No newline at end of file +    - if policy(Chouette::Company).create? +      li +        = link_to t('companies.actions.new'), new_referential_company_path(@referential), class: 'add' +    br diff --git a/app/views/referential_companies/show.html.slim b/app/views/referential_companies/show.html.slim index a445dad61..a1a767bbd 100644 --- a/app/views/referential_companies/show.html.slim +++ b/app/views/referential_companies/show.html.slim @@ -44,12 +44,15 @@  - content_for :sidebar do    ul.actions -    li -      = link_to t('companies.actions.new'), new_referential_company_path(@referential), class: 'add' -    li -      = link_to t('companies.actions.edit'), edit_referential_company_path(@referential, @company), class: 'edit' -    li -      = link_to  t('companies.actions.destroy'), referential_company_path(@referential, @company), :method => :delete,  :data => {:confirm =>  t('companies.actions.destroy_confirm')}, class: 'remove' -    br - -  = creation_tag(@company)
\ No newline at end of file +    - if policy(Chouette::Company).create? +      li +        = link_to t('companies.actions.new'), new_referential_company_path(@referential), class: 'add' +    - if policy(@company).update? +      li +        = link_to t('companies.actions.edit'), edit_referential_company_path(@referential, @company), class: 'edit' +    - if policy(@company).destroy? +      li +        = link_to  t('companies.actions.destroy'), referential_company_path(@referential, @company), :method => :delete,  :data => {:confirm =>  t('companies.actions.destroy_confirm')}, class: 'remove' +      br + +  = creation_tag(@company) diff --git a/app/views/referential_lines/_line.html.slim b/app/views/referential_lines/_line.html.slim index 0dffb50aa..1c8b8eaa8 100644 --- a/app/views/referential_lines/_line.html.slim +++ b/app/views/referential_lines/_line.html.slim @@ -3,7 +3,7 @@      ul.ce-LineBlock-header-list        li          = check_box_tag "ids[]", line.id, false, class: "multiple_selection", style: "display: none;" -         +          - if line.number && line.number.length <= 3            span.label.label-default.line_number style="#{number_style(line)}"              = line.number @@ -13,11 +13,11 @@            h5.ce-LineBlock-header-title = truncate(line.name, length: 24)        li -        - if edit +        - if edit && policy(Chouette::Line).update?            = link_to edit_referential_line_path(@referential, line), class: 'btn btn-default btn-sm' do              span.fa.fa-pencil -        - if delete +        - if delete && policy(Chouette::Line).destroy?            = link_to referential_line_path(@referential, line), method: :delete, data: { confirm: t('lines.actions.destroy_confirm') }, class: 'btn btn-danger btn-sm' do              span.fa.fa-trash-o @@ -46,4 +46,4 @@          = line.human_attribute_name('group_of_line')          = link_to_if( line.group_of_lines.first, line.group_of_lines.first.name, referential_group_of_line_path(@referential, line.group_of_lines.first), :title => "#{line.human_attribute_name('group_of_line')} #{line.group_of_lines.first.name}")        - else -        = t('lines.form.several_group_of_lines', :count => line.group_of_lines.count)
\ No newline at end of file +        = t('lines.form.several_group_of_lines', :count => line.group_of_lines.count) diff --git a/app/views/referential_lines/index.html.slim b/app/views/referential_lines/index.html.slim index 4922cde85..5647bde0c 100644 --- a/app/views/referential_lines/index.html.slim +++ b/app/views/referential_lines/index.html.slim @@ -24,25 +24,27 @@  - content_for :sidebar do    ul.actions -    li -      = link_to t('lines.actions.new'), new_referential_line_path(@referential), class: 'add' +    - if policy(Chouette::Line).create? +      li +        = link_to t('lines.actions.new'), new_referential_line_path(@referential), class: 'add' -  #multiple_selection_menu -    h4> = t(".multi_selection") +  - if policy(Chouette::Line).destroy? +    #multiple_selection_menu +      h4> = t(".multi_selection") -    .disabled -      a.enable href="#" -        = t(".multi_selection_enable") +      .disabled +        a.enable href="#" +          = t(".multi_selection_enable") -    .enabled style="display: none;" -      a.disable href="#" -        = t(".multi_selection_disable") +      .enabled style="display: none;" +        a.disable href="#" +          = t(".multi_selection_disable") -      ul.actions -        = link_to t(".delete_selected"), referential_lines_path(@referential), "data-multiple-method" => "delete", :class => "remove", "confirmation-text" => t("lines.actions.destroy_selection_confirm") +        ul.actions +          = link_to t(".delete_selected"), referential_lines_path(@referential), "data-multiple-method" => "delete", :class => "remove", "confirmation-text" => t("lines.actions.destroy_selection_confirm")        a.select_all href="#"          = t(".select_all")        = " | "        a.deselect_all href="#" -        = t(".deselect_all")
\ No newline at end of file +        = t(".deselect_all") diff --git a/app/views/referential_lines/show.html.slim b/app/views/referential_lines/show.html.slim index ae8314a14..495f0bda7 100644 --- a/app/views/referential_lines/show.html.slim +++ b/app/views/referential_lines/show.html.slim @@ -121,15 +121,19 @@ h3.routes = t('.itineraries')  - content_for :sidebar do    ul.actions -    li -      = link_to t('lines.actions.new'), new_referential_line_path(@referential), class: 'add' -    li -      = link_to t('lines.actions.edit'), edit_referential_line_path(@referential, @line), class: 'edit' -    li -      = link_to t('lines.actions.destroy'), referential_line_path(@referential, @line), method: :delete, :data => {:confirm => t('lines.actions.destroy_confirm')}, class: 'remove' +    - if policy(Chouette::Line).create? +      li +        = link_to t('lines.actions.new'), new_referential_line_path(@referential), class: 'add' +    - if policy(@line).update? +      li +        = link_to t('lines.actions.edit'), edit_referential_line_path(@referential, @line), class: 'edit' +    - if policy(@line).destroy? +      li +        = link_to t('lines.actions.destroy'), referential_line_path(@referential, @line), method: :delete, :data => {:confirm => t('lines.actions.destroy_confirm')}, class: 'remove'      - if !@line.hub_restricted? || (@line.hub_restricted? && @line.routes.size < 2) -      li -        = link_to t('routes.actions.new'), new_referential_line_route_path(@referential, @line), class: 'add' +        / FIXME #825 +        li +        / = link_to t('routes.actions.new'), new_referential_line_route_path(@referential, @line), class: 'add' -  = creation_tag(@line)
\ No newline at end of file +  = creation_tag(@line) diff --git a/app/views/referential_networks/_network.html.slim b/app/views/referential_networks/_network.html.slim index dc1aa6b5c..f7c7b66eb 100644 --- a/app/views/referential_networks/_network.html.slim +++ b/app/views/referential_networks/_network.html.slim @@ -2,13 +2,14 @@    .panel-heading      .panel-title.clearfix        span.pull-right -        = link_to edit_referential_network_path(@referential, network), class: 'btn btn-default btn-sm' do -          span.fa.fa-pencil - -        = link_to referential_network_path(@referential, network), method: :delete, :data => { :confirm => t('networks.actions.destroy_confirm') }, class: 'btn btn-danger btn-sm' do -          span.fa.fa-trash-o +        - if policy(network).update? +          = link_to edit_referential_network_path(@referential, network), class: 'btn btn-default btn-sm' do +            span.fa.fa-pencil +        - if policy(network).destroy? +          = link_to referential_network_path(@referential, network), method: :delete, :data => { :confirm => t('networks.actions.destroy_confirm') }, class: 'btn btn-danger btn-sm' do +            span.fa.fa-trash-o        h5          = link_to [@referential, network], class: 'preview', title: "#{Chouette::Network.model_name.human.capitalize} #{network.name}" do            span.name -            = truncate(network.name, :length => 20)
\ No newline at end of file +            = truncate(network.name, :length => 20) diff --git a/app/views/referential_networks/index.html.slim b/app/views/referential_networks/index.html.slim index f3ead931e..e3e9f2a07 100644 --- a/app/views/referential_networks/index.html.slim +++ b/app/views/referential_networks/index.html.slim @@ -19,5 +19,6 @@  - content_for :sidebar do    ul.actions      li +    - if policy(Chouette::Network).create?        = link_to t('networks.actions.new'), new_referential_network_path(@referential), class: 'add' -    br
\ No newline at end of file +    br diff --git a/app/views/referential_networks/show.html.slim b/app/views/referential_networks/show.html.slim index 936f6a4a8..d579d311b 100644 --- a/app/views/referential_networks/show.html.slim +++ b/app/views/referential_networks/show.html.slim @@ -36,12 +36,15 @@  - content_for :sidebar do    ul.actions -    li -      = link_to t('networks.actions.new'), new_referential_network_path(@referential), class: 'add' -    li -      = link_to t('networks.actions.edit'), edit_referential_network_path(@referential, @network), class: 'edit' -    li -      = link_to t('networks.actions.destroy'), referential_network_path(@referential, @network), method: :delete, data: { :confirm => t('networks.actions.destroy_confirm')}, class: 'remove' -    br -   -  = creation_tag(@network)
\ No newline at end of file +    - if policy(Chouette::Network).create? +      li +        = link_to t('networks.actions.new'), new_referential_network_path(@referential), class: 'add' +    - if policy(@network).update? +      li +        = link_to t('networks.actions.edit'), edit_referential_network_path(@referential, @network), class: 'edit' +    - if policy(@network).destroy? +      li +        = link_to t('networks.actions.destroy'), referential_network_path(@referential, @network), method: :delete, data: { :confirm => t('networks.actions.destroy_confirm')}, class: 'remove' +      br + +  = creation_tag(@network) diff --git a/app/views/referential_stop_areas/_genealogical.html.slim b/app/views/referential_stop_areas/_genealogical.html.slim index a44884c92..162c24d9b 100644 --- a/app/views/referential_stop_areas/_genealogical.html.slim +++ b/app/views/referential_stop_areas/_genealogical.html.slim @@ -5,7 +5,7 @@ h3 = genealogical_title      = link_to([@referential, @stop_area.parent], :title => t("area_types.label.#{@stop_area.parent.stop_area_type}") + "#{@stop_area.parent.name}") do        = image_tag "map/" + @stop_area.parent.stop_area_type + ".png"        = @stop_area.parent.name -     +    .link      = image_tag "icons/link.png" @@ -18,7 +18,7 @@ h3 = genealogical_title    .link = image_tag "icons/link.png"  - else -  .no_parent =   +  .no_parent  .target    = image_tag "map/" + @stop_area.stop_area_type + ".png" @@ -51,4 +51,4 @@ h3 = genealogical_title            span = route.line.number          = link_to([@referential, route.line , route]) do -          span = route.name
\ No newline at end of file +          span = route.name diff --git a/app/views/referential_stop_areas/_stop_area.html.slim b/app/views/referential_stop_areas/_stop_area.html.slim index f94bd319e..bb9ed7ce3 100644 --- a/app/views/referential_stop_areas/_stop_area.html.slim +++ b/app/views/referential_stop_areas/_stop_area.html.slim @@ -2,11 +2,13 @@    .panel-heading      .panel-title.clearfix        span.pull-right -        = link_to edit_referential_stop_area_path(@referential, stop_area), class: 'btn btn-default btn-sm' do -          span.fa.fa-pencil +        - if policy(stop_area).update? +          = link_to edit_referential_stop_area_path(@referential, stop_area), class: 'btn btn-default btn-sm' do +            span.fa.fa-pencil -        = link_to referential_stop_area_path(@referential, stop_area), method: :delete, :data => {:confirm => t('stop_areas.actions.destroy_confirm')}, class: 'btn btn-danger btn-sm' do -          span.fa.fa-trash-o +        - if policy(stop_area).destroy? +          = link_to referential_stop_area_path(@referential, stop_area), method: :delete, :data => {:confirm => t('stop_areas.actions.destroy_confirm')}, class: 'btn btn-danger btn-sm' do +            span.fa.fa-trash-o        h5          = link_to([@referential, stop_area], class: "preview", :title => t("area_types.label.#{stop_area.stop_area_type}") + " #{stop_area.name}") do @@ -34,11 +36,11 @@          - else            - stop_area.routing_lines.each do |line|              span.label.label-default.line = line.number -       +        - else          = "#{t('.lines')} : "          - if stop_area.lines.blank?            = t(".no_object")          - else            - stop_area.lines.each do |line| -            span.label.label-default.line = line.number || truncate( line.name, length: 4 )
\ No newline at end of file +            span.label.label-default.line = line.number || truncate( line.name, length: 4 ) diff --git a/app/views/referential_stop_areas/index.html.slim b/app/views/referential_stop_areas/index.html.slim index 85bd15d20..1b634b09c 100644 --- a/app/views/referential_stop_areas/index.html.slim +++ b/app/views/referential_stop_areas/index.html.slim @@ -26,7 +26,8 @@  - content_for :sidebar do    ul.actions +    - if policy(Chouette::StopArea).create? +      li +        = link_to t('stop_areas.actions.new'), new_referential_stop_area_path(@referential), class: 'add'      li -      = link_to t('stop_areas.actions.new'), new_referential_stop_area_path(@referential), class: 'add' -    li -      / = link_to t('stop_areas.actions.default_geometry'), default_geometry_referential_stop_areas_path(@referential), :method => :put, :class => "calculator"
\ No newline at end of file +      / = link_to t('stop_areas.actions.default_geometry'), default_geometry_referential_stop_areas_path(@referential), :method => :put, :class => "calculator" diff --git a/app/views/referential_stop_areas/show.html.slim b/app/views/referential_stop_areas/show.html.slim index 76414edf0..2278c4812 100644 --- a/app/views/referential_stop_areas/show.html.slim +++ b/app/views/referential_stop_areas/show.html.slim @@ -109,7 +109,7 @@  p.after_map  .genealogical.clearfix -  = render "stop_areas/genealogical" +  = render "referential_stop_areas/genealogical"  - if manage_access_points    div @@ -122,22 +122,25 @@ p.after_map      tr        td          ul.actions -          li -            = link_to t('stop_areas.actions.new'), new_referential_stop_area_path(@referential), class: 'add' -      	  li -            = link_to t('stop_areas.actions.edit'), edit_referential_stop_area_path(@referential, @stop_area), class: 'edit' -      	  li -            = link_to t('stop_areas.actions.destroy'), referential_stop_area_path(@referential, @stop_area), method: :delete, data: { :confirm => t('stop_areas.actions.destroy_confirm') }, class: 'remove' +          - if policy(Chouette::StopArea).new? +            li +              = link_to t('stop_areas.actions.new'), new_referential_stop_area_path(@referential), class: 'add' +          - if policy(@stop_area).update? +        	  li +              = link_to t('stop_areas.actions.edit'), edit_referential_stop_area_path(@referential, @stop_area), class: 'edit' +          - if policy(@stop_area).destroy? +        	  li +              = link_to t('stop_areas.actions.destroy'), referential_stop_area_path(@referential, @stop_area), method: :delete, data: { :confirm => t('stop_areas.actions.destroy_confirm') }, class: 'remove'      - if manage_itl -      tr -        td -          h4 = t('.itl_managment') -          ul.actions -            li -              = link_to t('stop_areas.actions.add_routing_lines'), add_routing_lines_referential_stop_area_path(@referential, @stop_area), class: 'add_routing_lines' -            li -              = link_to t('stop_areas.actions.add_routing_stops'), add_routing_stops_referential_stop_area_path(@referential, @stop_area), class: 'add_routing_stops' +      / tr +      /   td +      /     h4 = t('.itl_managment') +      /     ul.actions +      /       li +      /         = link_to t('stop_areas.actions.add_routing_lines'), add_routing_lines_referential_stop_area_path(@referential, @stop_area), class: 'add_routing_lines' +      /       li +      /         = link_to t('stop_areas.actions.add_routing_stops'), add_routing_stops_referential_stop_area_path(@referential, @stop_area), class: 'add_routing_stops'      - else        tr @@ -145,17 +148,17 @@ p.after_map            h4 = t('.stop_managment')            ul.actions              li -              = link_to t('stop_areas.actions.select_parent'), select_parent_referential_stop_area_path(@referential, @stop_area), class: 'parent' +              / = link_to t('stop_areas.actions.select_parent'), select_parent_referential_stop_area_path(@referential, @stop_area), class: 'parent'              - if @stop_area.parent == nil                li -                = link_to t('stop_areas.actions.clone_as_parent'), new_referential_stop_area_stop_area_copy_path(@referential, @stop_area, hierarchy: 'parent'), class: 'clone' +                / = link_to t('stop_areas.actions.clone_as_parent'), new_referential_stop_area_stop_area_copy_path(@referential, @stop_area, hierarchy: 'parent'), class: 'clone'              - if manage_children                li -                = link_to t('stop_areas.actions.add_children'), add_children_referential_stop_area_path(@referential, @stop_area), class: 'children' +                / = link_to t('stop_areas.actions.add_children'), add_children_referential_stop_area_path(@referential, @stop_area), class: 'children'                li -                = link_to t('stop_areas.actions.clone_as_child'), new_referential_stop_area_stop_area_copy_path(@referential, @stop_area, hierarchy: 'child'), class: 'clone' +                / = link_to t('stop_areas.actions.clone_as_child'), new_referential_stop_area_stop_area_copy_path(@referential, @stop_area, hierarchy: 'child'), class: 'clone'        - if manage_access_points          tr @@ -163,9 +166,9 @@ p.after_map              h4 = t(".access_managment")              ul.actions                li -                = link_to t('access_points.actions.new'), new_referential_stop_area_access_point_path(@referential,@stop_area), class: 'add' +                / = link_to t('access_points.actions.new'), new_referential_stop_area_access_point_path(@referential,@stop_area), class: 'add'                li -                = link_to t('stop_areas.actions.manage_access_links'), access_links_referential_stop_area_path(@referential,@stop_area), class: 'access_link' +                / = link_to t('stop_areas.actions.manage_access_links'), access_links_referential_stop_area_path(@referential,@stop_area), class: 'access_link'    br diff --git a/app/views/referentials/_counts.html.slim b/app/views/referentials/_counts.html.slim index fc3ad251e..9754ebc99 100644 --- a/app/views/referentials/_counts.html.slim +++ b/app/views/referentials/_counts.html.slim @@ -67,4 +67,4 @@      li.list-group-item        span.badge = @referential.access_points.size        = image_tag "map/access_in_out.png" -      = Referential.human_attribute_name("access_points")
\ No newline at end of file +      = Referential.human_attribute_name("access_points") diff --git a/app/views/referentials/index.html.slim b/app/views/referentials/index.html.slim index e37f157de..c03943ac7 100644 --- a/app/views/referentials/index.html.slim +++ b/app/views/referentials/index.html.slim @@ -14,12 +14,3 @@      / FIXME #823      - if false        li = link_to t('referentials.actions.new'), new_referential_path, class: 'add' - -  ul -    / FIXME #820 -    / Don't blame me. See #820 -    li = link_to 'Données Reflex', stop_area_referential_path(1) - -    / FIXME #824 -    / Don't blame me. See #824 -    li = link_to 'Données CodifLigne', line_referential_path(1) diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim index 044a745ec..1fc8f108f 100644 --- a/app/views/referentials/show.html.slim +++ b/app/views/referentials/show.html.slim @@ -66,6 +66,7 @@ h2      li = link_to t('referentials.actions.destroy'), referential_path(@referential), method: :delete, data: {:confirm => t('referentials.actions.destroy_confirm')}, class: "remove"      li = link_to t('api_keys.actions.new'), new_referential_api_key_path(@referential), class: 'add' +    li = link_to t('referentials.actions.clone'), new_referential_path(from: @referential.id), class: 'add'      br    h4 = t('.clean_up') diff --git a/app/views/routes/_form.html.slim b/app/views/routes/_form.html.slim index d23188fa1..32d738de1 100644 --- a/app/views/routes/_form.html.slim +++ b/app/views/routes/_form.html.slim @@ -1,3 +1,56 @@ += simple_form_for [@referential, @line, @route] do |f| +  .row +    .col-lg-4.col-md-4.col-sm-4.col-xs-4 +      = f.input :name +    .col-lg-4.col-md-4.col-sm-4.col-xs-4 +      = f.input :published_name +    .col-lg-4.col-md-4.col-sm-4.col-xs-4 +      = f.input :number + +  .row +    .col-lg-6.col-md-6.col-sm-5.col-xs-5 +      = f.input :comment +    .col-lg-6.col-md-6.col-sm-7.col-xs-7 +      = f.input :opposite_route, collection: @line.routes.select { |r| r.id != @route.id } + +  .row +    .col-lg-4.col-md-4.col-sm-4.col-xs-4 +      = f.input :direction, include_blank: false +    .col-lg-4.col-md-4.col-sm-4.col-xs-4 +      = f.input :wayback, include_blank: false +    .col-lg-4.col-md-4.col-sm-4.col-xs-4 +      = f.input :objectid, required: !@route.new_record?, input_html: { title: t("formtastic.titles#{format_restriction_for_locales(@referential)}.route.objectid") } + +  .row style="margin-top:20px" +    .col-lg-12.col-md-12.col-sm-12.col-xs-12 +      #stop_points +      /   div.clearfix style="margin-bottom:5px" +      /     label style="margin:0" Arrêts +      /     .btn.btn-primary.btn-xs.pull-right data-event="add_stop_point" +      /       span.fa.fa-plus +      /       = " #{t('routes.actions.add_stop_point')}" + +          / = link_to_add_association f, :stop_points, class: 'btn btn-primary btn-xs pull-right' do +          /   span.fa.fa-plus +          /   = " #{t('routes.actions.add_stop_point')}" +          / THIS IS BROKEN +          / = link_to t('routes.actions.new_stop_point'), new_referential_stop_area_path(@referential), target: '_blank' + +        / .list-group +        /   = f.simple_fields_for :stop_points do |point| +        /     = render 'stop_point_fields', f: point + +  .row +    .col-lg-12.col-md-12.col-sm-12.col-xs-12.text-right +      = link_to 'Annuler', :back, class: 'btn btn-link' +      = f.button :submit, class: 'btn btn-danger' + +// Get JSON data for route stop points += javascript_tag do +  | window.itinerary_stop = "#{URI.escape(@route.stop_areas.all.to_json)}" + +/ StopPoints Reactux component += javascript_include_tag 'es6_browserified/stop_points.js'  = semantic_form_for [@referential, @line, @route] do |form|    = form.inputs do      = form.input :name diff --git a/app/views/routes/_stop_point_fields.html.slim b/app/views/routes/_stop_point_fields.html.slim index 814e882c2..3b51e5e0d 100644 --- a/app/views/routes/_stop_point_fields.html.slim +++ b/app/views/routes/_stop_point_fields.html.slim @@ -1,16 +1,23 @@ -.nested-fields.stop_point.row -  = f.inputs do -    .col-md-1.resize -      = link_to_remove_association "<i class='fa fa-trash-o'></i>".html_safe, f -      span.handle alt="#{t('stop_points.index.move')}" title="#{t('stop_points.index.move')}" > -        i.fa.fa-arrows - -    .col-md-11 -      - if f.object.stop_area.nil? || f.object.new_record? -        = f.input :id, as: :hidden, :input_html => { :class => "stop_point_id added_stop_point", :value => "" } -        = f.input :position, as: :hidden, :input_html => { :class => "position" } +.list-group-item +  div style="display:inline-block;vertical-align:middle;width:75%;" +    - if f.object.stop_area.nil? || f.object.new_record? +        / = f.input :stop_area_id, label: false          = f.input :stop_area_id, :label => false, as: :search_stop_area, :json => referential_autocomplete_stop_areas_path(@referential, :format => :json)+"?filter=physical", :hint_text => t('search_hint'), :no_result_text => t('no_result_text'),:searching_text => t('searching_term'), :tokenLimit => 1, :input_html => { :class => "new_stop_point stop_area_id", :"data-pre" => Rabl::Renderer.new('autocomplete_stop_areas/index', [f.object.stop_area].compact, :view_path => 'app/views', :format => :json, :scope => self ).render } -      - else -        = f.input :id, as: :hidden, :input_html => { class: 'stop_point_id' } -        = f.input :position, as: :hidden, :input_html => { class: 'position' } -        = f.input :stop_area_id, label: false, as: :search_stop_area, :json => referential_autocomplete_stop_areas_path(@referential, :format => :json)+"?filter=physical", :hint_text => t('search_hint'), :no_result_text => t('no_result_text'),:searching_text => t('searching_term'), :tokenLimit => 1, :input_html => { :class => "stop_area_id", :"data-pre" => Rabl::Renderer.new('autocomplete_stop_areas/index', [f.object.stop_area].compact, :view_path => 'app/views', :format => :json, :scope => self ).render }
\ No newline at end of file +       +    - else +      = "#{@route.stop_areas.find(f.object.stop_area_id).name} " +      em.small +        = "#{@route.stop_areas.find(f.object.stop_area_id).zip_code} #{@route.stop_areas.find(f.object.stop_area_id).city_name}" +     +  div.text-right style="display:inline-block;vertical-align:middle;width:25%;" +    .btn-group.btn-group-sm style="padding:0;" +      .btn.btn-default +        span.fa.fa-times +      .btn.btn-primary +        span.fa.fa-arrow-up +      .btn.btn-primary +        span.fa.fa-arrow-down +      .btn.btn-danger +        span.fa.fa-trash +      / = link_to_remove_association f, class: 'btn btn-danger', data: {confirm: 'Are you sure?'} do +      /   span.fa.fa-trash diff --git a/app/views/routes/_test.html.slim b/app/views/routes/_test.html.slim new file mode 100644 index 000000000..deb525d32 --- /dev/null +++ b/app/views/routes/_test.html.slim @@ -0,0 +1,36 @@ +.list-group-item +  = f.inputs do +    - if f.object.stop_area.nil? || f.object.new_record? +      div style="display:inline-block;vertical-align:middle;width:75%;" +        span +          span.text-danger = "[#{@route.stop_areas.find(f.object.stop_area_id).position.inspect}] " +          = @route.stop_areas.find(f.object.stop_area_id).name +                 +        / = f.input :stop_area_id, :label => false, as: :search_stop_area, :json => referential_autocomplete_stop_areas_path(@referential, :format => :json)+"?filter=physical", :hint_text => t('search_hint'), :no_result_text => t('no_result_text'),:searching_text => t('searching_term'), :tokenLimit => 1, :input_html => { :class => "new_stop_point stop_area_id", :"data-pre" => Rabl::Renderer.new('autocomplete_stop_areas/index', [f.object.stop_area].compact, :view_path => 'app/views', :format => :json, :scope => self ).render } +         +        = f.input :stop_area_id, :label => false, as: :search_stop_area +         +        = f.input :id, as: :hidden, :input_html => { :class => "stop_point_id added_stop_point", :value => "" } +        = f.input :position, as: :hidden, :input_html => { :class => "position" } +   +    - else +      div style="display:inline-block;vertical-align:middle;width:75%;" +        span +          span.text-danger = "[#{@route.stop_areas.find(f.object.stop_area_id).position.inspect}] " +          = @route.stop_areas.find(f.object.stop_area_id).name +                   +        / = f.input :stop_area_id, label: false, as: :search_stop_area, :json => referential_autocomplete_stop_areas_path(@referential, :format => :json)+"?filter=physical", :hint_text => t('search_hint'), :no_result_text => t('no_result_text'),:searching_text => t('searching_term'), :tokenLimit => 1, :input_html => { :class => "stop_area_id", :"data-pre" => Rabl::Renderer.new('autocomplete_stop_areas/index', [f.object.stop_area].compact, :view_path => 'app/views', :format => :json, :scope => self ).render } +         +        = f.input :id, as: :hidden, :input_html => { class: 'stop_point_id' } +        = f.input :position, as: :hidden, :input_html => { class: 'position' } +     +    div.text-right style="display:inline-block;;vertical-align:middle;width:25%;" +      .btn-group.btn-group-sm style="padding:0;" +        .btn.btn-default +          span.fa.fa-times +        .btn.btn-primary +          span.fa.fa-arrow-up +        .btn.btn-primary +          span.fa.fa-arrow-down +        = link_to_remove_association f, class: 'btn btn-danger' do +          span.fa.fa-trash diff --git a/app/views/routes/edit.html.slim b/app/views/routes/edit.html.slim index 7e2c1f982..a0431dc32 100644 --- a/app/views/routes/edit.html.slim +++ b/app/views/routes/edit.html.slim @@ -1,3 +1,5 @@  = title_tag t('routes.edit.title', route: @route.name ) -== render 'form'
\ No newline at end of file +.row +  .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2 +    == render 'form' diff --git a/app/views/routes/new.html.slim b/app/views/routes/new.html.slim index e91ebf362..51410a2e5 100644 --- a/app/views/routes/new.html.slim +++ b/app/views/routes/new.html.slim @@ -1,3 +1,5 @@  = title_tag t('routes.new.title') -== render 'form'
\ No newline at end of file +.row +  .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2 +    == render 'form' diff --git a/app/views/shared/_header.html.slim b/app/views/shared/_header.html.slim index 27b0139c2..73e705e35 100644 --- a/app/views/shared/_header.html.slim +++ b/app/views/shared/_header.html.slim @@ -7,7 +7,7 @@ nav.navbar.navbar-default.navbar-fixed-top role="navigation"          span.icon-bar          span.icon-bar          span.icon-bar -        span.icon-bar.version = APP_VERSION +        / span.icon-bar.version = APP_VERSION        = link_to referentials_path, class: 'navbar-brand' do          / = image_tag("logo_chouette.png") @@ -85,12 +85,12 @@ nav.navbar.navbar-default.navbar-fixed-top role="navigation"                  = link_to referential_route_sections_path(@referential) do                    span.badge.pull-right = @referential.route_sections.size                    = Referential.human_attribute_name("route_sections") -                 +                li                  = link_to referential_timebands_path(@referential) do                    span.badge.pull-right = @referential.timebands.size                    = Referential.human_attribute_name("timebands") -           +            li              = link_to Referential.human_attribute_name("imports"), referential_imports_path(@referential)            li @@ -128,4 +128,4 @@ nav.navbar.navbar-default.navbar-fixed-top role="navigation"                li = link_to t('layouts.user.sign_out'), destroy_user_session_path, method: :delete                li.divider                li -                = tab_link_to Organisation.model_name.human, organisation_path
\ No newline at end of file +                = tab_link_to Organisation.model_name.human, organisation_path diff --git a/config/application.rb b/config/application.rb index 52da477a5..93a4cc8fd 100644 --- a/config/application.rb +++ b/config/application.rb @@ -26,5 +26,8 @@ module ChouetteIhm      # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.      # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]      config.i18n.default_locale = :fr + +    # Configure Browserify to use babelify to compile ES6 +    config.browserify_rails.commandline_options = "-t [ babelify --presets [ es2015 react ] ]"    end  end diff --git a/config/deploy.rb b/config/deploy.rb index cd6391ec3..34d1b6e4d 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -23,6 +23,11 @@ ssh_options[:forward_agent] = true  require "bundler/capistrano"  require 'whenever/capistrano' +require 'capistrano/npm' +set :npm_options, '--production --silent --no-progress' + +after 'deploy:finalize_update', 'npm:install' +  # Whenever  set :whenever_variables, ->{ "'environment=#{fetch :whenever_environment}&bundle_command=bin/bundle exec&additionnal_path=/var/lib/gems/2.2.0/bin'" } # invoke bin/bundle to use 'correct' ruby environment diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index d2f4ec33a..928236c4a 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -5,4 +5,4 @@ Rails.application.config.assets.version = '1.0'  # Precompile additional assets.  # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -# Rails.application.config.assets.precompile += %w( search.js ) +Rails.application.config.assets.precompile += %w( es6_browserified/*.js ) diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index d5492e529..472863fa8 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -5,8 +5,9 @@ SimpleForm.setup do |config|    # wrapper, change the order or even add your own to the    # stack. The options given below are used to wrap the    # whole input. -  config.wrappers :default, class: :input, -    hint_class: :field_with_hint, error_class: :field_with_errors do |b| +  config.wrappers :default, class: 'form-group', +    error_class: 'has-error' do |b| +    # hint_class: :field_with_hint, error_class: :field_with_errors do |b|      ## Extensions enabled by default      # Any of these extensions can be disabled for a      # given input by passing: `f.input EXTENSION_NAME => false`. @@ -41,8 +42,8 @@ SimpleForm.setup do |config|      ## Inputs      b.use :label_input -    b.use :hint,  wrap_with: { tag: :span, class: :hint }      b.use :error, wrap_with: { tag: :span, class: :error } +    b.use :hint,  wrap_with: { tag: :span, class: :hint }      ## full_messages_for      # If you want to display the full error message for the attribute, you can @@ -72,7 +73,7 @@ SimpleForm.setup do |config|    config.error_notification_tag = :div    # CSS class to add for error notification helper. -  config.error_notification_class = 'error_notification' +  config.error_notification_class = 'alert alert-error'    # ID to add for error notification helper.    # config.error_notification_id = nil @@ -90,25 +91,24 @@ SimpleForm.setup do |config|    # config.collection_wrapper_class = nil    # You can wrap each item in a collection of radio/check boxes with a tag, -  # defaulting to :span. Please note that when using :boolean_style = :nested, -  # SimpleForm will force this option to be a label. +  # defaulting to :span.    # config.item_wrapper_tag = :span    # You can define a class to use in all item wrappers. Defaulting to none.    # config.item_wrapper_class = nil    # How the label text should be generated altogether with the required text. -  # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" } +  config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" }    # You can define the class to use on all labels. Default is nil.    # config.label_class = nil    # You can define the default class to be used on forms. Can be overriden    # with `html: { :class }`. Defaulting to none. -  # config.default_form_class = nil +  config.default_form_class = 'form'    # You can define which elements should obtain additional classes -  # config.generate_additional_classes_for = [:wrapper, :label, :input] +  config.generate_additional_classes_for = [:label, :input]    # Whether attributes are required by default (or not). Default is true.    # config.required_by_default = true diff --git a/config/initializers/simple_form_bootstrap.rb b/config/initializers/simple_form_bootstrap.rb index 4f4dc23b3..be0f4bf55 100644 --- a/config/initializers/simple_form_bootstrap.rb +++ b/config/initializers/simple_form_bootstrap.rb @@ -14,8 +14,8 @@ SimpleForm.setup do |config|      b.use :label, class: 'control-label'      b.use :input, class: 'form-control' -    b.use :error, wrap_with: { tag: 'span', class: 'help-block' } -    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +    b.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }    end    config.wrappers :vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| @@ -26,8 +26,8 @@ SimpleForm.setup do |config|      b.use :label, class: 'control-label'      b.use :input -    b.use :error, wrap_with: { tag: 'span', class: 'help-block' } -    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +    b.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }    end    config.wrappers :vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| @@ -38,8 +38,8 @@ SimpleForm.setup do |config|        ba.use :label_input      end -    b.use :error, wrap_with: { tag: 'span', class: 'help-block' } -    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +    b.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }    end    config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| @@ -47,8 +47,8 @@ SimpleForm.setup do |config|      b.optional :readonly      b.use :label, class: 'control-label'      b.use :input -    b.use :error, wrap_with: { tag: 'span', class: 'help-block' } -    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +    b.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }    end    config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| @@ -62,8 +62,8 @@ SimpleForm.setup do |config|      b.wrapper tag: 'div', class: 'col-sm-9' do |ba|        ba.use :input, class: 'form-control' -      ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } -      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +      ba.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }      end    end @@ -76,8 +76,8 @@ SimpleForm.setup do |config|      b.wrapper tag: 'div', class: 'col-sm-9' do |ba|        ba.use :input -      ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } -      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +      ba.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }      end    end @@ -90,8 +90,8 @@ SimpleForm.setup do |config|          ba.use :label_input        end -      wr.use :error, wrap_with: { tag: 'span', class: 'help-block' } -      wr.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +      wr.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +      wr.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }      end    end @@ -103,8 +103,8 @@ SimpleForm.setup do |config|      b.wrapper tag: 'div', class: 'col-sm-9' do |ba|        ba.use :input -      ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } -      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +      ba.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }      end    end @@ -118,45 +118,32 @@ SimpleForm.setup do |config|      b.use :label, class: 'sr-only'      b.use :input, class: 'form-control' -    b.use :error, wrap_with: { tag: 'span', class: 'help-block' } -    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +    b.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +    b.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }    end -  config.wrappers :form_without_label, :tag => 'div', :class => 'form-group', :error_class => 'error' do |b| - +  config.wrappers :multi_select, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|      b.use :html5 -    b.use :placeholder -    b.optional :maxlength -    b.optional :pattern -    b.optional :min_max      b.optional :readonly - -    b.wrapper tag: 'div', class: 'col-sm-12' do |ba| +    b.use :label, class: 'control-label' +    b.wrapper tag: 'div', class: 'form-inline' do |ba|        ba.use :input, class: 'form-control' -      ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } -      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' } +      ba.use :error, wrap_with: { tag: 'span', class: 'help-block small' } +      ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block small' }      end -  end                                                                                       -                                                                                         -  config.wrappers :nested, :tag => 'span', :error_class => 'has-error' do |b| -     b.use :html5 -     b.use :placeholder -     b.optional :readonly -     b.use :input -     b.use :error, wrap_with: { tag: 'span', class: 'help-block' } -     b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }    end -    # Wrappers for forms and inputs using the Bootstrap toolkit.    # Check the Bootstrap docs (http://getbootstrap.com)    # to learn about the different styles for forms and inputs,    # buttons and other elements. -  config.default_wrapper = :horizontal_form +  config.default_wrapper = :vertical_form    config.wrapper_mappings = {      check_boxes: :vertical_radio_and_checkboxes,      radio_buttons: :vertical_radio_and_checkboxes,      file: :vertical_file_input,      boolean: :vertical_boolean, +    datetime: :multi_select, +    date: :multi_select, +    time: :multi_select    }  end - diff --git a/config/locales/referentials.en.yml b/config/locales/referentials.en.yml index 3691a8227..4609069c7 100644 --- a/config/locales/referentials.en.yml +++ b/config/locales/referentials.en.yml @@ -24,6 +24,7 @@ en:        destroy_confirm: "Do you confirm to destroy this data space ?"        destroy: "Destroy this data space"        edit: "Edit this data space" +      clone: "Clone this data space"      errors:        pg_excluded: "can't begins with pg_"        public_excluded: "public is a reserved value" @@ -81,4 +82,4 @@ en:    notice:      referential:        archived: "The data space has been successfully archived" -      unarchived: "The data space has been successfully unarchived"
\ No newline at end of file +      unarchived: "The data space has been successfully unarchived" diff --git a/config/locales/referentials.fr.yml b/config/locales/referentials.fr.yml index cada9c3a0..18ef4e8da 100644 --- a/config/locales/referentials.fr.yml +++ b/config/locales/referentials.fr.yml @@ -24,6 +24,7 @@ fr:        destroy_confirm: "Etes vous sûr de vouloir supprimer cet espace de données ?"        destroy: "Supprimer cet espace de données"        edit: "Modifier cet espace de données" +      clone: "Cloner cet espace de données"      errors:        pg_excluded: "ne peut pas commencer par pg_ (valeurs réservées)"        public_excluded: "public est une valeur réservée" diff --git a/lib/stif/my_workbench_scopes.rb b/lib/stif/my_workbench_scopes.rb new file mode 100644 index 000000000..89c4e659c --- /dev/null +++ b/lib/stif/my_workbench_scopes.rb @@ -0,0 +1,23 @@ +module Stif +  class MyWorkbenchScopes +    attr_accessor :workbench + +    def initialize(workbench) +      @workbench = workbench +    end + +    def line_scope(initial_scope) +      ids = self.parse_functional_scope +      ids ? initial_scope.where(objectid: ids) : initial_scope +    end + +    def parse_functional_scope +      return false unless @workbench.organisation.sso_attributes +      begin +        JSON.parse @workbench.organisation.sso_attributes['functional_scope'] +      rescue Exception => e +        Rails.logger.error "MyWorkbenchScopes : #{e}" +      end +    end +  end +end diff --git a/lib/tasks/ci.rake b/lib/tasks/ci.rake index 7fca878b6..2b584c08b 100644 --- a/lib/tasks/ci.rake +++ b/lib/tasks/ci.rake @@ -3,6 +3,7 @@ namespace :ci do    task :setup do      cp "config/database/jenkins.yml", "config/database.yml"      sh "RAILS_ENV=test rake db:migrate" +    sh "npm install"    end    def git_branch diff --git a/package.json b/package.json new file mode 100644 index 000000000..99c7916c1 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ +  "name": "something", +  "dependencies": { +    "babel-polyfill": "^6.16.0", +    "babel-preset-es2015": "^6.18.0", +    "babel-preset-react": "^6.16.0", +    "babelify": "^7.3.0", +    "browserify": "^13.1.1", +    "browserify-incremental": "^3.1.1", +    "react": "^15.3.2", +    "react-dom": "^15.3.2", +    "react-redux": "^4.4.5", +    "react-select2": "^4.0.2", +    "redux": "^3.6.0" +  }, +  "license": "MIT", +  "engines": { +    "node": ">= 0.10" +  } +} diff --git a/spec/features/access_points_spec.rb b/spec/features/access_points_spec.rb index a78545ee4..327c5879a 100644 --- a/spec/features/access_points_spec.rb +++ b/spec/features/access_points_spec.rb @@ -40,24 +40,25 @@ describe "Access points", :type => :feature do    end -  describe "new" do -    it "creates an access point" do -      visit referential_stop_area_path(referential, stop_area) -      click_link I18n.t("access_points.actions.new") -      fill_in "access_point[name]", :with => "My Access Point Name" -      click_button(I18n.t('formtastic.create',model: I18n.t('activerecord.models.access_point.one'))) -      expect(page).to have_content("My Access Point Name") -    end -  end +  # Fixme #1780 +  # describe "new" do +  #   it "creates an access point" do +  #     visit referential_stop_area_path(referential, stop_area) +  #     click_link I18n.t("access_points.actions.new") +  #     fill_in "access_point[name]", :with => "My Access Point Name" +  #     click_button(I18n.t('formtastic.create',model: I18n.t('activerecord.models.access_point.one'))) +  #     expect(page).to have_content("My Access Point Name") +  #   end +  # end -  describe "edit" do -    it "edits an acess point" do -      visit referential_stop_area_access_point_path(referential, stop_area, subject) -      click_link I18n.t("access_points.actions.edit") -      fill_in "access_point[name]", :with => "My New Access Point Name" -      click_button(I18n.t('formtastic.update',model: I18n.t('activerecord.models.access_point.one'))) -      expect(page).to have_content("My New Access Point Name") -    end -  end +  # describe "edit" do +  #   it "edits an acess point" do +  #     visit referential_stop_area_access_point_path(referential, stop_area, subject) +  #     click_link I18n.t("access_points.actions.edit") +  #     fill_in "access_point[name]", :with => "My New Access Point Name" +  #     click_button(I18n.t('formtastic.update',model: I18n.t('activerecord.models.access_point.one'))) +  #     expect(page).to have_content("My New Access Point Name") +  #   end +  # end  end diff --git a/spec/features/routes_spec.rb b/spec/features/routes_spec.rb index bb4b8ae3b..70f3448ab 100644 --- a/spec/features/routes_spec.rb +++ b/spec/features/routes_spec.rb @@ -41,39 +41,6 @@ describe "Routes", :type => :feature do      end    end -  describe "from line's page, select a route and edit it" do -    it "return to line's page with changed name" do -      visit referential_line_path(referential,line) -      click_link "#{route.name}" -      click_link "Modifier cette séquence d'arrêts" -      fill_in "route_name", :with => "#{route.name}-changed" -      click_button("Modifier séquence d'arrêts") -      expect(page).to have_content("#{route.name}-changed") -    end -  end - -  describe "from line's page, select a route and delete it" do -    it "return to line's page without route name" do -      visit referential_line_path(referential,line) -      click_link "#{route.name}" -      click_link "Supprimer cette séquence d'arrêts" -      expect(page).not_to have_content(route.name) -    end -  end - -  describe "from route's page, select edit boarding/alighting and update it" do -    it "Edits boarding/alighting properties on route stops" do -      visit referential_line_route_path(referential, line, route) -      click_link I18n.t('routes.actions.edit_boarding_alighting') -      expect(page).to have_content(I18n.t('routes.edit_boarding_alighting.title')) -      stop_points.each do |sp| -        expect(page).to have_content(sp.stop_area.name) -        expect(page).to have_content(sp.for_boarding) -        expect(page).to have_content(sp.for_alighting) -      end -    end -  end -    describe "Modifies boarding/alighting properties on route stops" do      it "Puts (http) an update request" do        #visit edit_boarding_alighting_referential_line_route_path(referential, line, route) diff --git a/spec/models/organisation_spec.rb b/spec/models/organisation_spec.rb index c0aba2eb8..823ee7ea6 100644 --- a/spec/models/organisation_spec.rb +++ b/spec/models/organisation_spec.rb @@ -36,7 +36,7 @@ describe Organisation, :type => :model do      it 'should retrieve functional scope' do        Organisation.portail_sync        org = Organisation.find_by(code: 'RATP') -      expect(org.sso_attributes['functional_scope']).to eq "[STIF:CODIFLIGNE:Line:C00840, STIF:CODIFLIGNE:Line:C00086]" +      expect(org.sso_attributes['functional_scope']).to eq "[\"STIF:CODIFLIGNE:Line:C00840\", \"STIF:CODIFLIGNE:Line:C00086\"]"      end      it 'should update existing organisations' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index c20e80ca1..bb43be63e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -36,7 +36,7 @@ describe User, :type => :model do        it 'should store organisation functional_scope' do          User.authenticate_with_cas_ticket(ticket)          org = Organisation.find_by(code: ticket.extra_attributes[:organisation_code]) -        expect(org.sso_attributes['functional_scope']).to eq "[STIF:CODIFLIGNE:Line:C00840, STIF:CODIFLIGNE:Line:C00086]" +        expect(org.sso_attributes['functional_scope']).to eq "[\"STIF:CODIFLIGNE:Line:C00840\", \"STIF:CODIFLIGNE:Line:C00086\"]"        end        it 'should not create a new organisation if organisation is already present' do diff --git a/spec/models/workbench_spec.rb b/spec/models/workbench_spec.rb index 023a65ea2..84149ddb0 100644 --- a/spec/models/workbench_spec.rb +++ b/spec/models/workbench_spec.rb @@ -1,7 +1,6 @@  require 'rails_helper'  RSpec.describe Workbench, :type => :model do -    it 'should have a valid factory' do      expect(FactoryGirl.build(:workbench)).to be_valid    end @@ -12,4 +11,26 @@ RSpec.describe Workbench, :type => :model do    it { should belong_to(:organisation) }    it { should belong_to(:line_referential) }    it { should belong_to(:stop_area_referential) } + +  it { should have_many(:lines).through(:line_referential) } +  it { should have_many(:networks).through(:line_referential) } +  it { should have_many(:companies).through(:line_referential) } +  it { should have_many(:group_of_lines).through(:line_referential) } + +  it { should have_many(:stop_areas).through(:stop_area_referential) } + +  context '.lines' do +    let!(:ids) { ['STIF:CODIFLIGNE:Line:C00840', 'STIF:CODIFLIGNE:Line:C00086'] } +    let!(:organisation) { create :organisation, sso_attributes: { functional_scope: ids.to_json } } +    let(:workbench) { create :workbench, organisation: organisation } + +    it 'should filter lines based on my organisation functional_scope' do +      ids.insert('STIF:CODIFLIGNE:Line:0000').each do |id| +        create :line, objectid: id, line_referential: workbench.line_referential +      end +      lines = workbench.lines +      expect(lines.count).to eq 2 +      expect(lines.map(&:objectid)).to include(*ids) +    end +  end  end | 
