diff options
48 files changed, 468 insertions, 140 deletions
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/index.js b/app/assets/javascripts/es6_browserified/itineraries/index.js index bb06126f7..13d89bec5 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/index.js +++ b/app/assets/javascripts/es6_browserified/itineraries/index.js @@ -5,7 +5,9 @@ var createStore = require('redux').createStore var reducers = require('./reducers') var App = require('./components/App') var { handleForm, handleStopPoints } = require('./form_helper') -let datas = JSON.parse(decodeURIComponent(window.itinerary_stop)) +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 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 ba5d91568..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' @@ -149,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) { @@ -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,9 +311,9 @@ 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 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/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/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 a613549c3..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> @@ -111,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/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/timetable.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js index edb965065..712808abd 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/timetable.js @@ -96,9 +96,9 @@ const timetable = (state = {}, action) => { let newPeriods = JSON.parse(JSON.stringify(action.timeTablePeriods)) if (action.modalProps.index !== false){ - updatePeriod = newPeriods[action.modalProps.index] - updatePeriod.period_start = period_start - updatePeriod.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 = { diff --git a/app/controllers/compliance_control_sets_controller.rb b/app/controllers/compliance_control_sets_controller.rb index c6f4288a9..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) } @@ -32,6 +34,33 @@ class ComplianceControlSetsController < BreadcrumbController 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 3333dc535..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] 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/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/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 587f74ce4..56cac8bd2 100644 --- a/app/views/compliance_control_sets/_filters.html.slim +++ b/app/views/compliance_control_sets/_filters.html.slim @@ -9,6 +9,13 @@ .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' 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/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/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 |
