diff options
78 files changed, 1134 insertions, 302 deletions
| diff --git a/Gemfile.lock b/Gemfile.lock index 9a7be2d84..62b7e3c99 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -305,7 +305,7 @@ GEM        mime-types-data (~> 3.2015)      mime-types-data (3.2016.0521)      mimemagic (0.3.2) -    mini_portile2 (2.2.0) +    mini_portile2 (2.3.0)      minitest (5.10.3)      multi_json (1.12.1)      multi_test (0.1.2) @@ -319,8 +319,8 @@ GEM      net-ssh-gateway (2.0.0)        net-ssh (>= 4.0.0)      newrelic_rpm (4.0.0.332) -    nokogiri (1.8.0) -      mini_portile2 (~> 2.2.0) +    nokogiri (1.8.1) +      mini_portile2 (~> 2.3.0)      open4 (1.3.4)      orm_adapter (0.5.0)      parser (2.4.0.0) diff --git a/INSTALL.md b/INSTALL.md index ea6fbd6ab..899654a25 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -128,8 +128,22 @@ To create `Referential` objects with some data (`Route`, `JourneyPattern`, `Vehi  # Troubleshooting +## Postgres +  If Postgres complains about illegal type `hstore` in your tests that is probably because the shared extension is not installed, here is what to do:        bundle exec rake db:test:purge  Thanks to `lib/tasks/extensions.rake`. + +## macOS + +### Nokogiri + +http://www.nokogiri.org/tutorials/installing_nokogiri.html tells us that `xz` can cause troubles, here is what to do  + +``` +brew unlink xz +gem install nokogiri # or bundle install +brew link xz +``` diff --git a/app/assets/javascripts/es6_browserified/helpers/clone.js b/app/assets/javascripts/es6_browserified/helpers/clone.js new file mode 100644 index 000000000..c3b627858 --- /dev/null +++ b/app/assets/javascripts/es6_browserified/helpers/clone.js @@ -0,0 +1,14 @@ +const _ = require("lodash") + +/* This function helps having a bit more security when we pass data from the backend to the React parts +  It clones the obj (window variable) and then conditionnaly delete the window variable +*/ + +const clone = (window, key, deletable = false) => { +  let obj = _.cloneDeep(window[key]) +   +  if (deletable) delete window[key] +  return obj +} + +module.exports = clone
\ No newline at end of file diff --git a/app/assets/javascripts/es6_browserified/itineraries/components/App.js b/app/assets/javascripts/es6_browserified/itineraries/components/App.js index e662d140c..f4cc5e4a6 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/components/App.js +++ b/app/assets/javascripts/es6_browserified/itineraries/components/App.js @@ -1,12 +1,28 @@  var React = require('react') +var { Component, PropTypes } = require('react')  var AddStopPoint = require('../containers/AddStopPoint')  var VisibleStopPoints = require('../containers/VisibleStopPoints') +const clone = require('../../helpers/clone') +const I18n = clone(window , "I18n", true) -const App = () => ( -  <div> -    <VisibleStopPoints /> -    <AddStopPoint /> -  </div> -) +class App extends Component { + +  getChildContext() { +    return { I18n } +  } + +  render() { +    return ( +      <div> +        <VisibleStopPoints /> +        <AddStopPoint /> +      </div> +    )   +  }  +} + +App.childContextTypes = { +  I18n: PropTypes.object +}  module.exports = App diff --git a/app/assets/javascripts/es6_browserified/itineraries/components/BSelect2.js b/app/assets/javascripts/es6_browserified/itineraries/components/BSelect2.js index 64c6d3ac7..9a82b7925 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/components/BSelect2.js +++ b/app/assets/javascripts/es6_browserified/itineraries/components/BSelect2.js @@ -1,6 +1,6 @@  var _ = require('lodash')  var React = require('react') -var PropTypes = require('react').PropTypes +var { Component, PropTypes } = require('react')  var Select2 = require('react-select2') @@ -9,9 +9,9 @@ var origin = window.location.origin  var path = window.location.pathname.split('/', 3).join('/') -class BSelect3 extends React.Component{ -  constructor(props) { -    super(props) +class BSelect3 extends Component{ +  constructor(props, context) { +    super(props, context)    }    onChange(e) {      this.props.onChange(this.props.index, { @@ -73,7 +73,7 @@ class BSelect3 extends React.Component{    }  } -class BSelect2 extends React.Component{ +class BSelect2 extends Component{    componentDidMount() {      this.refs.newSelect.el.select2('open')    } @@ -85,7 +85,7 @@ class BSelect2 extends React.Component{          onSelect={ this.props.onSelect }          ref='newSelect'          options={{ -          placeholder: 'Sélectionnez un arrêt existant...', +          placeholder: this.context.I18n.routes.edit.select2.placeholder,            allowClear: true,            language: 'fr', /* Doesn't seem to work... :( */            theme: 'bootstrap', @@ -121,4 +121,8 @@ class BSelect2 extends React.Component{    }  } +BSelect2.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = BSelect3 diff --git a/app/assets/javascripts/es6_browserified/itineraries/components/OlMap.js b/app/assets/javascripts/es6_browserified/itineraries/components/OlMap.js index b9e106c1a..937871346 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/components/OlMap.js +++ b/app/assets/javascripts/es6_browserified/itineraries/components/OlMap.js @@ -1,11 +1,10 @@  var _ = require('lodash')  var React = require('react') -var Component = require('react').Component -var PropTypes = require('react').PropTypes +var { Component, PropTypes } = require('react')  class OlMap extends Component{ -  constructor(props){ -    super(props) +  constructor(props, context){ +    super(props, context)    }    fetchApiURL(id){ @@ -116,40 +115,40 @@ class OlMap extends Component{                <strong>{this.props.value.olMap.json.name}</strong>              </p>              <p> -              <strong>Type d'arrêt : </strong> +              <strong>{this.context.I18n.routes.edit.stop_point_type} : </strong>                {this.props.value.olMap.json.area_type}              </p>              <p> -              <strong>Nom court : </strong> +              <strong>{this.context.I18n.routes.edit.short_name} : </strong>                {this.props.value.olMap.json.short_name}              </p>              <p> -              <strong>ID Reflex : </strong> +              <strong>{this.context.I18n.id_reflex} : </strong>                {this.props.value.olMap.json.user_objectid}              </p> -            <p><strong>Coordonnées : </strong></p> +            <p><strong>{this.context.I18n.routes.edit.map.coordinates} : </strong></p>              <p style={{paddingLeft: 10, marginTop: 0}}> -              <em>Proj.: </em>WSG84<br/> -              <em>Lat.: </em>{this.props.value.olMap.json.latitude} <br/> -              <em>Lon.: </em>{this.props.value.olMap.json.longitude} +              <em>{this.context.I18n.routes.edit.map.proj}.: </em>WSG84<br/> +              <em>{this.context.I18n.routes.edit.map.lat}.: </em>{this.props.value.olMap.json.latitude} <br/> +              <em>{this.context.I18n.routes.edit.map.lon}.: </em>{this.props.value.olMap.json.longitude}              </p>              <p> -              <strong>Code Postal : </strong> +              <strong>{this.context.I18n.routes.edit.map.postal_code} : </strong>                {this.props.value.olMap.json.zip_code}              </p>              <p> -              <strong>Commune : </strong> +              <strong>{this.context.I18n.routes.edit.map.city} : </strong>                {this.props.value.olMap.json.city_name}              </p>              <p> -              <strong>Commentaire : </strong> +              <strong>{this.context.I18n.routes.edit.map.comment} : </strong>                {this.props.value.olMap.json.comment}              </p>              {(this.props.value.stoparea_id != this.props.value.olMap.json.stoparea_id) &&(                <div className='btn btn-outline-primary btn-sm'                  onClick= {() => {this.props.onUpdateViaOlMap(this.props.index, this.props.value.olMap.json)}} -              >Sélectionner</div> +              >{this.context.I18n.actions.select}</div>              )}            </div>              <div className='map_content'> @@ -163,7 +162,11 @@ class OlMap extends Component{    }  } -OlMap.propTypes = { +OlMap.PropTypes = { +} + +OlMap.contextTypes = { +  I18n: PropTypes.object  }  module.exports = OlMap diff --git a/app/assets/javascripts/es6_browserified/itineraries/components/StopPoint.js b/app/assets/javascripts/es6_browserified/itineraries/components/StopPoint.js index 48f77b8e9..c3996f5b3 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/components/StopPoint.js +++ b/app/assets/javascripts/es6_browserified/itineraries/components/StopPoint.js @@ -3,7 +3,7 @@ var PropTypes = require('react').PropTypes  var BSelect2 = require('./BSelect2')  var OlMap = require('./OlMap') -const StopPoint = (props) => { +const StopPoint = (props, {I18n}) => {    return (      <div className='nested-fields'>        <div className='wrapper'> @@ -17,15 +17,15 @@ const StopPoint = (props) => {          <div>            <select className='form-control' value={props.value.for_boarding} id="for_boarding" onChange={props.onSelectChange}> -            <option value="normal">Montée autorisée</option> -            <option value="forbidden">Montée interdite</option> +            <option value="normal">{I18n.routes.edit.stop_point.boarding.normal}</option> +            <option value="forbidden">{I18n.routes.edit.stop_point.boarding.forbidden}</option>            </select>          </div>          <div>            <select className='form-control' value={props.value.for_alighting} id="for_alighting" onChange={props.onSelectChange}> -            <option value="normal">Descente autorisée</option> -            <option value="forbidden">Descente interdite</option> +            <option value="normal">{I18n.routes.edit.stop_point.alighting.normal}</option> +            <option value="forbidden">{I18n.routes.edit.stop_point.alighting.forbidden}</option>            </select>          </div> @@ -90,4 +90,8 @@ StopPoint.propTypes = {    value: PropTypes.object  } +StopPoint.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = StopPoint diff --git a/app/assets/javascripts/es6_browserified/itineraries/components/StopPointList.js b/app/assets/javascripts/es6_browserified/itineraries/components/StopPointList.js index 77077dbd8..37a480fca 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/components/StopPointList.js +++ b/app/assets/javascripts/es6_browserified/itineraries/components/StopPointList.js @@ -2,29 +2,29 @@ var React = require('react')  var PropTypes = require('react').PropTypes  var StopPoint = require('./StopPoint') -const StopPointList = ({ stopPoints, onDeleteClick, onMoveUpClick, onMoveDownClick, onChange, onSelectChange, onToggleMap, onToggleEdit, onSelectMarker, onUnselectMarker, onUpdateViaOlMap }) => { +const StopPointList = ({ stopPoints, onDeleteClick, onMoveUpClick, onMoveDownClick, onChange, onSelectChange, onToggleMap, onToggleEdit, onSelectMarker, onUnselectMarker, onUpdateViaOlMap }, {I18n}) => {    return (      <div className='subform'>        <div className='nested-head'>          <div className="wrapper">            <div style={{width: 100}}>              <div className="form-group"> -              <label className="control-label">ID Reflex</label> +              <label className="control-label">{I18n.reflex_id}</label>              </div>            </div>            <div>              <div className="form-group"> -              <label className="control-label">Arrêt</label> +              <label className="control-label">{I18n.simple_form.labels.stop_point.name}</label>              </div>            </div>            <div>              <div className="form-group"> -              <label className="control-label">Montée</label> +              <label className="control-label">{I18n.simple_form.labels.stop_point.for_boarding}</label>              </div>            </div>            <div>              <div className="form-group"> -              <label className="control-label">Descente</label> +              <label className="control-label">{I18n.simple_form.labels.stop_point.for_alighting}</label>              </div>            </div>            <div className='actions-5'></div> @@ -65,4 +65,8 @@ StopPointList.propTypes = {    onUnselectMarker : PropTypes.func.isRequired  } +StopPointList.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = StopPointList diff --git a/app/assets/javascripts/es6_browserified/itineraries/form_helper.js b/app/assets/javascripts/es6_browserified/itineraries/form_helper.js index 0baba27ef..f682e39c0 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/form_helper.js +++ b/app/assets/javascripts/es6_browserified/itineraries/form_helper.js @@ -1,11 +1,55 @@ -const addInput = (name, value, index) => { -  let form = document.querySelector('form') -  let input = document.createElement('input') -  let formatedName = 'route[stop_points_attributes]['+ index.toString()+']['+name+']' -  input.setAttribute('type', 'hidden') -  input.setAttribute('name', formatedName) -  input.setAttribute('value', value) -  form.appendChild(input) +const formHelper = { +  addInput: (name, value, index) => { +    let form = document.querySelector('form') +    let input = document.createElement('input') +    let formatedName = `route[stop_points_attributes][${index.toString()}][${name}]` +    input.setAttribute('type', 'hidden') +    input.setAttribute('name', formatedName) +    input.setAttribute('value', value) +    form.appendChild(input) +  },  +  addError: (ids) => { +    ids.forEach((id) => { +      if (!$(id).parents('.form-group').hasClass('has-error')) { +        $(id).parents('.form-group').addClass('has-error') +        $(id).parent().append(`<span class='help-block small'>${'doit être rempli(e)'}</span>`) +      } +    })  +  }, +  cleanInputs: (ids) => { +    ids.forEach((id) =>{ +      $(id).parents('.form-group').removeClass('has-error') +      $(id).siblings('span').remove() +    }) +  }, +  handleForm: (...ids) => { +    let filledInputs = [] +    let blankInputs = [] +    ids.forEach(id => { +      $(id).val() == "" ? blankInputs.push(id) : filledInputs.push(id) +    }) +     +    if (filledInputs.length > 0) formHelper.cleanInputs(filledInputs) +    if (blankInputs.length > 0) formHelper.addError(blankInputs)   +  }, +  handleStopPoints: (event, state) => { +    if (state.stopPoints.length >= 2) { +      state.stopPoints.map((stopPoint, i) => { +        formHelper.addInput('id', stopPoint.stoppoint_id ? stopPoint.stoppoint_id : '', i) +        formHelper.addInput('stop_area_id', stopPoint.stoparea_id, i) +        formHelper.addInput('position', i, i) +        formHelper.addInput('for_boarding', stopPoint.for_boarding, i) +        formHelper.addInput('for_alighting', stopPoint.for_alighting, i) +      }) +    if ($('.alert.alert-danger').length > 0) $('.alert.alert-danger').remove() +    } else { +      event.preventDefault() +      let msg = "L'itinéraire doit comporter au moins deux arrêts" +      if ($('.alert.alert-danger').length == 0) { +        $('#stop_points').find('.subform').after(`<div class='alert alert-danger'><span class='fa fa-lg fa-exclamation-circle'></span><span>" ${msg} "</span></div>`) +      } +    } +  }  } -module.exports = addInput +module.exports = formHelper
\ No newline at end of file diff --git a/app/assets/javascripts/es6_browserified/itineraries/index.js b/app/assets/javascripts/es6_browserified/itineraries/index.js index ad32b9519..13d89bec5 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/index.js +++ b/app/assets/javascripts/es6_browserified/itineraries/index.js @@ -4,8 +4,10 @@ var Provider = require('react-redux').Provider  var createStore = require('redux').createStore  var reducers = require('./reducers')  var App = require('./components/App') -var addInput = require('./form_helper') -let datas = JSON.parse(decodeURIComponent(window.itinerary_stop)) +var { handleForm, handleStopPoints } = require('./form_helper') +let clone = require('../helpers/clone') +let datas = clone(window, "itinerary_stop", true) +datas = JSON.parse(decodeURIComponent(datas))  // logger, DO NOT REMOVE  // var applyMiddleware = require('redux').applyMiddleware @@ -67,17 +69,12 @@ render(  document.querySelector('input[name=commit]').addEventListener('click', (event)=>{    let state = store.getState() -  if(state.stopPoints.length >= 2) { -    state.stopPoints.map((stopPoint, i) => { -      addInput('id', stopPoint.stoppoint_id ? stopPoint.stoppoint_id : '', i) -      addInput('stop_area_id',stopPoint.stoparea_id, i) -      addInput('position',i, i) -      addInput('for_boarding',stopPoint.for_boarding, i) -      addInput('for_alighting',stopPoint.for_alighting, i) -    }) -  } else { +  let name = $("#route_name").val() +  let publicName = $("#route_published_name").val() +  if (name == "" || publicName == "") {      event.preventDefault() -    let msg = "L'itinéraire doit comporter au moins deux arrêts" -    $('#stop_points').find('.subform').after("<div class='alert alert-danger'><span class='fa fa-lg fa-exclamation-circle'></span><span>" + msg + "</span></div>") +    handleForm("#route_name", "#route_published_name")    } +     +  handleStopPoints(event, state)  }) diff --git a/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js b/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js index a3b8accb3..f3a26b8d7 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js +++ b/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js @@ -1,5 +1,5 @@  var _ = require('lodash') -var addInput = require('../form_helper') +var { addInput } = require('../form_helper')  const stopPoint = (state = {}, action, length) => {    switch (action.type) { diff --git a/app/assets/javascripts/es6_browserified/itineraries/show.js b/app/assets/javascripts/es6_browserified/itineraries/show.js index 79a11701f..e88469900 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/show.js +++ b/app/assets/javascripts/es6_browserified/itineraries/show.js @@ -1,4 +1,7 @@ +const clone = require('../helpers/clone') +let route = clone(window, "route", true)  route = JSON.parse(decodeURIComponent(route)) +  const geoColPts = []  const geoColLns= []  const geoColEdges = [ diff --git a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js index 02ece1654..a421a8ed6 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js +++ b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js @@ -1,19 +1,21 @@  const _ = require('lodash') +const clone = require('../../helpers/clone') +const I18n = clone(window, "I18n")  const actions = { +  weekDays: (index) => { +    return _.range(1, 8).map(n => I18n.time_tables.edit.metas.days[n]) +  },    strToArrayDayTypes: (str) =>{ -    let weekDays = ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'] -    return weekDays.map((day, i) => str.indexOf(day) !== -1) +    return actions.weekDays().map(day => str.indexOf(day) !== -1)    }, -  arrayToStrDayTypes: (arr) => { -    let weekDays = ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'] -    let str = [] -    arr.map((dayActive, i) => { -      if(dayActive){ -        str.push(weekDays[i]) -      } -    }) -    return str.join(',') +  arrayToStrDayTypes: (dayTypes) => { +    let newDayTypes = dayTypes.reduce((arr, dayActive, i) => { +      if (dayActive) arr.push(actions.weekDays()[i]) +      return arr +    }, []) + +    return newDayTypes.join(',')    },    fetchingApi: () =>({      type: 'FETCH_API' @@ -113,14 +115,26 @@ const actions = {      timetableInDates,      error    }), -  includeDateInPeriod: (index, dayTypes, date) => ({ -    type: 'INCLUDE_DATE_IN_PERIOD', +  addIncludedDate: (index, dayTypes, date) => ({ +    type: 'ADD_INCLUDED_DATE',      index,      dayTypes,      date    }), -  excludeDateFromPeriod: (index, dayTypes, date) => ({ -    type: 'EXCLUDE_DATE_FROM_PERIOD', +  removeIncludedDate: (index, dayTypes, date) => ({ +    type: 'REMOVE_INCLUDED_DATE', +    index, +    dayTypes, +    date +  }), +  addExcludedDate: (index, dayTypes, date) => ({ +    type: 'ADD_EXCLUDED_DATE', +    index, +    dayTypes, +    date +  }), +  removeExcludedDate: (index, dayTypes, date) => ({ +    type: 'REMOVE_EXCLUDED_DATE',      index,      dayTypes,      date @@ -137,8 +151,8 @@ const actions = {      type : 'CLOSE_MODAL'    }),    monthName(strDate) { -    let monthList = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"] -    var date = new Date(strDate) +    let monthList = _.range(1,13).map(n => I18n.calendars.months[n]) +    let date = new Date(strDate)      return monthList[date.getMonth()]    },    getHumanDate(strDate, mLimit) { @@ -157,43 +171,31 @@ const actions = {      let date = new Date(strDate)      return date.toLocaleDateString()    }, +  updateSynthesis: ({current_month, time_table_dates: dates, time_table_periods: periods}) => { +    let newPeriods = _.reject(periods, 'deleted') +    let improvedCM = current_month.map((d, i) => { +      let isInPeriod = actions.isInPeriod(newPeriods, d.date) +      let isIncluded = _.some(dates, {'date': d.date, 'in_out': true}) -  updateSynthesis: (state, daytypes) => { -    let periods = state.time_table_periods - -    let isInPeriod = function(d){ -      let currentMonth = state.current_periode_range.split('-') -      let twodigitsDay = d.mday < 10 ? ('0' + d.mday) : d.mday -      let currentDate = new Date(currentMonth[0] + '-' + currentMonth[1] + '-' + twodigitsDay) - -      // We compare periods & currentDate, to determine if it is included or not -      let testDate = false -      periods.map((p, i) => { -        if (p.deleted) return false - -        let begin = new Date(p.period_start) -        let end = new Date(p.period_end) - -        if(testDate === false){ -          if(currentDate >= begin && currentDate <= end) { -            testDate = true -            // p.include_date = false -          } -        } -      }) -      return testDate -    } - -    let improvedCM = state.current_month.map((d, i) => { -      let bool = isInPeriod(state.current_month[i]) -      return _.assign({}, state.current_month[i], { -        in_periods: bool, -        include_date: bool ? false : state.current_month[i].include_date, -        excluded_date: !bool ? false : state.current_month[i].excluded_date +      return _.assign({}, current_month[i], { +        in_periods: isInPeriod, +        include_date: isIncluded, +        excluded_date: !isInPeriod ? false : current_month[i].excluded_date        })      })      return improvedCM    }, +  isInPeriod: (periods, date) => { +    date = new Date(date) + +    for (let period of periods) { +      let begin = new Date(period.period_start) +      let end = new Date(period.period_end)  +      if (date >= begin && date <= end) return true +    } + +    return false +  },    checkConfirmModal: (event, callback, stateChanged, dispatch, metas, timetable) => {      if(stateChanged){        const error = actions.errorModalKey(timetable.time_table_periods, metas.day_types) @@ -219,7 +221,7 @@ const actions = {        let period = periods[i]        if (index !== i && !period.deleted) {          if (new Date(period.period_start) <= end && new Date(period.period_end) >= start)  { -          error = 'Les périodes ne peuvent pas se chevaucher' +          error = I18n.time_tables.edit.error_submit.periods_overlaps            break          }        } @@ -233,7 +235,7 @@ const actions = {      for (let day of in_days) {        if (start <= new Date(day.date) && end >= new Date(day.date)) { -        error = 'Une période ne peut chevaucher une date dans un calendrier' +        error = I18n.time_tables.edit.error_submit.dates_overlaps          break        }      } @@ -241,7 +243,6 @@ const actions = {    },    fetchTimeTables: (dispatch, nextPage) => {      let urlJSON = window.location.pathname.split('/', 5).join('/') -    // console.log(nextPage)      if(nextPage) {        urlJSON += "/month.json?date=" + nextPage      }else{ @@ -310,21 +311,14 @@ const actions = {    errorModalMessage: (errorKey) => {      switch (errorKey) {        case "withoutPeriodsWithDaysTypes": -        return window.I18n.fr.time_tables.edit.error_modal.withoutPeriodsWithDaysTypes +        return I18n.time_tables.edit.error_modal.withoutPeriodsWithDaysTypes        case "withPeriodsWithoutDayTypes": -        return window.I18n.fr.time_tables.edit.error_modal.withPeriodsWithoutDayTypes +        return I18n.time_tables.edit.error_modal.withPeriodsWithoutDayTypes        default:          return errorKey      }    }, -  checkIfTTHasDate: (dates, date) => { -    if (_.some(dates, date)) { -       return _.reject(dates, ['date', date.date]) -     } else { -       return dates.concat(date) -     } -  }  }  module.exports = actions diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js b/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js index 40ae0eccf..674a03296 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js @@ -1,18 +1,17 @@  var React = require('react') -var Component = require('react').Component -var PropTypes = require('react').PropTypes +var { PropTypes } = require('react') -const ConfirmModal = ({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}) => ( +const ConfirmModal = ({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}, {I18n}) => (    <div className={ 'modal fade ' + ((modal.type == 'confirm') ? 'in' : '') } id='ConfirmModal'>      <div className='modal-container'>        <div className='modal-dialog'>          <div className='modal-content'>            <div className='modal-header'> -            <h4 className='modal-title'>Confirmation</h4> +            <h4 className='modal-title'>{I18n.time_tables.edit.confirm_modal.title}</h4>            </div>            <div className='modal-body'>              <div className='mt-md mb-md'> -              <p>Vous vous apprêtez à changer de page. Voulez-vous valider vos modifications avant cela ?</p> +              <p>{I18n.time_tables.edit.confirm_modal.message}</p>              </div>            </div>            <div className='modal-footer'> @@ -22,7 +21,7 @@ const ConfirmModal = ({dispatch, modal, onModalAccept, onModalCancel, timetable,                type='button'                onClick= {() => {onModalCancel(modal.confirmModal.callback)}}                > -              Ne pas valider +              {I18n.cancel}              </button>              <button                className='btn btn-primary' @@ -30,7 +29,7 @@ const ConfirmModal = ({dispatch, modal, onModalAccept, onModalCancel, timetable,                type='button'                onClick = {() => {onModalAccept(modal.confirmModal.callback, timetable, metas)}}                > -              Valider +              {I18n.actions.submit}              </button>            </div>          </div> @@ -45,4 +44,8 @@ ConfirmModal.propTypes = {    onModalCancel: PropTypes.func.isRequired  } +ConfirmModal.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = ConfirmModal diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js b/app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js index 4e8f7e363..2597a4870 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js @@ -1,15 +1,14 @@  var React = require('react') -var Component = require('react').Component -var PropTypes = require('react').PropTypes -var errorModalMessage = require('../actions').errorModalMessage +var { PropTypes } = require('react') +var { errorModalMessage } = require('../actions') -const ErrorModal = ({dispatch, modal, I18n, onModalClose}) => ( +const ErrorModal = ({dispatch, modal, onModalClose}, {I18n}) => (    <div className={ 'modal fade ' + ((modal.type == 'error') ? 'in' : '') } id='ErrorModal'>      <div className='modal-container'>        <div className='modal-dialog'>          <div className='modal-content'>            <div className='modal-header'> -            <h4 className='modal-title'>{window.I18n.fr.time_tables.edit.error_modal.title}</h4> +            <h4 className='modal-title'>{I18n.time_tables.edit.error_modal.title}</h4>            </div>            <div className='modal-body'>              <div className='mt-md mb-md'> @@ -23,7 +22,7 @@ const ErrorModal = ({dispatch, modal, I18n, onModalClose}) => (                type='button'                onClick= {() => {onModalClose()}}                > -              Retour +              {I18n.back}              </button>            </div>          </div> @@ -37,4 +36,8 @@ ErrorModal.propTypes = {    onModalClose: PropTypes.func.isRequired  } +ErrorModal.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = ErrorModal diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js index 4879e537f..80c2e4b7a 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js @@ -8,6 +8,15 @@ class ExceptionsInDay extends Component {      super(props)    } +  handleClick() { +    const {index, day, metas: {day_types} } = this.props +    if (day.in_periods && day_types[day.wday]) { +      day.excluded_date ? this.props.onRemoveExcludedDate(index, day_types, day.date) : this.props.onAddExcludedDate(index, day_types, day.date) +    } else { +      day.include_date ? this.props.onRemoveIncludedDate(index, day_types, day.date) : this.props.onAddIncludedDate(index, day_types, day.date) +    } +  } +    render() {      {/* display add or remove link, only if true in daytypes */}        {/* display add or remove link, according to context (presence in period, or not) */} @@ -20,14 +29,14 @@ class ExceptionsInDay extends Component {                data-actiontype='remove'                onClick={(e) => {                  $(e.currentTarget).toggleClass('active') -                this.props.onExcludeDateFromPeriod(this.props.index, this.props.metas.day_types, this.props.currentDate) +                this.handleClick()                }}              >                <span className='fa fa-times'></span>              </button>            </div>          ) -      } else if(this.props.value.current_month[this.props.index].in_periods == false) { +      } else {          return (            <div className='td'>              <button @@ -36,20 +45,21 @@ class ExceptionsInDay extends Component {                data-actiontype='add'                onClick={(e) => {                  $(e.currentTarget).toggleClass('active') -                this.props.onIncludeDateInPeriod(this.props.index, this.props.metas.day_types, this.props.currentDate) +                this.handleClick()                }}              >                <span className='fa fa-plus'></span>              </button>            </div>          ) -      } else if(this.props.value.current_month[this.props.index].in_periods == true && this.props.blueDaytype == false){ -        return ( -          <div className='td'></div> -        ) -      } else{ -        return false -      } +      // } else if(this.props.value.current_month[this.props.index].in_periods == true && this.props.blueDaytype == false){ +      //   return ( +      //     <div className='td'></div> +      //   ) +      // } else{ +      //   return false +      // } +    }    }  } diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js index a0fac84f3..26a96e4a6 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js @@ -1,9 +1,9 @@  var React = require('react') -var PropTypes = require('react').PropTypes -let weekDays = ['D', 'L', 'Ma', 'Me', 'J', 'V', 'S'] +var  { PropTypes }  = require('react') +const { weekDays } = require('../actions')  var TagsSelect2 = require('./TagsSelect2') -const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}) => { +const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}, {I18n}) => {    let colorList = ["", "#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7",   "#6321A0", "#E796C6", "#DD2DAA"]    return (      <div className='form-horizontal'> @@ -12,7 +12,7 @@ const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelec            {/* comment (name) */}            <div className="form-group">              <label htmlFor="" className="control-label col-sm-4 required"> -              Nom <abbr title="Champ requis">*</abbr> +              {I18n.time_tables.edit.metas.name} <abbr title="">*</abbr>              </label>              <div className="col-sm-8">                <input @@ -27,7 +27,7 @@ const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelec            {/* color */}            <div className="form-group"> -            <label htmlFor="" className="control-label col-sm-4">Couleur associée</label> +            <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.color}</label>              <div className="col-sm-8">                <div className="dropdown color_selector">                  <button @@ -72,7 +72,7 @@ const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelec            {/* tags */}            <div className="form-group"> -            <label htmlFor="" className="control-label col-sm-4">Etiquettes</label> +            <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.tag_list}</label>              <div className="col-sm-8">                <TagsSelect2                  initialTags={metas.initial_tags} @@ -85,16 +85,16 @@ const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelec            {/* calendar */}            <div className="form-group"> -            <label htmlFor="" className="control-label col-sm-4">Modèle de calendrier associé</label> +            <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.calendar}</label>              <div className="col-sm-8"> -              <span>{metas.calendar ? metas.calendar.name : 'Aucun'}</span> +              <span>{metas.calendar ? metas.calendar.name : I18n.time_tables.edit.metas.no_calendar}</span>              </div>            </div>            {/* day_types */}            <div className="form-group">              <label htmlFor="" className="control-label col-sm-4"> -              Journées d'applications pour les périodes ci-dessous +              {I18n.time_tables.edit.metas.day_types}              </label>              <div className="col-sm-8">                <div className="form-group labelled-checkbox-group"> @@ -112,7 +112,7 @@ const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelec                            type="checkbox"                            checked={day ? 'checked' : ''}                            /> -                        <span className='lcbx-group-item-label'>{weekDays[i]}</span> +                        <span className='lcbx-group-item-label'>{weekDays()[i]}</span>                        </label>                      </div>                    </div> @@ -135,4 +135,8 @@ Metas.propTypes = {    onUnselect2Tags: PropTypes.func.isRequired  } +Metas.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = Metas diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js index 3234a3fd7..d494109cc 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js @@ -1,5 +1,5 @@  var React = require('react') -var PropTypes = require('react').PropTypes +var { PropTypes } = require('react')  var _ = require('lodash')  let monthsArray = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'] @@ -32,7 +32,7 @@ const makeYearsOptions = (yearSelected) => {    return arr  } -const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm, onValidatePeriodForm}) => ( +const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm, onValidatePeriodForm}, {I18n}) => (    <div className="container-fluid">      <div className="row">        <div className="col lg-6 col-lg-offset-3"> @@ -44,7 +44,7 @@ const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriod                    <div>                      <div className="form-group">                        <label htmlFor="" className="control-label required"> -                        Début de période +                        {I18n.time_tables.edit.period_form.begin}                          <abbr title="requis">*</abbr>                        </label>                      </div> @@ -52,7 +52,7 @@ const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriod                    <div>                      <div className="form-group">                        <label htmlFor="" className="control-label required"> -                        Fin de période +                      {I18n.time_tables.edit.period_form.end}                          <abbr title="requis">*</abbr>                        </label>                      </div> @@ -103,14 +103,14 @@ const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriod                    className='btn btn-link'                    onClick={onClosePeriodForm}                  > -                  Annuler +                  {I18n.cancel}                  </button>                  <button                    type='button'                    className='btn btn-outline-primary mr-sm'                    onClick={() => onValidatePeriodForm(modal.modalProps, timetable.time_table_periods, metas, _.filter(timetable.time_table_dates, ['in_out', true]))}                  > -                  Valider +                  {I18n.actions.submit}                  </button>                </div>              </div> @@ -122,7 +122,7 @@ const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriod                  className='btn btn-outline-primary'                  onClick={onOpenAddPeriodForm}                  > -                Ajouter une période +                {I18n.time_tables.actions.add_period}                </button>              </div>            } @@ -142,4 +142,8 @@ PeriodForm.propTypes = {    timetable: PropTypes.object.isRequired  } +PeriodForm.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = PeriodForm diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js index cf4cbfb32..704e21331 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js @@ -1,11 +1,10 @@  var React = require('react') -var Component = require('react').Component -var PropTypes = require('react').PropTypes +var { Component, PropTypes } = require('react')  var actions = require('../actions')  class PeriodManager extends Component { -  constructor(props) { -    super(props) +  constructor(props, context) { +    super(props, context)    }    toEndPeriod(curr, end) { @@ -82,4 +81,8 @@ PeriodManager.propTypes = {    onOpenEditPeriodForm: PropTypes.func.isRequired  } +PeriodManager.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = PeriodManager diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js index ca44d3a07..f56509b99 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js @@ -35,7 +35,7 @@ class PeriodsInDay extends Component {    render() {      return (        <div -        className={this.isIn(this.props.currentDate)} +        className={this.isIn(this.props.currentDate) + (this.props.metas.day_types[this.props.day.wday] || !this.props.day.in_periods ? '' : ' out_from_daytypes')}        >          {this.props.value.map((p, i) => {            if(!p.deleted){ diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/TagsSelect2.js b/app/assets/javascripts/es6_browserified/time_tables/components/TagsSelect2.js index a1f41a693..46188cdd1 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/TagsSelect2.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/TagsSelect2.js @@ -9,8 +9,8 @@ var path = window.location.pathname.split('/', 4).join('/')  var _ = require('lodash')  class TagsSelect2 extends React.Component{ -  constructor(props) { -    super(props) +  constructor(props, context) { +    super(props, context)    }    mapKeys(array){ @@ -38,7 +38,7 @@ class TagsSelect2 extends React.Component{            allowClear: true,            theme: 'bootstrap',            width: '100%', -          placeholder: 'Ajoutez ou cherchez une étiquette...', +          placeholder: this.context.I18n.time_tables.edit.select2.tag.placeholder,            ajax: {              url: origin + path + '/tags.json',              dataType: 'json', @@ -74,4 +74,8 @@ const formatRepo = (props) => {    if(props.name) return props.name  } +TagsSelect2.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = TagsSelect2 diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js index 71621c874..93a0a90fe 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/TimeTableDay.js @@ -1,6 +1,5 @@  var React = require('react') -var Component = require('react').Component -var PropTypes = require('react').PropTypes +var { Component, PropTypes } = require('react')  class TimeTableDay extends Component {    constructor(props) { diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js b/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js index 3af1a11a4..22e971c6b 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Timetable.js @@ -1,14 +1,13 @@  var React = require('react') -var Component = require('react').Component -var PropTypes = require('react').PropTypes +var { Component, PropTypes}  = require('react')  var TimeTableDay = require('./TimeTableDay')  var PeriodsInDay = require('./PeriodsInDay')  var ExceptionsInDay = require('./ExceptionsInDay')  var actions = require('../actions')  class Timetable extends Component{ -  constructor(props){ -    super(props) +  constructor(props, context){ +    super(props, context)    }    currentDate(mFirstday, day) { @@ -31,11 +30,11 @@ class Timetable extends Component{          <div className="table table-2entries mb-sm">            <div className="t2e-head w20">              <div className="th"> -              <div className="strong">Synthèse</div> +              <div className="strong">{this.context.I18n.time_tables.synthesis}</div>              </div> -            <div className="td"><span>Journées d'application</span></div> -            <div className="td"><span>Périodes</span></div> -            <div className="td"><span>Exceptions</span></div> +            <div className="td"><span>{this.context.I18n.time_tables.edit.day_types}</span></div> +            <div className="td"><span>{this.context.I18n.time_tables.edit.periods}</span></div> +            <div className="td"><span>{this.context.I18n.time_tables.edit.exceptions}</span></div>            </div>            <div className="t2e-item-list w80">              <div> @@ -60,13 +59,14 @@ class Timetable extends Component{                  {this.props.timetable.current_month.map((d, i) =>                    <div                      key={i} -                    className={'td-group' + (this.props.metas.day_types[d.wday] || !d.in_periods ? '' : ' out_from_daytypes') + (d.wday == 0 ? ' last_wday' : '')} +                    className={'td-group'+ (d.wday == 0 ? ' last_wday' : '')}                    >                      {/* day_types */} -                    <div className="td"></div> +                    <div className={"td" + (this.props.metas.day_types[d.wday] || !d.in_periods ? '' : ' out_from_daytypes') }></div>                      {/* periods */}                      <PeriodsInDay +                      day={d}                        index={i}                        value={this.props.timetable.time_table_periods}                        currentDate={this.currentDate(this.props.timetable.current_periode_range, d.mday)} @@ -77,11 +77,16 @@ class Timetable extends Component{                      {/* exceptions */}                      <ExceptionsInDay +                      day={d}                        index={i}                        value={this.props.timetable}                        currentDate={d.date}                        metas={this.props.metas}                        blueDaytype={this.props.metas.day_types[d.wday]} +                      onAddIncludedDate={this.props.onAddIncludedDate} +                      onRemoveIncludedDate={this.props.onRemoveIncludedDate} +                      onAddExcludedDate={this.props.onAddExcludedDate} +                      onRemoveExcludedDate={this.props.onRemoveExcludedDate}                        onExcludeDateFromPeriod={this.props.onExcludeDateFromPeriod}                        onIncludeDateInPeriod={this.props.onIncludeDateInPeriod}                      /> @@ -105,4 +110,8 @@ Timetable.propTypes = {    onIncludeDateInPeriod: PropTypes.func.isRequired  } +Timetable.contextTypes = { +  I18n: PropTypes.object +} +  module.exports = Timetable diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/App.js b/app/assets/javascripts/es6_browserified/time_tables/containers/App.js index 02f0ddbd8..f12fb8a71 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/containers/App.js +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/App.js @@ -1,6 +1,6 @@  var React = require('react')  var connect = require('react-redux').connect -var Component = require('react').Component +var { Component, PropTypes} = require('react')  var actions = require('../actions')  var Metas = require('./Metas')  var Timetable = require('./Timetable') @@ -10,11 +10,18 @@ var SaveTimetable = require('./SaveTimetable')  var ConfirmModal = require('./ConfirmModal')  var ErrorModal = require('./ErrorModal') +const clone = require('../../helpers/clone') +const I18n = clone(window, "I18n", true) +  class App extends Component {    componentDidMount(){      this.props.onLoadFirstPage()    } +  getChildContext() { +    return { I18n } +  } +    render(){      return(        <div className='row'> @@ -41,6 +48,10 @@ const mapDispatchToProps = (dispatch) => {    }  } +App.childContextTypes = { +  I18n: PropTypes.object +} +  const timeTableApp = connect(null, mapDispatchToProps)(App)  module.exports = timeTableApp diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js b/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js index 639a1e2ab..a37e99982 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js @@ -15,6 +15,18 @@ const mapDispatchToProps = (dispatch) => {      onDeletePeriod: (index, dayTypes) =>{        dispatch(actions.deletePeriod(index, dayTypes))      }, +    onAddIncludedDate: (index, dayTypes, date) => { +      dispatch(actions.addIncludedDate(index, dayTypes, date)) +    }, +    onRemoveIncludedDate: (index, dayTypes, date) => { +      dispatch(actions.removeIncludedDate(index, dayTypes, date)) +    }, +    onAddExcludedDate: (index, dayTypes, date) => { +      dispatch(actions.addExcludedDate(index, dayTypes, date)) +    }, +    onRemoveExcludedDate: (index, dayTypes, date) => { +      dispatch(actions.removeExcludedDate(index, dayTypes, date)) +    },      onExcludeDateFromPeriod: (index, dayTypes, date) => {        dispatch(actions.excludeDateFromPeriod(index, dayTypes, date))      }, diff --git a/app/assets/javascripts/es6_browserified/time_tables/index.js b/app/assets/javascripts/es6_browserified/time_tables/index.js index a91747991..6c352df6b 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/index.js +++ b/app/assets/javascripts/es6_browserified/time_tables/index.js @@ -5,6 +5,9 @@ var createStore = require('redux').createStore  var timeTablesApp = require('./reducers')  var App = require('./containers/App') +const clone = require('../helpers/clone') +const actionType = clone(window, "actionType", true) +  // logger, DO NOT REMOVE  // var applyMiddleware = require('redux').applyMiddleware  // var createLogger = require('redux-logger') @@ -13,7 +16,7 @@ var App = require('./containers/App')  var initialState = {    status: { -    actionType: window.actionType, +    actionType: actionType,      policy: window.perms,      fetchSuccess: true,      isFetching: false diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js index 2ce084efd..ab5ed3d91 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js @@ -15,8 +15,10 @@ const metas = (state = {}, action) => {      case 'RECEIVE_MONTH':        let dt = (typeof state.day_types === 'string') ? actions.strToArrayDayTypes(state.day_types) : state.day_types        return _.assign({}, state, {day_types: dt}) -    case 'INCLUDE_DATE_IN_PERIOD': -    case 'EXCLUDE_DATE_FROM_PERIOD': +    case 'ADD_INCLUDED_DATE': +    case 'REMOVE_INCLUDED_DATE': +    case 'ADD_EXCLUDED_DATE': +    case 'REMOVE_EXCLUDED_DATE':      case 'DELETE_PERIOD':      case 'VALIDATE_PERIOD_FORM':        return _.assign({}, state, {calendar: null}) diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js index 45fec6b5f..f38b124d9 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js @@ -20,8 +20,10 @@ const pagination = (state = {}, action) => {      case 'CHANGE_PAGE':        toggleOnConfirmModal()        return _.assign({}, state, {currentPage : action.page, stateChanged: false}) -    case 'INCLUDE_DATE_IN_PERIOD': -    case 'EXCLUDE_DATE_FROM_PERIOD': +    case 'ADD_INCLUDED_DATE': +    case 'REMOVE_INCLUDED_DATE': +    case 'ADD_EXCLUDED_DATE': +    case 'REMOVE_EXCLUDED_DATE':      case 'DELETE_PERIOD':      case 'VALIDATE_PERIOD_FORM':      case 'UPDATE_COMMENT': diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js index 390bdffb0..712808abd 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js @@ -1,7 +1,6 @@  const _ = require('lodash')  var actions = require('../actions') -let newState = {} -let newDates = [] +let newState, newPeriods, newDates, newCM  const timetable = (state = {}, action) => {    switch (action.type) { @@ -11,14 +10,14 @@ const timetable = (state = {}, action) => {          current_periode_range: action.json.current_periode_range,          periode_range: action.json.periode_range,          time_table_periods: action.json.time_table_periods, -        time_table_dates: action.json.time_table_dates +        time_table_dates: _.sortBy(action.json.time_table_dates, ['date'])        }) -      return _.assign({}, fetchedState, {current_month: actions.updateSynthesis(fetchedState, actions.strToArrayDayTypes(action.json.day_types))}) +      return _.assign({}, fetchedState, {current_month: actions.updateSynthesis(fetchedState)})      case 'RECEIVE_MONTH':        newState = _.assign({}, state, {          current_month: action.json.days        }) -      return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, actions.strToArrayDayTypes(action.json.day_types))}) +      return _.assign({}, newState, {current_month: actions.updateSynthesis(newState)})      case 'GO_TO_PREVIOUS_PAGE':      case 'GO_TO_NEXT_PAGE':        let nextPage = action.nextPage ? 1 : -1 @@ -31,34 +30,44 @@ const timetable = (state = {}, action) => {        actions.fetchTimeTables(action.dispatch, action.page)        return _.assign({}, state, {current_periode_range: action.page})      case 'DELETE_PERIOD': -      let ttperiods = state.time_table_periods.map((period, i) =>{ +      newPeriods = state.time_table_periods.map((period, i) =>{          if(i == action.index){            period.deleted = true          }          return period        }) -      newState = _.assign({}, state, {time_table_periods : ttperiods}) -      return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.dayTypes)}) -    case 'INCLUDE_DATE_IN_PERIOD': -      newDates = actions.checkIfTTHasDate(state.time_table_dates, {date: action.date, in_out: true}) -      let newCMi = state.current_month.map((d, i) => { -        if(i == action.index){ -          d.include_date = !d.include_date -        } +      let deletedPeriod = Array.of(state.time_table_periods[action.index]) +      newDates = _.reject(state.time_table_dates, d => actions.isInPeriod(deletedPeriod, d.date) && !d.in_out) +      newState = _.assign({}, state, {time_table_periods : newPeriods, time_table_dates: newDates}) +      return _.assign({}, newState, { current_month: actions.updateSynthesis(newState)}) +    case 'ADD_INCLUDED_DATE': +      newDates = state.time_table_dates.concat({date: action.date, in_out: true}) +      newCM = state.current_month.map((d, i) => { +        if (i == action.index) d.include_date = true          return d        }) -      newState = _.assign({}, state, {current_month: newCMi, time_table_dates: newDates}) -      return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.dayTypes)}) -    case 'EXCLUDE_DATE_FROM_PERIOD': -      newDates = actions.checkIfTTHasDate(state.time_table_dates, {date: action.date, in_out: false}) -      let newCMe = state.current_month.map((d, i) => { -        if(i == action.index){ -          d.excluded_date = !d.excluded_date -        } +      return _.assign({}, state, {current_month: newCM, time_table_dates: newDates}) +    case 'REMOVE_INCLUDED_DATE': +      newDates = _.reject(state.time_table_dates, ['date', action.date]) +      newCM = state.current_month.map((d, i) => { +        if (i == action.index) d.include_date = false +        return d +      }) +      return _.assign({}, state, {current_month: newCM, time_table_dates: newDates}) +    case 'ADD_EXCLUDED_DATE': +      newDates = state.time_table_dates.concat({date: action.date, in_out: false}) +      newCM = state.current_month.map((d, i) => { +        if (i == action.index) d.excluded_date = true +        return d +      }) +      return _.assign({}, state, {current_month: newCM, time_table_dates: newDates}) +    case 'REMOVE_EXCLUDED_DATE': +      newDates = _.reject(state.time_table_dates, ['date', action.date]) +      newCM = state.current_month.map((d, i) => { +        if (i == action.index) d.excluded_date = false          return d        }) -      newState = _.assign({}, state, {current_month: newCMe, time_table_dates: newDates}) -      return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.dayTypes)}) +      return _.assign({}, state, {current_month: newCM, time_table_dates: newDates})      case 'UPDATE_DAY_TYPES':        // We get the week days of the activated day types to reject the out_dates that that are out of newDayTypes        let weekDays = _.reduce(action.dayTypes, (array, dt, i) => { @@ -67,11 +76,17 @@ const timetable = (state = {}, action) => {        }, [])        newDates =  _.reject(state.time_table_dates, (d) => { -        return d.in_out == false && !weekDays.includes(new Date(d.date).getDay()) +        let weekDay = new Date(d.date).getDay() + +        if (d.in_out) { +          return actions.isInPeriod(state.time_table_periods, d.date) && weekDays.includes(weekDay) +        } else { +          return !weekDays.includes(weekDay) +        }        })        return _.assign({}, state, {time_table_dates: newDates})      case 'UPDATE_CURRENT_MONTH_FROM_DAYTYPES': -      return _.assign({}, state, {current_month: actions.updateSynthesis(state, action.dayTypes)}) +      return _.assign({}, state, {current_month: actions.updateSynthesis(state)})      case 'VALIDATE_PERIOD_FORM':        if (action.error != '') return state @@ -81,8 +96,10 @@ const timetable = (state = {}, action) => {        let newPeriods = JSON.parse(JSON.stringify(action.timeTablePeriods))        if (action.modalProps.index !== false){ -        newPeriods[action.modalProps.index].period_start = period_start -        newPeriods[action.modalProps.index].period_end = period_end +        let updatedPeriod = newPeriods[action.modalProps.index] +        updatedPeriod.period_start = period_start +        updatedPeriod.period_end = period_end +        newDates = _.reject(state.time_table_dates, d => actions.isInPeriod(newPeriods, d.date) && !d.in_out)        }else{          let newPeriod = {            period_start: period_start, @@ -90,8 +107,10 @@ const timetable = (state = {}, action) => {          }          newPeriods.push(newPeriod)        } -      newState =_.assign({}, state, {time_table_periods: newPeriods}) -      return _.assign({}, newState, {current_month: actions.updateSynthesis(newState, action.metas.day_types)}) +       +      newDates = newDates || state.time_table_dates +      newState =_.assign({}, state, {time_table_periods: newPeriods, time_table_dates: newDates}) +      return _.assign({}, newState, {current_month: actions.updateSynthesis(newState)})      default:        return state    } diff --git a/app/assets/stylesheets/modules/_timetables.sass b/app/assets/stylesheets/modules/_timetables.sass index 84f1af043..b06972ef9 100644 --- a/app/assets/stylesheets/modules/_timetables.sass +++ b/app/assets/stylesheets/modules/_timetables.sass @@ -85,11 +85,14 @@          &:not(:last-child) > .td            border-right: 2px solid $darkgrey -      &.out_from_daytypes -        background-image: linear-gradient(45deg, rgba($grey, 0.15) 0%, rgba($grey, 0.15) 49%, rgba($grey, 0.5) 50%, rgba($grey, 0.15) 51%, rgba($grey, 0.15) 99%, rgba($grey, 0.15) 100%) -        background-size: 25px 25px +      // &.out_from_daytypes +      //   background-image: linear-gradient(45deg, rgba($grey, 0.15) 0%, rgba($grey, 0.15) 49%, rgba($grey, 0.5) 50%, rgba($grey, 0.15) 51%, rgba($grey, 0.15) 99%, rgba($grey, 0.15) 100%) +      //   background-size: 25px 25px        > .td +        &.out_from_daytypes +          background-image: linear-gradient(45deg, rgba($grey, 0.15) 0%, rgba($grey, 0.15) 49%, rgba($grey, 0.5) 50%, rgba($grey, 0.15) 51%, rgba($grey, 0.15) 99%, rgba($grey, 0.15) 100%) +          background-size: 25px 25px          &.in_periods            background-color: rgba($gold, 0.5)            border-left-color: rgba($gold, 0.5) diff --git a/app/controllers/compliance_control_sets_controller.rb b/app/controllers/compliance_control_sets_controller.rb index 6edfa3fcc..20ddcbe97 100644 --- a/app/controllers/compliance_control_sets_controller.rb +++ b/app/controllers/compliance_control_sets_controller.rb @@ -1,10 +1,12 @@  class ComplianceControlSetsController < BreadcrumbController    defaults resource_class: ComplianceControlSet +  before_action :ransack_updated_at_params, only: [:index]    respond_to :html    def index      index! do |format| -      @q_for_form = @compliance_control_sets.ransack(params[:q]) +      scope = ransack_period @compliance_control_sets +      @q_for_form = scope.ransack(params[:q])        format.html {          @compliance_control_sets = decorate_compliance_control_sets(@q_for_form.result)        } @@ -24,8 +26,41 @@ class ComplianceControlSetsController < BreadcrumbController      )    end +  protected + +  # def begin_of_association_chain +  #   current_organisation +  # end +    private +  def ransack_updated_at_params +    start_date = [] +    end_date = [] + +    if params[:q] && params[:q][:updated_at] && !params[:q][:updated_at].has_value?(nil) && !params[:q][:updated_at].has_value?("") +      [1, 2, 3].each do |key| +        start_date <<  params[:q][:updated_at]["begin(#{key}i)"].to_i +        end_date <<  params[:q][:updated_at]["end(#{key}i)"].to_i +      end +      params[:q].delete([:updated_at]) +      @begin_range = DateTime.new(*start_date,0,0,0) rescue nil +      @end_range = DateTime.new(*end_date,23,59,59) rescue nil +    end +  end + +  # Fake ransack filter +  def ransack_period scope +    return scope unless !!@begin_range && !!@end_range + +    if @begin_range > @end_range +      flash.now[:error] = t('imports.filters.error_period_filter') +    else +      scope = scope.where_updated_at_between(@begin_range, @end_range) +    end +    scope +  end +    def compliance_control_set_params      params.require(:compliance_control_set).permit(:name, :id)    end diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index fa8919f20..f2e65e445 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -1,4 +1,5 @@  class ImportsController < BreadcrumbController +  include PolicyChecker    skip_before_action :authenticate_user!, only: [:download]    defaults resource_class: Import, collection_name: 'imports', instance_name: 'import'    before_action :ransack_started_at_params, only: [:index] @@ -89,7 +90,6 @@ class ImportsController < BreadcrumbController    def ransack_status_params      if params[:q] -      binding.pry        return params[:q].delete(:status_eq_any) if params[:q][:status_eq_any].empty? || ( (Import.status.values & params[:q][:status_eq_any]).length >= 4 )        params[:q][:status_eq_any].push("new", "running") if params[:q][:status_eq_any].include?("pending")        params[:q][:status_eq_any].push("aborted", "canceled") if params[:q][:status_eq_any].include?("failed") diff --git a/app/decorators/company_decorator.rb b/app/decorators/company_decorator.rb index 402bd3ab6..764cce3a0 100644 --- a/app/decorators/company_decorator.rb +++ b/app/decorators/company_decorator.rb @@ -19,8 +19,6 @@ class CompanyDecorator < Draper::Decorator      links = []      if h.policy(Chouette::Company).create? -      require 'pry' -      binding.pry        links << Link.new(          content: h.t('companies.actions.new'),          href: h.new_line_referential_company_path(context[:referential]) diff --git a/app/helpers/compliance_control_sets_helper.rb b/app/helpers/compliance_control_sets_helper.rb index 3e02e0ef7..6ba4bed4f 100644 --- a/app/helpers/compliance_control_sets_helper.rb +++ b/app/helpers/compliance_control_sets_helper.rb @@ -1,2 +1,7 @@  module ComplianceControlSetsHelper + +  def organisations_filters_values +    [current_organisation, Organisation.find_by_name("STIF")].uniq +  end +  end diff --git a/app/models/chouette/stif_netex_objectid.rb b/app/models/chouette/stif_netex_objectid.rb index a0a91668a..93e7a1e85 100644 --- a/app/models/chouette/stif_netex_objectid.rb +++ b/app/models/chouette/stif_netex_objectid.rb @@ -3,7 +3,7 @@ class Chouette::StifNetexObjectid < String      parts.present?    end -  @@format = /^([A-Za-z_]+):([A-Za-z]+):([0-9A-Za-z_-]+):([A-Za-z]+)$/ +  @@format = /^([A-Za-z_-]+):([A-Za-z]+):([0-9A-Za-z_-]+):([A-Za-z]+)$/    cattr_reader :format    def parts diff --git a/app/models/chouette/vehicle_journey_at_stop.rb b/app/models/chouette/vehicle_journey_at_stop.rb index 156cc761f..a4a4a02c8 100644 --- a/app/models/chouette/vehicle_journey_at_stop.rb +++ b/app/models/chouette/vehicle_journey_at_stop.rb @@ -41,7 +41,7 @@ module Chouette            :arrival_day_offset,            I18n.t(              'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', -            local_id: vehicle_journey.objectid.local_id, +            short_id: vehicle_journey.objectid.short_id,              max: DAY_OFFSET_MAX + 1            )          ) @@ -52,7 +52,7 @@ module Chouette            :departure_day_offset,            I18n.t(              'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', -            local_id: vehicle_journey.objectid.local_id, +            short_id: vehicle_journey.objectid.short_id,              max: DAY_OFFSET_MAX + 1            )          ) diff --git a/app/models/compliance_control_set.rb b/app/models/compliance_control_set.rb index cefdfbf1f..4dafd48c7 100644 --- a/app/models/compliance_control_set.rb +++ b/app/models/compliance_control_set.rb @@ -4,4 +4,8 @@ class ComplianceControlSet < ActiveRecord::Base    validates :name, presence: true +  scope :where_updated_at_between, ->(start_date, end_date) do +    where('updated_at BETWEEN ? AND ?', start_date, end_date) +  end +  end diff --git a/app/models/concerns/stif_netex_attributes_support.rb b/app/models/concerns/stif_netex_attributes_support.rb index 795872755..0d569b613 100644 --- a/app/models/concerns/stif_netex_attributes_support.rb +++ b/app/models/concerns/stif_netex_attributes_support.rb @@ -49,7 +49,7 @@ module StifNetexAttributesSupport    end    def provider_id -    self.referential.workbench.organisation.name.parameterize +    self.referential.workbench.organisation.name.parameterize.underscore    end    def boiv_id diff --git a/app/models/referential.rb b/app/models/referential.rb index af08aa868..c7b52ddf8 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -45,6 +45,8 @@ class Referential < ActiveRecord::Base    has_many :stop_areas, through: :stop_area_referential    belongs_to :workbench +  belongs_to :referential_suite +    scope :ready, -> { where(ready: true) }    scope :in_periode, ->(periode) { where(id: referential_ids_in_periode(periode)) }    scope :include_metadatas_lines, ->(line_ids) { where('referential_metadata.line_ids && ARRAY[?]::bigint[]', line_ids) } diff --git a/app/models/referential_suite.rb b/app/models/referential_suite.rb new file mode 100644 index 000000000..9fd25ef3f --- /dev/null +++ b/app/models/referential_suite.rb @@ -0,0 +1,6 @@ +class ReferentialSuite < ActiveRecord::Base +  belongs_to :new, class_name: 'Referential' +  belongs_to :current, class_name: 'Referential' + +  has_many :referentials +end diff --git a/app/models/vehicle_journey_import.rb b/app/models/vehicle_journey_import.rb index 44a6d457e..250f3a9e9 100644 --- a/app/models/vehicle_journey_import.rb +++ b/app/models/vehicle_journey_import.rb @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -  class VehicleJourneyImport    include ActiveModel::Validations    include ActiveModel::Conversion diff --git a/app/policies/import_policy.rb b/app/policies/import_policy.rb index 9e1d99a66..b12dcc167 100644 --- a/app/policies/import_policy.rb +++ b/app/policies/import_policy.rb @@ -4,4 +4,16 @@ class ImportPolicy < ApplicationPolicy        scope      end    end + +  def create? +    !archived? && user.has_permission?('imports.create') +  end + +  def destroy? +    !archived? && user.has_permission?('imports.destroy') +  end + +  def update? +    !archived? && user.has_permission?('imports.update') +  end  end diff --git a/app/views/compliance_control_sets/_filters.html.slim b/app/views/compliance_control_sets/_filters.html.slim index 7ee050636..56cac8bd2 100644 --- a/app/views/compliance_control_sets/_filters.html.slim +++ b/app/views/compliance_control_sets/_filters.html.slim @@ -5,6 +5,18 @@        span.input-group-btn          button.btn.btn-default type='submit'            span.fa.fa-search +  .ffg-row +    .form-group.togglable +      = f.label t('activerecord.models.organisation.one'), required: false, class: 'control-label' +      = f.input :organisation_name_eq_any, collection: organisations_filters_values, as: :check_boxes, label: false, label_method: lambda {|w| ("<span>#{w.name}</span>").html_safe}, required: false, wrapper_html: {class: 'checkbox_list'} +       +    .form-group.togglable +      = f.label Import.human_attribute_name(:updated_at), required: false, class: 'control-label' +      .filter_menu +        = f.simple_fields_for :updated_at do |p| +          = p.input :begin, as: :date, label: false, wrapper_html: {class: 'date smart_date filter_menu-item'}, default: @begin_range, include_blank: @begin_range ? false : true +          = p.input :end, as: :date, label: false, wrapper_html: {class: 'date smart_date filter_menu-item'}, default: @end_range, include_blank: @end_range ? false : true +    .actions      = link_to t('actions.erase'), @compliance_control_set, class: 'btn btn-link' -    = f.submit t('actions.filter'), class: 'btn btn-default', id: 'referential_filter_btn' +    = f.submit t('actions.filter'), class: 'btn btn-default', id: 'referential_filter_btn'
\ No newline at end of file diff --git a/app/views/compliance_control_sets/index.html.slim b/app/views/compliance_control_sets/index.html.slim index 95833a01c..aee1595ef 100644 --- a/app/views/compliance_control_sets/index.html.slim +++ b/app/views/compliance_control_sets/index.html.slim @@ -24,7 +24,7 @@                    key: :name, \                    attribute: 'name', \                    link_to: lambda do |compliance_control_set| \ -                      compliance_control_set_path(@compliance_control_sets, compliance_control_set) \ +                      compliance_control_set_path(compliance_control_set) \                      end \                  ), \                  TableBuilderHelper::Column.new( \ @@ -45,6 +45,7 @@                  ) \                ],                      sortable: true, +                    links: [:show],                      cls: 'table has-filter has-search'      - unless @compliance_control_sets.any?        .row.mt-xs diff --git a/app/views/routes/_form.html.slim b/app/views/routes/_form.html.slim index 244b427dc..24c0d3c4a 100644 --- a/app/views/routes/_form.html.slim +++ b/app/views/routes/_form.html.slim @@ -26,7 +26,8 @@  // Get JSON data for route stop points  = javascript_tag do -  | window.itinerary_stop = "#{URI.escape(route_json_for_edit(@route))}" +  | window.itinerary_stop = "#{URI.escape(route_json_for_edit(@route))}"; +  | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe};  / StopPoints Reactux component  = javascript_include_tag 'es6_browserified/itineraries/index.js' diff --git a/app/views/time_tables/edit.html.slim b/app/views/time_tables/edit.html.slim index ed55dc4e3..cc6f31489 100644 --- a/app/views/time_tables/edit.html.slim +++ b/app/views/time_tables/edit.html.slim @@ -11,6 +11,6 @@  = javascript_tag do    | window.actionType = "#{raw params[:action]}"; -  | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; +  | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe};  = javascript_include_tag 'es6_browserified/time_tables/index.js' diff --git a/config/locales/actions.en.yml b/config/locales/actions.en.yml index 8dea51ca3..c34462d0d 100644 --- a/config/locales/actions.en.yml +++ b/config/locales/actions.en.yml @@ -20,8 +20,10 @@ en:      filter: 'Filter'      erase: 'Erase'      create_api_key: "Create an API key" +    select: Select    or: "or"    cancel: "Cancel" +  back: "Go Back"    search_hint: "Type in a search term"    no_result_text: "No Results"    searching_term: "Searching..." diff --git a/config/locales/actions.fr.yml b/config/locales/actions.fr.yml index 01fc06326..df16d2aab 100644 --- a/config/locales/actions.fr.yml +++ b/config/locales/actions.fr.yml @@ -20,8 +20,10 @@ fr:      filter: 'Filtrer'      erase: 'Effacer'      create_api_key: "Créer une clé d'API" +    select: Sélectionner    or: "ou"    cancel: "Annuler" +  back: "Retour"    search_hint: "Entrez un texte à rechercher"    no_result_text: "Aucun résultat"    searching_term: "Recherche en cours..." diff --git a/config/locales/compliance_control_sets.en.yml b/config/locales/compliance_control_sets.en.yml new file mode 100644 index 000000000..497ca50c3 --- /dev/null +++ b/config/locales/compliance_control_sets.en.yml @@ -0,0 +1,24 @@ +fr: +  compliance_control_sets: +    index: +      title: Control games +      new: Creating a control set +      edit: Editing a Control Game +    actions: +          new: Add +          edit: Edit +          destroy: Delete +          destroy_confirm: Are you sure to remove the control games ? +    filters: +          name: Specify a control game name... +    search_no_results: No control game matches your search +  activerecord: +          models: +            compliance_control_set: Calendar +          attributes: +            compliance_control_set: +              name: Name +              assignment: Affectation +              owner_jdc: Owner of the control game +              control_numbers: Nb contrôle +              updated_at: Update
\ No newline at end of file diff --git a/config/locales/compliance_control_sets.fr.yml b/config/locales/compliance_control_sets.fr.yml index fedfeede7..f5bb7c67b 100644 --- a/config/locales/compliance_control_sets.fr.yml +++ b/config/locales/compliance_control_sets.fr.yml @@ -10,8 +10,8 @@ fr:            destroy: Supprimer            destroy_confirm: Etes vous sûr de supprimer ce jeux de contrôle ?      filters: -          name: 'Indiquez un nom de jeux de contrôle...' -    search_no_results: 'Aucun jeu de contrôle ne correspond à votre recherche' +          name: Indiquez un nom de jeux de contrôle... +    search_no_results: Aucun jeu de contrôle ne correspond à votre recherche    activerecord:            models:              compliance_control_set: Calendrier diff --git a/config/locales/routes.en.yml b/config/locales/routes.en.yml index 63d7b198c..3b1fd85cc 100644 --- a/config/locales/routes.en.yml +++ b/config/locales/routes.en.yml @@ -17,6 +17,25 @@ en:        title: "Add a new route"      edit:        title: "Update route %{route}" +      select2: +        placeholder: "Select a stop point..." +      map: +        stop_point_type: Stop point type +        short_name: Short name +        coordinates: Coordinates +        proj: Proj +        lat: Lat +        lon: Lon +        postal_code: Zip Code +        city: City +        comment: Comment +      stop_point: +        boarding: +          normal: Normal boarding +          forbidden: Forbidden boarding +        alighting: +          normal: Normal alighting +          forbidden: Forbidden alighting      show:        title: "Route %{route}"        stop_points: "Stop point on route list" diff --git a/config/locales/routes.fr.yml b/config/locales/routes.fr.yml index a494e60ec..43c40645d 100644 --- a/config/locales/routes.fr.yml +++ b/config/locales/routes.fr.yml @@ -17,6 +17,25 @@ fr:        title: "Ajouter un itinéraire"      edit:        title: "Editer l'itinéraire %{route}" +      select2: +        placeholder: "Sélectionnez un arrêt existant..." +      map: +        stop_point_type: Type d'arrêt +        short_name: Nom court +        coordinates: Coordonnées +        proj: Proj +        lat: Lat +        lon: Lon +        postal_code: Code Postal +        city: Commune +        comment: Commentaire +      stop_point: +        boarding: +          normal: Montée autorisée +          forbidden: Montée interdite +        alighting: +          normal: Descente autorisée +          forbidden: Descente interdite      show:        title: "Itinéraire %{route} de la ligne %{line}"        stop_points: "Liste des arrêts de l'itinéraire" diff --git a/config/locales/stop_points.en.yml b/config/locales/stop_points.en.yml index 1ef8002d0..d22d85731 100644 --- a/config/locales/stop_points.en.yml +++ b/config/locales/stop_points.en.yml @@ -52,5 +52,6 @@ en:    simple_form:      labels:        stop_point: +        name: Stop Point          for_boarding: "Pickup"          for_alighting: "Drop off" diff --git a/config/locales/stop_points.fr.yml b/config/locales/stop_points.fr.yml index d90041945..d3c873442 100644 --- a/config/locales/stop_points.fr.yml +++ b/config/locales/stop_points.fr.yml @@ -52,5 +52,6 @@ fr:    simple_form:      labels:        stop_point: +        name: Arrêt           for_boarding: "Montée"          for_alighting: "Descente" diff --git a/config/locales/time_tables.en.yml b/config/locales/time_tables.en.yml index d67e30edb..e68836f99 100644 --- a/config/locales/time_tables.en.yml +++ b/config/locales/time_tables.en.yml @@ -29,10 +29,39 @@ en:        title: "Duplicate timetable"      edit:        title: "Update timetable %{time_table}" +      day_types: Day types +      periods: Periods +      exceptions: Exceptions +      synthesis: Synthesis        error_modal:          title: "Error"          withoutPeriodsWithDaysTypes: "A timetable can't have day type(s) without period(s)." -        withPeriodsWithoutDayTypes: "A tiemetable can't have period(s) swithout day type(s)."   +        withPeriodsWithoutDayTypes: "A tiemetable can't have period(s) swithout day type(s)."  +      error_submit: +        periods_overlaps: "Periods cannot overlap in a timetable" +        dates_overlaps: "A period cannot overlap a date in a timetable" +      confirm_modal: +        title: "Confirm" +        message: "You are about to change pages. Do you want to validate your changes before this?" +      metas: +        name: Name +        calendar: Associated calendar +        no_calendar: None +        day_types: Periods day tpes +        days: +          1: Su +          2: Mo +          3: Tu +          4: We +          5: Th +          6: Fr +          7: Sa +      select2: +        tag: +          placeholder: Add or search a tag... +      period_form: +        begin: Period start +        end: Period end      show:        title: "Timetable %{time_table}"        dates: "Application dates" diff --git a/config/locales/time_tables.fr.yml b/config/locales/time_tables.fr.yml index 06d1d59e8..b85f7ca33 100644 --- a/config/locales/time_tables.fr.yml +++ b/config/locales/time_tables.fr.yml @@ -29,10 +29,39 @@ fr:        title: "Dupliquer un calendrier"      edit:        title: "Editer le calendrier %{time_table}" +      day_types: Journées d'application +      periods: Périodes +      exceptions: Exceptions +      synthesis: Synthèse        error_modal:          title: "Erreur"          withoutPeriodsWithDaysTypes: "Un calendrier d'application ne peut pas avoir de journée(s) d'application sans période(s)." -        withPeriodsWithoutDayTypes: "Un calendrier d'application ne peut pas avoir de période(s) sans journée(s) d'application."   +        withPeriodsWithoutDayTypes: "Un calendrier d'application ne peut pas avoir de période(s) sans journée(s) d'application." +      error_submit: +        periods_overlaps: "Les périodes ne peuvent pas se chevaucher" +        dates_overlaps: "Une période ne peut chevaucher une date dans un calendrier" +      confirm_modal: +        title: "Confirmation" +        message: "Vous vous apprêtez à changer de page. Voulez-vous valider vos modifications avant cela ?" +      metas: +        name: Nom +        calendar: Modèle de calendrier associée +        no_calendar: Aucun +        day_types: Journées d'applications pour les périodes ci-dessous +        days: +          1: Di +          2: Lu +          3: Ma +          4: Me +          5: Je +          6: Ve +          7: Sa +      select2: +        tag: +          placeholder: Ajoutez ou cherchez une étiquette... +      period_form: +        begin: Début de période +        end: Fin de période      show:        title: Calendrier %{time_table}        dates: "Dates d'application" diff --git a/config/locales/vehicle_journey_at_stops.en.yml b/config/locales/vehicle_journey_at_stops.en.yml index a96effa2b..8a3bd9394 100644 --- a/config/locales/vehicle_journey_at_stops.en.yml +++ b/config/locales/vehicle_journey_at_stops.en.yml @@ -1,4 +1,4 @@  en:    vehicle_journey_at_stops:      errors: -      day_offset_must_not_exceed_max: "The vehicle journey with ID %{local_id} cannot have times exceeding %{max} days" +      day_offset_must_not_exceed_max: "The vehicle journey with ID %{short_id} cannot have times exceeding %{max} days" diff --git a/config/locales/vehicle_journey_at_stops.fr.yml b/config/locales/vehicle_journey_at_stops.fr.yml index 3eff79cf4..f5139fb79 100644 --- a/config/locales/vehicle_journey_at_stops.fr.yml +++ b/config/locales/vehicle_journey_at_stops.fr.yml @@ -1,4 +1,4 @@  fr:    vehicle_journey_at_stops:      errors: -      day_offset_must_not_exceed_max: "La course avec l'identifiant %{local_id} ne peut pas avoir des horaires sur plus de %{max} jours" +      day_offset_must_not_exceed_max: "La course avec l'identifiant %{short_id} ne peut pas avoir des horaires sur plus de %{max} jours" diff --git a/db/migrate/20170922161352_create_referential_suites.rb b/db/migrate/20170922161352_create_referential_suites.rb new file mode 100644 index 000000000..5a8693d01 --- /dev/null +++ b/db/migrate/20170922161352_create_referential_suites.rb @@ -0,0 +1,10 @@ +class CreateReferentialSuites < ActiveRecord::Migration +  def change +    create_table :referential_suites do |t| +      t.bigint :new_id, index: true +      t.bigint :current_id, index: true + +      t.timestamps null: false +    end +  end +end diff --git a/db/migrate/20170922165315_add_referential_suite_to_referentials.rb b/db/migrate/20170922165315_add_referential_suite_to_referentials.rb new file mode 100644 index 000000000..a01ba4d40 --- /dev/null +++ b/db/migrate/20170922165315_add_referential_suite_to_referentials.rb @@ -0,0 +1,8 @@ +class AddReferentialSuiteToReferentials < ActiveRecord::Migration +  def change +    add_reference :referentials, :referential_suite, +      index: true, +      foreign_key: true, +      type: :bigint +  end +end diff --git a/db/schema.rb b/db/schema.rb index 2b62fa7f1..89f002aee 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@  #  # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170918103913) do +ActiveRecord::Schema.define(version: 20170922165315) do    # These are extensions that must be enabled in order to support this database    enable_extension "plpgsql" @@ -602,6 +602,16 @@ ActiveRecord::Schema.define(version: 20170918103913) do    add_index "referential_metadata", ["referential_id"], name: "index_referential_metadata_on_referential_id", using: :btree    add_index "referential_metadata", ["referential_source_id"], name: "index_referential_metadata_on_referential_source_id", using: :btree +  create_table "referential_suites", id: :bigserial, force: :cascade do |t| +    t.integer  "new_id",     limit: 8 +    t.integer  "current_id", limit: 8 +    t.datetime "created_at",           null: false +    t.datetime "updated_at",           null: false +  end + +  add_index "referential_suites", ["current_id"], name: "index_referential_suites_on_current_id", using: :btree +  add_index "referential_suites", ["new_id"], name: "index_referential_suites_on_new_id", using: :btree +    create_table "referentials", id: :bigserial, force: :cascade do |t|      t.string   "name"      t.string   "slug" @@ -622,9 +632,11 @@ ActiveRecord::Schema.define(version: 20170918103913) do      t.datetime "archived_at"      t.integer  "created_from_id",          limit: 8      t.boolean  "ready",                              default: false +    t.integer  "referential_suite_id",     limit: 8    end    add_index "referentials", ["created_from_id"], name: "index_referentials_on_created_from_id", using: :btree +  add_index "referentials", ["referential_suite_id"], name: "index_referentials_on_referential_suite_id", using: :btree    create_table "route_sections", id: :bigserial, force: :cascade do |t|      t.integer  "departure_id",       limit: 8 @@ -987,6 +999,7 @@ ActiveRecord::Schema.define(version: 20170918103913) do    add_foreign_key "journey_patterns", "stop_points", column: "departure_stop_point_id", name: "departure_point_fkey", on_delete: :nullify    add_foreign_key "journey_patterns_stop_points", "journey_patterns", name: "jpsp_jp_fkey", on_delete: :cascade    add_foreign_key "journey_patterns_stop_points", "stop_points", name: "jpsp_stoppoint_fkey", on_delete: :cascade +  add_foreign_key "referentials", "referential_suites"    add_foreign_key "route_sections", "stop_areas", column: "arrival_id"    add_foreign_key "route_sections", "stop_areas", column: "departure_id"    add_foreign_key "routes", "routes", column: "opposite_route_id", name: "route_opposite_route_fkey" diff --git a/lib/model_attribute.rb b/lib/model_attribute.rb new file mode 100644 index 000000000..4d246853a --- /dev/null +++ b/lib/model_attribute.rb @@ -0,0 +1,101 @@ +class ModelAttribute +  attr_reader :klass, :name, :data_type + +  def self.all +    @__all__ ||= [] +  end + +  def self.define(klass, name, data_type) +    all << new(klass, name, data_type) +  end + +  def self.classes +    all +      .map(&:klass) +      .uniq +      .map(&:to_s) +      .map(&:camelize) +  end + +  def self.group_by_class +    all.group_by(&:klass) +  end + +  def self.from_code(code) +    klass, name = code.split('#').map(&:to_sym) + +    methods_by_class(klass).select do |model_attr| +      model_attr.name == name +    end.first +  end + +  def self.methods_by_class(klass) +    all.select do |model_attr| +      model_attr.klass == klass +    end +  end + +  def self.methods_by_class_and_type(klass, type) +    methods_by_class(klass).select do |model_attr| +      model_attr.data_type == type +    end +  end + +  def initialize(klass, name, data_type) +    @klass = klass +    @name = name +    @data_type = data_type +  end + +  # Chouette::Route +  define :route, :name, :string +  define :route, :published_name, :string +  define :route, :comment, :string +  define :route, :number, :string +  define :route, :direction, :string +  define :route, :wayback, :string + +  # Chouette::JourneyPattern +  define :journey_pattern, :name, :string +  define :journey_pattern, :published_name, :string +  define :journey_pattern, :comment, :string +  define :journey_pattern, :registration_number, :string +  define :journey_pattern, :section_status, :integer + +  # Chouette::VehicleJourney +  define :vehicle_journey, :comment, :string +  define :vehicle_journey, :status_value, :string +  define :vehicle_journey, :transport_mode, :string +  define :vehicle_journey, :facility, :string +  define :vehicle_journey, :published_journey_name, :string +  define :vehicle_journey, :published_journey_identifier, :string +  define :vehicle_journey, :vehicle_type_identifier, :string +  define :vehicle_journey, :number, :integer +  define :vehicle_journey, :mobility_restricted_suitability, :boolean +  define :vehicle_journey, :flexible_service, :boolean + +  # Chouette::Footnote +  define :footnote, :code, :string +  define :footnote, :label, :string + +  # Chouette::TimeTable +  define :time_table, :version, :string +  define :time_table, :comment, :string +  define :time_table, :start_date, :date +  define :time_table, :end_date, :date +  define :time_table, :color, :string + +  # Chouette::RoutingConstraintZone +  define :routing_constraint_zone, :name, :string + +  def code +    "#{@klass}##{@name}" +  end + +  def ==(other) +    self.class === other && +      klass == other.klass && +      name == other.name && +      data_type == other.data_type +  end +end diff --git a/lib/stif/permission_translator.rb b/lib/stif/permission_translator.rb index 2bc565968..15d5ffc89 100644 --- a/lib/stif/permission_translator.rb +++ b/lib/stif/permission_translator.rb @@ -19,6 +19,7 @@ module Stif          access_points          connection_links calendars          footnotes +        imports          journey_patterns          referentials routes routing_constraint_zones          time_tables @@ -29,7 +30,6 @@ module Stif      end      def destructive_permissions_for(models) -      @__destructive_permissions_for__ ||=          models.product( %w{create destroy update} ).map{ |model_action| model_action.join('.') }      end diff --git a/spec/controllers/imports_controller_spec.rb b/spec/controllers/imports_controller_spec.rb index f07190496..22be9f6ed 100644 --- a/spec/controllers/imports_controller_spec.rb +++ b/spec/controllers/imports_controller_spec.rb @@ -5,10 +5,16 @@ RSpec.describe ImportsController, :type => :controller do    let(:import)    { create :import, workbench: workbench }    describe 'GET #new' do -    it 'should be successful' do +    it 'should be successful if authorized' do        get :new, workbench_id: workbench.id        expect(response).to be_success      end + +    it 'should be unsuccessful unless authorized' do +      remove_permissions('imports.create', from_user: @user, save: true) +      get :new, workbench_id: workbench.id +      expect(response).not_to be_success +    end    end    describe 'GET #download' do @@ -18,4 +24,5 @@ RSpec.describe ImportsController, :type => :controller do        expect( response.body ).to eq(import.file.read)      end    end +  end diff --git a/spec/javascripts/time_table/actions_spec.js b/spec/javascripts/time_table/actions_spec.js index f052aeece..a7344586d 100644 --- a/spec/javascripts/time_table/actions_spec.js +++ b/spec/javascripts/time_table/actions_spec.js @@ -171,28 +171,52 @@ describe('actions', () => {      expect(actions.validatePeriodForm(modalProps, timeTablePeriods, metas, timetableInDates, error)).toEqual(expectedAction)    }) -  it('should create an action to include date in period', () => { +  it('should create an action to add an included date', () => {      let index = 1      let date = actions.formatDate(new Date)      const expectedAction = { -      type: 'INCLUDE_DATE_IN_PERIOD', +      type: 'ADD_INCLUDED_DATE',        index,        dayTypes,        date      } -    expect(actions.includeDateInPeriod(index, dayTypes, date)).toEqual(expectedAction) +    expect(actions.addIncludedDate(index, dayTypes, date)).toEqual(expectedAction)    }) -  it('should create an action to exclude date from period', () => { +  it('should create an action to remove an included dat', () => {      let index = 1      let date = actions.formatDate(new Date)      const expectedAction = { -      type: 'EXCLUDE_DATE_FROM_PERIOD', +      type: 'REMOVE_INCLUDED_DATE',        index,        dayTypes,        date      } -    expect(actions.excludeDateFromPeriod(index, dayTypes, date)).toEqual(expectedAction) +    expect(actions.removeIncludedDate(index, dayTypes, date)).toEqual(expectedAction) +  }) + +  it('should create an action to add an excluded date in period', () => { +    let index = 1 +    let date = actions.formatDate(new Date) +    const expectedAction = { +      type: 'ADD_EXCLUDED_DATE', +      index, +      dayTypes, +      date +    } +    expect(actions.addExcludedDate(index, dayTypes, date)).toEqual(expectedAction) +  }) + +  it('should create an action to remove an excluded date from period', () => { +    let index = 1 +    let date = actions.formatDate(new Date) +    const expectedAction = { +      type: 'REMOVE_EXCLUDED_DATE', +      index, +      dayTypes, +      date +    } +    expect(actions.removeExcludedDate(index, dayTypes, date)).toEqual(expectedAction)    })    it('should create an action to open confirm modal', () => { diff --git a/spec/javascripts/time_table/reducers/modal_spec.js b/spec/javascripts/time_table/reducers/modal_spec.js index 570eb85ed..05d58a138 100644 --- a/spec/javascripts/time_table/reducers/modal_spec.js +++ b/spec/javascripts/time_table/reducers/modal_spec.js @@ -171,12 +171,14 @@ describe('modal reducer', () => {      let ttperiods = []      let ttdates = [] +    let metas = []      expect(        modalReducer(state, {          type: 'VALIDATE_PERIOD_FORM',          modalProps : modProps,          timeTablePeriods: ttperiods, +        metas: metas,          timetableInDates: ttdates,          error: 'La date de départ doit être antérieure à la date de fin'        }) @@ -289,9 +291,12 @@ describe('modal reducer', () => {        index: false,        error: ''      } -    let ttperiods3 = [] +    let ttperiods3 = []      let ttdates3 = [{date: "2017-08-04", include_date: true}] +    let metas = { +      day_types: [true,true,true,true,true,true,true] +    }      let newModalProps3 = {        active: true, @@ -315,6 +320,7 @@ describe('modal reducer', () => {          modalProps : modProps3,          timeTablePeriods: ttperiods3,          timetableInDates: ttdates3, +        metas: metas,          error: "Une période ne peut chevaucher une date dans un calendrier"        })      ).toEqual(Object.assign({}, state3, {modalProps: newModalProps3})) diff --git a/spec/javascripts/time_table/reducers/pagination_spec.js b/spec/javascripts/time_table/reducers/pagination_spec.js index 5da58427e..3c1edb9c5 100644 --- a/spec/javascripts/time_table/reducers/pagination_spec.js +++ b/spec/javascripts/time_table/reducers/pagination_spec.js @@ -76,20 +76,38 @@ describe('pagination reducer', () => {      ).toEqual(Object.assign({}, state, {currentPage : page, stateChanged: false}))    }) -  it('should handle INCLUDE_DATE_IN_PERIOD', () => { +  it('should handle ADD_INCLUDED_DATE', () => {      expect(        paginationReducer(state, { -        type: 'INCLUDE_DATE_IN_PERIOD' +        type: 'ADD_INCLUDED_DATE'        })      ).toEqual(Object.assign({}, state, {stateChanged: true}))    }) -  it('should handle EXCLUDE_DATE_FROM_PERIOD', () => { + +  it('should handle REMOVE_INCLUDED_DATE', () => { +    expect( +      paginationReducer(state, { +        type: 'REMOVE_INCLUDED_DATE' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) + +  it('should handle ADD_EXCLUDED_DATE', () => {      expect(        paginationReducer(state, { -        type: 'EXCLUDE_DATE_FROM_PERIOD' +        type: 'ADD_EXCLUDED_DATE'        })      ).toEqual(Object.assign({}, state, {stateChanged: true}))    }) + +  it('should handle REMOVE_EXCLUDED_DATE', () => { +    expect( +      paginationReducer(state, { +        type: 'REMOVE_EXCLUDED_DATE' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +    it('should handle DELETE_PERIOD', () => {      expect(        paginationReducer(state, { diff --git a/spec/javascripts/time_table/reducers/timetable_spec.js b/spec/javascripts/time_table/reducers/timetable_spec.js index 6585a78a0..21f6d236d 100644 --- a/spec/javascripts/time_table/reducers/timetable_spec.js +++ b/spec/javascripts/time_table/reducers/timetable_spec.js @@ -6,14 +6,13 @@ const dispatch = function(){}  let arrDayTypes = [true, true, true, true, true, true, true]  let strDayTypes = 'LuMaMeJeVeSaDi'  let time_table_periods = [{"id":261,"period_start":"2017-02-23","period_end":"2017-03-05"},{"id":262,"period_start":"2017-03-15","period_end":"2017-03-25"},{"id":263,"period_start":"2017-04-04","period_end":"2017-04-14"},{"id":264,"period_start":"2017-04-24","period_end":"2017-05-04"},{"id":265,"period_start":"2017-05-14","period_end":"2017-05-24"}] +let time_table_dates = []  let current_periode_range = "2017-05-01"  let periode_range = ["2014-05-01","2014-06-01","2014-07-01","2014-08-01","2014-09-01","2014-10-01","2014-11-01","2014-12-01","2015-01-01","2015-02-01","2015-03-01","2015-04-01","2015-05-01","2015-06-01","2015-07-01","2015-08-01","2015-09-01","2015-10-01","2015-11-01","2015-12-01","2016-01-01","2016-02-01","2016-03-01","2016-04-01","2016-05-01","2016-06-01","2016-07-01","2016-08-01","2016-09-01","2016-10-01","2016-11-01","2016-12-01","2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01","2018-08-01","2018-09-01","2018-10-01","2018-11-01","2018-12-01","2019-01-01","2019-02-01","2019-03-01","2019-04-01","2019-05-01","2019-06-01","2019-07-01","2019-08-01","2019-09-01","2019-10-01","2019-11-01","2019-12-01","2020-01-01","2020-02-01","2020-03-01","2020-04-01","2020-05-01"]  let current_month = [{"day":"lundi","date":"2017-05-01","wday":1,"wnumber":"18","mday":1,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-02","wday":2,"wnumber":"18","mday":2,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-03","wday":3,"wnumber":"18","mday":3,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-04","wday":4,"wnumber":"18","mday":4,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-05","wday":5,"wnumber":"18","mday":5,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-06","wday":6,"wnumber":"18","mday":6,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-07","wday":0,"wnumber":"18","mday":7,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-08","wday":1,"wnumber":"19","mday":8,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-09","wday":2,"wnumber":"19","mday":9,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-10","wday":3,"wnumber":"19","mday":10,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-11","wday":4,"wnumber":"19","mday":11,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-12","wday":5,"wnumber":"19","mday":12,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-13","wday":6,"wnumber":"19","mday":13,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-14","wday":0,"wnumber":"19","mday":14,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-15","wday":1,"wnumber":"20","mday":15,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-16","wday":2,"wnumber":"20","mday":16,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-17","wday":3,"wnumber":"20","mday":17,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-18","wday":4,"wnumber":"20","mday":18,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-19","wday":5,"wnumber":"20","mday":19,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-20","wday":6,"wnumber":"20","mday":20,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-21","wday":0,"wnumber":"20","mday":21,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-22","wday":1,"wnumber":"21","mday":22,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-23","wday":2,"wnumber":"21","mday":23,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-24","wday":3,"wnumber":"21","mday":24,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-25","wday":4,"wnumber":"21","mday":25,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-26","wday":5,"wnumber":"21","mday":26,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-27","wday":6,"wnumber":"21","mday":27,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-28","wday":0,"wnumber":"21","mday":28,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-29","wday":1,"wnumber":"22","mday":29,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-30","wday":2,"wnumber":"22","mday":30,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-31","wday":3,"wnumber":"22","mday":31,"include_date":false,"excluded_date":false}]  let newCurrentMonth = [{"day":"lundi","date":"2017-05-01","wday":1,"wnumber":"18","mday":1,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mardi","date":"2017-05-02","wday":2,"wnumber":"18","mday":2,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mercredi","date":"2017-05-03","wday":3,"wnumber":"18","mday":3,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"jeudi","date":"2017-05-04","wday":4,"wnumber":"18","mday":4,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"vendredi","date":"2017-05-05","wday":5,"wnumber":"18","mday":5,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"samedi","date":"2017-05-06","wday":6,"wnumber":"18","mday":6,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"dimanche","date":"2017-05-07","wday":0,"wnumber":"18","mday":7,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"lundi","date":"2017-05-08","wday":1,"wnumber":"19","mday":8,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mardi","date":"2017-05-09","wday":2,"wnumber":"19","mday":9,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mercredi","date":"2017-05-10","wday":3,"wnumber":"19","mday":10,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"jeudi","date":"2017-05-11","wday":4,"wnumber":"19","mday":11,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"vendredi","date":"2017-05-12","wday":5,"wnumber":"19","mday":12,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"samedi","date":"2017-05-13","wday":6,"wnumber":"19","mday":13,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"dimanche","date":"2017-05-14","wday":0,"wnumber":"19","mday":14,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"lundi","date":"2017-05-15","wday":1,"wnumber":"20","mday":15,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mardi","date":"2017-05-16","wday":2,"wnumber":"20","mday":16,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mercredi","date":"2017-05-17","wday":3,"wnumber":"20","mday":17,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"jeudi","date":"2017-05-18","wday":4,"wnumber":"20","mday":18,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"vendredi","date":"2017-05-19","wday":5,"wnumber":"20","mday":19,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"samedi","date":"2017-05-20","wday":6,"wnumber":"20","mday":20,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"dimanche","date":"2017-05-21","wday":0,"wnumber":"20","mday":21,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"lundi","date":"2017-05-22","wday":1,"wnumber":"21","mday":22,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mardi","date":"2017-05-23","wday":2,"wnumber":"21","mday":23,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mercredi","date":"2017-05-24","wday":3,"wnumber":"21","mday":24,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"jeudi","date":"2017-05-25","wday":4,"wnumber":"21","mday":25,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"vendredi","date":"2017-05-26","wday":5,"wnumber":"21","mday":26,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"samedi","date":"2017-05-27","wday":6,"wnumber":"21","mday":27,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"dimanche","date":"2017-05-28","wday":0,"wnumber":"21","mday":28,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"lundi","date":"2017-05-29","wday":1,"wnumber":"22","mday":29,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mardi","date":"2017-05-30","wday":2,"wnumber":"22","mday":30,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mercredi","date":"2017-05-31","wday":3,"wnumber":"22","mday":31,"include_date":false,"excluded_date":false,"in_periods":false}] -let time_table_dates = [] -  let json = {    current_month: current_month,    current_periode_range: current_periode_range, @@ -46,7 +45,7 @@ describe('timetable reducer with empty state', () => {        current_periode_range: current_periode_range,        periode_range: periode_range,        time_table_periods: time_table_periods, -      time_table_dates: time_table_dates +      time_table_dates: []      }      expect(        timetableReducer(state, { @@ -125,24 +124,46 @@ describe('timetable reducer with filled state', () => {      ).toEqual(Object.assign({}, state, {current_periode_range: newPage}))    }) -  it('should handle DELETE_PERIOD', () => { -    state.time_table_periods[0].deleted = true +  it('should handle DELETE_PERIOD and remove excluded days that were in period', () => { +    state.time_table_dates.push({date: "2017-05-01", in_out: false}) +    state.current_month[0].excluded_date = true +    state.time_table_periods[3].deleted = true + +    let begin = new Date(state.time_table_periods[3].period_start) +    let end = new Date(state.time_table_periods[3].period_end) + +    let newState = Object.assign({}, state, { +      time_table_dates: [], +      current_month: state.current_month.map((d, i) => { +        if (new Date(d.date) >=  begin && new Date(d.date) <= end) { +          d.excluded_date = false +          d.in_periods = false +        } +        return d +      }) +    })      expect(        timetableReducer(state, {          type: 'DELETE_PERIOD', -        index: 0, +        index: 3,          dayTypes: arrDayTypes        }) -    ).toEqual(state) +    ).toEqual(newState)    }) -  it('should handle INCLUDE_DATE_IN_PERIOD and add in_day if TT doesnt have it', () => { +  it('should handle ADD_INCLUDED_DATE', () => {      let newDates = state.time_table_dates.concat({date: "2017-05-05", in_out: true}) -    let newState = Object.assign({}, state, {time_table_dates: newDates}) -    state.current_month[4].include_date = true + +    let newCM = newCurrentMonth.map((d,i) => { +      if (i == 4) d.include_date = true +      return d +    }) + +    let newState = Object.assign({}, state, {time_table_dates: newDates, current_month: newCM}) +      expect(        timetableReducer(state, { -        type: 'INCLUDE_DATE_IN_PERIOD', +        type: 'ADD_INCLUDED_DATE',          index: 4,          dayTypes: arrDayTypes,          date: "2017-05-05" @@ -150,13 +171,20 @@ describe('timetable reducer with filled state', () => {      ).toEqual(newState)    }) -  it('should handle INCLUDE_DATE_IN_PERIOD and remove in_day if TT has it', () => { +  it('should handle REMOVE_INCLUDED_DATE', () => {      state.current_month[4].include_date = true      state.time_table_dates.push({date: "2017-05-05", in_out: true}) -    let newState = Object.assign({}, state, {time_table_dates: []}) + +    let newCM = newCurrentMonth.map((d,i) => { +      if (i == 4) d.include_date = false +      return d +    }) + +    let newDates = state.time_table_dates.filter(d => d.date != "2017-05-05" && d.in_out != true ) +    let newState = Object.assign({}, state, {time_table_dates: newDates, current_month: newCM})      expect(        timetableReducer(state, { -        type: 'INCLUDE_DATE_IN_PERIOD', +        type: 'REMOVE_INCLUDED_DATE',          index: 4,          dayTypes: arrDayTypes,          date: "2017-05-05" @@ -164,13 +192,22 @@ describe('timetable reducer with filled state', () => {      ).toEqual(newState)    }) -  it('should handle EXCLUDE_DATE_FROM_PERIOD and add out_day if TT doesnt have it', () => { +  it('should handle ADD_EXCLUDED_DATE', () => {      let newDates = state.time_table_dates.concat({date: "2017-05-01", in_out: false}) -    let newState = Object.assign({}, state, {time_table_dates: newDates}) -    state.current_month[0].excluded_date = true + +    let newCM = newCurrentMonth.map((d,i) => { +      if (i == 0){ +        d.include_date = false +        d.excluded_date = true +      }  +      return d +    }) + +    let newState = Object.assign({}, state, {time_table_dates: newDates, current_month: newCM}) +      expect(        timetableReducer(state, { -        type: 'EXCLUDE_DATE_FROM_PERIOD', +        type: 'ADD_EXCLUDED_DATE',          index: 0,          dayTypes: arrDayTypes,          date: "2017-05-01" @@ -178,13 +215,13 @@ describe('timetable reducer with filled state', () => {      ).toEqual(newState)    }) -  it('should handle EXCLUDE_DATE_FROM_PERIOD and remove out_day if TT has it', () => { +  it('should handle REMOVE_EXCLUDED_DATE', () => {      state.time_table_dates = [{date: "2017-05-01", in_out: false}] -    state.current_month[0].excluded_date = true +    state.current_month[0].excluded_date = false      let newState = Object.assign({}, state, {time_table_dates: []})      expect(        timetableReducer(state, { -        type: 'EXCLUDE_DATE_FROM_PERIOD', +        type: 'REMOVE_EXCLUDED_DATE',          index: 0,          dayTypes: arrDayTypes,          date: "2017-05-01" @@ -194,9 +231,10 @@ describe('timetable reducer with filled state', () => {    it('should handle UPDATE_DAY_TYPES and remove out_day that are out of day types', () => {      state.time_table_dates = [{date: "2017-05-01", in_out: false}] -    let newArrDayTypes = arrDayTypes.slice(0) -    newArrDayTypes[1] = false -    let newState = Object.assign({}, state, {time_table_dates: []}) +    let newArrDayTypes = Array.from(arrDayTypes, (dt, i) => { +      if (i == 1) dt = false +      return dt +    })      expect(        timetableReducer(state, {          type: 'UPDATE_DAY_TYPES', @@ -205,9 +243,19 @@ describe('timetable reducer with filled state', () => {      ).toEqual([])    }) +  it('should handle UPDATE_DAY_TYPES and remove in_day that are in day types and in period', () => { +    state.time_table_dates = [{ date: "2017-05-16", in_out: true }] +    expect( +      timetableReducer(state, { +        type: 'UPDATE_DAY_TYPES', +        dayTypes: arrDayTypes +      }).time_table_dates +    ).toEqual([]) +  }) +    it('should handle VALIDATE_PERIOD_FORM and add period if modalProps index = false', () => {      let newPeriods = state.time_table_periods.concat({"period_start": "2018-05-15", "period_end": "2018-05-24"}) -    let newState = Object.assign({}, state, {time_table_periods: newPeriods}) +    let newState = Object.assign({}, state, {time_table_periods: newPeriods, time_table_dates: []})      let modalProps = {        active: false,        begin: { @@ -236,4 +284,53 @@ describe('timetable reducer with filled state', () => {        })      ).toEqual(newState)    }) + +  it('should handle VALIDATE_PERIOD_FORM and update period if modalProps index != false', () => { + +    let begin = new Date(state.time_table_periods[0].period_start) +    let end = new Date(state.time_table_periods[0].period_end) +    let newCM = newCurrentMonth.map((d) => { +      if (new Date (d.date) >= begin && new Date(d.date) <= end) { +        d.in_periods = false +        d.excluded_date = false +      } +      return d +    }) + +    let newPeriods = state.time_table_periods.map( (p,i) => { +        if (i == 0) { +          p.period_start = "2018-05-15" +          p.period_end = "2018-05-24" +        } +        return p +    }) +    let newState = Object.assign({}, state, {time_table_periods: newPeriods}) + +    let modalProps = { +      active: false, +      begin: { +        day: '15', +        month: '05', +        year: '2018' +      }, +      end: { +        day: '24', +        month: '05', +        year: '2018' +      }, +      error: '', +      index: 0 +    } +    expect( +      timetableReducer(state, { +        type: 'VALIDATE_PERIOD_FORM', +        modalProps: modalProps, +        timeTablePeriods: state.time_table_periods, +        metas: { +          day_types: arrDayTypes +        }, +        timetableInDates: state.time_table_dates.filter(d => d.in_out == true) +      }) +    ).toEqual(newState) +  })  }) diff --git a/spec/lib/model_attribute_spec.rb b/spec/lib/model_attribute_spec.rb new file mode 100644 index 000000000..cdba87a90 --- /dev/null +++ b/spec/lib/model_attribute_spec.rb @@ -0,0 +1,111 @@ +RSpec.describe ModelAttribute do +  before(:each) do +    ModelAttribute.instance_variable_set(:@__all__, []) +  end + +  describe ".define" do +    it "adds a new instance of ModelAttribute to .all" do +      expect do +        ModelAttribute.define(:route, :name, :string) +      end.to change { ModelAttribute.all.length }.by(1) + +      model_attr = ModelAttribute.all.last + +      expect(model_attr).to be_an_instance_of(ModelAttribute) +      expect(model_attr.klass).to eq(:route) +      expect(model_attr.name).to eq(:name) +      expect(model_attr.data_type).to eq(:string) +    end +  end + +  describe ".classes" do +    it "returns the list of classes of ModelAttributes in .all" do +      ModelAttribute.define(:route, :name, :string) +      ModelAttribute.define(:journey_pattern, :name, :string) +      ModelAttribute.define(:time_table, :start_date, :date) + +      expect(ModelAttribute.classes).to match_array([ +        'Route', +        'JourneyPattern', +        'TimeTable' +      ]) +    end +  end + +  describe ".from_code" do +    it "returns a ModelAttribute from a given code" do +      ModelAttribute.define(:journey_pattern, :name, :string) + +      expect(ModelAttribute.from_code('journey_pattern#name')).to eq( +        ModelAttribute.new(:journey_pattern, :name, :string) +      ) +    end +  end + +  describe ".group_by_class" do +    it "returns all ModelAttributes grouped by klass" do +      ModelAttribute.define(:route, :name, :string) +      ModelAttribute.define(:route, :published_name, :string) +      ModelAttribute.define(:journey_pattern, :name, :string) +      ModelAttribute.define(:vehicle_journey, :number, :integer) + +      expect(ModelAttribute.group_by_class).to eq({ +        route: [ +          ModelAttribute.new(:route, :name, :string), +          ModelAttribute.new(:route, :published_name, :string), +        ], +        journey_pattern: [ +          ModelAttribute.new(:journey_pattern, :name, :string), +        ], +        vehicle_journey: [ +          ModelAttribute.new(:vehicle_journey, :number, :integer) +        ] +      }) +    end +  end + +  describe ".methods_by_class" do +    it "returns all ModelAttributes for a given class" do +      ModelAttribute.define(:route, :name, :string) +      ModelAttribute.define(:route, :published_name, :string) +      ModelAttribute.define(:route, :direction, :string) +      ModelAttribute.define(:journey_pattern, :name, :string) + +      expect(ModelAttribute.methods_by_class(:route)).to match_array([ +        ModelAttribute.new(:route, :name, :string), +        ModelAttribute.new(:route, :published_name, :string), +        ModelAttribute.new(:route, :direction, :string) +      ]) +    end +  end + +  describe ".methods_by_class_and_type" do +    it "returns ModelAttributes of a certain class and type" do +      ModelAttribute.define(:route, :name, :string) +      ModelAttribute.define(:route, :checked_at, :date) +      ModelAttribute.define(:journey_pattern, :name, :string) +      ModelAttribute.define(:journey_pattern, :section_status, :integer) + +      expect(ModelAttribute.methods_by_class_and_type(:route, :string)).to match_array([ +        ModelAttribute.new(:route, :name, :string) +      ]) +    end +  end + +  describe "#code" do +    it "returns a string representation of the attribute" do +      model_attr = ModelAttribute.new(:route, :name, :string) + +      expect(model_attr.code).to eq('route#name') +    end +  end + +  describe "#==" do +    it "returns true when :klass, :name, and :data_type attributes match" do +      route_name = ModelAttribute.new(:route, :name, :string) +      other_route_name = ModelAttribute.new(:route, :name, :string) + +      expect(route_name == other_route_name).to be true +    end +  end +end diff --git a/spec/models/chouette/vehicle_journey_at_stop_spec.rb b/spec/models/chouette/vehicle_journey_at_stop_spec.rb index 4f9d12730..03e6fcb7d 100644 --- a/spec/models/chouette/vehicle_journey_at_stop_spec.rb +++ b/spec/models/chouette/vehicle_journey_at_stop_spec.rb @@ -51,7 +51,7 @@ RSpec.describe Chouette::VehicleJourneyAtStop, type: :model do        )        error_message = I18n.t(          'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', -        local_id: at_stop.vehicle_journey.objectid.local_id, +        short_id: at_stop.vehicle_journey.objectid.short_id,          max: bad_offset        ) diff --git a/spec/models/referential_spec.rb b/spec/models/referential_spec.rb index f9ace08cc..bb8fabb2e 100644 --- a/spec/models/referential_spec.rb +++ b/spec/models/referential_spec.rb @@ -10,6 +10,7 @@ describe Referential, :type => :model do    it { should have_many(:metadatas) }    it { should belong_to(:workbench) } +  it { should belong_to(:referential_suite) }    context ".referential_ids_in_periode" do      it 'should retrieve referential id in periode range' do diff --git a/spec/models/referential_suite_spec.rb b/spec/models/referential_suite_spec.rb new file mode 100644 index 000000000..771187b55 --- /dev/null +++ b/spec/models/referential_suite_spec.rb @@ -0,0 +1,5 @@ +RSpec.describe ReferentialSuite, type: :model do +  it { should belong_to(:new).class_name('Referential') } +  it { should belong_to(:current).class_name('Referential') } +  it { should have_many(:referentials) } +end diff --git a/spec/policies/api_key_policy_spec.rb b/spec/policies/api_key_policy_spec.rb index f0242978e..3638a05b2 100644 --- a/spec/policies/api_key_policy_spec.rb +++ b/spec/policies/api_key_policy_spec.rb @@ -21,7 +21,7 @@ RSpec.describe ApiKeyPolicy do      end      context 'permission present → '  do        it 'allows a user with a different organisation' do -        add_permissions('api_keys.create', for_user: user) +        add_permissions('api_keys.create', to_user: user)          expect_it.to permit(user_context, record)        end      end @@ -40,7 +40,7 @@ RSpec.describe ApiKeyPolicy do      context 'permission present → '  do        before do -        add_permissions('api_keys.update', for_user: user) +        add_permissions('api_keys.update', to_user: user)        end        it 'denies a user with a different organisation' do diff --git a/spec/policies/import_policy_spec.rb b/spec/policies/import_policy_spec.rb new file mode 100644 index 000000000..fd9f3172c --- /dev/null +++ b/spec/policies/import_policy_spec.rb @@ -0,0 +1,41 @@ +RSpec.describe ImportPolicy, type: :policy do + +  let( :record ){ build_stubbed :import } +  before { stub_policy_scope(record) } + +  # +  #  Non Destructive +  #  --------------- + +  context 'Non Destructive actions →' do +    permissions :index? do +      it_behaves_like 'always allowed', 'anything', archived: true +    end +    permissions :show? do +      it_behaves_like 'always allowed', 'anything', archived: true +    end +  end + + +  # +  #  Destructive +  #  ----------- + +  context 'Destructive actions →' do +    permissions :create? do +      it_behaves_like 'permitted policy', 'imports.create', archived: true +    end +    permissions :destroy? do +      it_behaves_like 'permitted policy', 'imports.destroy', archived: true +    end +    permissions :edit? do +      it_behaves_like 'permitted policy', 'imports.update', archived: true +    end +    permissions :new? do +      it_behaves_like 'permitted policy', 'imports.create', archived: true +    end +    permissions :update? do +      it_behaves_like 'permitted policy', 'imports.update', archived: true +    end +  end +end diff --git a/spec/policies/referential_policy_spec.rb b/spec/policies/referential_policy_spec.rb index 69d0eb17b..d00415fc6 100644 --- a/spec/policies/referential_policy_spec.rb +++ b/spec/policies/referential_policy_spec.rb @@ -9,7 +9,7 @@ RSpec.describe ReferentialPolicy, type: :policy do    permissions :create? do      it 'permissions present → allowed' do -      add_permissions('referentials.create', for_user: user) +      add_permissions('referentials.create', to_user: user)        expect_it.to permit(user_context, record)      end      it 'permissions absent → forbidden' do @@ -19,7 +19,7 @@ RSpec.describe ReferentialPolicy, type: :policy do    permissions :new? do      it 'permissions present → allowed' do -      add_permissions('referentials.create', for_user: user) +      add_permissions('referentials.create', to_user: user)        expect_it.to permit(user_context, record)      end      it 'permissions absent → forbidden' do @@ -53,7 +53,7 @@ RSpec.describe ReferentialPolicy, type: :policy do      context 'permission present →' do        before do -        add_permissions('referentials.update', for_user: user) +        add_permissions('referentials.update', to_user: user)        end        context 'same organisation →' do @@ -108,7 +108,7 @@ RSpec.describe ReferentialPolicy, type: :policy do      context 'permission present →' do        before do -        add_permissions('referentials.update', for_user: user) +        add_permissions('referentials.update', to_user: user)        end        context 'same organisation →' do diff --git a/spec/support/permissions.rb b/spec/support/permissions.rb index 467c07a32..13666aca3 100644 --- a/spec/support/permissions.rb +++ b/spec/support/permissions.rb @@ -18,6 +18,7 @@ module Support        connection_links        calendars        footnotes +      imports        journey_patterns        referentials        routes diff --git a/spec/support/pundit/policies.rb b/spec/support/pundit/policies.rb index d5bb63243..a3489d9db 100644 --- a/spec/support/pundit/policies.rb +++ b/spec/support/pundit/policies.rb @@ -3,18 +3,18 @@ require 'pundit/rspec'  module Support    module Pundit      module Policies -      def add_permissions(*permissions, for_user:) -        for_user.permissions ||= [] -        for_user.permissions += permissions.flatten +      def add_permissions(*permissions, to_user:) +        to_user.permissions ||= [] +        to_user.permissions += permissions.flatten        end        def create_user_context(user:, referential:)          UserContext.new(user, referential: referential)        end -      def add_permissions(*permissions, for_user:) -        for_user.permissions ||= [] -        for_user.permissions += permissions.flatten +      def remove_permissions(*permissions, from_user:, save: false) +        from_user.permissions -= permissions.flatten +        from_user.save! if save        end      end @@ -30,7 +30,7 @@ module Support        end        def with_user_permission(permission, &blk)          it "with user permission #{permission.inspect}" do -          add_permissions(permission, for_user: user) +          add_permissions(permission, to_user: user)            blk.()          end        end @@ -41,7 +41,7 @@ module Support          perms, options = permissions.partition{|x| String === x}          context "with permissions #{perms.inspect}...", *options do            before do -            add_permissions(*permissions, for_user: @user) +            add_permissions(*permissions, to_user: @user)            end            instance_eval(&blk)          end @@ -51,6 +51,7 @@ module Support  end  RSpec.configure do | c | +  c.include Support::Pundit::Policies, type: :controller    c.include Support::Pundit::Policies, type: :policy    c.extend Support::Pundit::PoliciesMacros, type: :policy    c.include Support::Pundit::Policies, type: :feature diff --git a/spec/support/pundit/shared_examples.rb b/spec/support/pundit/shared_examples.rb index 63a106759..49c6845da 100644 --- a/spec/support/pundit/shared_examples.rb +++ b/spec/support/pundit/shared_examples.rb @@ -18,7 +18,7 @@ RSpec.shared_examples 'always allowed' do    context 'different organisations →' do      before do -      add_permissions(permission, for_user: user) +      add_permissions(permission, to_user: user)      end      it "allows a user with a different organisation" do        expect_it.to permit(user_context, record) @@ -51,7 +51,7 @@ RSpec.shared_examples 'always forbidden' do    context 'different organisations →' do      before do -      add_permissions(permission, for_user: user) +      add_permissions(permission, to_user: user)      end      it "denies a user with a different organisation" do        expect_it.not_to permit(user_context, record) @@ -80,7 +80,7 @@ RSpec.shared_examples 'permitted policy and same organisation' do    context 'permission present → '  do      before do -      add_permissions(permission, for_user: user) +      add_permissions(permission, to_user: user)      end      it 'denies a user with a different organisation' do @@ -113,7 +113,7 @@ RSpec.shared_examples 'permitted policy' do    context 'permission present → '  do      before do -      add_permissions(permission, for_user: user) +      add_permissions(permission, to_user: user)      end      it 'allows user' do | 
