aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXinhui2017-06-28 17:20:23 +0200
committerXinhui2017-06-28 17:20:23 +0200
commitbed9795be0bb85d7c8c311fe4ee0fb12e46832b4 (patch)
tree18459aa9dcb81821a88f2acdf9ba72cee9be5616
parent641b1458236d2718a76ffaf0c04a5998623276bf (diff)
parentb260c832c8e129dbeacfe065d01bd0732dd80701 (diff)
downloadchouette-core-bed9795be0bb85d7c8c311fe4ee0fb12e46832b4.tar.bz2
Merge branch 'master' into staging
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js16
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js6
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js13
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js11
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js3
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/DuplicateVehicleJourney.js4
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js3
-rw-r--r--app/assets/javascripts/smart_date.coffee26
-rw-r--r--app/assets/stylesheets/components/_dropdown.sass2
-rw-r--r--app/assets/stylesheets/components/_tables.sass3
-rw-r--r--app/controllers/calendars_controller.rb8
-rw-r--r--app/controllers/devise/cas_sessions_controller.rb10
-rw-r--r--app/controllers/lines_controller.rb5
-rw-r--r--app/controllers/referentials_controller.rb28
-rw-r--r--app/controllers/routes_controller.rb6
-rw-r--r--app/controllers/routing_constraint_zones_controller.rb4
-rw-r--r--app/controllers/time_tables_controller.rb4
-rw-r--r--app/controllers/workbenches_controller.rb18
-rw-r--r--app/decorators/calendar_decorator.rb18
-rw-r--r--app/decorators/company_decorator.rb41
-rw-r--r--app/decorators/line_decorator.rb45
-rw-r--r--app/decorators/model_decorator.rb3
-rw-r--r--app/decorators/referential_decorator.rb56
-rw-r--r--app/decorators/route_decorator.rb64
-rw-r--r--app/decorators/routing_constraint_zone_decorator.rb42
-rw-r--r--app/decorators/time_table_decorator.rb53
-rw-r--r--app/helpers/lines_helper.rb4
-rw-r--r--app/helpers/links_helper.rb5
-rw-r--r--app/helpers/multiple_selection_toolbox_helper.rb40
-rw-r--r--app/helpers/table_builder_helper.rb234
-rw-r--r--app/helpers/table_builder_helper/column.rb36
-rw-r--r--app/helpers/table_builder_helper/custom_links.rb77
-rw-r--r--app/helpers/table_builder_helper/url.rb25
-rw-r--r--app/models/calendar.rb125
-rw-r--r--app/models/calendar/date_value.rb32
-rw-r--r--app/models/calendar/period.rb63
-rw-r--r--app/models/chouette/journey_pattern.rb3
-rw-r--r--app/models/chouette/time_table.rb63
-rw-r--r--app/models/chouette/vehicle_journey.rb2
-rw-r--r--app/models/clean_up.rb10
-rw-r--r--app/models/concerns/default_attributes_support.rb4
-rw-r--r--app/models/user.rb3
-rw-r--r--app/views/calendars/_date_value_fields.html.slim2
-rw-r--r--app/views/calendars/_filters.html.slim2
-rw-r--r--app/views/calendars/_period_fields.html.slim4
-rw-r--r--app/views/calendars/show.html.slim12
-rw-r--r--app/views/lines/show.html.slim17
-rw-r--r--app/views/referentials/_period_fields.html.slim4
-rw-r--r--app/views/referentials/show.html.slim28
-rw-r--r--app/views/routes/show.html.slim17
-rw-r--r--app/views/routing_constraint_zones/_filters.html.slim2
-rw-r--r--app/views/routing_constraint_zones/show.html.slim13
-rw-r--r--app/views/time_tables/_date_fields.html.slim2
-rw-r--r--app/views/time_tables/_excluded_date_fields.html.slim2
-rw-r--r--app/views/time_tables/_filter.html.slim4
-rw-r--r--app/views/time_tables/_period_fields.html.slim4
-rw-r--r--app/views/time_tables/show.html.slim21
-rw-r--r--app/views/workbenches/_filters.html.slim8
-rw-r--r--app/views/workbenches/show.html.slim53
-rwxr-xr-xbin/rails5
-rwxr-xr-xbin/rake5
-rw-r--r--config/deploy.rb2
-rw-r--r--config/deploy/dev.rb1
-rw-r--r--config/locales/calendars.en.yml2
-rw-r--r--config/locales/calendars.fr.yml2
-rw-r--r--config/locales/journey_patterns.en.yml6
-rw-r--r--config/locales/journey_patterns.fr.yml6
-rw-r--r--config/locales/routes.en.yml2
-rw-r--r--config/locales/routes.fr.yml2
-rw-r--r--lib/html_element.rb15
-rw-r--r--lib/link.rb10
-rw-r--r--spec/controllers/autocomplete_calendars_controller_spec.rb2
-rw-r--r--spec/factories/chouette_2_factories.rb79
-rw-r--r--spec/factories/clean_ups.rb1
-rw-r--r--spec/factories/compliance_check_results.rb8
-rw-r--r--spec/factories/compliance_check_tasks.rb8
-rw-r--r--spec/factories/export_log_messages.rb6
-rw-r--r--spec/factories/exports.rb5
-rw-r--r--spec/factories/import_tasks.rb10
-rw-r--r--spec/factories/kml_exports.rb5
-rw-r--r--spec/factories/organisations.rb6
-rw-r--r--spec/factories/referentials.rb12
-rw-r--r--spec/factories/rule_parameter_sets.rb9
-rw-r--r--spec/factories/time_table_combinations.rb3
-rw-r--r--spec/factories/users.rb10
-rw-r--r--spec/factories/vehicle_translations.rb6
-rw-r--r--spec/features/calendars_spec.rb72
-rw-r--r--spec/features/referentials_permissions_spec.rb2
-rw-r--r--spec/helpers/table_builder_helper/column_spec.rb23
-rw-r--r--spec/helpers/table_builder_helper/custom_links_spec.rb27
-rw-r--r--spec/helpers/table_builder_helper_spec.rb371
-rw-r--r--spec/javascripts/spec_helper.coffee32
-rw-r--r--spec/javascripts/vehicle_journeys/actions_spec.js6
-rw-r--r--spec/javascripts/vehicle_journeys/reducers/filters_spec.js4
-rw-r--r--spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js8
-rw-r--r--spec/models/calendar/date_value_spec.rb35
-rw-r--r--spec/models/calendar/period_spec.rb68
-rw-r--r--spec/models/calendar_spec.rb121
-rw-r--r--spec/models/chouette/journey_pattern_spec.rb83
-rw-r--r--spec/models/chouette/time_table_spec.rb103
-rw-r--r--spec/models/chouette/vehicle_journey_spec.rb31
-rw-r--r--spec/models/clean_up_spec.rb27
-rw-r--r--spec/support/pundit/policies.rb2
-rw-r--r--spec/views/lines/show.html.erb_spec.rb8
-rw-r--r--spec/views/time_tables/show.html.erb_spec.rb9
109 files changed, 2023 insertions, 657 deletions
diff --git a/Gemfile b/Gemfile
index 814a5ef14..203945992 100644
--- a/Gemfile
+++ b/Gemfile
@@ -161,6 +161,7 @@ group :test do
gem 'cucumber-rails', require: false
gem 'simplecov', :require => false
gem 'simplecov-rcov', :require => false
+ gem 'htmlbeautifier'
end
group :test, :development, :dev do
diff --git a/Gemfile.lock b/Gemfile.lock
index e87e6bb3b..eea6f4ba5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -261,6 +261,7 @@ GEM
hashdiff (0.3.2)
highline (1.7.8)
hike (1.2.3)
+ htmlbeautifier (1.3.1)
httparty (0.14.0)
multi_xml (>= 0.5.2)
i18n (0.8.1)
@@ -604,6 +605,7 @@ DEPENDENCIES
georuby-ext (= 0.0.5)
google-analytics-rails
has_array_of!
+ htmlbeautifier
i18n-tasks
inherited_resources
jbuilder (~> 2.0)
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 1a6c67b6b..a8a92c522 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js
@@ -61,30 +61,30 @@ const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriod
<div className="nested-fields">
<div className="wrapper">
<div>
- <div className={'form-group date' + (modal.modalProps.error ? ' has-error' : '')}>
+ <div className={'form-group date smart_date' + (modal.modalProps.error ? ' has-error' : '')}>
<div className="form-inline">
- <select value={formatNumber(modal.modalProps.begin.day)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'begin', 'day')} id="q_validity_period_begin_gteq_3i" className="date required form-control">
+ <select value={formatNumber(modal.modalProps.begin.day)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'day')} id="q_validity_period_begin_gteq_3i" className="date required form-control">
{makeDaysOptions(modal.modalProps.begin.day)}
</select>
- <select value={formatNumber(modal.modalProps.begin.month)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'begin', 'month')} id="q_validity_period_begin_gteq_2i" className="date required form-control">
+ <select value={formatNumber(modal.modalProps.begin.month)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'month')} id="q_validity_period_begin_gteq_2i" className="date required form-control">
{makeMonthsOptions(modal.modalProps.begin.month)}
</select>
- <select value={modal.modalProps.begin.year} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'begin', 'year')} id="q_validity_period_begin_gteq_1i" className="date required form-control">
+ <select value={modal.modalProps.begin.year} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'year')} id="q_validity_period_begin_gteq_1i" className="date required form-control">
{makeYearsOptions(modal.modalProps.begin.year)}
</select>
</div>
</div>
</div>
<div>
- <div className={'form-group date' + (modal.modalProps.error ? ' has-error' : '')}>
+ <div className={'form-group date smart_date' + (modal.modalProps.error ? ' has-error' : '')}>
<div className="form-inline">
- <select value={formatNumber(modal.modalProps.end.day)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'end', 'day')} id="q_validity_period_end_gteq_3i" className="date required form-control">
+ <select value={formatNumber(modal.modalProps.end.day)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'day')} id="q_validity_period_end_gteq_3i" className="date required form-control">
{makeDaysOptions(modal.modalProps.end.day)}
</select>
- <select value={formatNumber(modal.modalProps.end.month)} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'end', 'month')} id="q_validity_period_end_gteq_2i" className="date required form-control">
+ <select value={formatNumber(modal.modalProps.end.month)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'month')} id="q_validity_period_end_gteq_2i" className="date required form-control">
{makeMonthsOptions(modal.modalProps.end.month)}
</select>
- <select value={modal.modalProps.end.year} onChange={(e) => onUpdatePeriodForm(e.currentTarget.value, 'end', 'year')} id="q_validity_period_end_gteq_1i" className="date required form-control">
+ <select value={modal.modalProps.end.year} onChange={(e) => onUpdatePeriodForm(e, 'end', 'year')} id="q_validity_period_end_gteq_1i" className="date required form-control">
{makeYearsOptions(modal.modalProps.end.year)}
</select>
</div>
diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js b/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js
index b6004c7f1..a7edbc328 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js
@@ -18,8 +18,10 @@ const mapDispatchToProps = (dispatch) => {
onClosePeriodForm: () => {
dispatch(actions.closePeriodForm())
},
- onUpdatePeriodForm: (val, group, selectType) => {
- dispatch(actions.updatePeriodForm(val, group, selectType))
+ onUpdatePeriodForm: (e, group, selectType) => {
+ dispatch(actions.updatePeriodForm(e.currentTarget.value, group, selectType))
+ let selector = '#q_validity_period_' + group + '_gteq_3i'
+ dispatch(actions.updatePeriodForm($(selector).val(), group, 'day'))
},
onValidatePeriodForm: (modalProps, timeTablePeriods, metas) => {
dispatch(actions.validatePeriodForm(modalProps, timeTablePeriods, metas))
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js
index 0af1bb53d..e90d2d307 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js
@@ -134,9 +134,10 @@ const actions = {
type: 'SHIFT_VEHICLEJOURNEY',
data
}),
- duplicateVehicleJourney : (data) => ({
+ duplicateVehicleJourney : (data, departureDelta) => ({
type: 'DUPLICATE_VEHICLEJOURNEY',
- data
+ data,
+ departureDelta
}),
deleteVehicleJourneys : () => ({
type: 'DELETE_VEHICLEJOURNEYS'
@@ -458,20 +459,20 @@ const actions = {
schedule.arrival_time.hour = parseInt(schedule.arrival_time.hour) + hours
}
- if(schedule.departure_time.hour > 23){
+ if(parseInt(schedule.departure_time.hour) > 23){
schedule.departure_time.hour = '23'
schedule.departure_time.minute = '59'
}
- if(schedule.arrival_time.hour > 23){
+ if(parseInt(schedule.arrival_time.hour) > 23){
schedule.arrival_time.hour = '23'
schedule.arrival_time.minute = '59'
}
- if(schedule.departure_time.hour < 0){
+ if(parseInt(schedule.departure_time.hour) < 0){
schedule.departure_time.hour = '00'
schedule.departure_time.minute = '00'
}
- if(schedule.arrival_time.hour > 23){
+ if(parseInt(schedule.arrival_time.hour) < 0){
schedule.arrival_time.hour = '00'
schedule.arrival_time.minute = '00'
}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
index 0cf102693..aa1a13b11 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
@@ -18,8 +18,8 @@ class DuplicateVehicleJourney extends Component {
}
}
let val = actions.getDuplicateDelta(_.find(actions.getSelected(this.props.vehicleJourneys)[0].vehicle_journey_at_stops, {'dummy': false}), newDeparture)
- this.refs.additional_time.value = parseInt(this.refs.additional_time.value) + val
- this.props.onDuplicateVehicleJourney(this.refs)
+ this.refs.additional_time.value = parseInt(this.refs.additional_time.value)
+ this.props.onDuplicateVehicleJourney(this.refs, val)
this.props.onModalClose()
$('#DuplicateVehicleJourneyModal').modal('hide')
}
@@ -51,10 +51,9 @@ class DuplicateVehicleJourney extends Component {
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
- <h4 className='modal-title'>Dupliquer une course</h4>
- {(this.props.modal.type == 'duplicate') && (
- <em>Dupliquer les horaires de la course {actions.humanOID(actions.getSelected(this.props.vehicleJourneys)[0].objectid)}</em>
- )}
+ <h4 className='modal-title'>
+ Dupliquer { actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'plusieurs courses' : 'une course' }
+ </h4>
</div>
{(this.props.modal.type == 'duplicate') && (
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js
index 932c56532..9919ee9dd 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js
@@ -70,7 +70,7 @@ class EditVehicleJourney extends Component {
<input
type='text'
className='form-control'
- value={(this.props.modal.modalProps.vehicleJourney.journey_pattern.objectid) + ' - ' + (this.props.modal.modalProps.vehicleJourney.journey_pattern.name)}
+ value={actions.humanOID(this.props.modal.modalProps.vehicleJourney.journey_pattern.objectid) + ' - ' + (this.props.modal.modalProps.vehicleJourney.journey_pattern.name)}
disabled={true}
/>
</div>
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js
index ee7d01cf5..2164344c2 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js
@@ -40,7 +40,7 @@ class ShiftVehicleJourney extends Component {
<div className='modal-header'>
<h4 className='modal-title'>Mettre à jour une course</h4>
{(this.props.modal.type == 'shift') && (
- <em>Mettre à jour les horaires de la course {actions.getSelected(this.props.vehicleJourneys)[0].objectid}</em>
+ <em>Mettre à jour les horaires de la course {actions.humanOID(actions.getSelected(this.props.vehicleJourneys)[0].objectid)}</em>
)}
</div>
@@ -57,6 +57,7 @@ class ShiftVehicleJourney extends Component {
min='-59'
max='59'
className='form-control'
+ defaultValue='0'
onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
required
/>
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/DuplicateVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/DuplicateVehicleJourney.js
index 6cf6f4039..224b52a19 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/DuplicateVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/DuplicateVehicleJourney.js
@@ -19,8 +19,8 @@ const mapDispatchToProps = (dispatch) => {
onOpenDuplicateModal: () =>{
dispatch(actions.openDuplicateModal())
},
- onDuplicateVehicleJourney: (data) =>{
- dispatch(actions.duplicateVehicleJourney(data))
+ onDuplicateVehicleJourney: (data, departureDelta) =>{
+ dispatch(actions.duplicateVehicleJourney(data, departureDelta))
}
}
}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js
index 09588f824..b4a70ec08 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/filters.js
@@ -15,7 +15,7 @@ const filters = (state = {}, action) => {
minute: '59'
}
}
- newQuery = _.assign({}, state.query, {interval: interval, journeyPattern: {}, vehicleJourney: {}, timetable: {}, withoutSchedule: true, withoutTimeTable: false })
+ newQuery = _.assign({}, state.query, {interval: interval, journeyPattern: {}, vehicleJourney: {}, timetable: {}, withoutSchedule: true, withoutTimeTable: true })
return _.assign({}, state, {query: newQuery, queryString: ''})
case 'TOGGLE_WITHOUT_SCHEDULE':
newQuery = _.assign({}, state.query, {withoutSchedule: !state.query.withoutSchedule})
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js
index c7e8d58e7..d463d4b8f 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js
@@ -182,11 +182,12 @@ const vehicleJourneys = (state = [], action) => {
let dupes = []
let selectedIndex
let val = action.data.additional_time.value
+ let departureDelta = action.departureDelta
state.map((vj, i) => {
if(vj.selected){
selectedIndex = i
for (i = 0; i< action.data.duplicate_number.value; i++){
- action.data.additional_time.value = val * (i + 1)
+ action.data.additional_time.value = (parseInt(val) * (i + 1)) + departureDelta
dupeVj = vehicleJourney(vj, action, false)
dupeVj.published_journey_name = dupeVj.published_journey_name + '-' + i
dupeVj.selected = false
diff --git a/app/assets/javascripts/smart_date.coffee b/app/assets/javascripts/smart_date.coffee
new file mode 100644
index 000000000..afc0c7ddf
--- /dev/null
+++ b/app/assets/javascripts/smart_date.coffee
@@ -0,0 +1,26 @@
+window.legalDaysPerMonth =
+ false: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+ true: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+
+
+window.legal
+window.correctDay = (dateValues) ->
+ [day, month, year] = dateValues
+ return day unless day > 0 && month > 0 && year > 0
+
+ legallyMaximumDay = legalDaysPerMonth[isLeapYear(year)][month - 1]
+ Math.min day, legallyMaximumDay
+
+window.isLeapYear = (year) ->
+ (year % 4 == 0) && ((year % 400 == 0) || (year % 100 != 0))
+
+window.smartCorrectDate = ->
+ allSelectors = $(@).parent().children('select')
+ allVals = allSelectors.map (index, sel) ->
+ parseInt($(sel).val())
+ correctedDay = correctDay allVals
+ daySelector = allSelectors.first()
+ $(daySelector).val(correctedDay)
+
+$ ->
+ $(document).on 'change', '.smart_date select', smartCorrectDate
diff --git a/app/assets/stylesheets/components/_dropdown.sass b/app/assets/stylesheets/components/_dropdown.sass
index 99dc6292e..8a8d69063 100644
--- a/app/assets/stylesheets/components/_dropdown.sass
+++ b/app/assets/stylesheets/components/_dropdown.sass
@@ -3,6 +3,8 @@
//-------------//
.dropdown-menu
+ z-index: 2001
+
> li > button
display: block
width: 100%
diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass
index e3a33e131..20679a3ba 100644
--- a/app/assets/stylesheets/components/_tables.sass
+++ b/app/assets/stylesheets/components/_tables.sass
@@ -296,6 +296,9 @@
border-right: 1px solid rgba($grey, 0.5)
.th
+ > div
+ min-height: 19px
+
> *:first-child
padding-right: 30px
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 86d567882..23af4129b 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -5,6 +5,12 @@ class CalendarsController < BreadcrumbController
respond_to :html
respond_to :js, only: :index
+ def show
+ show! do
+ @calendar = @calendar.decorate
+ end
+ end
+
private
def calendar_params
permitted_params = [:id, :name, :short_name, periods_attributes: [:id, :begin, :end, :_destroy], date_values_attributes: [:id, :value, :_destroy]]
@@ -49,7 +55,7 @@ class CalendarsController < BreadcrumbController
date << params[:q][key].to_i
params[:q].delete(key)
end
- params[:q]['contains_date'] = Date.new(*date)
+ params[:q]['contains_date'] = Date.new(*date) rescue nil
end
end
diff --git a/app/controllers/devise/cas_sessions_controller.rb b/app/controllers/devise/cas_sessions_controller.rb
index 0a9d9ecb2..d000d0309 100644
--- a/app/controllers/devise/cas_sessions_controller.rb
+++ b/app/controllers/devise/cas_sessions_controller.rb
@@ -20,14 +20,14 @@ class Devise::CasSessionsController < Devise::SessionsController
if LoginPolicy.new(current_user).boiv?
redirect_to after_sign_in_path_for(current_user)
else
- redirect_to root_path, flash: {alert: t('devise.sessions.new.unauthorized')}
+ destroy message: t('devise.sessions.new.unauthorized')
end
end
def unregistered
end
- def destroy
+ def destroy message: nil
# if :cas_create_user is false a CAS session might be open but not signed_in
# in such case we destroy the session here
if signed_in?(resource_name)
@@ -36,7 +36,11 @@ class Devise::CasSessionsController < Devise::SessionsController
reset_session
end
- redirect_to(cas_logout_url)
+ if message
+ redirect_to(cas_logout_url, flash: {alert: message})
+ else
+ redirect_to(cas_logout_url)
+ end
end
def single_sign_out
diff --git a/app/controllers/lines_controller.rb b/app/controllers/lines_controller.rb
index 7eedaeb05..1e2056aad 100644
--- a/app/controllers/lines_controller.rb
+++ b/app/controllers/lines_controller.rb
@@ -25,6 +25,11 @@ class LinesController < BreadcrumbController
def show
@group_of_lines = resource.group_of_lines
show! do
+ @line = @line.decorate(context: {
+ line_referential: @line_referential,
+ current_organisation: current_organisation
+ })
+
build_breadcrumb :show
end
end
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb
index aa5b359da..1239d512f 100644
--- a/app/controllers/referentials_controller.rb
+++ b/app/controllers/referentials_controller.rb
@@ -24,19 +24,21 @@ class ReferentialsController < BreadcrumbController
end
def show
- resource.switch
- show! do |format|
- format.json {
- render :json => { :lines_count => resource.lines.count,
- :networks_count => resource.networks.count,
- :vehicle_journeys_count => resource.vehicle_journeys.count + resource.vehicle_journey_frequencies.count,
- :time_tables_count => resource.time_tables.count,
- :referential_id => resource.id}
- }
- format.html { build_breadcrumb :show}
- end
-
- @reflines = lines_collection.paginate(page: params[:page], per_page: 10)
+ resource.switch
+ show! do |format|
+ @referential = @referential.decorate
+
+ format.json {
+ render :json => { :lines_count => resource.lines.count,
+ :networks_count => resource.networks.count,
+ :vehicle_journeys_count => resource.vehicle_journeys.count + resource.vehicle_journey_frequencies.count,
+ :time_tables_count => resource.time_tables.count,
+ :referential_id => resource.id}
+ }
+ format.html { build_breadcrumb :show}
+ end
+
+ @reflines = lines_collection.paginate(page: params[:page], per_page: 10)
end
def edit
diff --git a/app/controllers/routes_controller.rb b/app/controllers/routes_controller.rb
index 73febc4b9..786bd57cc 100644
--- a/app/controllers/routes_controller.rb
+++ b/app/controllers/routes_controller.rb
@@ -42,6 +42,12 @@ class RoutesController < ChouetteController
end
show! do
+ @route = @route.decorate(context: {
+ referential: @referential,
+ line: @line,
+ route_sp: @route_sp
+ })
+
build_breadcrumb :show
end
end
diff --git a/app/controllers/routing_constraint_zones_controller.rb b/app/controllers/routing_constraint_zones_controller.rb
index 7707427b0..9d2fd712c 100644
--- a/app/controllers/routing_constraint_zones_controller.rb
+++ b/app/controllers/routing_constraint_zones_controller.rb
@@ -16,6 +16,10 @@ class RoutingConstraintZonesController < ChouetteController
def show
@routing_constraint_zone = collection.find(params[:id])
+ @routing_constraint_zone = @routing_constraint_zone.decorate(context: {
+ referential: @referential,
+ line: @line
+ })
end
protected
diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb
index 5c4552afb..3704f2885 100644
--- a/app/controllers/time_tables_controller.rb
+++ b/app/controllers/time_tables_controller.rb
@@ -14,6 +14,10 @@ class TimeTablesController < ChouetteController
@year = params[:year] ? params[:year].to_i : Date.today.cwyear
@time_table_combination = TimeTableCombination.new
show! do
+ @time_table = @time_table.decorate(context: {
+ referential: @referential
+ })
+
build_breadcrumb :show
end
end
diff --git a/app/controllers/workbenches_controller.rb b/app/controllers/workbenches_controller.rb
index ccd55965b..d03841356 100644
--- a/app/controllers/workbenches_controller.rb
+++ b/app/controllers/workbenches_controller.rb
@@ -11,11 +11,18 @@ class WorkbenchesController < BreadcrumbController
scope = ransack_status(scope)
# Ignore archived_at_not_null/archived_at_null managed by ransack_status scope
- q_for_result =
- scope.ransack(params[:q].merge(archived_at_not_null: nil, archived_at_null: nil))
- @wbench_refs = sort_result(q_for_result.result).paginate(page: params[:page], per_page: 30)
+ # We clone params[:q] so we can delete fake ransack filter arguments before calling search method,
+ # which will allow us to preserve params[:q] for sorting
+ ransack_params = params[:q].merge(archived_at_not_null: nil, archived_at_null: nil).clone
+ ransack_params.delete('associated_lines_id_eq')
+
+ @q = scope.ransack(ransack_params)
+ @wbench_refs = sort_result(@q.result).paginate(page: params[:page], per_page: 30)
+ @wbench_refs = ModelDecorator.decorate(
+ @wbench_refs,
+ with: ReferentialDecorator
+ )
- @q = scope.ransack(params[:q])
show! do
build_breadcrumb :show
end
@@ -33,7 +40,7 @@ class WorkbenchesController < BreadcrumbController
private
def resource
- @workbench = Workbench.find params[:id]
+ @workbench = current_organisation.workbenches.find params[:id]
end
def sort_result collection
@@ -59,7 +66,6 @@ class WorkbenchesController < BreadcrumbController
def ransack_associated_lines scope
if params[:q] && params[:q]['associated_lines_id_eq']
scope = scope.include_metadatas_lines([params[:q]['associated_lines_id_eq']])
- params[:q].delete('associated_lines_id_eq')
end
scope
end
diff --git a/app/decorators/calendar_decorator.rb b/app/decorators/calendar_decorator.rb
new file mode 100644
index 000000000..37e2cfe80
--- /dev/null
+++ b/app/decorators/calendar_decorator.rb
@@ -0,0 +1,18 @@
+class CalendarDecorator < Draper::Decorator
+ delegate_all
+
+ def action_links
+ links = []
+
+ if h.policy(object).destroy?
+ links << Link.new(
+ content: h.destroy_link_content,
+ href: h.calendar_path(object),
+ method: :delete,
+ data: { confirm: h.t('calendars.actions.destroy_confirm') }
+ )
+ end
+
+ links
+ end
+end
diff --git a/app/decorators/company_decorator.rb b/app/decorators/company_decorator.rb
index 3a0cc16ce..51c1f3c61 100644
--- a/app/decorators/company_decorator.rb
+++ b/app/decorators/company_decorator.rb
@@ -1,4 +1,6 @@
class CompanyDecorator < Draper::Decorator
+ decorates Chouette::Company
+
delegate_all
def self.collection_decorator_class
@@ -9,4 +11,43 @@ class CompanyDecorator < Draper::Decorator
object.lines.count
end
+ # Requires:
+ # context: {
+ # line_referential:
+ # }
+ def action_links
+ links = []
+
+ if h.policy(Chouette::Company).create?
+ links << Link.new(
+ content: h.t('companies.actions.new'),
+ href: h.new_line_referential_company_path(context[:line_referential])
+ )
+ end
+
+ if h.policy(object).update?
+ links << Link.new(
+ content: h.t('companies.actions.edit'),
+ href: h.edit_line_referential_company_path(
+ context[:line_referential],
+ object
+ )
+ )
+ end
+
+ if h.policy(object).destroy?
+ links << Link.new(
+ content: t('companies.actions.destroy'),
+ href: h.line_referential_company_path(
+ context[:line_referential],
+ object
+ ),
+ method: :delete,
+ data: { confirm: h.t('companies.actions.destroy_confirm') }
+ )
+ end
+
+ links
+ end
+
end
diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb
new file mode 100644
index 000000000..f351103b2
--- /dev/null
+++ b/app/decorators/line_decorator.rb
@@ -0,0 +1,45 @@
+class LineDecorator < Draper::Decorator
+ decorates Chouette::Line
+
+ delegate_all
+
+ # Requires:
+ # context: {
+ # line_referential: ,
+ # current_organisation:
+ # }
+ def action_links
+ links = []
+
+ links << Link.new(
+ content: h.t('lines.actions.show_network'),
+ href: [context[:line_referential], object.network]
+ )
+
+ links << Link.new(
+ content: h.t('lines.actions.show_company'),
+ href: [context[:line_referential], object.company]
+ )
+
+ if h.policy(Chouette::Line).create? &&
+ context[:line_referential].organisations.include?(
+ context[:current_organisation]
+ )
+ links << Link.new(
+ content: h.t('lines.actions.new'),
+ href: h.new_line_referential_line_path(context[:line_referential])
+ )
+ end
+
+ if h.policy(object).destroy?
+ links << Link.new(
+ content: h.destroy_link_content('lines.actions.destroy_confirm'),
+ href: h.line_referential_line_path(context[:line_referential], object),
+ method: :delete,
+ data: { confirm: h.t('lines.actions.destroy_confirm') }
+ )
+ end
+
+ links
+ end
+end
diff --git a/app/decorators/model_decorator.rb b/app/decorators/model_decorator.rb
new file mode 100644
index 000000000..dee014cc3
--- /dev/null
+++ b/app/decorators/model_decorator.rb
@@ -0,0 +1,3 @@
+class ModelDecorator < PaginatingDecorator
+ delegate :model
+end
diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb
new file mode 100644
index 000000000..b95b04f9f
--- /dev/null
+++ b/app/decorators/referential_decorator.rb
@@ -0,0 +1,56 @@
+class ReferentialDecorator < Draper::Decorator
+ delegate_all
+
+ def action_links
+ links = [
+ Link.new(
+ content: h.t('time_tables.index.title'),
+ href: h.referential_time_tables_path(object)
+ )
+ ]
+
+ if h.policy(object).clone?
+ links << Link.new(
+ content: h.t('actions.clone'),
+ href: h.new_referential_path(from: object.id)
+ )
+ end
+
+ if h.policy(object).edit?
+
+ if object.archived?
+ links << Link.new(
+ content: h.t('actions.unarchive'),
+ href: h.unarchive_referential_path(object.id),
+ method: :put
+ )
+ else
+ links << HTMLElement.new(
+ :button,
+ 'Purger',
+ type: 'button',
+ data: {
+ toggle: 'modal',
+ target: '#purgeModal'
+ }
+ )
+ links << Link.new(
+ content: h.t('actions.archive'),
+ href: h.archive_referential_path(object.id),
+ method: :put
+ )
+ end
+ end
+
+ if h.policy(object).destroy? && !object.archived?
+ links << Link.new(
+ content: h.destroy_link_content,
+ href: h.referential_path(object),
+ method: :delete,
+ data: { confirm: h.t('referentials.actions.destroy_confirm') }
+ )
+ end
+
+ links
+ end
+end
diff --git a/app/decorators/route_decorator.rb b/app/decorators/route_decorator.rb
new file mode 100644
index 000000000..99b174dff
--- /dev/null
+++ b/app/decorators/route_decorator.rb
@@ -0,0 +1,64 @@
+class RouteDecorator < Draper::Decorator
+ decorates Chouette::Route
+
+ delegate_all
+
+ # Requires:
+ # context: {
+ # referential: ,
+ # line: ,
+ # route_sp
+ # }
+ def action_links
+ links = []
+
+ if context[:route_sp].any?
+ links << Link.new(
+ content: h.t('journey_patterns.index.title'),
+ href: [
+ context[:referential],
+ context[:line],
+ object,
+ :journey_patterns_collection
+ ]
+ )
+ end
+
+ if object.journey_patterns.present?
+ links << Link.new(
+ content: h.t('vehicle_journeys.actions.index'),
+ href: [
+ context[:referential],
+ context[:line],
+ object,
+ :vehicle_journeys
+ ]
+ )
+ end
+
+ links << Link.new(
+ content: h.t('vehicle_journey_exports.new.title'),
+ href: h.referential_line_route_vehicle_journey_exports_path(
+ context[:referential],
+ context[:line],
+ object,
+ format: :zip
+ )
+ )
+
+ if h.policy(object).destroy?
+ links << Link.new(
+ content: h.destroy_link_content,
+ href: h.referential_line_route_path(
+ context[:referential],
+ context[:line],
+ object
+ ),
+ method: :delete,
+ data: { confirm: h.t('routes.actions.destroy_confirm') }
+ )
+ end
+
+ links
+ end
+end
diff --git a/app/decorators/routing_constraint_zone_decorator.rb b/app/decorators/routing_constraint_zone_decorator.rb
new file mode 100644
index 000000000..0b438a554
--- /dev/null
+++ b/app/decorators/routing_constraint_zone_decorator.rb
@@ -0,0 +1,42 @@
+class RoutingConstraintZoneDecorator < Draper::Decorator
+ decorates Chouette::RoutingConstraintZone
+
+ delegate_all
+
+ # Requires:
+ # context: {
+ # referential: ,
+ # line:
+ # }
+ def action_links
+ links = []
+
+ if h.policy(object).update?
+ links << Link.new(
+ content: h.t('actions.edit'),
+ href: h.edit_referential_line_routing_constraint_zone_path(
+ context[:referential],
+ context[:line],
+ object
+ )
+ )
+ end
+
+ if h.policy(object).destroy?
+ links << Link.new(
+ content: h.destroy_link_content,
+ href: h.referential_line_routing_constraint_zone_path(
+ context[:referential],
+ context[:line],
+ object
+ ),
+ method: :delete,
+ data: {
+ confirm: h.t('routing_constraint_zones.actions.destroy_confirm')
+ }
+ )
+ end
+
+ links
+ end
+end
diff --git a/app/decorators/time_table_decorator.rb b/app/decorators/time_table_decorator.rb
new file mode 100644
index 000000000..526537310
--- /dev/null
+++ b/app/decorators/time_table_decorator.rb
@@ -0,0 +1,53 @@
+class TimeTableDecorator < Draper::Decorator
+ decorates Chouette::TimeTable
+
+ delegate_all
+
+ # Requires:
+ # context: {
+ # referential: ,
+ # }
+ def action_links
+ links = []
+
+ if object.calendar
+ links << Link.new(
+ content: h.t('actions.actualize'),
+ href: h.actualize_referential_time_table_path(
+ context[:referential],
+ object
+ ),
+ method: :post
+ )
+ end
+
+ links << Link.new(
+ content: h.t('actions.combine'),
+ href: h.new_referential_time_table_time_table_combination_path(
+ context[:referential],
+ object
+ )
+ )
+
+ if h.policy(object).duplicate?
+ links << Link.new(
+ content: h.t('actions.duplicate'),
+ href: h.duplicate_referential_time_table_path(
+ context[:referential],
+ object
+ )
+ )
+ end
+
+ if h.policy(object).destroy?
+ links << Link.new(
+ content: h.destroy_link_content,
+ href: h.referential_time_table_path(context[:referential], object),
+ method: :delete,
+ data: { confirm: h.t('time_tables.actions.destroy_confirm') }
+ )
+ end
+
+ links
+ end
+end
diff --git a/app/helpers/lines_helper.rb b/app/helpers/lines_helper.rb
index 45e6cd939..ccf3a12a2 100644
--- a/app/helpers/lines_helper.rb
+++ b/app/helpers/lines_helper.rb
@@ -6,11 +6,11 @@ module LinesHelper
end
def sorted_transport_submode
- Chouette::Line.transport_submode.values.sort_by{|m| t("enumerize.line.transport_submode.#{m}") }
+ Chouette::Line.transport_submode.values.sort_by{|m| t("enumerize.line.transport_submode.#{m}").parameterize }
end
def sorted_transport_mode
- Chouette::Line.transport_mode.values.sort_by{|m| t("enumerize.line.transport_mode.#{m}") }
+ Chouette::Line.transport_mode.values.sort_by{|m| t("enumerize.line.transport_mode.#{m}").parameterize }
end
def colors?(line)
diff --git a/app/helpers/links_helper.rb b/app/helpers/links_helper.rb
new file mode 100644
index 000000000..683b66a52
--- /dev/null
+++ b/app/helpers/links_helper.rb
@@ -0,0 +1,5 @@
+module LinksHelper
+ def destroy_link_content(translation_key = 'actions.destroy')
+ content_tag(:span, nil, class: 'fa fa-trash') + t(translation_key)
+ end
+end
diff --git a/app/helpers/multiple_selection_toolbox_helper.rb b/app/helpers/multiple_selection_toolbox_helper.rb
new file mode 100644
index 000000000..85294af6d
--- /dev/null
+++ b/app/helpers/multiple_selection_toolbox_helper.rb
@@ -0,0 +1,40 @@
+module MultipleSelectionToolboxHelper
+ # Box of links that floats at the bottom right of the page
+ def multiple_selection_toolbox(actions)
+ links = content_tag :ul do
+ delete_path = nil
+
+ if params[:controller] = 'workbenches'
+ delete_path = referentials_workbench_path
+ end
+
+ actions.map do |action|
+ if action == :delete
+ action_link = link_to(
+ '#',
+ method: :delete,
+ data: {
+ path: delete_path,
+ confirm: 'Etes-vous sûr(e) de vouloir effectuer cette action ?'
+ },
+ title: t("actions.#{action}")
+ ) do
+ content_tag :span, '', class: 'fa fa-trash'
+ end
+ end
+
+ content_tag :li, action_link, class: 'st_action'
+ end.join.html_safe
+ end
+
+ label = content_tag(
+ :span,
+ ("<span>0</span> élément(s) sélectionné(s)").html_safe,
+ class: 'info-msg'
+ )
+
+ content_tag :div, '', class: 'select_toolbox noselect' do
+ links + label
+ end
+ end
+end
diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb
new file mode 100644
index 000000000..b93e9b22b
--- /dev/null
+++ b/app/helpers/table_builder_helper.rb
@@ -0,0 +1,234 @@
+require 'table_builder_helper/column'
+require 'table_builder_helper/custom_links'
+require 'table_builder_helper/url'
+
+# table_builder_2
+# A Rails helper that constructs an HTML table from a collection of objects. It
+# receives the collection and an array of columns that get transformed into
+# `<td>`s. A column of checkboxes can be added to the left side of the table
+# for multiple selection. Columns are sortable by default, but sorting can be
+# disabled either at the table level or at the column level. An optional
+# `links` argument takes a set of symbols corresponding to controller actions
+# that should be inserted in a gear menu next to each row in the table. That
+# menu will also be populated with links defined in `collection#action_links`,
+# a list of `Link` objects defined in a decorator for the given object.
+#
+# Depends on `params` and `current_referential`.
+#
+# Example:
+# table_builder_2(
+# @companies,
+# [
+# TableBuilderHelper::Column.new(
+# name: 'ID Codif',
+# attribute: Proc.new { |n| n.try(:objectid).try(:local_id) },
+# sortable: false
+# ),
+# TableBuilderHelper::Column.new(
+# key: :name,
+# attribute: 'name'
+# ),
+# TableBuilderHelper::Column.new(
+# key: :phone,
+# attribute: 'phone'
+# ),
+# TableBuilderHelper::Column.new(
+# key: :email,
+# attribute: 'email'
+# ),
+# TableBuilderHelper::Column.new(
+# key: :url,
+# attribute: 'url'
+# ),
+# ],
+# links: [:show, :edit],
+# cls: 'table has-search'
+# )
+module TableBuilderHelper
+ # TODO: rename this after migration from `table_builder`
+ def table_builder_2(
+ # An `ActiveRecord::Relation`, wrapped in a decorator to provide a list of
+ # `Link` objects via an `#action_links` method
+ collection,
+
+ # An array of `TableBuilderHelper::Column`s
+ columns,
+
+ # When false, no columns will be sortable
+ sortable: true,
+
+ # When true, adds a column of checkboxes to the left side of the table
+ selectable: false,
+
+ # A set of controller actions that will be added as links to the top of the
+ # gear menu
+ links: [],
+
+ # A CSS class to apply to the <table>
+ cls: ''
+ )
+ content_tag :table,
+ thead(collection, columns, sortable, selectable, links.any?) +
+ tbody(collection, columns, selectable, links),
+ class: cls
+ end
+
+ private
+
+ def thead(collection, columns, sortable, selectable, has_links)
+ content_tag :thead do
+ content_tag :tr do
+ hcont = []
+
+ if selectable
+ hcont << content_tag(:th, checkbox(id_name: '0', value: 'all'))
+ end
+
+ columns.each do |column|
+ hcont << content_tag(:th, build_column_header(
+ column,
+ sortable,
+ collection.model,
+ params,
+ params[:sort],
+ params[:direction]
+ ))
+ end
+
+ # Inserts a blank column for the gear menu
+ hcont << content_tag(:th, '') if has_links
+
+ hcont.join.html_safe
+ end
+ end
+ end
+
+ def tbody(collection, columns, selectable, links)
+ content_tag :tbody do
+ collection.map do |item|
+
+ content_tag :tr do
+ bcont = []
+
+ if selectable
+ bcont << content_tag(
+ :td,
+ checkbox(id_name: item.try(:id), value: item.try(:id))
+ )
+ end
+
+ columns.each do |column|
+ value = column.value(item)
+
+ if column_is_linkable?(column)
+ # Build a link to the `item`
+ polymorph_url = URL.polymorphic_url_parts(item)
+ bcont << content_tag(:td, link_to(value, polymorph_url), title: 'Voir')
+ else
+ bcont << content_tag(:td, value)
+ end
+ end
+
+ if links.any?
+ bcont << content_tag(
+ :td,
+ build_links(item, links),
+ class: 'actions'
+ )
+ end
+
+ bcont.join.html_safe
+ end
+ end.join.html_safe
+ end
+ end
+
+ def build_links(item, links)
+ trigger = content_tag(
+ :div,
+ class: 'btn dropdown-toggle',
+ data: { toggle: 'dropdown' }
+ ) do
+ content_tag :span, '', class: 'fa fa-cog'
+ end
+
+ menu = content_tag :ul, class: 'dropdown-menu' do
+ (
+ CustomLinks.new(item, pundit_user, links).links +
+ item.action_links.select { |link| link.is_a?(Link) }
+ ).map do |link|
+ gear_menu_link(link)
+ end.join.html_safe
+ end
+
+ content_tag :div, trigger + menu, class: 'btn-group'
+ end
+
+ def build_column_header(
+ column,
+ table_is_sortable,
+ collection_model,
+ params,
+ sort_on,
+ sort_direction
+ )
+ if !table_is_sortable
+ return column.header_label(collection_model)
+ end
+
+ return column.name if !column.sortable
+
+ direction =
+ if column.key.to_s == sort_on && sort_direction == 'desc'
+ 'asc'
+ else
+ 'desc'
+ end
+
+ link_to(params.merge({direction: direction, sort: column.key})) do
+ arrow_up = content_tag(
+ :span,
+ '',
+ class: "fa fa-sort-asc #{direction == 'desc' ? 'active' : ''}"
+ )
+ arrow_down = content_tag(
+ :span,
+ '',
+ class: "fa fa-sort-desc #{direction == 'asc' ? 'active' : ''}"
+ )
+
+ arrow_icons = content_tag :span, arrow_up + arrow_down, class: 'orderers'
+
+ (
+ column.header_label(collection_model) +
+ arrow_icons
+ ).html_safe
+ end
+ end
+
+ def checkbox(id_name:, value:)
+ content_tag :div, '', class: 'checkbox' do
+ check_box_tag(id_name, value).concat(
+ content_tag(:label, '', for: id_name)
+ )
+ end
+ end
+
+ def column_is_linkable?(column)
+ column.attribute == 'name' || column.attribute == 'comment'
+ end
+
+ def gear_menu_link(link)
+ content_tag(
+ :li,
+ link_to(
+ link.href,
+ method: link.method,
+ data: link.data
+ ) do
+ link.content
+ end,
+ class: ('delete-action' if link.method == :delete)
+ )
+ end
+end
diff --git a/app/helpers/table_builder_helper/column.rb b/app/helpers/table_builder_helper/column.rb
new file mode 100644
index 000000000..800a8282e
--- /dev/null
+++ b/app/helpers/table_builder_helper/column.rb
@@ -0,0 +1,36 @@
+module TableBuilderHelper
+ class Column
+ attr_reader :key, :name, :attribute, :sortable
+
+ def initialize(key: nil, name: '', attribute:, sortable: true)
+ if key.nil? && name.empty?
+ raise ColumnMustHaveKeyOrNameError
+ end
+
+ @key = key
+ @name = name
+ @attribute = attribute
+ @sortable = sortable
+ end
+
+ def value(obj)
+ if @attribute.is_a?(Proc)
+ @attribute.call(obj)
+ else
+ obj.try(@attribute)
+ end
+ end
+
+ def header_label(model = nil)
+ return @name unless @name.empty?
+
+ # Transform `Chouette::Line` into "line"
+ model_key = model.to_s.demodulize.underscore
+
+ I18n.t("activerecord.attributes.#{model_key}.#{@key}")
+ end
+ end
+
+
+ class ColumnMustHaveKeyOrNameError < StandardError; end
+end
diff --git a/app/helpers/table_builder_helper/custom_links.rb b/app/helpers/table_builder_helper/custom_links.rb
new file mode 100644
index 000000000..abb907678
--- /dev/null
+++ b/app/helpers/table_builder_helper/custom_links.rb
@@ -0,0 +1,77 @@
+require 'table_builder_helper/url'
+
+module TableBuilderHelper
+ class CustomLinks
+ ACTIONS_TO_HTTP_METHODS = {
+ delete: :delete,
+ archive: :put,
+ unarchive: :put
+ }
+
+ def initialize(obj, user_context, actions)
+ @obj = obj
+ @user_context = user_context
+ @actions = actions
+ end
+
+ def links
+ actions_after_policy_check.map do |action|
+ Link.new(
+ content: I18n.t("actions.#{action}"),
+ href: polymorphic_url(action),
+ method: method_for_action(action)
+ )
+ end
+ end
+
+ def polymorphic_url(action)
+ polymorph_url = []
+
+ unless [:show, :delete].include?(action)
+ polymorph_url << action
+ end
+
+ polymorph_url += URL.polymorphic_url_parts(@obj)
+ end
+
+ def method_for_action(action)
+ ACTIONS_TO_HTTP_METHODS[action]
+ end
+
+ def actions_after_policy_check
+ @actions.select do |action|
+ # Has policy and can destroy
+ (action == :delete &&
+ Pundit.policy(@user_context, @obj).present? &&
+ Pundit.policy(@user_context, @obj).destroy?) ||
+
+ # Doesn't have policy
+ (action == :delete &&
+ !Pundit.policy(@user_context, @obj).present?) ||
+
+ # Has policy and can update
+ (action == :edit &&
+ Pundit.policy(@user_context, @obj).present? &&
+ Pundit.policy(@user_context, @obj).update?) ||
+
+ # Doesn't have policy
+ (action == :edit &&
+ !Pundit.policy(@user_context, @obj).present?) ||
+
+ # Object isn't archived
+ (action == :archive && !@obj.archived?) ||
+
+ # Object is archived
+ (action == :unarchive && @obj.archived?) ||
+
+ action_is_allowed_regardless_of_policy(action)
+ end
+ end
+
+ private
+
+ def action_is_allowed_regardless_of_policy(action)
+ ![:delete, :edit, :archive, :unarchive].include?(action)
+ end
+ end
+end
diff --git a/app/helpers/table_builder_helper/url.rb b/app/helpers/table_builder_helper/url.rb
new file mode 100644
index 000000000..f60864ac1
--- /dev/null
+++ b/app/helpers/table_builder_helper/url.rb
@@ -0,0 +1,25 @@
+module TableBuilderHelper
+ # Depends on `current_referential`, defined in object controllers
+ class URL
+ def self.polymorphic_url_parts(item)
+ polymorph_url = []
+
+ unless item.is_a?(Calendar) || item.is_a?(Referential)
+ if current_referential
+ polymorph_url << current_referential
+ polymorph_url << item.line if item.respond_to? :line
+ polymorph_url << item.route.line if item.is_a?(Chouette::RoutingConstraintZone)
+ polymorph_url << item if item.respond_to? :line_referential
+ polymorph_url << item.stop_area if item.respond_to? :stop_area
+ polymorph_url << item if item.respond_to? :stop_points || item.is_a?(Chouette::TimeTable)
+ elsif item.respond_to? :referential
+ polymorph_url << item.referential
+ end
+ else
+ polymorph_url << item
+ end
+
+ polymorph_url
+ end
+ end
+end
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index 39e0d6d49..70fea9619 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -1,5 +1,9 @@
require 'range_ext'
+require_relative 'calendar/date_value'
+require_relative 'calendar/period'
+
class Calendar < ActiveRecord::Base
+
belongs_to :organisation
has_many :time_tables
@@ -30,69 +34,11 @@ class Calendar < ActiveRecord::Base
end
end
- class Period
- include ActiveAttr::Model
-
- attribute :id, type: Integer
- attribute :begin, type: Date
- attribute :end, type: Date
-
- validates_presence_of :begin, :end
- validate :check_end_greather_than_begin
-
- def check_end_greather_than_begin
- if self.begin and self.end and self.begin > self.end
- errors.add(:end, :invalid)
- end
- end
-
- def self.from_range(index, range)
- Period.new id: index, begin: range.begin, end: range.end
- end
-
- def range
- if self.begin and self.end and self.begin <= self.end
- Range.new self.begin, self.end
- end
- end
-
- def intersect?(*other)
- return false if range.nil?
-
- other = other.flatten
- other = other.delete_if { |o| o.id == id } if id
-
- other.any? do |period|
- if other_range = period.range
- (range & other_range).present?
- end
- end
- end
-
- def cover? date
- range.cover? date
- end
-
- # Stuff required for coocon
- def new_record?
- !persisted?
- end
-
- def persisted?
- id.present?
- end
-
- def mark_for_destruction
- self._destroy = true
- end
-
- attribute :_destroy, type: Boolean
- alias_method :marked_for_destruction?, :_destroy
- end
+ ### Calendar::Period
# Required by coocon
def build_period
- Period.new
+ Calendar::Period.new
end
def periods
@@ -100,22 +46,16 @@ class Calendar < ActiveRecord::Base
end
def init_periods
- if date_ranges
- date_ranges.each_with_index.map { |r, index| Period.from_range(index, r) }
- else
- []
- end
+ (date_ranges || [])
+ .each_with_index
+ .map( &Calendar::Period.method(:from_range) )
end
private :init_periods
validate :validate_periods
def validate_periods
- periods_are_valid = true
-
- unless periods.all?(&:valid?)
- periods_are_valid = false
- end
+ periods_are_valid = periods.all?(&:valid?)
periods.each do |period|
if period.intersect?(periods)
@@ -141,7 +81,7 @@ class Calendar < ActiveRecord::Base
['begin', 'end'].map do |attr|
period_attribute[attr] = flatten_date_array(period_attribute, attr)
end
- period = Period.new(period_attribute.merge(id: index))
+ period = Calendar::Period.new(period_attribute.merge(id: index))
@periods << period unless period.marked_for_destruction?
end
@@ -164,40 +104,11 @@ class Calendar < ActiveRecord::Base
private :clear_periods
-### dates
-
- class DateValue
- include ActiveAttr::Model
-
- attribute :id, type: Integer
- attribute :value, type: Date
-
- validates_presence_of :value
-
- def self.from_date(index, date)
- DateValue.new id: index, value: date
- end
-
- # Stuff required for coocon
- def new_record?
- !persisted?
- end
-
- def persisted?
- id.present?
- end
-
- def mark_for_destruction
- self._destroy = true
- end
-
- attribute :_destroy, type: Boolean
- alias_method :marked_for_destruction?, :_destroy
- end
+ ### Calendar::DateValue
# Required by coocon
def build_date_value
- DateValue.new
+ Calendar::DateValue.new
end
def date_values
@@ -206,7 +117,7 @@ class Calendar < ActiveRecord::Base
def init_date_values
if dates
- dates.each_with_index.map { |d, index| DateValue.from_date(index, d) }
+ dates.each_with_index.map { |d, index| Calendar::DateValue.from_date(index, d) }
else
[]
end
@@ -216,11 +127,7 @@ class Calendar < ActiveRecord::Base
validate :validate_date_values
def validate_date_values
- date_values_are_valid = true
-
- unless date_values.all?(&:valid?)
- date_values_are_valid = false
- end
+ date_values_are_valid = date_values.all?(&:valid?)
date_values.each do |date_value|
if date_values.count { |d| d.value == date_value.value } > 1
@@ -244,7 +151,7 @@ class Calendar < ActiveRecord::Base
@date_values = []
attributes.each do |index, date_value_attribute|
date_value_attribute['value'] = flatten_date_array(date_value_attribute, 'value')
- date_value = DateValue.new(date_value_attribute.merge(id: index))
+ date_value = Calendar::DateValue.new(date_value_attribute.merge(id: index))
@date_values << date_value unless date_value.marked_for_destruction?
end
diff --git a/app/models/calendar/date_value.rb b/app/models/calendar/date_value.rb
new file mode 100644
index 000000000..a4a405d43
--- /dev/null
+++ b/app/models/calendar/date_value.rb
@@ -0,0 +1,32 @@
+class Calendar < ActiveRecord::Base
+
+ class DateValue
+ include ActiveAttr::Model
+
+ attribute :id, type: Integer
+ attribute :value, type: Date
+
+ validates_presence_of :value
+
+ def self.from_date(index, date)
+ new id: index, value: date
+ end
+
+ # Stuff required for coocon
+ def new_record?
+ !persisted?
+ end
+
+ def persisted?
+ id.present?
+ end
+
+ def mark_for_destruction
+ self._destroy = true
+ end
+
+ attribute :_destroy, type: Boolean
+ alias_method :marked_for_destruction?, :_destroy
+ end
+
+end
diff --git a/app/models/calendar/period.rb b/app/models/calendar/period.rb
new file mode 100644
index 000000000..1c423dfcc
--- /dev/null
+++ b/app/models/calendar/period.rb
@@ -0,0 +1,63 @@
+class Calendar < ActiveRecord::Base
+
+ class Period
+ include ActiveAttr::Model
+
+ attribute :id, type: Integer
+ attribute :begin, type: Date
+ attribute :end, type: Date
+
+ validates_presence_of :begin, :end
+ validate :check_end_greather_than_begin
+
+ def check_end_greather_than_begin
+ if self.begin && self.end && self.begin >= self.end
+ errors.add(:base, I18n.t('calendars.errors.short_period'))
+ end
+ end
+
+ def self.from_range(range, index)
+ last = range.exclude_end? ? range.end - 1.day : range.end
+ Period.new id: index, begin: range.begin, end: last
+ end
+
+ def range
+ if self.begin && self.end && self.begin <= self.end
+ Range.new self.begin, self.end
+ end
+ end
+
+ def intersect?(*other)
+ return false if range.nil?
+
+ other = other.flatten
+ other = other.delete_if { |o| o.id == id } if id
+
+ other.any? do |period|
+ if other_range = period.range
+ (range & other_range).present?
+ end
+ end
+ end
+
+ def cover? date
+ range.cover? date
+ end
+
+ # Stuff required for coocon
+ def new_record?
+ !persisted?
+ end
+
+ def persisted?
+ id.present?
+ end
+
+ def mark_for_destruction
+ self._destroy = true
+ end
+
+ attribute :_destroy, type: Boolean
+ alias_method :marked_for_destruction?, :_destroy
+ end
+end
diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb
index 34b0d9345..a146dcff1 100644
--- a/app/models/chouette/journey_pattern.rb
+++ b/app/models/chouette/journey_pattern.rb
@@ -14,19 +14,18 @@ class Chouette::JourneyPattern < Chouette::TridentActiveRecord
validates_presence_of :route
validates_presence_of :name
+ validates :stop_points, length: { minimum: 2, too_short: :minimum }, on: :update
enum section_status: { todo: 0, completed: 1, control: 2 }
attr_accessor :control_checked
after_update :control_route_sections, :unless => "control_checked"
-
def self.state_update route, state
transaction do
state.each do |item|
item.delete('errors')
jp = find_by(objectid: item['object_id']) || state_create_instance(route, item)
next if item['deletable'] && jp.persisted? && jp.destroy
-
# Update attributes and stop_points associations
jp.update_attributes(state_permited_attributes(item))
jp.state_stop_points_update(item) if !jp.errors.any? && jp.persisted?
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index 4186af6d2..c566452f4 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -477,63 +477,22 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
# merge effective days from another timetable
def merge!(another_tt)
transaction do
- # merge dates
- self.dates ||= []
- another_tt.included_days.each do |d|
- add_included_day d
- end
+ self.periods = another_tt.clone_periods + self.periods
+ self.periods = self.optimize_periods
- # if one tt has no period, just merge lists
- if self.periods.empty? || another_tt.periods.empty?
- if !another_tt.periods.empty?
- # copy periods
- self.periods = another_tt.clone_periods
- end
- else
- # check if periods can be kept
- common_day_types = self.int_day_types & another_tt.int_day_types & 508
- # if common day types : merge periods
- if common_day_types != 0
- periods = self.optimize_periods
- another_periods = another_tt.optimize_periods
- # add not common days of both periods as peculiar days
- self.effective_days_of_periods(self.class.valid_days(self.int_day_types ^ common_day_types)).each do |d|
- self.dates |= [Chouette::TimeTableDate.new(:date => d, :in_out => true)]
- end
- another_tt.effective_days_of_periods(self.class.valid_days(another_tt.int_day_types ^ common_day_types)).each do |d|
- add_included_day d
- end
- # merge periods
- self.periods = periods | another_periods
- self.int_day_types = common_day_types
- self.periods = self.optimize_periods
- else
- # convert all period in days
- self.effective_days_of_periods.each do |d|
- self.dates << Chouette::TimeTableDate.new(:date => d, :in_out => true) unless self.include_in_dates?(d)
- end
- another_tt.effective_days_of_periods.each do |d|
- add_included_day d
+ # For included dates
+ another_tt.included_days.map{ |d| add_included_day(d) }
+
+ # For excluded dates
+ existing_out_date = self.dates.where(in_out: false).map(&:date)
+ another_tt.dates.where(in_out: false).each do |d|
+ unless existing_out_date.include?(d.date)
+ self.dates << Chouette::TimeTableDate.new(:date => d.date, :in_out => false)
end
end
- end
- # if remained excluded dates are valid in other tt , remove it from result
- self.dates.each do |date|
- date.in_out = true if date.in_out == false && another_tt.include_day?(date.date)
+ self.save!
end
- # if peculiar dates are valid in new periods, remove them
- if !self.periods.empty?
- days_in_period = self.effective_days_of_periods
- dates = []
- self.dates.each do |date|
- dates << date unless date.in_out && days_in_period.include?(date.date)
- end
- self.dates = dates
- end
- self.dates.to_a.sort! { |a,b| a.date <=> b.date}
- self.save!
- end
self.convert_continuous_dates_to_periods
end
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 44dd85864..3a5851310 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -256,7 +256,7 @@ module Chouette
.where(
%Q(
"vehicle_journey_at_stops"."departure_time" >= ?
- AND "vehicle_journey_at_stops"."departure_time" < ?
+ AND "vehicle_journey_at_stops"."departure_time" <= ?
#{
if allow_empty
'OR "vehicle_journey_at_stops"."id" IS NULL'
diff --git a/app/models/clean_up.rb b/app/models/clean_up.rb
index a791065aa..e39928a17 100644
--- a/app/models/clean_up.rb
+++ b/app/models/clean_up.rb
@@ -19,6 +19,7 @@ class CleanUp < ActiveRecord::Base
result['time_table'] = send("destroy_time_tables_#{self.date_type}").try(:count)
result['time_table_date'] = send("destroy_time_tables_dates_#{self.date_type}").try(:count)
result['time_table_period'] = send("destroy_time_tables_periods_#{self.date_type}").try(:count)
+ result['vehicle_journey'] = destroy_vehicle_journey_without_time_table.try(:count)
self.overlapping_periods.each do |period|
exclude_dates_in_overlapping_period(period)
end
@@ -100,9 +101,16 @@ class CleanUp < ActiveRecord::Base
end
end
+ def destroy_vehicle_journey_without_time_table
+ Chouette::VehicleJourney.without_any_time_table.destroy_all
+ end
+
def destroy_time_tables(time_tables)
+ # Delete vehicle_journey time_table association
time_tables.each do |time_table|
- time_table.vehicle_journeys.map(&:destroy)
+ time_table.vehicle_journeys.each do |vj|
+ vj.time_tables.delete(time_table)
+ end
end
time_tables.destroy_all
end
diff --git a/app/models/concerns/default_attributes_support.rb b/app/models/concerns/default_attributes_support.rb
index ecad26856..7928093e6 100644
--- a/app/models/concerns/default_attributes_support.rb
+++ b/app/models/concerns/default_attributes_support.rb
@@ -63,10 +63,10 @@ module DefaultAttributesSupport
def fix_uniq_objectid
base_objectid = objectid.rpartition(":").first
self.objectid = "#{base_objectid}:#{id}"
- if !valid?
+ if !valid?(:objectid)
base_objectid="#{objectid}_"
cnt=1
- while !valid?
+ while !valid?(:objectid)
self.objectid = "#{base_objectid}#{cnt}"
cnt += 1
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 1a06746da..4ba05b164 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -31,13 +31,14 @@ class User < ActiveRecord::Base
@@edit_offer_permissions = ['routes.create', 'routes.edit', 'routes.destroy', 'journey_patterns.create', 'journey_patterns.edit', 'journey_patterns.destroy',
'vehicle_journeys.create', 'vehicle_journeys.edit', 'vehicle_journeys.destroy', 'time_tables.create', 'time_tables.edit', 'time_tables.destroy',
'footnotes.edit', 'footnotes.create', 'footnotes.destroy', 'routing_constraint_zones.create', 'routing_constraint_zones.edit',
- 'routing_constraint_zones.destroy', 'referentials.create', 'referentials.edit', 'referentials.destroy']
+ 'routing_constraint_zones.destroy', 'referentials.create', 'referentials.edit', 'referentials.destroy', 'boiv:edit-offer']
mattr_reader :edit_offer_permissions
def self.all_permissions
edit_offer_permissions
end
+ # Callback invoked by DeviseCasAuthenticable::Model#authernticate_with_cas_ticket
def cas_extra_attributes=(extra_attributes)
extra = extra_attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
self.name = extra[:full_name]
diff --git a/app/views/calendars/_date_value_fields.html.slim b/app/views/calendars/_date_value_fields.html.slim
index 2d072fa0a..7bde06a94 100644
--- a/app/views/calendars/_date_value_fields.html.slim
+++ b/app/views/calendars/_date_value_fields.html.slim
@@ -8,6 +8,6 @@
.wrapper
div
- = f.input :value, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :value, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
div
= link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/calendars/_filters.html.slim b/app/views/calendars/_filters.html.slim
index 6bcf25f99..4c30f69dc 100644
--- a/app/views/calendars/_filters.html.slim
+++ b/app/views/calendars/_filters.html.slim
@@ -15,7 +15,7 @@
.form-group
= f.label Calendar.human_attribute_name(:date), class: 'control-label'
- = f.input :contains_date, as: :date, label: false, wrapper_html: { class: 'date' }, class: 'form-control', include_blank: true
+ = f.input :contains_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }, class: 'form-control', include_blank: true
.actions
= link_to 'Effacer', calendars_path, class: 'btn btn-link'
diff --git a/app/views/calendars/_period_fields.html.slim b/app/views/calendars/_period_fields.html.slim
index 1e201a39f..95e204554 100644
--- a/app/views/calendars/_period_fields.html.slim
+++ b/app/views/calendars/_period_fields.html.slim
@@ -8,8 +8,8 @@
.wrapper
div
- = f.input :begin, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :begin, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
div
- = f.input :end, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :end, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
div
= link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim
index 3886cefaa..d7849005b 100644
--- a/app/views/calendars/show.html.slim
+++ b/app/views/calendars/show.html.slim
@@ -8,10 +8,12 @@
/ Below is secondary actions & optional contents (filters, ...)
.row.mb-sm
.col-lg-12.text-right
- - if policy(@calendar).destroy?
- = link_to calendar_path(@calendar), method: :delete, data: { confirm: t('calendars.actions.destroy_confirm') }, class: 'btn btn-primary' do
- span.fa.fa-trash
- span = t('actions.destroy')
+ - @calendar.action_links.each do |link|
+ = link_to link.href,
+ method: link.method,
+ data: link.data,
+ class: 'btn btn-primary' do
+ = link.content
/ PageContent
.page_content
@@ -23,4 +25,4 @@
Calendar.human_attribute_name(:shared) => t("#{@calendar.shared}"),
'Organisation' => @calendar.organisation.name,
Calendar.human_attribute_name(:dates) => @calendar.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe,
- Calendar.human_attribute_name(:date_ranges) => @calendar.date_ranges.collect{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe }
+ Calendar.human_attribute_name(:date_ranges) => @calendar.date_ranges.collect{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end - (d.exclude_end? ? 1.day : 0), format: :short))}.join('<br>').html_safe }
diff --git a/app/views/lines/show.html.slim b/app/views/lines/show.html.slim
index dbc019e72..6f75432e1 100644
--- a/app/views/lines/show.html.slim
+++ b/app/views/lines/show.html.slim
@@ -7,17 +7,12 @@
/ Below is secundary actions & optional contents
.row
.col-lg-12.text-right.mb-sm
- = link_to t('lines.actions.show_network'), [@line_referential, @line.network], class: 'btn btn-primary'
- = link_to t('lines.actions.show_company'), [@line_referential, @line.company], class: 'btn btn-primary'
-
- - if policy(Chouette::Line).create? && @line_referential.organisations.include?(current_organisation)
- = link_to t('lines.actions.new'), new_line_referential_line_path(@line_referential), class: 'btn btn-primary'
- - if false && policy(@line).update?
- = link_to t('lines.actions.edit'), edit_line_referential_line_path(@line_referential, @line), class: 'btn btn-primary'
- - if policy(@line).destroy?
- = link_to line_referential_line_path(@line_referential, @line), method: :delete, data: {confirm: t('lines.actions.destroy_confirm')}, class: 'btn btn-primary' do
- span.fa.fa-trash
- span = t('lines.actions.destroy')
+ - @line.action_links.each do |link|
+ = link_to link.href,
+ method: link.method,
+ data: link.data,
+ class: 'btn btn-primary' do
+ = link.content
/ PageContent
.page_content
diff --git a/app/views/referentials/_period_fields.html.slim b/app/views/referentials/_period_fields.html.slim
index 1e201a39f..95e204554 100644
--- a/app/views/referentials/_period_fields.html.slim
+++ b/app/views/referentials/_period_fields.html.slim
@@ -8,8 +8,8 @@
.wrapper
div
- = f.input :begin, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :begin, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
div
- = f.input :end, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :end, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
div
= link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim
index 3c1e36302..8308cf770 100644
--- a/app/views/referentials/show.html.slim
+++ b/app/views/referentials/show.html.slim
@@ -8,23 +8,15 @@
/ Below is secondary actions & optional contents (filters, ...)
.row.mb-sm
.col-lg-12.text-right
- = link_to t('time_tables.index.title'), referential_time_tables_path(@referential), class: 'btn btn-primary'
-
- - if policy(@referential).clone?
- = link_to t('actions.clone'), new_referential_path(from: @referential.id), class: 'btn btn-primary'
-
- - if policy(@referential).edit?
- button.btn.btn-primary type='button' data-toggle='modal' data-target='#purgeModal' Purger
-
- - if @referential.archived?
- = link_to t('actions.unarchive'), unarchive_referential_path(@referential.id), method: :put, class: 'btn btn-primary'
+ - @referential.action_links.each do |link|
+ - if link.is_a?(HTMLElement)
+ = link.to_html(class: 'btn btn-primary')
- else
- = link_to t('actions.archive'), archive_referential_path(@referential.id), method: :put, class: 'btn btn-primary'
-
- - if policy(@referential).destroy?
- = link_to referential_path(@referential), method: :delete, data: {confirm: t('referentials.actions.destroy_confirm')}, class: 'btn btn-primary' do
- span.fa.fa-trash
- span = t('actions.destroy')
+ = link_to link.href,
+ method: link.method,
+ data: link.data,
+ class: 'btn btn-primary' do
+ = link.content
/ PageContent
.page_content
@@ -76,9 +68,9 @@
.row
.col-lg-8.col-ld-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-12
= f.input :date_type, as: :radio_buttons, label: false
- = f.input :begin_date, as: :date, label: t('titles.clean_up.begin_date'),:wrapper_html => { class: 'date', title: t('titles.clean_up.begin_date') }
+ = f.input :begin_date, as: :date, label: t('titles.clean_up.begin_date'),:wrapper_html => { class: 'date smart_date', title: t('titles.clean_up.begin_date') }
- = f.input :end_date, as: :date, label: t('titles.clean_up.end_date'), :wrapper_html => { class: 'date cleanup_end_date_wrapper hidden', title: t('titles.clean_up.end_date') }
+ = f.input :end_date, as: :date, label: t('titles.clean_up.end_date'), :wrapper_html => { class: 'date cleanup_end_date_wrapper hidden smert_date', title: t('titles.clean_up.end_date') }
.modal-footer
button.btn.btn-link type='button' data-dismiss='modal' Annuler
diff --git a/app/views/routes/show.html.slim b/app/views/routes/show.html.slim
index 6d2d4e90d..92a5080ae 100644
--- a/app/views/routes/show.html.slim
+++ b/app/views/routes/show.html.slim
@@ -8,17 +8,12 @@
/ Below is secundary actions & optional contents (filters, ...)
.row.mb-sm
.col-lg-12.text-right
- - if @route_sp.any?
- = link_to t('journey_patterns.index.title'), [@referential, @line, @route, :journey_patterns_collection], class: 'btn btn-primary'
- - if @route.journey_patterns.present?
- = link_to t('vehicle_journeys.actions.index'), [@referential, @line, @route, :vehicle_journeys], class: 'btn btn-primary'
-
- = link_to t('vehicle_journey_exports.new.title'), referential_line_route_vehicle_journey_exports_path(@referential, @line, @route, format: :zip), class: 'btn btn-primary'
-
- - if policy(@route).destroy?
- = link_to referential_line_route_path(@referential, @line, @route), method: :delete, data: {confirm: t('routes.actions.destroy_confirm')}, class: 'btn btn-primary' do
- span.fa.fa-trash
- span = t('actions.destroy')
+ - @route.action_links.each do |link|
+ = link_to link.href,
+ method: link.method,
+ data: link.data,
+ class: 'btn btn-primary' do
+ = link.content
/ PageContent
.page_content
diff --git a/app/views/routing_constraint_zones/_filters.html.slim b/app/views/routing_constraint_zones/_filters.html.slim
index 18ef40d61..07d351b1f 100644
--- a/app/views/routing_constraint_zones/_filters.html.slim
+++ b/app/views/routing_constraint_zones/_filters.html.slim
@@ -9,7 +9,7 @@
.ffg-row
.form-group
= f.label 'Itinéraire associé', required: false, class: 'control-label'
- = f.input :route_id, as: :select, collection: @line.routing_constraint_zones.pluck(:route_id), label: false, label_method: lambda {|r| @line.routing_constraint_zones.find_by(route_id: r).route_name}, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez un itinéraire...' }, wrapper_html: { class: 'select2ed'}
+ = f.input :route_id_eq, as: :select, collection: @line.routing_constraint_zones.pluck(:route_id).uniq, label: false, label_method: lambda { |r| @line.routing_constraint_zones.find_by(route_id: r).route_name }, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez un itinéraire...' }, wrapper_html: { class: 'select2ed'}
.actions
= link_to 'Effacer', referential_line_routing_constraint_zones_path(@referential, @line), class: 'btn btn-link'
diff --git a/app/views/routing_constraint_zones/show.html.slim b/app/views/routing_constraint_zones/show.html.slim
index f0c244387..1dad4f561 100644
--- a/app/views/routing_constraint_zones/show.html.slim
+++ b/app/views/routing_constraint_zones/show.html.slim
@@ -7,13 +7,12 @@
/ Below is secundary actions & optional contents
.row
.col-lg-12.text-right.mb-sm
- - if policy(@routing_constraint_zone).update?
- = link_to t('actions.edit'), edit_referential_line_routing_constraint_zone_path(@referential, @line, @routing_constraint_zone), class: 'btn btn-primary'
-
- - if policy(@routing_constraint_zone).destroy?
- = link_to referential_line_routing_constraint_zone_path(@referential, @line, @routing_constraint_zone), method: :delete, data: {confirm: t('routing_constraint_zones.actions.destroy_confirm')}, class: 'btn btn-primary' do
- span.fa.fa-trash
- span = t('actions.destroy')
+ - @routing_constraint_zone.action_links.each do |link|
+ = link_to link.href,
+ method: link.method,
+ data: link.data,
+ class: 'btn btn-primary' do
+ = link.content
/ PageContent
.page_content
diff --git a/app/views/time_tables/_date_fields.html.slim b/app/views/time_tables/_date_fields.html.slim
index 1599dd7ff..811c241bb 100644
--- a/app/views/time_tables/_date_fields.html.slim
+++ b/app/views/time_tables/_date_fields.html.slim
@@ -8,7 +8,7 @@
.wrapper
div
- = f.input :date, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
= f.input :in_out, as: :hidden, :input_html => {:value => true}
div
= link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/time_tables/_excluded_date_fields.html.slim b/app/views/time_tables/_excluded_date_fields.html.slim
index dba5bf952..8e4003920 100644
--- a/app/views/time_tables/_excluded_date_fields.html.slim
+++ b/app/views/time_tables/_excluded_date_fields.html.slim
@@ -9,7 +9,7 @@
.wrapper
div
/ = f.label @time_table.human_attribute_name("date"), class: 'col-md-1'
- = f.input :date, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
= f.input :in_out, as: :hidden, input_html: {value: false}
div
= link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/time_tables/_filter.html.slim b/app/views/time_tables/_filter.html.slim
index cee915911..10e46d884 100644
--- a/app/views/time_tables/_filter.html.slim
+++ b/app/views/time_tables/_filter.html.slim
@@ -18,8 +18,8 @@
.form-group.togglable
= f.label @time_tables.human_attribute_name(:bounding_dates), required: false, class: 'control-label'
.filter_menu
- = f.input :start_date_gteq, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date filter_menu-item' }, default: @begin_range, include_blank: @begin_range ? false : true
- = f.input :end_date_lteq, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date filter_menu-item' }, default: @end_range, include_blank: @end_range ? false : true
+ = f.input :start_date_gteq, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @begin_range, include_blank: @begin_range ? false : true
+ = f.input :end_date_lteq, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @end_range, include_blank: @end_range ? false : true
.actions
diff --git a/app/views/time_tables/_period_fields.html.slim b/app/views/time_tables/_period_fields.html.slim
index f879ded00..6b3fde311 100644
--- a/app/views/time_tables/_period_fields.html.slim
+++ b/app/views/time_tables/_period_fields.html.slim
@@ -8,8 +8,8 @@
.wrapper
div
- = f.input :period_start, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :period_start, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
div
- = f.input :period_end, as: :date, label: false, wrapper_html: { class: 'date' }
+ = f.input :period_end, as: :date, label: false, wrapper_html: { class: 'date smart_date' }
div
= link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: 'Etes-vous sûr(e) ?' }, title: t('actions.delete')
diff --git a/app/views/time_tables/show.html.slim b/app/views/time_tables/show.html.slim
index 2e71ebb9e..f596fd480 100644
--- a/app/views/time_tables/show.html.slim
+++ b/app/views/time_tables/show.html.slim
@@ -10,21 +10,12 @@
/ Below is secundary actions & optional contents (filters, ...)
.row.mb-sm
.col-lg-12.text-right
- / - if policy(@time_table).create? && @referential.organisation == current_organisation
- / = link_to t('time_tables.actions.new'), new_referential_time_table_path(@referential), class: 'btn btn-primary'
- - if @time_table.calendar
- = link_to t('actions.actualize'), actualize_referential_time_table_path(@referential, @time_table), method: :post, class: 'btn btn-primary'
-
- /- if policy(@time_table).create? && @referential.organisation == current_organisation
- = link_to t('actions.combine'), new_referential_time_table_time_table_combination_path(@referential, @time_table), class: 'btn btn-primary'
-
- - if policy(@time_table).duplicate?
- = link_to t('actions.duplicate'), duplicate_referential_time_table_path(@referential, @time_table), class: 'btn btn-primary'
-
- - if policy(@time_table).destroy?
- = link_to referential_time_table_path(@referential, @time_table), method: :delete, data: {confirm: t('time_tables.actions.destroy_confirm')}, class: 'btn btn-primary' do
- span.fa.fa-trash
- span = t('actions.destroy')
+ - @time_table.action_links.each do |link|
+ = link_to link.href,
+ method: link.method,
+ data: link.data,
+ class: 'btn btn-primary' do
+ = link.content
/ PageContent
.page_content
diff --git a/app/views/workbenches/_filters.html.slim b/app/views/workbenches/_filters.html.slim
index 7c5055963..4eaf910c0 100644
--- a/app/views/workbenches/_filters.html.slim
+++ b/app/views/workbenches/_filters.html.slim
@@ -12,7 +12,7 @@
= f.input :associated_lines_id_eq, as: :select, collection: @workbench.lines.includes(:company).order(:name), input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez une ligne...' }, label: false, label_method: :display_name, wrapper_html: { class: 'select2ed'}
.form-group.togglable
- = f.label @wbench_refs.human_attribute_name(:status), required: false, class: 'control-label'
+ = f.label Referential.human_attribute_name(:status), required: false, class: 'control-label'
.form-group.checkbox_list
= f.input :archived_at_not_null, label: ("<span>Conservé<span class='fa fa-archive'></span></span>").html_safe, as: :boolean, wrapper_html: { class: 'checkbox-wrapper' }
= f.input :archived_at_null, label: ("<span>En préparation<span class='sb sb-lg sb-preparing'></span></span>").html_safe, as: :boolean, wrapper_html: { class: 'checkbox-wrapper' }
@@ -22,11 +22,11 @@
= f.input :organisation_name_eq_any, collection: Organisation.order('name').pluck(:name), as: :check_boxes, label: false, label_method: lambda{|w| ("<span>#{w}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }
.form-group.togglable
- = f.label @wbench_refs.human_attribute_name(:validity_period), required: false, class: 'control-label'
+ = f.label Referential.human_attribute_name(:validity_period), required: false, class: 'control-label'
.filter_menu
= f.simple_fields_for :validity_period do |p|
- = p.input :begin_gteq, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date filter_menu-item' }, default: @begin_range, include_blank: @begin_range ? false : true
- = p.input :end_lteq, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date filter_menu-item' }, default: @end_range, include_blank: @end_range ? false : true
+ = p.input :begin_gteq, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @begin_range, include_blank: @begin_range ? false : true
+ = p.input :end_lteq, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @end_range, include_blank: @end_range ? false : true
.actions
= link_to 'Effacer', @workbench, class: 'btn btn-link'
diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim
index 77e670923..37c396b46 100644
--- a/app/views/workbenches/show.html.slim
+++ b/app/views/workbenches/show.html.slim
@@ -22,18 +22,47 @@
- if @wbench_refs.any?
.row
.col-lg-12
- = table_builder @wbench_refs,
- { :name => 'name',
- :status => Proc.new {|w| w.archived? ? ("<div class='td-block'><span class='fa fa-archive'></span><span>Conservé</span></div>").html_safe : ("<div class='td-block'><span class='sb sb-lg sb-preparing'></span><span>En préparation</span></div>").html_safe},
- :organisation => Proc.new {|w| w.organisation.name},
- :validity_period => Proc.new {|w| w.validity_period.nil? ? '-' : t('validity_range', debut: l(w.try(:validity_period).try(:begin), format: :short), end: l(w.try(:validity_period).try(:end), format: :short))},
- :lines => Proc.new {|w| w.lines.count},
- :created_at => Proc.new {|w| l(w.created_at, format: :short)},
- :updated_at => Proc.new {|w| l(w.updated_at, format: :short)},
- :published_at => ''},
- [:show, :edit, :archive, :unarchive, :delete],
- [:delete],
- 'table has-filter has-search'
+ .select_table
+ = table_builder_2 @wbench_refs,
+ [ \
+ TableBuilderHelper::Column.new( \
+ key: :name, \
+ attribute: 'name' \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :status, \
+ attribute: Proc.new {|w| w.archived? ? ("<div class='td-block'><span class='fa fa-archive'></span><span>Conservé</span></div>").html_safe : ("<div class='td-block'><span class='sb sb-lg sb-preparing'></span><span>En préparation</span></div>").html_safe} \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :organisation, \
+ attribute: Proc.new {|w| w.organisation.name} \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :validity_period, \
+ attribute: Proc.new {|w| w.validity_period.nil? ? '-' : t('validity_range', debut: l(w.try(:validity_period).try(:begin), format: :short), end: l(w.try(:validity_period).try(:end), format: :short))} \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :lines, \
+ attribute: Proc.new {|w| w.lines.count} \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :created_at, \
+ attribute: Proc.new {|w| l(w.created_at, format: :short)} \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :updated_at, \
+ attribute: Proc.new {|w| l(w.updated_at, format: :short)} \
+ ), \
+ TableBuilderHelper::Column.new( \
+ key: :published_at, \
+ attribute: '' \
+ ) \
+ ],
+ selectable: true,
+ links: [:show, :edit],
+ cls: 'table has-filter has-search'
+
+ = multiple_selection_toolbox([:delete])
= new_pagination @wbench_refs, 'pull-right'
diff --git a/bin/rails b/bin/rails
index 7feb6a30e..f2b0313dd 100755
--- a/bin/rails
+++ b/bin/rails
@@ -1,7 +1,8 @@
#!/usr/bin/env ruby
begin
- load File.expand_path("../spring", __FILE__)
-rescue LoadError
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
end
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
diff --git a/bin/rake b/bin/rake
index 8017a0271..d87d5f578 100755
--- a/bin/rake
+++ b/bin/rake
@@ -1,7 +1,8 @@
#!/usr/bin/env ruby
begin
- load File.expand_path("../spring", __FILE__)
-rescue LoadError
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
end
require_relative '../config/boot'
require 'rake'
diff --git a/config/deploy.rb b/config/deploy.rb
index 78def7de7..0445ec480 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -12,7 +12,7 @@ set :group_writable, true
set :bundle_cmd, "/var/lib/gems/2.2.0/bin/bundle"
set :rake, "#{bundle_cmd} exec /var/lib/gems/2.2.0/bin/rake"
-set :keep_releases, 5
+set :keep_releases, -> { fetch(:kept_releases, 5) }
after "deploy:restart", "deploy:cleanup"
set :rails_env, -> { fetch(:stage) }
diff --git a/config/deploy/dev.rb b/config/deploy/dev.rb
index 400f1d787..5150311e9 100644
--- a/config/deploy/dev.rb
+++ b/config/deploy/dev.rb
@@ -1 +1,2 @@
server "stif-boiv-dev.af83.priv", :app, :web, :db, :primary => true
+set :kept_releases, 2
diff --git a/config/locales/calendars.en.yml b/config/locales/calendars.en.yml
index 26ca913b6..f6c5ba852 100644
--- a/config/locales/calendars.en.yml
+++ b/config/locales/calendars.en.yml
@@ -18,6 +18,7 @@ en:
destroy_confirm: Are you sure you want destroy this calendar?
errors:
overlapped_periods: Another period is overlapped with this period
+ short_period: A period needs to last at least two days
index:
title: Calendars
all: All
@@ -57,3 +58,4 @@ en:
dates:
date_in_date_ranges: A date can not be in Dates and in Date ranges.
date_in_dates: A date can appear only once in the list of dates.
+ illegal_date: The date %{date} does not exist.
diff --git a/config/locales/calendars.fr.yml b/config/locales/calendars.fr.yml
index fe16cbfed..1b403c3f2 100644
--- a/config/locales/calendars.fr.yml
+++ b/config/locales/calendars.fr.yml
@@ -31,6 +31,7 @@ fr:
destroy_confirm: Etes vous sûr de supprimer cet calendrier ?
errors:
overlapped_periods: Une autre période chevauche cette période
+ short_period: Une période doit être d'un duréé de deux jours minimum
index:
title: Calendriers
all: Tous
@@ -70,3 +71,4 @@ fr:
dates:
date_in_date_ranges: Une même date ne peut pas être incluse à la fois dans la liste et dans les intervalles de dates.
date_in_dates: Une même date ne peut pas être incluse plusieurs fois dans la liste.
+ illegal_date: La date %{date} n'existe pas.
diff --git a/config/locales/journey_patterns.en.yml b/config/locales/journey_patterns.en.yml
index d62d79e58..80adc2337 100644
--- a/config/locales/journey_patterns.en.yml
+++ b/config/locales/journey_patterns.en.yml
@@ -24,6 +24,12 @@ en:
form:
warning: "Be careful, selection is also applied to the %{count} vehicle journeys associated to this journey pattern"
activerecord:
+ errors:
+ models:
+ journey_pattern:
+ attributes:
+ stop_points:
+ minimum: 'Must at least have two stop points'
models:
journey_pattern:
zero: "journey pattern"
diff --git a/config/locales/journey_patterns.fr.yml b/config/locales/journey_patterns.fr.yml
index 39005e464..0dceb2f43 100644
--- a/config/locales/journey_patterns.fr.yml
+++ b/config/locales/journey_patterns.fr.yml
@@ -24,6 +24,12 @@ fr:
form:
warning: "Attention, la sélection s'applique aussi aux %{count} courses de la mission"
activerecord:
+ errors:
+ models:
+ journey_pattern:
+ attributes:
+ stop_points:
+ minimum: 'Une mission doit avoir au minimum deux arrêts'
models:
journey_pattern:
zero: "mission"
diff --git a/config/locales/routes.en.yml b/config/locales/routes.en.yml
index 718521c51..d8160772f 100644
--- a/config/locales/routes.en.yml
+++ b/config/locales/routes.en.yml
@@ -58,7 +58,7 @@ en:
comment: "Comments"
number: "Number"
direction: "Direction"
- wayback: "Wayback"
+ wayback: "Direction"
stop_points: "Nb Stop points"
opposite_route: "Reversed route"
opposite_route_id: "Reversed route"
diff --git a/config/locales/routes.fr.yml b/config/locales/routes.fr.yml
index 67e538be4..0af2832a2 100644
--- a/config/locales/routes.fr.yml
+++ b/config/locales/routes.fr.yml
@@ -58,7 +58,7 @@ fr:
number: "Indice"
comment: "Commentaire"
direction: "Direction"
- wayback: "Sens"
+ wayback: "Direction"
stop_points: "Nb arrêts"
journey_patterns: "Nb missions"
opposite_route: "Itinéraire associé"
diff --git a/lib/html_element.rb b/lib/html_element.rb
new file mode 100644
index 000000000..469fd7565
--- /dev/null
+++ b/lib/html_element.rb
@@ -0,0 +1,15 @@
+class HTMLElement
+ def initialize(tag_name, content = nil, options = nil)
+ @tag_name = tag_name
+ @content = content
+ @options = options
+ end
+
+ def to_html(options = {})
+ ApplicationController.helpers.content_tag(
+ @tag_name,
+ @content,
+ @options.merge(options)
+ )
+ end
+end
diff --git a/lib/link.rb b/lib/link.rb
new file mode 100644
index 000000000..7683a808f
--- /dev/null
+++ b/lib/link.rb
@@ -0,0 +1,10 @@
+class Link
+ attr_reader :content, :href, :method, :data
+
+ def initialize(content: nil, href:, method: nil, data: nil)
+ @content = content
+ @href = href
+ @method = method
+ @data = data
+ end
+end
diff --git a/spec/controllers/autocomplete_calendars_controller_spec.rb b/spec/controllers/autocomplete_calendars_controller_spec.rb
index 3ff75fadf..812cd92f9 100644
--- a/spec/controllers/autocomplete_calendars_controller_spec.rb
+++ b/spec/controllers/autocomplete_calendars_controller_spec.rb
@@ -1,5 +1,3 @@
-require 'rails_helper'
-
RSpec.describe AutocompleteCalendarsController, type: :controller do
end
diff --git a/spec/factories/chouette_2_factories.rb b/spec/factories/chouette_2_factories.rb
deleted file mode 100644
index e8eba13e6..000000000
--- a/spec/factories/chouette_2_factories.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-FactoryGirl.define do
-
- factory :organisation do
- sequence(:name) { |n| "Organisation #{n}" }
- sequence(:code) { |n| "000#{n}" }
- end
-
- factory :referential do
- sequence(:name) { |n| "Test #{n}" }
- sequence(:slug) { |n| "test_#{n}" }
- sequence(:prefix) { |n| "test_#{n}" }
- association :organisation
- association :workbench
- association :line_referential
- association :stop_area_referential
- time_zone "Europe/Paris"
- end
-
- factory :rule_parameter_set do
- sequence(:name) { |n| "Test #{n}" }
- association :organisation
- after(:create) do |rsp|
- rsp.parameters = RuleParameterSet.default_for_all_modes( rsp.organisation).parameters
- end
- end
-
- factory :user do
- association :organisation
- sequence(:name) { |n| "chouette#{n}" }
- sequence(:username) { |n| "chouette#{n}" }
- sequence(:email) { |n| "chouette#{n}@dryade.priv" }
- password "secret"
- password_confirmation "secret"
- end
-
- factory :import_task do |f|
- user_name "dummy"
- user_id 123
- no_save false
- format "Neptune"
- resources { Rack::Test::UploadedFile.new 'spec/fixtures/neptune.zip', 'application/zip', false }
- referential { Referential.find_by_slug("first") }
- end
-
- factory :kml_export do
- referential { Referential.find_by_slug("first") }
- end
-
- factory :export do
- referential { Referential.find_by_slug("first") }
- end
-
- factory :export_log_message do
- association :export
- sequence(:key) { |n| "key_#{n}" }
- end
-
- factory :vehicle_translation do
- count 1
- duration 1
- end
-
- factory :compliance_check_result do
- association :compliance_check_task
- rule_code "2-NEPTUNE-StopArea-6"
- severity "warning"
- status "nok"
- end
-
- factory :compliance_check_task do
- user_id 1
- user_name "Dummy"
- status "pending"
- referential { Referential.find_by_slug("first") }
- end
-
- factory :time_table_combination
-
-end
diff --git a/spec/factories/clean_ups.rb b/spec/factories/clean_ups.rb
index d3746c3b2..7107769ff 100644
--- a/spec/factories/clean_ups.rb
+++ b/spec/factories/clean_ups.rb
@@ -2,5 +2,6 @@ FactoryGirl.define do
factory :clean_up do
begin_date { Date.today}
end_date { Date.today + 1.month }
+ date_type { :before }
end
end
diff --git a/spec/factories/compliance_check_results.rb b/spec/factories/compliance_check_results.rb
new file mode 100644
index 000000000..7a3a3e956
--- /dev/null
+++ b/spec/factories/compliance_check_results.rb
@@ -0,0 +1,8 @@
+FactoryGirl.define do
+ factory :compliance_check_result do
+ association :compliance_check_task
+ rule_code "2-NEPTUNE-StopArea-6"
+ severity "warning"
+ status "nok"
+ end
+end
diff --git a/spec/factories/compliance_check_tasks.rb b/spec/factories/compliance_check_tasks.rb
new file mode 100644
index 000000000..e9fdeb5ef
--- /dev/null
+++ b/spec/factories/compliance_check_tasks.rb
@@ -0,0 +1,8 @@
+FactoryGirl.define do
+ factory :compliance_check_task do
+ user_id 1
+ user_name "Dummy"
+ status "pending"
+ referential { Referential.find_by_slug("first") }
+ end
+end
diff --git a/spec/factories/export_log_messages.rb b/spec/factories/export_log_messages.rb
new file mode 100644
index 000000000..849efd7b1
--- /dev/null
+++ b/spec/factories/export_log_messages.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+ factory :export_log_message do
+ association :export
+ sequence(:key) { |n| "key_#{n}" }
+ end
+end
diff --git a/spec/factories/exports.rb b/spec/factories/exports.rb
new file mode 100644
index 000000000..34427edb8
--- /dev/null
+++ b/spec/factories/exports.rb
@@ -0,0 +1,5 @@
+FactoryGirl.define do
+ factory :export do
+ referential { Referential.find_by_slug("first") }
+ end
+end
diff --git a/spec/factories/import_tasks.rb b/spec/factories/import_tasks.rb
new file mode 100644
index 000000000..9ca6db899
--- /dev/null
+++ b/spec/factories/import_tasks.rb
@@ -0,0 +1,10 @@
+FactoryGirl.define do
+ factory :import_task do |f|
+ user_name "dummy"
+ user_id 123
+ no_save false
+ format "Neptune"
+ resources { Rack::Test::UploadedFile.new 'spec/fixtures/neptune.zip', 'application/zip', false }
+ referential { Referential.find_by_slug("first") }
+ end
+end
diff --git a/spec/factories/kml_exports.rb b/spec/factories/kml_exports.rb
new file mode 100644
index 000000000..feb86c5b6
--- /dev/null
+++ b/spec/factories/kml_exports.rb
@@ -0,0 +1,5 @@
+FactoryGirl.define do
+ factory :kml_export do
+ referential { Referential.find_by_slug("first") }
+ end
+end
diff --git a/spec/factories/organisations.rb b/spec/factories/organisations.rb
new file mode 100644
index 000000000..239557a0e
--- /dev/null
+++ b/spec/factories/organisations.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+ factory :organisation do
+ sequence(:name) { |n| "Organisation #{n}" }
+ sequence(:code) { |n| "000#{n}" }
+ end
+end
diff --git a/spec/factories/referentials.rb b/spec/factories/referentials.rb
new file mode 100644
index 000000000..dd5bf1c2e
--- /dev/null
+++ b/spec/factories/referentials.rb
@@ -0,0 +1,12 @@
+FactoryGirl.define do
+ factory :referential do
+ sequence(:name) { |n| "Test #{n}" }
+ sequence(:slug) { |n| "test_#{n}" }
+ sequence(:prefix) { |n| "test_#{n}" }
+ association :organisation
+ association :workbench
+ association :line_referential
+ association :stop_area_referential
+ time_zone "Europe/Paris"
+ end
+end
diff --git a/spec/factories/rule_parameter_sets.rb b/spec/factories/rule_parameter_sets.rb
new file mode 100644
index 000000000..e20fff8ce
--- /dev/null
+++ b/spec/factories/rule_parameter_sets.rb
@@ -0,0 +1,9 @@
+FactoryGirl.define do
+ factory :rule_parameter_set do
+ sequence(:name) { |n| "Test #{n}" }
+ association :organisation
+ after(:create) do |rsp|
+ rsp.parameters = RuleParameterSet.default_for_all_modes( rsp.organisation).parameters
+ end
+ end
+end
diff --git a/spec/factories/time_table_combinations.rb b/spec/factories/time_table_combinations.rb
new file mode 100644
index 000000000..364d6460e
--- /dev/null
+++ b/spec/factories/time_table_combinations.rb
@@ -0,0 +1,3 @@
+FactoryGirl.define do
+ factory :time_table_combination
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
new file mode 100644
index 000000000..8f0ec38c0
--- /dev/null
+++ b/spec/factories/users.rb
@@ -0,0 +1,10 @@
+FactoryGirl.define do
+ factory :user do
+ association :organisation
+ sequence(:name) { |n| "chouette#{n}" }
+ sequence(:username) { |n| "chouette#{n}" }
+ sequence(:email) { |n| "chouette#{n}@dryade.priv" }
+ password "secret"
+ password_confirmation "secret"
+ end
+end
diff --git a/spec/factories/vehicle_translations.rb b/spec/factories/vehicle_translations.rb
new file mode 100644
index 000000000..1f0175222
--- /dev/null
+++ b/spec/factories/vehicle_translations.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+ factory :vehicle_translation do
+ count 1
+ duration 1
+ end
+end
diff --git a/spec/features/calendars_spec.rb b/spec/features/calendars_spec.rb
deleted file mode 100644
index e15624295..000000000
--- a/spec/features/calendars_spec.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-require 'spec_helper'
-
-describe 'Calendars', type: :feature do
- login_user
-
- let!(:calendars) { Array.new(2) { create :calendar, organisation_id: 1 } }
- let!(:shared_calendar_other_org) { create :calendar, shared: true }
- let!(:unshared_calendar_other_org) { create :calendar }
-
- describe 'index' do
- before(:each) { visit calendars_path }
-
- it 'displays calendars of the current organisation' do
- expect(page).to have_content(calendars.first.short_name)
- # expect(page).to have_content(shared_calendar_other_org.short_name)
- # expect(page).not_to have_content(unshared_calendar_other_org.short_name)
- end
-
- context 'filtering' do
- it 'supports filtering by short name' do
- fill_in 'q[name_or_short_name_cont]', with: calendars.first.short_name
- click_button 'search_btn'
- expect(page).to have_content(calendars.first.short_name)
- expect(page).not_to have_content(calendars.last.short_name)
- end
-
- it 'supports filtering by name' do
- fill_in 'q[name_or_short_name_cont]', with: calendars.first.name
- click_button 'search_btn'
- expect(page).to have_content(calendars.first.name)
- expect(page).not_to have_content(calendars.last.name)
- end
-
-
- it 'supports filtering by shared' do
- shared_calendar = create :calendar, organisation_id: 1, shared: true
- visit calendars_path
- # select I18n.t('true'), from: 'q[shared]'
- find(:css, '#q_shared_true').set(true)
- click_button 'filter_btn'
- expect(page).to have_content(shared_calendar.short_name)
- expect(page).not_to have_content(calendars.first.short_name)
- end
-
- # wip
- # it 'supports filtering by date' do
- # july_calendar = create :calendar, dates: [Date.new(2017, 7, 7)], date_ranges: [Date.new(2017, 7, 15)..Date.new(2017, 7, 30)], organisation_id: 1
- # visit calendars_path
- # select '7', from: 'q_contains_date_3i'
- # select 'juillet', from: 'q_contains_date_2i'
- # select '2017', from: 'q_contains_date_1i'
- # click_button 'filter_btn'
- # expect(page).to have_content(july_calendar.short_name)
- # expect(page).not_to have_content(calendars.first.short_name)
- # select '18', from: 'q_contains_date_3i'
- # select 'juillet', from: 'q_contains_date_2i'
- # select '2017', from: 'q_contains_date_1i'
- # click_button 'filter_btn'
- # expect(page).to have_content(july_calendar.short_name)
- # expect(page).not_to have_content(calendars.first.short_name)
- # end
- end
- end
-
- describe 'show' do
- it 'displays calendar' do
- visit calendar_path(calendars.first)
- expect(page).to have_content(calendars.first.name)
- end
- end
-end
diff --git a/spec/features/referentials_permissions_spec.rb b/spec/features/referentials_permissions_spec.rb
index 0216eeeb0..c37dff5b9 100644
--- a/spec/features/referentials_permissions_spec.rb
+++ b/spec/features/referentials_permissions_spec.rb
@@ -31,7 +31,7 @@ describe "Referentials", :type => :feature do
end
it 'shows the delete button' do
expected_href = referential_path(referential)
- expect( page ).to have_css(%{a[href=#{expected_href.inspect}] span}, text: destroy_link_text)
+ expect( page ).to have_css(%{a[href=#{expected_href.inspect}]}, text: destroy_link_text)
end
end
diff --git a/spec/helpers/table_builder_helper/column_spec.rb b/spec/helpers/table_builder_helper/column_spec.rb
new file mode 100644
index 000000000..0f27703b2
--- /dev/null
+++ b/spec/helpers/table_builder_helper/column_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe TableBuilderHelper::Column do
+ describe "#header_label" do
+ it "returns the column @name if present" do
+ expect(
+ TableBuilderHelper::Column.new(
+ name: 'ID Codif',
+ attribute: nil
+ ).header_label
+ ).to eq('ID Codif')
+ end
+
+ it "returns the I18n translation of @key if @name not present" do
+ expect(
+ TableBuilderHelper::Column.new(
+ key: :phone,
+ attribute: 'phone'
+ ).header_label(Chouette::Company)
+ ).to eq('Numéro de téléphone')
+ end
+ end
+end
diff --git a/spec/helpers/table_builder_helper/custom_links_spec.rb b/spec/helpers/table_builder_helper/custom_links_spec.rb
new file mode 100644
index 000000000..b64e97527
--- /dev/null
+++ b/spec/helpers/table_builder_helper/custom_links_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe TableBuilderHelper::CustomLinks do
+ describe "#actions_after_policy_check" do
+ it "includes :show" do
+ referential = build_stubbed(:referential)
+ user_context = UserContext.new(
+ build_stubbed(
+ :user,
+ organisation: referential.organisation,
+ permissions: [
+ 'boiv:read-offer'
+ ]
+ ),
+ referential: referential
+ )
+
+ expect(
+ TableBuilderHelper::CustomLinks.new(
+ referential,
+ user_context,
+ [:show]
+ ).actions_after_policy_check
+ ).to eq([:show])
+ end
+ end
+end
diff --git a/spec/helpers/table_builder_helper_spec.rb b/spec/helpers/table_builder_helper_spec.rb
new file mode 100644
index 000000000..8f4d98c88
--- /dev/null
+++ b/spec/helpers/table_builder_helper_spec.rb
@@ -0,0 +1,371 @@
+require 'htmlbeautifier'
+
+module TableBuilderHelper
+ include Pundit
+end
+
+describe TableBuilderHelper, type: :helper do
+ describe "#table_builder_2" do
+ it "builds a table" do
+ referential = build_stubbed(:referential)
+ workbench = referential.workbench
+
+ user_context = UserContext.new(
+ build_stubbed(
+ :user,
+ organisation: referential.organisation,
+ permissions: [
+ 'referentials.create',
+ 'referentials.edit',
+ 'referentials.destroy'
+ ]
+ ),
+ referential: referential
+ )
+ allow(helper).to receive(:current_user).and_return(user_context)
+
+ referentials = [referential]
+
+ allow(referentials).to receive(:model).and_return(Referential)
+
+ allow(helper).to receive(:params).and_return({
+ controller: 'workbenches',
+ action: 'show',
+ id: referentials[0].workbench.id
+ })
+
+ referentials = ModelDecorator.decorate(
+ referentials,
+ with: ReferentialDecorator
+ )
+
+ expected = <<-HTML
+<table class="table has-filter has-search">
+ <thead>
+ <tr>
+ <th>
+ <div class="checkbox"><input type="checkbox" name="0" id="0" value="all" /><label for="0"></label></div>
+ </th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=name">Nom<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=status">Etat<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=organisation">Organisation<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=validity_period">Période de validité englobante<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=lines">Lignes<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=created_at">Créé le<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=updated_at">Edité le<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/workbenches/#{workbench.id}?direction=desc&amp;sort=published_at">Intégré le<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div class="checkbox"><input type="checkbox" name="#{referential.id}" id="#{referential.id}" value="#{referential.id}" /><label for="#{referential.id}"></label></div>
+ </td>
+ <td title="Voir"><a href="/referentials/#{referential.id}">#{referential.name}</a></td>
+ <td>
+ <div class='td-block'><span class='sb sb-lg sb-preparing'></span><span>En préparation</span></div>
+ </td>
+ <td>#{referential.organisation.name}</td>
+ <td>-</td>
+ <td>#{referential.lines.count}</td>
+ <td>#{I18n.localize(referential.created_at, format: :short)}</td>
+ <td>#{I18n.localize(referential.updated_at, format: :short)}</td>
+ <td></td>
+ <td class="actions">
+ <div class="btn-group">
+ <div class="btn dropdown-toggle" data-toggle="dropdown"><span class="fa fa-cog"></span></div>
+ <ul class="dropdown-menu">
+ <li><a href="/referentials/#{referential.id}">Consulter</a></li>
+ <li><a href="/referentials/#{referential.id}/edit">Editer</a></li>
+ <li><a href="/referentials/#{referential.id}/time_tables">Calendriers</a></li>
+ <li><a href="/referentials/new?from=#{referential.id}">Dupliquer</a></li>
+ <li><a rel="nofollow" data-method="put" href="/referentials/#{referential.id}/archive">Conserver</a></li>
+ <li class="delete-action"><a data-confirm="Etes vous sûr de vouloir supprimer ce jeu de données ?" rel="nofollow" data-method="delete" href="/referentials/#{referential.id}"><span class="fa fa-trash"></span>Supprimer</a></li>
+ </ul>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+ HTML
+
+ html_str = helper.table_builder_2(
+ referentials,
+ [
+ TableBuilderHelper::Column.new(
+ key: :name,
+ attribute: 'name'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :status,
+ attribute: Proc.new do |w|
+ if w.archived?
+ ("<div class='td-block'><span class='fa fa-archive'></span><span>Conservé</span></div>").html_safe
+ else
+ ("<div class='td-block'><span class='sb sb-lg sb-preparing'></span><span>En préparation</span></div>").html_safe
+ end
+ end
+ ),
+ TableBuilderHelper::Column.new(
+ key: :organisation,
+ attribute: Proc.new {|w| w.organisation.name}
+ ),
+ TableBuilderHelper::Column.new(
+ key: :validity_period,
+ attribute: Proc.new do |w|
+ if w.validity_period.nil?
+ '-'
+ else
+ t(
+ 'validity_range',
+ debut: l(w.try(:validity_period).try(:begin), format: :short),
+ end: l(w.try(:validity_period).try(:end), format: :short)
+ )
+ end
+ end
+ ),
+ TableBuilderHelper::Column.new(
+ key: :lines,
+ attribute: Proc.new {|w| w.lines.count}
+ ),
+ TableBuilderHelper::Column.new(
+ key: :created_at,
+ attribute: Proc.new {|w| l(w.created_at, format: :short)}
+ ),
+ TableBuilderHelper::Column.new(
+ key: :updated_at,
+ attribute: Proc.new {|w| l(w.updated_at, format: :short)}
+ ),
+ TableBuilderHelper::Column.new(
+ key: :published_at,
+ attribute: ''
+ )
+ ],
+ selectable: true,
+ links: [:show, :edit],
+ cls: 'table has-filter has-search'
+ )
+
+ beautified_html = HtmlBeautifier.beautify(html_str, indent: ' ')
+
+ expect(beautified_html).to eq(expected.chomp)
+ end
+
+ it "can set a column as non-sortable" do
+ company = build_stubbed(:company)
+ line_referential = build_stubbed(
+ :line_referential,
+ companies: [company]
+ )
+ referential = build_stubbed(
+ :referential,
+ line_referential: line_referential
+ )
+
+ user_context = UserContext.new(
+ build_stubbed(
+ :user,
+ organisation: referential.organisation,
+ permissions: [
+ 'referentials.create',
+ 'referentials.edit',
+ 'referentials.destroy'
+ ]
+ ),
+ referential: referential
+ )
+ allow(helper).to receive(:current_user).and_return(user_context)
+ allow(TableBuilderHelper::URL).to receive(:current_referential)
+ .and_return(referential)
+
+ companies = [company]
+
+ allow(companies).to receive(:model).and_return(Chouette::Company)
+
+ allow(helper).to receive(:params).and_return({
+ controller: 'referential_companies',
+ action: 'index',
+ referential_id: referential.id
+ })
+
+ companies = ModelDecorator.decorate(
+ companies,
+ with: CompanyDecorator
+ )
+
+ expected = <<-HTML
+<table class="table has-search">
+ <thead>
+ <tr>
+ <th>ID Codif</th>
+ <th><a href="/referentials/#{referential.id}/companies?direction=desc&amp;sort=name">Nom<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/referentials/#{referential.id}/companies?direction=desc&amp;sort=phone">Numéro de téléphone<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/referentials/#{referential.id}/companies?direction=desc&amp;sort=email">Email<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th><a href="/referentials/#{referential.id}/companies?direction=desc&amp;sort=url">Page web associée<span class="orderers"><span class="fa fa-sort-asc active"></span><span class="fa fa-sort-desc "></span></span></a></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>#{company.objectid.local_id}</td>
+ <td title="Voir"><a href="/referentials/#{referential.id}/companies/#{company.id}">#{company.name}</a></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td class="actions">
+ <div class="btn-group">
+ <div class="btn dropdown-toggle" data-toggle="dropdown"><span class="fa fa-cog"></span></div>
+ <ul class="dropdown-menu">
+ <li><a href="/referentials/#{referential.id}/companies/#{company.id}">Consulter</a></li>
+ </ul>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+ HTML
+
+ html_str = helper.table_builder_2(
+ companies,
+ [
+ TableBuilderHelper::Column.new(
+ name: 'ID Codif',
+ attribute: Proc.new { |n| n.try(:objectid).try(:local_id) },
+ sortable: false
+ ),
+ TableBuilderHelper::Column.new(
+ key: :name,
+ attribute: 'name'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :phone,
+ attribute: 'phone'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :email,
+ attribute: 'email'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :url,
+ attribute: 'url'
+ ),
+ ],
+ links: [:show, :edit, :delete],
+ cls: 'table has-search'
+ )
+
+ beautified_html = HtmlBeautifier.beautify(html_str, indent: ' ')
+
+ expect(beautified_html).to eq(expected.chomp)
+ end
+
+ it "can set all columns as non-sortable" do
+ company = build_stubbed(:company)
+ line_referential = build_stubbed(
+ :line_referential,
+ companies: [company]
+ )
+ referential = build_stubbed(
+ :referential,
+ line_referential: line_referential
+ )
+
+ user_context = UserContext.new(
+ build_stubbed(
+ :user,
+ organisation: referential.organisation,
+ permissions: [
+ 'referentials.create',
+ 'referentials.edit',
+ 'referentials.destroy'
+ ]
+ ),
+ referential: referential
+ )
+ allow(helper).to receive(:current_user).and_return(user_context)
+ allow(TableBuilderHelper::URL).to receive(:current_referential)
+ .and_return(referential)
+
+ companies = [company]
+
+ allow(companies).to receive(:model).and_return(Chouette::Company)
+
+ allow(helper).to receive(:params).and_return({
+ controller: 'referential_companies',
+ action: 'index',
+ referential_id: referential.id
+ })
+
+ companies = ModelDecorator.decorate(
+ companies,
+ with: CompanyDecorator
+ )
+
+ expected = <<-HTML
+<table class="table has-search">
+ <thead>
+ <tr>
+ <th>ID Codif</th>
+ <th>Nom</th>
+ <th>Numéro de téléphone</th>
+ <th>Email</th>
+ <th>Page web associée</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>#{company.objectid.local_id}</td>
+ <td title="Voir"><a href="/referentials/#{referential.id}/companies/#{company.id}">#{company.name}</a></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td class="actions">
+ <div class="btn-group">
+ <div class="btn dropdown-toggle" data-toggle="dropdown"><span class="fa fa-cog"></span></div>
+ <ul class="dropdown-menu">
+ <li><a href="/referentials/#{referential.id}/companies/#{company.id}">Consulter</a></li>
+ </ul>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+ HTML
+
+ html_str = helper.table_builder_2(
+ companies,
+ [
+ TableBuilderHelper::Column.new(
+ name: 'ID Codif',
+ attribute: Proc.new { |n| n.try(:objectid).try(:local_id) }
+ ),
+ TableBuilderHelper::Column.new(
+ key: :name,
+ attribute: 'name'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :phone,
+ attribute: 'phone'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :email,
+ attribute: 'email'
+ ),
+ TableBuilderHelper::Column.new(
+ key: :url,
+ attribute: 'url'
+ ),
+ ],
+ sortable: false,
+ links: [:show, :edit, :delete],
+ cls: 'table has-search'
+ )
+
+ beautified_html = HtmlBeautifier.beautify(html_str, indent: ' ')
+
+ expect(beautified_html).to eq(expected.chomp)
+ end
+ end
+end
diff --git a/spec/javascripts/spec_helper.coffee b/spec/javascripts/spec_helper.coffee
new file mode 100644
index 000000000..9ff516885
--- /dev/null
+++ b/spec/javascripts/spec_helper.coffee
@@ -0,0 +1,32 @@
+# Teaspoon includes some support files, but you can use anything from your own support path too.
+# require support/jasmine-jquery-1.7.0
+# require support/jasmine-jquery-2.0.0
+# require support/jasmine-jquery-2.1.0
+# require support/sinon
+# require support/your-support-file
+#
+# PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion.
+# Use this polyfill to avoid the confusion.
+#= require support/phantomjs-shims
+#
+# You can require your own javascript files here. By default this will include everything in application, however you
+# may get better load performance if you require the specific files that are being used in the spec that tests them.
+#= require application
+#
+# Deferring execution
+# If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call
+# Teaspoon.execute() after everything has been loaded. Simple example of a timeout:
+#
+# Teaspoon.defer = true
+# setTimeout(Teaspoon.execute, 1000)
+#
+# Matching files
+# By default Teaspoon will look for files that match _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your
+# spec path and it'll be included in the default suite automatically. If you want to customize suites, check out the
+# configuration in teaspoon_env.rb
+#
+# Manifest
+# If you'd rather require your spec files manually (to control order for instance) you can disable the suite matcher in
+# the configuration and use this file as a manifest.
+#
+# For more information: http://github.com/modeset/teaspoon
diff --git a/spec/javascripts/vehicle_journeys/actions_spec.js b/spec/javascripts/vehicle_journeys/actions_spec.js
index 19f65046f..d96baf8ef 100644
--- a/spec/javascripts/vehicle_journeys/actions_spec.js
+++ b/spec/javascripts/vehicle_journeys/actions_spec.js
@@ -188,11 +188,13 @@ describe('when clicking on validate button inside editing modal', () => {
describe('when clicking on validate button inside duplicating modal', () => {
it('should create an action to duplicate a vehiclejourney schedule', () => {
const data = {}
+ const departureDelta = 0
const expectedAction = {
type: 'DUPLICATE_VEHICLEJOURNEY',
- data
+ data,
+ departureDelta
}
- expect(actions.duplicateVehicleJourney(data)).toEqual(expectedAction)
+ expect(actions.duplicateVehicleJourney(data, departureDelta)).toEqual(expectedAction)
})
})
describe('when clicking on edit notes modal', () => {
diff --git a/spec/javascripts/vehicle_journeys/reducers/filters_spec.js b/spec/javascripts/vehicle_journeys/reducers/filters_spec.js
index 2ab88386a..0a6729c8b 100644
--- a/spec/javascripts/vehicle_journeys/reducers/filters_spec.js
+++ b/spec/javascripts/vehicle_journeys/reducers/filters_spec.js
@@ -31,7 +31,7 @@ describe('filters reducer', () => {
vehicleJourney: {},
timetable: {},
withoutSchedule: true,
- withoutTimeTable: false
+ withoutTimeTable: true
},
queryString: ''
}
@@ -153,7 +153,7 @@ describe('filters reducer', () => {
"&q%5Bvehicle_journey_at_stops_departure_time_gteq%5D=11%3A11",
"&q%5Bvehicle_journey_at_stops_departure_time_lteq%5D=22%3A22",
"&q%5Bvehicle_journey_without_departure_time%5D=true",
- "&q%5Bvehicle_journey_without_time_table%5D=false"
+ "&q%5Bvehicle_journey_without_time_table%5D=true"
].join('')
expect(
diff --git a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js
index 23ebc3d9f..620e2ffdd 100644
--- a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js
+++ b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js
@@ -216,14 +216,15 @@ describe('vehicleJourneys reducer', () => {
delta: 627,
arrival_time : {
hour: '12',
- minute: '00'
+ minute: '01'
},
departure_time : {
hour: '22',
- minute: '27'
+ minute: '28'
},
stop_area_object_id : "FR:92024:ZDE:420553:STIF"
}]
+ let departureDelta = 1
let fakeData = {
duplicate_number: {value : 1},
additional_time: {value: '5'}
@@ -234,7 +235,8 @@ describe('vehicleJourneys reducer', () => {
expect(
vjReducer(state, {
type: 'DUPLICATE_VEHICLEJOURNEY',
- data: fakeData
+ data: fakeData,
+ departureDelta
})
).toEqual([state[0], newVJ, state[1]])
})
diff --git a/spec/models/calendar/date_value_spec.rb b/spec/models/calendar/date_value_spec.rb
new file mode 100644
index 000000000..70dca3cc6
--- /dev/null
+++ b/spec/models/calendar/date_value_spec.rb
@@ -0,0 +1,35 @@
+RSpec.describe Calendar::DateValue, type: :model do
+ describe 'DateValue' do
+ subject { date_value }
+
+ def date_value(attributes = {})
+ @__date_values__ ||= Hash.new
+ @__date_values__.fetch(attributes) do
+ @__date_values__[attributes] = Calendar::DateValue.new(attributes)
+ end
+ end
+
+ it 'should support mark_for_destruction (required by cocoon)' do
+ date_value.mark_for_destruction
+ expect(date_value).to be_marked_for_destruction
+ end
+
+ it 'should support _destroy attribute (required by coocon)' do
+ date_value._destroy = true
+ expect(date_value).to be_marked_for_destruction
+ end
+
+ it 'should support new_record? (required by cocoon)' do
+ expect(Calendar::DateValue.new).to be_new_record
+ expect(date_value(id: 42)).not_to be_new_record
+ end
+
+ it 'should cast value as date attribute' do
+ expect(date_value(value: '2017-01-03').value).to eq(Date.new(2017,01,03))
+ end
+
+ it 'validates presence' do
+ is_expected.to validate_presence_of(:value)
+ end
+ end
+end
diff --git a/spec/models/calendar/period_spec.rb b/spec/models/calendar/period_spec.rb
new file mode 100644
index 000000000..233733cbf
--- /dev/null
+++ b/spec/models/calendar/period_spec.rb
@@ -0,0 +1,68 @@
+RSpec.describe Calendar::Period, type: :model do
+
+
+ subject { period }
+
+ def period(attributes = {})
+ @__period__ ||= {}
+ @__period__.fetch(attributes){
+ @__period__[attributes] = Calendar::Period.new(attributes)
+ }
+ end
+
+ it 'should support mark_for_destruction (required by cocoon)' do
+ period.mark_for_destruction
+ expect(period).to be_marked_for_destruction
+ end
+
+ it 'should support _destroy attribute (required by coocon)' do
+ period._destroy = true
+ expect(period).to be_marked_for_destruction
+ end
+
+ it 'should support new_record? (required by cocoon)' do
+ expect(Calendar::Period.new).to be_new_record
+ expect(period(id: 42)).not_to be_new_record
+ end
+
+ it 'should cast begin as date attribute' do
+ expect(period(begin: '2016-11-22').begin).to eq(Date.new(2016,11,22))
+ end
+
+ it 'should cast end as date attribute' do
+ expect(period(end: '2016-11-22').end).to eq(Date.new(2016,11,22))
+ end
+
+ it { is_expected.to validate_presence_of(:begin) }
+ it { is_expected.to validate_presence_of(:end) }
+
+ it 'should validate that end is greather than or equlals to begin' do
+ expect(period(begin: '2016-11-21', end: '2016-11-22')).to be_valid
+ expect(period(begin: '2016-11-21', end: '2016-11-21')).to_not be_valid
+ expect(period(begin: '2016-11-22', end: '2016-11-21')).to_not be_valid
+ end
+
+ describe 'intersect?' do
+ it 'should detect date in common with other date_ranges' do
+ november = period(begin: '2016-11-01', end: '2016-11-30')
+ mid_november_mid_december = period(begin: '2016-11-15', end: '2016-12-15')
+ expect(november.intersect?(mid_november_mid_december)).to be(true)
+ end
+
+ it 'should not intersect when no date is in common' do
+ november = period(begin: '2016-11-01', end: '2016-11-30')
+ december = period(begin: '2016-12-01', end: '2016-12-31')
+
+ expect(november.intersect?(december)).to be(false)
+
+ january = period(begin: '2017-01-01', end: '2017-01-31')
+ expect(november.intersect?(december, january)).to be(false)
+ end
+
+ it 'should not intersect itself' do
+ period = period(id: 42, begin: '2016-11-01', end: '2016-11-30')
+ expect(period.intersect?(period)).to be(false)
+ end
+
+ end
+end
diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb
index 33d9676cd..cf7e4aa27 100644
--- a/spec/models/calendar_spec.rb
+++ b/spec/models/calendar_spec.rb
@@ -1,5 +1,3 @@
-require 'rails_helper'
-
RSpec.describe Calendar, :type => :model do
it { should belong_to(:organisation) }
@@ -23,134 +21,25 @@ RSpec.describe Calendar, :type => :model do
describe 'validations' do
it 'validates that dates and date_ranges do not overlap' do
- calendar = build(:calendar, dates: [Date.today], date_ranges: [Date.today..Date.tomorrow])
- expect {
- calendar.save!
- }.to raise_error(ActiveRecord::RecordInvalid)
+ expect(build(:calendar, dates: [Date.today], date_ranges: [Date.today..Date.tomorrow])).to_not be_valid
end
it 'validates that there are no duplicates in dates' do
- calendar = build(:calendar, dates: [Date.yesterday, Date.yesterday], date_ranges: [Date.today..Date.tomorrow])
- expect {
- calendar.save!
- }.to raise_error(ActiveRecord::RecordInvalid)
+ expect(build(:calendar, dates: [Date.yesterday, Date.yesterday], date_ranges: [Date.today..Date.tomorrow])).to_not be_valid
end
end
- describe 'Period' do
-
- subject { period }
-
- def period(attributes = {})
- return @period if attributes.empty? and @period
- Calendar::Period.new(attributes).tap do |period|
- @period = period if attributes.empty?
- end
- end
-
- it 'should support mark_for_destruction (required by cocoon)' do
- period.mark_for_destruction
- expect(period).to be_marked_for_destruction
- end
-
- it 'should support _destroy attribute (required by coocon)' do
- period._destroy = true
- expect(period).to be_marked_for_destruction
- end
-
- it 'should support new_record? (required by cocoon)' do
- expect(Calendar::Period.new).to be_new_record
- expect(period(id: 42)).not_to be_new_record
- end
-
- it 'should cast begin as date attribute' do
- expect(period(begin: '2016-11-22').begin).to eq(Date.new(2016,11,22))
- end
-
- it 'should cast end as date attribute' do
- expect(period(end: '2016-11-22').end).to eq(Date.new(2016,11,22))
- end
-
- it { is_expected.to validate_presence_of(:begin) }
- it { is_expected.to validate_presence_of(:end) }
-
- it 'should validate that end is greather than or equlals to begin' do
- expect(period(begin: '2016-11-21', end: '2016-11-22')).to be_valid
- expect(period(begin: '2016-11-21', end: '2016-11-21')).to be_valid
- expect(period(begin: '2016-11-22', end: '2016-11-21')).to_not be_valid
- end
-
- describe 'intersect?' do
- it 'should detect date in common with other date_ranges' do
- november = period(begin: '2016-11-01', end: '2016-11-30')
- mid_november_mid_december = period(begin: '2016-11-15', end: '2016-12-15')
- expect(november.intersect?(mid_november_mid_december)).to be(true)
- end
-
- it 'should not intersect when no date is in common' do
- november = period(begin: '2016-11-01', end: '2016-11-30')
- december = period(begin: '2016-12-01', end: '2016-12-31')
-
- expect(november.intersect?(december)).to be(false)
-
- january = period(begin: '2017-01-01', end: '2017-01-31')
- expect(november.intersect?(december, january)).to be(false)
- end
-
- it 'should not intersect itself' do
- period = period(id: 42, begin: '2016-11-01', end: '2016-11-30')
- expect(period.intersect?(period)).to be(false)
- end
-
- end
- end
describe 'before_validation' do
let(:calendar) { create(:calendar, date_ranges: []) }
it 'shoud fill date_ranges with date ranges' do
- expected_ranges = [
- Range.new(Date.today, Date.tomorrow)
- ]
- expected_ranges.each_with_index do |range, index|
- calendar.date_ranges << Calendar::Period.from_range(index, range)
- end
+ expected_range = Date.today..Date.tomorrow
+ calendar.date_ranges << expected_range
calendar.valid?
- expect(calendar.date_ranges.map { |period| period.begin..period.end }).to eq(expected_ranges)
+ expect(calendar.date_ranges.map { |period| period.begin..period.end }).to eq([expected_range])
end
end
- describe 'DateValue' do
- subject { date_value }
-
- def date_value(attributes = {})
- return @date_value if attributes.empty? and @date_value
- Calendar::DateValue.new(attributes).tap do |date_value|
- @date_value = date_value if attributes.empty?
- end
- end
-
- it 'should support mark_for_destruction (required by cocoon)' do
- date_value.mark_for_destruction
- expect(date_value).to be_marked_for_destruction
- end
-
- it 'should support _destroy attribute (required by coocon)' do
- date_value._destroy = true
- expect(date_value).to be_marked_for_destruction
- end
-
- it 'should support new_record? (required by cocoon)' do
- expect(Calendar::DateValue.new).to be_new_record
- expect(date_value(id: 42)).not_to be_new_record
- end
-
- it 'should cast value as date attribute' do
- expect(date_value(value: '2017-01-03').value).to eq(Date.new(2017,01,03))
- end
-
- it { is_expected.to validate_presence_of(:value) }
- end
end
-
diff --git a/spec/models/chouette/journey_pattern_spec.rb b/spec/models/chouette/journey_pattern_spec.rb
index 19b5060d2..aaf9a694f 100644
--- a/spec/models/chouette/journey_pattern_spec.rb
+++ b/spec/models/chouette/journey_pattern_spec.rb
@@ -2,6 +2,26 @@ require 'spec_helper'
describe Chouette::JourneyPattern, :type => :model do
+ context 'validate minimum stop_points size' do
+ let(:journey_pattern) { create :journey_pattern }
+ let(:stop_points) { journey_pattern.stop_points }
+
+ it 'should be valid if it has at least two sp' do
+ journey_pattern.stop_points.first(stop_points.size - 2).each do |sp|
+ journey_pattern.stop_points.delete(sp)
+ end
+ expect(journey_pattern).to be_valid
+ end
+
+ it 'should not be valid if it has less then two sp' do
+ journey_pattern.stop_points.first(stop_points.size - 1).each do |sp|
+ journey_pattern.stop_points.delete(sp)
+ end
+ expect(journey_pattern).to_not be_valid
+ expect(journey_pattern.errors).to have_key(:stop_points)
+ end
+ end
+
describe "state_update" do
def journey_pattern_to_state jp
jp.attributes.slice('name', 'published_name', 'registration_number').tap do |item|
@@ -24,12 +44,14 @@ describe Chouette::JourneyPattern, :type => :model do
end
it 'should attach checked stop_points' do
- state['stop_points'].each{|sp| sp['checked'] = true}
# Make sure journey_pattern has no stop_points
- journey_pattern.stop_points.delete_all
+ state['stop_points'].each{|sp| sp['checked'] = false}
+ journey_pattern.state_stop_points_update(state)
expect(journey_pattern.reload.stop_points).to be_empty
+ state['stop_points'].each{|sp| sp['checked'] = true}
journey_pattern.state_stop_points_update(state)
+
expect(journey_pattern.reload.stop_points.count).to eq(5)
end
@@ -89,6 +111,63 @@ describe Chouette::JourneyPattern, :type => :model do
expect(collection.first).to_not have_key('object_id')
end
+
+ it 'should create journey_pattern' do
+ new_state = journey_pattern_to_state(build(:journey_pattern, objectid: nil, route: route))
+ Chouette::JourneyPattern.state_create_instance route, new_state
+ expect(new_state['object_id']).to be_truthy
+ expect(new_state['new_record']).to be_truthy
+ end
+
+ it 'should delete journey_pattern' do
+ state['deletable'] = true
+ collection = [state]
+ expect {
+ Chouette::JourneyPattern.state_update route, collection
+ }.to change{Chouette::JourneyPattern.count}.from(1).to(0)
+
+ expect(collection).to be_empty
+ end
+
+ it 'should delete multiple journey_pattern' do
+ collection = 5.times.collect{journey_pattern_to_state(create(:journey_pattern, route: route))}
+ collection.map{|i| i['deletable'] = true}
+
+ expect {
+ Chouette::JourneyPattern.state_update route, collection
+ }.to change{Chouette::JourneyPattern.count}.from(5).to(0)
+ end
+
+ it 'should validate journey_pattern on update' do
+ journey_pattern.name = ''
+ collection = [state]
+ Chouette::JourneyPattern.state_update route, collection
+ expect(collection.first['errors']).to have_key(:name)
+ end
+
+ it 'should validate journey_pattern on create' do
+ new_state = journey_pattern_to_state(build(:journey_pattern, name: '', objectid: nil, route: route))
+ collection = [new_state]
+ expect {
+ Chouette::JourneyPattern.state_update route, collection
+ }.to_not change{Chouette::JourneyPattern.count}
+
+ expect(collection.first['errors']).to have_key(:name)
+ expect(collection.first).to_not have_key('object_id')
+ end
+
+ it 'should not save any journey_pattern of collection if one is invalid' do
+ journey_pattern.name = ''
+ valid_state = journey_pattern_to_state(build(:journey_pattern, objectid: nil, route: route))
+ invalid_state = journey_pattern_to_state(journey_pattern)
+ collection = [valid_state, invalid_state]
+
+ expect {
+ Chouette::JourneyPattern.state_update route, collection
+ }.to_not change{Chouette::JourneyPattern.count}
+
+ expect(collection.first).to_not have_key('object_id')
+ end
end
describe "#stop_point_ids" do
diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb
index 7a8863cb3..76c5def5c 100644
--- a/spec/models/chouette/time_table_spec.rb
+++ b/spec/models/chouette/time_table_spec.rb
@@ -2,18 +2,74 @@ require 'spec_helper'
describe Chouette::TimeTable, :type => :model do
subject { create(:time_table) }
+ let(:subject_periods_to_range) { subject.periods.map{|p| p.period_start..p.period_end } }
it { is_expected.to validate_presence_of :comment }
it { is_expected.to validate_uniqueness_of :objectid }
- context "merge with calendar" do
- let(:calendar) { create(:calendar) }
+ describe "#merge! with time_table" do
+ let(:another_tt) { create(:time_table) }
+ let(:another_tt_periods_to_range) { another_tt.periods.map{|p| p.period_start..p.period_end } }
+
+ # Make sur we don't have overlapping periods or dates
+ before do
+ another_tt.periods.each do |p|
+ p.period_start = p.period_start + 1.year
+ p.period_end = p.period_end + 1.year
+ end
+ another_tt.dates.each{| d| d.date = d.date + 1.year }
+ end
+
+ it 'should merge dates' do
+ subject.dates.clear
+ subject.merge!(another_tt)
+ expect(subject.dates.map(&:date)).to include(*another_tt.dates.map(&:date))
+ end
+
+ it 'should merge periods' do
+ subject.periods.clear
+ subject.merge!(another_tt)
+
+ expect(subject_periods_to_range).to include(*another_tt_periods_to_range)
+ end
+
+ it 'should not modify int_day_types' do
+ int_day_types = subject.int_day_types
+ subject.merge!(another_tt)
+ expect(subject.int_day_types).to eq int_day_types
+ end
+
+ it 'should merge date in_out false' do
+ another_tt.dates.last.in_out = false
+ another_tt.save
+
+ subject.merge!(another_tt)
+ expect(subject.dates.map(&:date)).to include(another_tt.dates.last.date)
+ end
+ end
+
+ context "#merge! with calendar" do
+ let(:calendar) { create(:calendar, date_ranges: [Date.today + 1.year..Date.tomorrow + 1.year]) }
- it 'should add calendar dates to time_table' do
+ it 'should merge calendar dates' do
subject.dates.clear
subject.merge!(calendar.convert_to_time_table)
expect(subject.dates.map(&:date)).to include(*calendar.dates)
end
+
+ it 'should merge calendar periods with no periods in source' do
+ subject.periods.clear
+ another_tt = calendar.convert_to_time_table
+ subject.merge!(another_tt)
+ expect(subject_periods_to_range).to include(*calendar.date_ranges)
+ end
+
+ it 'should add calendar periods with existing periods in source' do
+ another_tt = calendar.convert_to_time_table
+ subject.merge!(another_tt)
+
+ expect(subject_periods_to_range).to include(*calendar.date_ranges)
+ end
end
describe "actualize" do
@@ -981,47 +1037,6 @@ end
end
end
-
- describe "#merge!" do
- context "timetables have periods with common day_types " do
- before do
- subject.periods.clear
- subject.dates.clear
- subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,5))
- subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,6))
- subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true)
- subject.int_day_types = 4|16|32|128
- another_tt = create(:time_table , :int_day_types => (4|16|64|128) )
- another_tt.periods.clear
- another_tt.dates.clear
- another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,5), :period_end => Date.new(2014,8,12))
- another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,15), :period_end => Date.new(2014,7,25))
- subject.merge! another_tt
- subject.reload
- end
- it "should have merged periods" do
- expect(subject.periods.size).to eq(3)
- expect(subject.periods[0].period_start).to eq(Date.new(2014, 6, 30))
- expect(subject.periods[0].period_end).to eq(Date.new(2014, 7, 6))
- expect(subject.periods[1].period_start).to eq(Date.new(2014, 7, 15))
- expect(subject.periods[1].period_end).to eq(Date.new(2014, 7, 25))
- expect(subject.periods[2].period_start).to eq(Date.new(2014, 8, 1))
- expect(subject.periods[2].period_end).to eq(Date.new(2014, 8, 12))
- end
- it "should not modify day_types" do
- expect(subject.int_day_types).to eq(4|16|128)
- end
- it "should have dates for thursdays and fridays" do
- expect(subject.dates.size).to eq(4)
- expect(subject.dates[0].date).to eq(Date.new(2014,7,3))
- expect(subject.dates[1].date).to eq(Date.new(2014,7,18))
- expect(subject.dates[2].date).to eq(Date.new(2014,7,25))
- expect(subject.dates[3].date).to eq(Date.new(2014,8,8))
- end
- end
-
- end
-
describe "#intersect!" do
context "timetables have periods with common day_types " do
before do
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 8f9080b99..c78ef5b33 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -351,7 +351,7 @@ describe Chouette::VehicleJourney, :type => :model do
end
end
- describe ".departure_time_between" do
+ describe ".where_departure_time_between" do
it "selects vehicle journeys whose departure times are between the
specified range" do
journey_early = create(
@@ -404,6 +404,35 @@ describe Chouette::VehicleJourney, :type => :model do
.to_a
).to eq([journey])
end
+
+ it "uses an inclusive range" do
+ journey_early = create(
+ :vehicle_journey,
+ stop_departure_time: '03:00:00'
+ )
+
+ route = journey_early.route
+ journey_pattern = journey_early.journey_pattern
+
+ journey_late = create(
+ :vehicle_journey,
+ route: route,
+ journey_pattern: journey_pattern,
+ stop_departure_time: '04:00:00'
+ )
+
+ expect(route
+ .vehicle_journeys
+ .select('DISTINCT "vehicle_journeys".*')
+ .joins('
+ LEFT JOIN "vehicle_journey_at_stops"
+ ON "vehicle_journey_at_stops"."vehicle_journey_id" =
+ "vehicle_journeys"."id"
+ ')
+ .where_departure_time_between('03:00', '04:00', allow_empty: true)
+ .to_a
+ ).to match_array([journey_early, journey_late])
+ end
end
describe ".without_time_tables" do
diff --git a/spec/models/clean_up_spec.rb b/spec/models/clean_up_spec.rb
index 4b1bf4da9..ee88ca773 100644
--- a/spec/models/clean_up_spec.rb
+++ b/spec/models/clean_up_spec.rb
@@ -191,7 +191,7 @@ RSpec.describe CleanUp, :type => :model do
context '#destroy_time_tables' do
let!(:time_table) { create(:time_table) }
- let(:cleaner) { create(:clean_up, date_type: :before) }
+ let(:cleaner) { create(:clean_up) }
it 'should destroy all time_tables' do
expect{cleaner.destroy_time_tables(Chouette::TimeTable.all)}.to change {
@@ -199,11 +199,26 @@ RSpec.describe CleanUp, :type => :model do
}.by(-1)
end
- it 'should destroy associated vehicle_journeys' do
- create(:vehicle_journey, time_tables: [time_table])
- expect{cleaner.destroy_time_tables(Chouette::TimeTable.all)}.to change {
- Chouette::VehicleJourney.count
- }.by(-1)
+ it 'should destroy time_table vehicle_journey association' do
+ vj = create(:vehicle_journey, time_tables: [time_table])
+ cleaner.destroy_time_tables(Chouette::TimeTable.all)
+ expect(vj.reload.time_tables).to be_empty
+ end
+ end
+
+ context '#destroy_vehicle_journey_without_time_table' do
+ let(:cleaner) { create(:clean_up) }
+
+ it 'should destroy vehicle_journey' do
+ vj = create(:vehicle_journey)
+ expect{cleaner.destroy_vehicle_journey_without_time_table
+ }.to change { Chouette::VehicleJourney.count }.by(-1)
+ end
+
+ it 'should not destroy vehicle_journey with time_table' do
+ create(:vehicle_journey, time_tables: [create(:time_table)])
+ expect{cleaner.destroy_vehicle_journey_without_time_table
+ }.to_not change { Chouette::VehicleJourney.count }
end
end
diff --git a/spec/support/pundit/policies.rb b/spec/support/pundit/policies.rb
index e18309226..56433b2ee 100644
--- a/spec/support/pundit/policies.rb
+++ b/spec/support/pundit/policies.rb
@@ -9,7 +9,7 @@ module Support
end
def create_user_context(user:, referential:)
- OpenStruct.new(user: user, context: {referential: referential})
+ UserContext.new(user, referential: referential)
end
def add_permissions(*permissions, for_user:)
diff --git a/spec/views/lines/show.html.erb_spec.rb b/spec/views/lines/show.html.erb_spec.rb
index 3a9efa0ce..7bc120f1a 100644
--- a/spec/views/lines/show.html.erb_spec.rb
+++ b/spec/views/lines/show.html.erb_spec.rb
@@ -3,7 +3,13 @@ require 'spec_helper'
describe "/lines/show", :type => :view do
assign_referential
- let!(:line) { assign :line, create(:line) }
+ let!(:line) do
+ line = create(:line)
+ assign :line, line.decorate(context: {
+ line_referential: line.line_referential,
+ current_organisation: referential.organisation
+ })
+ end
let!(:line_referential) { assign :line_referential, line.line_referential }
let!(:routes) { assign :routes, Array.new(2) { create(:route, :line => line) }.paginate }
let!(:map) { assign(:map, double(:to_html => '<div id="map"/>'.html_safe)) }
diff --git a/spec/views/time_tables/show.html.erb_spec.rb b/spec/views/time_tables/show.html.erb_spec.rb
index f429f9dec..edfb3f3cc 100644
--- a/spec/views/time_tables/show.html.erb_spec.rb
+++ b/spec/views/time_tables/show.html.erb_spec.rb
@@ -3,7 +3,14 @@ require 'spec_helper'
describe "/time_tables/show", :type => :view do
assign_referential
- let!(:time_table) { assign(:time_table, create(:time_table)) }
+ let!(:time_table) do
+ assign(
+ :time_table,
+ create(:time_table).decorate(context: {
+ referential: referential
+ })
+ )
+ end
let!(:year) { assign(:year, Date.today.cwyear) }
let!(:time_table_combination) {assign(:time_table_combination, TimeTableCombination.new)}