diff options
76 files changed, 816 insertions, 302 deletions
diff --git a/app/assets/javascripts/cleanup.coffee b/app/assets/javascripts/cleanup.coffee index 7e291aa9e..41aa1ff15 100644 --- a/app/assets/javascripts/cleanup.coffee +++ b/app/assets/javascripts/cleanup.coffee @@ -4,11 +4,16 @@ $(document).on("change", 'input[name="clean_up[date_type]"]', (e) -> if type == 'before' end_date.hide() - $("label[for='clean_up_begin_date_3i']").html("Date de fin de la purge"); + $('label.begin_date').addClass 'hidden' + $('label.end_date').removeClass 'hidden' + else if type == 'after' end_date.hide() - $("label[for='clean_up_begin_date_3i']").html("Date de début de la purge"); + $('label.begin_date').removeClass 'hidden' + $('label.end_date').addClass 'hidden' + else - $("label[for='clean_up_begin_date_3i']").html("Date de début de la purge"); + $('label.begin_date').removeClass 'hidden' + $('label.end_date').addClass 'hidden' end_date.show() ) diff --git a/app/assets/javascripts/es6_browserified/itineraries/index.js b/app/assets/javascripts/es6_browserified/itineraries/index.js index a8f3048fa..ad32b9519 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/index.js +++ b/app/assets/javascripts/es6_browserified/itineraries/index.js @@ -69,28 +69,15 @@ document.querySelector('input[name=commit]').addEventListener('click', (event)=> if(state.stopPoints.length >= 2) { state.stopPoints.map((stopPoint, i) => { - addInput('id', (datas[i]) ? datas[i].stoppoint_id : '', i) + addInput('id', stopPoint.stoppoint_id ? stopPoint.stoppoint_id : '', i) addInput('stop_area_id',stopPoint.stoparea_id, i) addInput('position',i, i) addInput('for_boarding',stopPoint.for_boarding, i) addInput('for_alighting',stopPoint.for_alighting, i) }) - if(state.stopPoints.length < datas.length){ - for(var j= state.stopPoints.length; j < datas.length; j++){ - updateFormForDeletion(datas[j]) - } - } } else { event.preventDefault() let msg = "L'itinéraire doit comporter au moins deux arrêts" $('#stop_points').find('.subform').after("<div class='alert alert-danger'><span class='fa fa-lg fa-exclamation-circle'></span><span>" + msg + "</span></div>") } }) - -const updateFormForDeletion = (stop) =>{ - if (stop.stoppoint_id !== undefined){ - let now = Date.now() - addInput('id', stop.stoppoint_id, now) - addInput('_destroy', 'true', now) - } -} diff --git a/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js b/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js index 24c3e5d87..a3b8accb3 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js +++ b/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js @@ -20,6 +20,14 @@ const stopPoint = (state = {}, action, length) => { } } +const updateFormForDeletion = (stop) =>{ + if (stop.stoppoint_id !== undefined){ + let now = Date.now() + addInput('id', stop.stoppoint_id, now) + addInput('_destroy', 'true', now) + } +} + const stopPoints = (state = [], action) => { switch (action.type) { case 'ADD_STOP': @@ -30,18 +38,19 @@ const stopPoints = (state = [], action) => { case 'MOVE_STOP_UP': return [ ...state.slice(0, action.index - 1), - state[action.index], - state[action.index - 1], + _.assign({}, state[action.index], { stoppoint_id: state[action.index - 1].stoppoint_id }), + _.assign({}, state[action.index - 1], { stoppoint_id: state[action.index].stoppoint_id }), ...state.slice(action.index + 1) ] case 'MOVE_STOP_DOWN': return [ ...state.slice(0, action.index), - state[action.index + 1], - state[action.index], + _.assign({}, state[action.index + 1], { stoppoint_id: state[action.index].stoppoint_id }), + _.assign({}, state[action.index], { stoppoint_id: state[action.index + 1].stoppoint_id }), ...state.slice(action.index + 2) ] case 'DELETE_STOP': + updateFormForDeletion(state[action.index]) return [ ...state.slice(0, action.index), ...state.slice(action.index + 1).map((stopPoint)=>{ @@ -56,7 +65,7 @@ const stopPoints = (state = [], action) => { {}, t, { - stoppoint_id: "", + stoppoint_id: t.stoppoint_id, text: action.text.text, stoparea_id: action.text.stoparea_id, user_objectid: action.text.user_objectid, diff --git a/app/assets/javascripts/routing_constraint_zones.coffee b/app/assets/javascripts/routing_constraint_zones.coffee index c01c9ca2f..8fdcb3b50 100644 --- a/app/assets/javascripts/routing_constraint_zones.coffee +++ b/app/assets/javascripts/routing_constraint_zones.coffee @@ -1,18 +1,17 @@ -$ -> - - update_stop_points = () -> - url = $('#routing_constraint_zone_route_id').attr("data-url") - routing_constraint_zone_json = $('#routing_constraint_zone_route_id').attr("data-object") - route_id = $('#routing_constraint_zone_route_id').val() - $.ajax - url: url - dataType: 'script' - data: { route_id: route_id, routing_constraint_zone_json: routing_constraint_zone_json } - error: (jqXHR, textStatus, errorThrown) -> - console.log("ERROR") - success: (data, textStatus, jqXHR) -> - console.log("SUCCESS") +update_stop_points = () -> + url = $('#routing_constraint_zone_route_id').attr("data-url") + routing_constraint_zone_json = $('#routing_constraint_zone_route_id').attr("data-object") + route_id = $('#routing_constraint_zone_route_id').val() + $.ajax + url: url + dataType: 'script' + data: { route_id: route_id, routing_constraint_zone_json: routing_constraint_zone_json } + error: (jqXHR, textStatus, errorThrown) -> + console.log("ERROR") + success: (data, textStatus, jqXHR) -> + console.log("SUCCESS") - $("#itl_form #routing_constraint_zone_route_id").on 'change', -> update_stop_points() - - update_stop_points() +$ -> + if $('#routing_constraint_zone_route_id').hasClass('new_routing_constraint_zone_route') + $("#routing_constraint_zone_route_id").on 'change', -> update_stop_points() + update_stop_points() diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 838c46168..afd376092 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -9,7 +9,7 @@ class ReferentialsController < BreadcrumbController def new if params[:from] source_referential = Referential.find(params[:from]) - @referential = Referential.new_from(source_referential, organisation: current_organisation) + @referential = Referential.new_from(source_referential) end new! do @@ -118,7 +118,7 @@ class ReferentialsController < BreadcrumbController end def create_resource(referential) - referential.organisation = current_organisation unless referential.created_from + referential.organisation = current_organisation referential.ready = true super end diff --git a/app/controllers/routing_constraint_zones_controller.rb b/app/controllers/routing_constraint_zones_controller.rb index 6c3cb8a29..6541eb492 100644 --- a/app/controllers/routing_constraint_zones_controller.rb +++ b/app/controllers/routing_constraint_zones_controller.rb @@ -58,23 +58,40 @@ class RoutingConstraintZonesController < ChouetteController @q = current_referential.routing_constraint_zones.search(params[:q]) @routing_constraint_zones ||= begin - if sort_column && sort_direction - routing_constraint_zones = @q.result(distinct: true).order(sort_column + ' ' + sort_direction) - else - routing_constraint_zones = @q.result(distinct: true).order(:name) - end - routing_constraint_zones = routing_constraint_zones.paginate(page: params[:page], per_page: 10) + routing_constraint_zones = sort_collection + routing_constraint_zones = routing_constraint_zones.paginate( + page: params[:page], + per_page: 10 + ) end end private def sort_column - (Chouette::RoutingConstraintZone.column_names).include?(params[:sort]) ? params[:sort] : 'name' + ( + Chouette::RoutingConstraintZone.column_names + + [ + 'stop_points_count', + 'route' + ] + ).include?(params[:sort]) ? params[:sort] : 'name' end def sort_direction %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc' end + def sort_collection + sort_by = sort_column + + if sort_by == 'stop_points_count' + @q.result.order_by_stop_points_count(sort_direction) + elsif sort_by == 'route' + @q.result.order_by_route_name(sort_direction) + else + @q.result.order(sort_column + ' ' + sort_direction) + end + end + def routing_constraint_zone_params params.require(:routing_constraint_zone).permit( :name, diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index edeb5a32f..0054963c9 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -35,7 +35,7 @@ class TimeTablesController < ChouetteController def create tt_params = time_table_params - if tt_params[:calendar_id] + if tt_params[:calendar_id] && tt_params[:calendar_id] != "" %i(monday tuesday wednesday thursday friday saturday sunday).map { |d| tt_params[d] = true } calendar = Calendar.find(tt_params[:calendar_id]) tt_params[:calendar_id] = nil if tt_params.has_key?(:dates_attributes) || tt_params.has_key?(:periods_attributes) @@ -115,7 +115,8 @@ class TimeTablesController < ChouetteController end def tags - @tags = ActsAsTaggableOn::Tag.where("tags.name = ?", "%#{params[:tag]}%") + # @tags = ActsAsTaggableOn::Tag.where("tags.name = ?", "%#{params[:tag]}%") + @tags = Chouette::TimeTable.tags_on(:tags) respond_to do |format| format.json { render :json => @tags.map{|t| {:id => t.id, :name => t.name }} } end diff --git a/app/controllers/workbenches_controller.rb b/app/controllers/workbenches_controller.rb index 171db6a07..19af28a98 100644 --- a/app/controllers/workbenches_controller.rb +++ b/app/controllers/workbenches_controller.rb @@ -7,6 +7,7 @@ class WorkbenchesController < BreadcrumbController def index # Only display Wb with selected name, according to #4108 @workbench = current_organisation.workbenches.find_by(name: "Gestion de l'offre") + @referentials = @workbench.all_referentials @calendars = Calendar.where('organisation_id = ? OR shared = ?', current_organisation.id, true) end diff --git a/app/helpers/breadcrumb_helper.rb b/app/helpers/breadcrumb_helper.rb index 1f8690f38..55031d4f3 100644 --- a/app/helpers/breadcrumb_helper.rb +++ b/app/helpers/breadcrumb_helper.rb @@ -73,13 +73,13 @@ module BreadcrumbHelper end def calendar_breadcrumb(action) - add_breadcrumb I18n.t('breadcrumbs.referentials'), referentials_path + add_breadcrumb I18n.t('breadcrumbs.referentials'), workbenches_path add_breadcrumb I18n.t('calendars.index.title'), calendars_path add_breadcrumb @calendar.name if %i(show edit).include? action end def workbench_breadcrumb(action) - add_breadcrumb I18n.t("breadcrumbs.referentials"), referentials_path + add_breadcrumb I18n.t("breadcrumbs.referentials"), workbenches_path add_breadcrumb breadcrumb_label(@workbench), workbench_path(@workbench), :title => breadcrumb_tooltip(@workbench) end @@ -215,7 +215,7 @@ module BreadcrumbHelper end def import_breadcrumb (action) - add_breadcrumb I18n.t("breadcrumbs.referentials"), referentials_path + add_breadcrumb I18n.t("breadcrumbs.referentials"), workbenches_path add_breadcrumb breadcrumb_label(@workbench), workbench_path(@workbench), :title => breadcrumb_tooltip(@workbench) add_breadcrumb I18n.t("breadcrumbs.imports"), workbench_imports_path(@workbench) @@ -257,7 +257,7 @@ module BreadcrumbHelper end def organisation_breadcrumb (action = :index) - add_breadcrumb I18n.t("breadcrumbs.referentials"), referentials_path + add_breadcrumb I18n.t("breadcrumbs.referentials"), workbenches_path add_breadcrumb breadcrumb_label(@organisation), organisation_path,:title => breadcrumb_tooltip(@organisation) unless action == :index end diff --git a/app/mailers/calendar_mailer.rb b/app/mailers/calendar_mailer.rb index cc8175a07..e2a368c6c 100644 --- a/app/mailers/calendar_mailer.rb +++ b/app/mailers/calendar_mailer.rb @@ -1,11 +1,13 @@ class CalendarMailer < ApplicationMailer - def updated calendar, user - @calendar = calendar + def updated calendar_id, user_id + @calendar = Calendar.find(calendar_id) + user = User.find(user_id) mail to: user.email, subject: t('mailers.calendar_mailer.updated.subject') end - def created calendar, user - @calendar = calendar + def created calendar_id, user_id + @calendar = Calendar.find(calendar_id) + user = User.find(user_id) mail to: user.email, subject: t('mailers.calendar_mailer.created.subject') end end diff --git a/app/models/calendar_observer.rb b/app/models/calendar_observer.rb index 789805356..c81addff4 100644 --- a/app/models/calendar_observer.rb +++ b/app/models/calendar_observer.rb @@ -4,7 +4,7 @@ class CalendarObserver < ActiveRecord::Observer return unless calendar.shared User.with_organisation.each do |user| - MailerJob.perform_later('CalendarMailer', 'updated', [calendar, user]) + MailerJob.perform_later('CalendarMailer', 'updated', [calendar.id, user.id]) end end @@ -12,7 +12,7 @@ class CalendarObserver < ActiveRecord::Observer return unless calendar.shared User.with_organisation.each do |user| - MailerJob.perform_later('CalendarMailer', 'created', [calendar, user]) + MailerJob.perform_later('CalendarMailer', 'created', [calendar.id, user.id]) end end end diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb index 868d8121e..f238d7339 100644 --- a/app/models/chouette/journey_pattern.rb +++ b/app/models/chouette/journey_pattern.rb @@ -14,7 +14,7 @@ class Chouette::JourneyPattern < Chouette::TridentActiveRecord validates_presence_of :route validates_presence_of :name - validates :stop_points, length: { minimum: 2, too_short: :minimum }, on: :update + #validates :stop_points, length: { minimum: 2, too_short: :minimum }, on: :update enum section_status: { todo: 0, completed: 1, control: 2 } attr_accessor :control_checked @@ -170,4 +170,3 @@ class Chouette::JourneyPattern < Chouette::TridentActiveRecord end end - diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb index 33a2fbb00..63d2d1606 100644 --- a/app/models/chouette/line.rb +++ b/app/models/chouette/line.rb @@ -1,5 +1,5 @@ class Chouette::Line < Chouette::ActiveRecord - include DefaultNetexAttributesSupport + include StifCodifligneAttributesSupport include LineRestrictions include LineReferentialSupport include StifTransportModeEnumerations diff --git a/app/models/chouette/routing_constraint_zone.rb b/app/models/chouette/routing_constraint_zone.rb index 77f51c466..a649b0b8e 100644 --- a/app/models/chouette/routing_constraint_zone.rb +++ b/app/models/chouette/routing_constraint_zone.rb @@ -3,9 +3,18 @@ class Chouette::RoutingConstraintZone < Chouette::TridentActiveRecord has_array_of :stop_points, class_name: 'Chouette::StopPoint' validates_presence_of :name, :stop_points, :route - validates :stop_point_ids, length: { minimum: 2, too_short: I18n.t('activerecord.errors.models.routing_constraint_zone.attributes.stop_points.not_enough_stop_points') } + # validates :stop_point_ids, length: { minimum: 2, too_short: I18n.t('activerecord.errors.models.routing_constraint_zone.attributes.stop_points.not_enough_stop_points') } validate :stop_points_belong_to_route, :not_all_stop_points_selected + scope :order_by_stop_points_count, ->(direction) do + order("array_length(stop_point_ids, 1) #{direction}") + end + + scope :order_by_route_name, ->(direction) do + joins(:route) + .order("routes.name #{direction}") + end + def stop_points_belong_to_route errors.add(:stop_point_ids, I18n.t('activerecord.errors.models.routing_constraint_zone.attributes.stop_points.stop_points_not_from_route')) unless stop_points.all? { |sp| route.stop_points.include? sp } end diff --git a/app/models/chouette/stif_codifligne_objectid.rb b/app/models/chouette/stif_codifligne_objectid.rb new file mode 100644 index 000000000..46109e24f --- /dev/null +++ b/app/models/chouette/stif_codifligne_objectid.rb @@ -0,0 +1,18 @@ +class Chouette::StifCodifligneObjectid < String + + @@format = /^([A-Za-z_]+):([A-Za-z]+):([A-Za-z]+):([0-9A-Za-z_-]+)$/ + cattr_reader :format + + def parts + match(format).try(:captures) + end + + def object_type + parts.try(:third) + end + + def local_id + parts.try(:fourth) + end + +end diff --git a/app/models/chouette/stif_reflex_objectid.rb b/app/models/chouette/stif_reflex_objectid.rb new file mode 100644 index 000000000..c41a9325a --- /dev/null +++ b/app/models/chouette/stif_reflex_objectid.rb @@ -0,0 +1,18 @@ +class Chouette::StifReflexObjectid < String + + @@format = /^([A-Za-z_]+):([0-9A-Za-z_-]+):([A-Za-z]+):([0-9A-Za-z_-]+):([A-Za-z]+)$/ + cattr_reader :format + + def parts + match(format).try(:captures) + end + + def object_type + parts.try(:third) + end + + def local_id + parts.try(:fourth) + end + +end diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb index 4d98027d6..43bc82f7f 100644 --- a/app/models/chouette/stop_area.rb +++ b/app/models/chouette/stop_area.rb @@ -4,20 +4,16 @@ require 'geo_ruby' class Chouette::StopArea < Chouette::ActiveRecord # FIXME http://jira.codehaus.org/browse/JRUBY-6358 self.primary_key = "id" + include Geokit::Mappable + include StifReflexAttributesSupport include ProjectionFields include StopAreaRestrictions + include StopAreaReferentialSupport extend Enumerize enumerize :area_type, in: %i(zdep zder zdlp zdlr lda) - def self.model_name - ActiveModel::Name.new self, Chouette, self.name.demodulize - end - # Refs #1627 - # include DefaultAttributesSupport - include StopAreaReferentialSupport - with_options dependent: :destroy do |assoc| assoc.has_many :stop_points assoc.has_many :access_points @@ -57,13 +53,6 @@ class Chouette::StopArea < Chouette::ActiveRecord after_update :clean_invalid_access_links before_save :coordinates_to_lat_lng - # Refs #1627 - before_validation :prepare_auto_columns - def prepare_auto_columns - self.object_version = 1 - self.creator_id = 'chouette' - end - def combine_lat_lng if self.latitude.nil? || self.longitude.nil? "" diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index d907d797e..713ce0b21 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -38,26 +38,26 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord validates_associated :periods def continuous_dates + in_days = self.dates.where(in_out: true).sort_by(&:date) chunk = {} group = nil - self.dates.where(in_out: true).each_with_index do |date, index| + in_days.each_with_index do |date, index| group ||= index - group = (date.date == dates[index - 1].date + 1.day) ? group : group + 1 + group = (date.date == in_days[index - 1].date + 1.day) ? group : group + 1 chunk[group] ||= [] chunk[group] << date end - chunk.values + # Remove less than 2 continuous day chunk + chunk.values.delete_if {|dates| dates.count < 2} end def convert_continuous_dates_to_periods chunks = self.continuous_dates - # Remove less than 3 continuous day chunk - chunks.delete_if {|chunk| chunk.count < 3} transaction do chunks.each do |chunk| self.periods.create!(period_start: chunk.first.date, period_end: chunk.last.date) - chunk.map(&:destroy) + self.dates.delete(chunk) end end end @@ -415,7 +415,7 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord def clone_periods periods = [] self.periods.each { |p| periods << p.copy} - periods + periods.sort_by(&:period_start) end def included_days @@ -436,7 +436,7 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord # produce a copy of periods without anyone overlapping or including another - def optimize_periods + def optimize_overlapping_periods periods = self.clone_periods optimized = [] i=0 @@ -461,6 +461,59 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord optimized.sort { |a,b| a.period_start <=> b.period_start} end + def continuous_periods + periods = self.periods.sort_by(&:period_start) + chunk = {} + group = nil + periods.each_with_index do |period, index| + group ||= index + group = (period.period_start - 1.day == periods[index - 1].period_end) ? group : group + 1 + chunk[group] ||= [] + chunk[group] << period + end + chunk.values.delete_if {|periods| periods.count < 2} + end + + def convert_continuous_periods_into_one + chunks = self.continuous_periods + + transaction do + chunks.each do |chunk| + self.periods.create!(period_start: chunk.first.period_start, period_end: chunk.last.period_end) + self.periods.delete chunk + end + end + end + + #update a period if a in_day is just before or after + def optimize_continuous_dates_and_periods + return self.periods if self.included_days.empty? || periods.empty? + + periods = self.clone_periods + optimized = [] + + i = 0 + while i < periods.length + period = periods[i] + j = 0 + in_days = self.reload.dates.where(in_out: true).sort_by(&:date) + while j < in_days.length + day = in_days[j] + if period.period_start - 1.day === day.date + period.period_start = day.date + self.dates.delete day + elsif period.period_end + 1.day === day.date + period.period_end = day.date + self.dates.delete day + end + j += 1 + end + i += 1 + optimized << period + end + optimized + end + # add a peculiar day or switch it from excluded to included def add_included_day(d) if self.excluded_date?(d) @@ -478,22 +531,27 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord def merge!(another_tt) transaction do self.periods = another_tt.clone_periods + self.periods - self.periods = self.optimize_periods # 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) + self.dates.where(in_out: false).each do |d| + self.dates.delete d if another_tt.include_in_periods?(d.date) && !another_tt.excluded_date?(d.date) + end + + another_tt.excluded_days.each do |d| + unless self.reload.excluded_date?(d) + self.dates << Chouette::TimeTableDate.new(date: d, in_out: false) end + self.save! end - self.save! - end - self.convert_continuous_dates_to_periods + self.convert_continuous_dates_to_periods + self.periods = self.optimize_continuous_dates_and_periods + self.convert_continuous_periods_into_one + self.periods = self.optimize_overlapping_periods + end end def included_days_in_dates_and_periods diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 19299d098..bc2713973 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -23,7 +23,10 @@ module Chouette validates_presence_of :route validates_presence_of :journey_pattern - validates :vehicle_journey_at_stops, :vjas_departure_time_must_be_before_next_stop_arrival_time, + validates :vehicle_journey_at_stops, + # Validation temporarily removed for day offsets + # :vjas_departure_time_must_be_before_next_stop_arrival_time, + vehicle_journey_at_stops_are_in_increasing_time_order: true validates_presence_of :number @@ -34,6 +37,12 @@ module Chouette before_validation :set_default_values, :calculate_vehicle_journey_at_stop_day_offset + # TODO: Remove this validator + # We've eliminated this validation because it prevented vehicle journeys + # from being saved with at-stops having a day offset greater than 0, + # because these would have times that were "earlier" than the previous + # at-stop. TBD by Luc whether we're deleting this validation altogether or + # instead rejiggering it to work with day offsets. def vjas_departure_time_must_be_before_next_stop_arrival_time notice = 'departure time must be before next stop arrival time' vehicle_journey_at_stops.each_with_index do |current_stop, index| diff --git a/app/models/chouette/vehicle_journey_at_stop.rb b/app/models/chouette/vehicle_journey_at_stop.rb index 5dfec8352..35d94aa75 100644 --- a/app/models/chouette/vehicle_journey_at_stop.rb +++ b/app/models/chouette/vehicle_journey_at_stop.rb @@ -3,6 +3,8 @@ module Chouette include ForBoardingEnumerations include ForAlightingEnumerations + DAY_OFFSET_MAX = 1 + # FIXME http://jira.codehaus.org/browse/JRUBY-6358 self.primary_key = "id" @@ -24,11 +26,46 @@ module Chouette end end + validate :day_offset_must_be_within_range + after_initialize :set_virtual_attributes def set_virtual_attributes @_destroy = false @dummy = false end + def day_offset_must_be_within_range + if day_offset_outside_range?(arrival_day_offset) + errors.add( + :arrival_day_offset, + I18n.t( + 'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', + local_id: vehicle_journey.objectid.local_id, + max: DAY_OFFSET_MAX + 1 + ) + ) + end + + if day_offset_outside_range?(departure_day_offset) + errors.add( + :departure_day_offset, + I18n.t( + 'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', + local_id: vehicle_journey.objectid.local_id, + max: DAY_OFFSET_MAX + 1 + ) + ) + end + end + + def day_offset_outside_range?(offset) + # At-stops that were created before the database-default of 0 will have + # nil offsets. Handle these gracefully by forcing them to a 0 offset. + offset ||= 0 + + offset < 0 || offset > DAY_OFFSET_MAX + end + + end end diff --git a/app/models/concerns/stif_codifligne_attributes_support.rb b/app/models/concerns/stif_codifligne_attributes_support.rb new file mode 100644 index 000000000..d4370e505 --- /dev/null +++ b/app/models/concerns/stif_codifligne_attributes_support.rb @@ -0,0 +1,21 @@ +module StifCodifligneAttributesSupport + extend ActiveSupport::Concern + + included do + validates_presence_of :objectid + end + + module ClassMethods + def object_id_key + model_name + end + + def model_name + ActiveModel::Name.new self, Chouette, self.name.demodulize + end + end + + def objectid + Chouette::StifCodifligneObjectid.new read_attribute(:objectid).to_s + end +end diff --git a/app/models/concerns/stif_reflex_attributes_support.rb b/app/models/concerns/stif_reflex_attributes_support.rb new file mode 100644 index 000000000..e6236a146 --- /dev/null +++ b/app/models/concerns/stif_reflex_attributes_support.rb @@ -0,0 +1,21 @@ +module StifReflexAttributesSupport + extend ActiveSupport::Concern + + included do + validates_presence_of :objectid + end + + module ClassMethods + def object_id_key + model_name + end + + def model_name + ActiveModel::Name.new self, Chouette, self.name.demodulize + end + end + + def objectid + Chouette::StifReflexObjectid.new read_attribute(:objectid).to_s + end +end diff --git a/app/models/referential.rb b/app/models/referential.rb index e65d6a33b..cb2c7b23b 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -130,14 +130,13 @@ class Referential < ActiveRecord::Base self end - def self.new_from(from, organisation:) + def self.new_from(from) Referential.new( name: I18n.t("activerecord.copy", :name => from.name), slug: "#{from.slug}_clone", prefix: from.prefix, time_zone: from.time_zone, bounds: from.bounds, - organisation: organisation, line_referential: from.line_referential, stop_area_referential: from.stop_area_referential, workbench: from.workbench, @@ -206,7 +205,6 @@ class Referential < ActiveRecord::Base end def clone_associations - self.organisation = created_from.organisation self.line_referential = created_from.line_referential self.stop_area_referential = created_from.stop_area_referential self.workbench = created_from.workbench diff --git a/app/models/user.rb b/app/models/user.rb index 64d66883f..c2aa14bda 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -29,29 +29,6 @@ class User < ActiveRecord::Base scope :with_organisation, -> { where.not(organisation_id: nil) } - def self.destructive_permissions_for(models) - models.product( %w{create destroy update} ).map{ |model_action| model_action.join('.') } - end - - @@edit_offer_permissions = - destructive_permissions_for( %w[ - access_points - connection_links - calendars - footnotes - journey_patterns - referentials - routes - routing_constraint_zones - time_tables - vehicle_journeys - ]) << '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) @@ -59,7 +36,7 @@ class User < ActiveRecord::Base self.name = extra[:full_name] self.email = extra[:email] self.organisation = Organisation.sync_update extra[:organisation_code], extra[:organisation_name], extra[:functional_scope] - self.permissions = extra[:permissions].include?('boiv:edit-offer') ? @@edit_offer_permissions : [] + self.permissions = Stif::PermissionTranslator.translate(extra[:permissions]) end def self.portail_api_request @@ -87,7 +64,7 @@ class User < ActiveRecord::Base user.locked_at = el['locked_at'] user.organisation = Organisation.sync_update el['organization_code'], el['organization_name'], el['functional_scope'] user.synced_at = Time.now - user.permissions = el['permissions'].include?('boiv:edit-offer') ? @@edit_offer_permissions : [] + user.permissions = Stif::PermissionTranslator.translate(el['permissions']) user.save end end diff --git a/app/policies/login_policy.rb b/app/policies/login_policy.rb index 3364c37ac..5b07b97f3 100644 --- a/app/policies/login_policy.rb +++ b/app/policies/login_policy.rb @@ -7,7 +7,7 @@ class LoginPolicy end def boiv? - !(user.permissions || []).grep(%r{\Aboiv:.}).empty? + (user.permissions || []).include?('sessions:create') end end diff --git a/app/views/calendar_mailer/created.html.slim b/app/views/calendar_mailer/created.html.slim index da15b7189..37b2a86ea 100644 --- a/app/views/calendar_mailer/created.html.slim +++ b/app/views/calendar_mailer/created.html.slim @@ -1 +1,25 @@ div = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: calendars_url) + +table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif;width:550px;margin:0px auto;color:#333333;" + + thead + tr + th + .jumbotron style="background-color:#007fbb;color:#fff;padding:20px 25px;margin-bottom:0px;text-align:left;" + h1.brandname style="display:inline-block;font-size:20px;font-weight:700;text-transform:uppercase;margin:0;" + = t('brandname') + + tbody + tr + td style="padding:40px 0px 60px 0px;" + p style="font-size:14px;font-weight:700;margin:0px 0px 10px 0px;" + = t('mailers.calendar_mailer.updated.subject') + + p style="font-size:14px;margin:0px 0px 10px 0px;" + = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: calendars_url).html_safe + + tr + td style="text-align:center;padding:20px 0 0 0;border-top:1px solid #007fbb;" + p style="font-size:12px;font-weight:700;margin:0px 0px 10px 0px;opacity:0.85;" + em = t('mailers.calendar_mailer.sent_by') + " " + = link_to t('brandname'), unauthenticated_root_url, style: "color:#333333;text-decoration:underline;" diff --git a/app/views/calendar_mailer/updated.html.slim b/app/views/calendar_mailer/updated.html.slim index f70480107..bf128439a 100644 --- a/app/views/calendar_mailer/updated.html.slim +++ b/app/views/calendar_mailer/updated.html.slim @@ -1,2 +1,23 @@ -div = t('mailers.calendar_mailer.updated.body', cal_name: @calendar.name, cal_index_url: calendars_url) +table style="border-collapse:collapse;font-family:'Open Sans', Arial, sans serif;width:550px;margin:0px auto;color:#333333;" + thead + tr + th + .jumbotron style="background-color:#007fbb;color:#fff;padding:20px 25px;margin-bottom:0px;text-align:left;" + h1.brandname style="display:inline-block;font-size:20px;font-weight:700;text-transform:uppercase;margin:0;" + = t('brandname') + + tbody + tr + td style="padding:40px 0px 60px 0px;" + p style="font-size:14px;font-weight:700;margin:0px 0px 10px 0px;" + = t('mailers.calendar_mailer.updated.subject') + + p style="font-size:14px;margin:0px 0px 10px 0px;" + = t('mailers.calendar_mailer.updated.body', cal_name: @calendar.name, cal_index_url: calendars_url).html_safe + + tr + td style="text-align:center;padding:20px 0 0 0;border-top:1px solid #007fbb;" + p style="font-size:12px;font-weight:700;margin:0px 0px 10px 0px;opacity:0.85;" + em = t('mailers.calendar_mailer.sent_by') + " " + = link_to t('brandname'), unauthenticated_root_url, style: "color:#333333;text-decoration:underline;" diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 6eab2a761..d6a22c4f8 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -6,7 +6,7 @@ html lang=I18n.locale = csrf_meta_tag - title STIF BOIV + title = t('brandname') = stylesheet_link_tag 'base' = stylesheet_link_tag 'application' diff --git a/app/views/layouts/mailer.html.slim b/app/views/layouts/mailer.html.slim index 1485e8188..5db6917b3 100644 --- a/app/views/layouts/mailer.html.slim +++ b/app/views/layouts/mailer.html.slim @@ -2,21 +2,6 @@ doctype html html head meta charset="utf-8" - title= message.subject - - body style="font-family: Verdana, Helvetica, Arial, MS Trebuchet, sans-serif; font-size: 14px; width: 600px; background: #E5E5E5; padding: 15px" - - <style> - h2 { font-size: 18px; } - h3 { font-size: 14px; } - a { color: black; } - </style> - - h1 style="background: #61970B; height: 75px; font-size: 24px; font-weight: normal; color: white; padding: 20px 0 0 30px;" - |Chouette - - div style="background: white; margin-bottom: 10px; padding: 15px; -moz-box-shadow: 3px 3px 4px #bbbbbb; -webkit-box-shadow: 3px 3px 4px #BBB; box-shadow: 3px 3px 4px #BBB; border-right: 1px solid #BBB; border-bottom: 1px solid #BBB;" - = yield - - div style="color: #333333; text-align:center; font-size: 10px;" - = "Envoyé par #{link_to 'Chouette', unauthenticated_root_url}" + /!* Email styles need to be inline */ + body + = yield diff --git a/app/views/layouts/navigation/_main_nav_left.html.slim b/app/views/layouts/navigation/_main_nav_left.html.slim index 9dfc828c0..8e82ac528 100644 --- a/app/views/layouts/navigation/_main_nav_left.html.slim +++ b/app/views/layouts/navigation/_main_nav_left.html.slim @@ -5,7 +5,7 @@ .menu-content .closeMenu title='Fermer le menu' - .brandname = "IBOO" + .brandname = t('brandname') #menu-items.panel-group .menu-item.panel diff --git a/app/views/layouts/navigation/_main_nav_top.html.slim b/app/views/layouts/navigation/_main_nav_top.html.slim index 4cdd5f053..d6c849d3f 100644 --- a/app/views/layouts/navigation/_main_nav_top.html.slim +++ b/app/views/layouts/navigation/_main_nav_top.html.slim @@ -1,5 +1,5 @@ .nav-menu#menu_top - .brandname IBOO + .brandname = t('brandname') .menu-content .menu-item diff --git a/app/views/referentials/_filters.html.slim b/app/views/referentials/_filters.html.slim index 9302ccaa8..1cc6bb410 100644 --- a/app/views/referentials/_filters.html.slim +++ b/app/views/referentials/_filters.html.slim @@ -20,5 +20,5 @@ = f.input :company_id_eq_any, collection: LineReferential.first.companies.order('name').pluck(:id), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{LineReferential.first.companies.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' } .actions - = link_to 'Effacer', @workbench, class: 'btn btn-link' - = f.submit 'Filtrer', class: 'btn btn-default' + = link_to t('actions.erase'), @workbench, class: 'btn btn-link' + = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim index 31d71bcdc..a9e308699 100644 --- a/app/views/referentials/_form.html.slim +++ b/app/views/referentials/_form.html.slim @@ -42,11 +42,11 @@ = render 'period_fields', f: period_form .links.nested-linker - = link_to_add_association 'Ajouter une période', subform, :periods, class: 'btn btn-outline-primary' + = link_to_add_association t('simple_form.labels.referential.actions.add_period'), subform, :periods, class: 'btn btn-outline-primary' .separator - = subform.input :lines, as: :select, collection: @referential.workbench.lines.includes(:company).order(:name), selected: subform.object.line_ids, label_method: :display_name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Sélection de lignes', 'multiple': 'multiple', style: 'width: 100%' } + = subform.input :lines, as: :select, collection: @referential.workbench.lines.includes(:company).order(:name), selected: subform.object.line_ids, label_method: :display_name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': t('simple_form.labels.referential.placeholders.select_lines'), 'multiple': 'multiple', style: 'width: 100%' } .hidden = form.input :workbench_id, as: :hidden diff --git a/app/views/referentials/_period_fields.html.slim b/app/views/referentials/_period_fields.html.slim index 95e204554..4d2372f7b 100644 --- a/app/views/referentials/_period_fields.html.slim +++ b/app/views/referentials/_period_fields.html.slim @@ -12,4 +12,4 @@ div = 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') + = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: t('are_you_sure')}, title: t('actions.delete') diff --git a/app/views/referentials/index.html.slim b/app/views/referentials/index.html.slim index 8943d419c..8bb66da21 100644 --- a/app/views/referentials/index.html.slim +++ b/app/views/referentials/index.html.slim @@ -1,7 +1,7 @@ / FIXME #827 - current_organisation.workbenches.each do |workbench| h2 = link_to workbench.name, workbench - p = "#{workbench.referentials.count} jeu(x) de données à l'heure actuelle" + p = t('workbenches.referential_count', count: workbench.referentials.count) / FIXME #823 - if false @@ -11,8 +11,8 @@ - content_for :sidebar do ul.actions - li = link_to 'Données Reflex', stop_area_referential_path(1) - li = link_to 'Données CodifLigne', line_referential_path(1) + li = link_to t('reflex_data'), stop_area_referential_path(1) + li = link_to t('codif_data'), line_referential_path(1) li = link_to t('calendars.standard_calendars'), calendars_path - if false li = link_to t('referentials.actions.new'), new_referential_path, class: 'add' diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim index d3687c3a7..49c152789 100644 --- a/app/views/referentials/show.html.slim +++ b/app/views/referentials/show.html.slim @@ -24,7 +24,7 @@ .row .col-lg-6.col-md-6.col-sm-12.col-xs-12 = definition_list t('metadatas'), - { 'Statut' => @referential.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, + { t('activerecord.attributes.referential.status') => @referential.archived? ? "<div class='td-block'><span class='fa fa-archive'></span><span>#{t('activerecord.attributes.referential.archived_at')}</span></div>".html_safe : "<div class='td-block'><span class='sb sb-lg sb-preparing'></span><span>#{t('activerecord.attributes.referential.archived_at_null')}</span></div>".html_safe, @referential.human_attribute_name(:validity_period) => (@referential.validity_period.present? ? t('validity_range', debut: l(@referential.try(:validity_period).try(:begin), format: :short), end: l(@referential.try(:validity_period).try(:end), format: :short)) : '-'), @referential.human_attribute_name(:organisation) => @referential.organisation.name, @referential.human_attribute_name(:published_at) => '-' } @@ -41,7 +41,7 @@ = table_builder_2 @reflines, [ \ TableBuilderHelper::Column.new( \ - name: 'ID Codifligne', \ + name: t('id_codif'), \ attribute: Proc.new { |n| n.objectid.local_id }, \ sortable: false \ ), \ @@ -85,17 +85,21 @@ = modalbox 'purgeModal' do = simple_form_for [@referential, CleanUp.new] do |f| .modal-header - h4.modal-title Purger le JDD + h4.modal-title #{t('simple_form.labels.clean_up.title')} .modal-body .container-fluid .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 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 smart_date', title: t('titles.clean_up.end_date'), id: "end_date" } + .col-lg-8.col-ld-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-12 + label.control-label.begin_date = t('titles.clean_up.begin_date') + label.control-label.end_date.hidden = t('titles.clean_up.end_date') + = f.input :begin_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' } + + = f.input :end_date, as: :date, label: t('titles.clean_up.end_date'), wrapper_html: { class: 'date cleanup_end_date_wrapper smart_date', id: "end_date" } .modal-footer - button.btn.btn-link type='button' data-dismiss='modal' Annuler + button.btn.btn-link type='button' data-dismiss='modal' #{t('cancel')} - unless policy(@referential).archived? = f.button :submit, t('actions.clean_up') , class: 'btn btn-primary' diff --git a/app/views/routing_constraint_zones/new.html.slim b/app/views/routing_constraint_zones/new.html.slim index 4e792f8af..600663b18 100644 --- a/app/views/routing_constraint_zones/new.html.slim +++ b/app/views/routing_constraint_zones/new.html.slim @@ -12,7 +12,7 @@ .row .col-lg-12 = form.input :name - = form.input :route_id, collection: @line.routes, include_blank: false, input_html: {data: {url: new_referential_line_routing_constraint_zone_path(@referential, @line), object: @routing_constraint_zone.to_json }} + = form.input :route_id, collection: @line.routes, include_blank: false, input_html: { class: 'new_routing_constraint_zone_route', data: {url: new_referential_line_routing_constraint_zone_path(@referential, @line), object: @routing_constraint_zone.to_json }} .separator diff --git a/app/views/time_tables/_filter.html.slim b/app/views/time_tables/_filter.html.slim index 1015eca31..392dc4f50 100644 --- a/app/views/time_tables/_filter.html.slim +++ b/app/views/time_tables/_filter.html.slim @@ -7,10 +7,6 @@ span.fa.fa-search .ffg-row - / .form-group.togglable - / = f.label Chouette::TimeTable.human_attribute_name(:color), required: false, class: 'control-label' - / = f.input :color_cont_any, collection: ["#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"], as: :check_boxes, label: false, label_method: lambda{|tt| ("<span style='height:19px;'><span class='fa fa-circle' style='position:relative;top:0;margin-top:0;color:" + tt + "'></span></span>").html_safe }, required: false, wrapper_html: { class: 'checkbox_list' } - .form-group = f.label Chouette::TimeTable.human_attribute_name(:tag_search), required: false, class: 'control-label' = f.input :tag_search, as: :tags, collection: Chouette::TimeTable.tags_on(:tags).pluck(:name), label: false, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez une étiquette...' }, wrapper_html: { class: 'select2ed'}, include_blank: false diff --git a/app/views/time_tables/time_tables.json.rabl b/app/views/time_tables/time_tables.json.rabl index dec29cb69..b91ca9ae9 100644 --- a/app/views/time_tables/time_tables.json.rabl +++ b/app/views/time_tables/time_tables.json.rabl @@ -2,4 +2,4 @@ collection @time_tables, :object_root => false node do |time_table| { :id => time_table.id, :comment => time_table.comment, :time_table_bounding => time_table_bounding( time_table), :composition_info => composition_info(time_table) } -end +end diff --git a/app/views/workbenches/_filters.html.slim b/app/views/workbenches/_filters.html.slim index d7ac79577..8da7ecf51 100644 --- a/app/views/workbenches/_filters.html.slim +++ b/app/views/workbenches/_filters.html.slim @@ -1,24 +1,24 @@ = search_form_for @q_for_form, url: workbench_path(@workbench.id), builder: SimpleForm::FormBuilder, class: 'form form-filter' do |f| .ffg-row .input-group.search_bar - = f.search_field :name_cont, class: 'form-control', placeholder: 'Indiquez un nom de référentiel...' + = f.search_field :name_cont, class: 'form-control', placeholder: t('referentials.filters.name') span.input-group-btn button.btn.btn-default type='submit' span.fa.fa-search .ffg-row .form-group - = f.label 'Ligne', required: false, class: 'control-label' - = 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'} + = f.label t('activerecord.models.line.one').upcase, required: false, class: 'control-label' + = f.input :associated_lines_id_eq, as: :select, collection: @workbench.lines.includes(:company).order(:name), input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': t('referentials.filters.line') }, label: false, label_method: :display_name, wrapper_html: { class: 'select2ed'} .form-group.togglable = 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' } + = f.input :archived_at_not_null, label: ("<span>#{t('activerecord.attributes.referential.archived_at')}<span class='fa fa-archive'></span></span>").html_safe, as: :boolean, wrapper_html: { class: 'checkbox-wrapper' } + = f.input :archived_at_null, label: ("<span>#{t('activerecord.attributes.referential.archived_at_null')}<span class='sb sb-lg sb-preparing'></span></span>").html_safe, as: :boolean, wrapper_html: { class: 'checkbox-wrapper' } .form-group.togglable - = f.label 'Organisation(s)', required: false, class: 'control-label' + = f.label t('activerecord.models.organisation.one'), required: false, class: 'control-label' = 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 @@ -29,5 +29,5 @@ = 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' - = f.submit 'Filtrer', class: 'btn btn-default' + = link_to t('actions.erase'), @workbench, class: 'btn btn-link' + = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/workbenches/index.html.slim b/app/views/workbenches/index.html.slim index 1bd116a85..ca61d439d 100644 --- a/app/views/workbenches/index.html.slim +++ b/app/views/workbenches/index.html.slim @@ -33,14 +33,14 @@ h3.panel-title.with_actions div = t('.offers.referentials') - span.badge.ml-xs = @workbench.referentials.count if @workbench.all_referentials.any? + span.badge.ml-xs = @referentials.count if @referentials.any? div = link_to '', workbench_path(@workbench), class: ' fa fa-chevron-right pull-right', title: t('.offers.see') - - if @workbench.all_referentials.any? + - if @referentials.any? .list-group - - @workbench.all_referentials.each_with_index do |referential, i| + - @referentials.each_with_index do |referential, i| = link_to referential.name, referential_path(referential), class: 'list-group-item' if i < 6 - else diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim index bb8b71ab2..6a41ca569 100644 --- a/app/views/workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim @@ -7,8 +7,8 @@ / Below is secundary actions & optional contents (filters, ...) .row.mb-sm .col-lg-12.text-right - = link_to t('actions.import'), workbench_imports_path(@workbench), class: 'btn btn-primary' - if policy(Referential).create? + = link_to t('actions.import'), workbench_imports_path(@workbench), class: 'btn btn-primary' = link_to t('actions.add'), new_referential_path(workbench_id: @workbench), class: 'btn btn-primary' / PageContent @@ -31,7 +31,7 @@ ), \ 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} \ + attribute: Proc.new {|w| w.archived? ? ("<div class='td-block'><span class='fa fa-archive'></span><span>#{t('activerecord.attributes.referential.archived_at')}</span></div>").html_safe : ("<div class='td-block'><span class='sb sb-lg sb-preparing'></span><span>#{t('activerecord.attributes.referential.archived_at_null')}</span></div>").html_safe} \ ), \ TableBuilderHelper::Column.new( \ key: :organisation, \ @@ -43,7 +43,7 @@ ), \ TableBuilderHelper::Column.new( \ key: :lines, \ - name: 'Nb lignes', \ + name: t('activerecord.attributes.referential.number_of_lines'), \ attribute: Proc.new {|w| w.lines.count} \ ), \ TableBuilderHelper::Column.new( \ diff --git a/config/locales/actions.en.yml b/config/locales/actions.en.yml index f6979136a..36b76d01c 100644 --- a/config/locales/actions.en.yml +++ b/config/locales/actions.en.yml @@ -17,6 +17,8 @@ en: combine: 'Combine' actualize: 'Actualize' import: 'Import' + filter: 'Filter' + erase: 'Erase' or: "or" cancel: "Cancel" search_hint: "Type in a search term" diff --git a/config/locales/actions.fr.yml b/config/locales/actions.fr.yml index 86e72088a..f581142c7 100644 --- a/config/locales/actions.fr.yml +++ b/config/locales/actions.fr.yml @@ -17,6 +17,8 @@ fr: combine: 'Combiner' actualize: 'Actualiser' import: 'Importer' + filter: 'Filtrer' + erase: 'Effacer' or: "ou" cancel: "Annuler" search_hint: "Entrez un texte à rechercher" diff --git a/config/locales/clean_ups.en.yml b/config/locales/clean_ups.en.yml index fcfcfec7b..876694592 100644 --- a/config/locales/clean_ups.en.yml +++ b/config/locales/clean_ups.en.yml @@ -34,3 +34,7 @@ en: presence: "A clean up must have a begin date" end_date: presence: "A clean up must have a end date" + simple_form: + labels: + clean_up: + title: "Clean Up the referential" diff --git a/config/locales/clean_ups.fr.yml b/config/locales/clean_ups.fr.yml index e2db82998..59d7c5dbc 100644 --- a/config/locales/clean_ups.fr.yml +++ b/config/locales/clean_ups.fr.yml @@ -33,3 +33,7 @@ fr: presence: "Une purge doit avoir une date de début" end_date: presence: "Une purge doit avoir une date de fin" + simple_form: + labels: + clean_up: + title: "Purger le JDD" diff --git a/config/locales/en.yml b/config/locales/en.yml index b25f5fd7f..b65484bc2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -55,4 +55,7 @@ en: id_codif: 'Codifligne ID' id_reflex: 'Reflex ID' + codif_data: 'Codifligne datas' + reflex_data: 'Reflex datas' objectid: 'ID' + brandname: IBOO diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 8ef183d91..49e41a570 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -55,4 +55,7 @@ fr: id_codif: 'ID Codifligne' id_reflex: 'ID Reflex' + codif_data: 'Données Codifligne' + reflex_data: 'Données Reflex' objectid: 'ID' + brandname: IBOO diff --git a/config/locales/mailers.en.yml b/config/locales/mailers.en.yml index 9f89c9d46..857033401 100644 --- a/config/locales/mailers.en.yml +++ b/config/locales/mailers.en.yml @@ -1,9 +1,10 @@ en: mailers: calendar_mailer: + sent_by: "Sent by" created: subject: A new shared calendar has been created - body: A new shared calendar% {cal_name} has been added by STIF. You can now view it in the list of shared calendars %{cal_index_url} + body: "A new shared calendar %{cal_name} has been added by STIF. You can now view it in the <a href='%{cal_index_url}' target='_blank' style='color:#333333;'>list of shared calendars</a>." updated: subject: A shared calendar has been updated - body: A new shared calendar% {cal_name} has been updated by STIF. You can now view it in the list of shared calendars %{cal_index_url} + body: "A new shared calendar %{cal_name} has been updated by STIF. You can now view it in the <a href='%{cal_index_url}' target='_blank' style='color:#333333;'>list of shared calendars</a>." diff --git a/config/locales/mailers.fr.yml b/config/locales/mailers.fr.yml index a448f13d9..74af036de 100644 --- a/config/locales/mailers.fr.yml +++ b/config/locales/mailers.fr.yml @@ -1,9 +1,10 @@ fr: mailers: calendar_mailer: + sent_by: "Envoyé par" created: subject: Un nouveau calendrier partagé à été ajouté - body: 'Un calendrier partagé %{cal_name} a été ajouté par le STIF. Vous pouvez maintenant le consulter dans la liste des calendriers partagés : %{cal_index_url}' + body: "Un calendrier partagé %{cal_name} a été ajouté par le STIF. Vous pouvez maintenant le consulter dans la <a href='%{cal_index_url}' target='_blank' style='color:#333333;'>liste des calendriers partagés</a>." updated: - subject: Un nouveau calendrier partagé à été mise à jour - body: 'Un calendrier partagé %{cal_name} a été mis à jour par le STIF. Vous pouvez maintenant le consulter dans la liste des calendriers partagés : %{cal_index_url}' + subject: Un nouveau calendrier partagé à été mis à jour + body: "Un calendrier partagé %{cal_name} a été mis à jour par le STIF. Vous pouvez maintenant le consulter dans la <a href='%{cal_index_url}' target='_blank' style='color:#333333;'>liste des calendriers partagés</a>." diff --git a/config/locales/organisations.en.yml b/config/locales/organisations.en.yml index 617e61ca2..a64920daf 100644 --- a/config/locales/organisations.en.yml +++ b/config/locales/organisations.en.yml @@ -10,6 +10,11 @@ en: users: "Users" rule_parameter_sets: "Rule parameter sets" activerecord: + models: + organisation: + zero: "organization" + one: "organization" + other: "organizations" attributes: organisation: name: "Name" diff --git a/config/locales/organisations.fr.yml b/config/locales/organisations.fr.yml index db7482fbc..50cb9f3be 100644 --- a/config/locales/organisations.fr.yml +++ b/config/locales/organisations.fr.yml @@ -10,6 +10,11 @@ fr: users: "Utilisateurs" rule_parameter_sets: "Jeux de paramètres" activerecord: + models: + organisation: + zero: "organisation" + one: "organisation" + other: "organisations" attributes: organisation: name: "Nom" diff --git a/config/locales/referentials.en.yml b/config/locales/referentials.en.yml index 5f39f03f4..e29df7cf4 100644 --- a/config/locales/referentials.en.yml +++ b/config/locales/referentials.en.yml @@ -2,6 +2,8 @@ en: referentials: filters: name_or_number_or_objectid: 'Search by name, number or objectid' + name: 'Search by name' + line: 'Seach by associated lines' search_no_results: 'No data space matching your query' index: title: 'Data spaces' @@ -59,7 +61,6 @@ en: upper_corner: "Top,Right corner for default bounding box" lower_corner: "Bottom,Left corner for default bounding box" resources: "Neptune Import File" - validity_period: "Validity period" no_validity_period: "undefined" start_validity_period: "from" end_validity_period: "to" @@ -86,14 +87,16 @@ en: data_format: "Favorite format for export" timebands: "Time bands" routing_constraint_zone: Routing constraint zone - validity_period: "Validity period" + validity_period: "Inclusive validity period" updated_at: "Updated" published_at: "Integrated" archived_at: "Archived" + archived_at_null: "Unarchived" created_from: 'Created from' updated_at: "Updated" created_at: "Created" organisation: 'Organisation' + number_of_lines: 'No. of lines' formtastic: titles: referential: @@ -112,6 +115,10 @@ en: periods: begin: 'Period beginning' end: 'Period end' + actions: + add_period: 'Add a period' + placeholders: + select_lines: 'Selection of lignes' notice: referentials: diff --git a/config/locales/referentials.fr.yml b/config/locales/referentials.fr.yml index f69c26276..643295c0e 100644 --- a/config/locales/referentials.fr.yml +++ b/config/locales/referentials.fr.yml @@ -2,6 +2,8 @@ fr: referentials: filters: name_or_number_or_objectid: 'Indiquez un nom de ligne, nom court ou objectid' + name: 'Indiquez un nom de référentiel...' + line: 'Indiquez une ligne...' search_no_results: 'Aucun jeu de données ne correspond à votre recherche' index: title: 'Jeux de données' @@ -59,7 +61,6 @@ fr: upper_corner: "Point haut/droite de l'emprise par défaut" lower_corner: "Point bas/gauche de l'emprise par défaut" resources: "Import Neptune" - validity_period: "Période de validité" no_validity_period: "non définie" start_validity_period: "du" end_validity_period: "au" @@ -91,8 +92,10 @@ fr: created_at: "Créé le" published_at: "Intégré le" archived_at: "Conservé" + archived_at_null: "En préparation" created_from: 'Créé à partir de' organisation: 'Organisation' + number_of_lines: 'Nb lignes' formtastic: titles: referential: @@ -111,6 +114,10 @@ fr: periods: begin: 'Début de période' end: 'Fin de période' + actions: + add_period: 'Ajouter une période' + placeholders: + select_lines: 'Sélection de lignes' notice: referentials: deleted: "Les jeux de données on été supprimés" diff --git a/config/locales/vehicle_journey_at_stops.en.yml b/config/locales/vehicle_journey_at_stops.en.yml new file mode 100644 index 000000000..a96effa2b --- /dev/null +++ b/config/locales/vehicle_journey_at_stops.en.yml @@ -0,0 +1,4 @@ +en: + vehicle_journey_at_stops: + errors: + day_offset_must_not_exceed_max: "The vehicle journey with ID %{local_id} cannot have times exceeding %{max} days" diff --git a/config/locales/vehicle_journey_at_stops.fr.yml b/config/locales/vehicle_journey_at_stops.fr.yml new file mode 100644 index 000000000..3eff79cf4 --- /dev/null +++ b/config/locales/vehicle_journey_at_stops.fr.yml @@ -0,0 +1,4 @@ +fr: + vehicle_journey_at_stops: + errors: + day_offset_must_not_exceed_max: "La course avec l'identifiant %{local_id} ne peut pas avoir des horaires sur plus de %{max} jours" diff --git a/config/locales/workbenches.en.yml b/config/locales/workbenches.en.yml index 8525a4b9f..8a458e118 100644 --- a/config/locales/workbenches.en.yml +++ b/config/locales/workbenches.en.yml @@ -10,3 +10,7 @@ en: calendars: "Calendars" see: "See the list" no_content: "No content yet." + referential_count: + zero: "Currently, there is no referential in your workbench" + one: "Currently, there is one referential in your workbench" + other: "Currently, there are #{count} referentials in your workbench" diff --git a/config/locales/workbenches.fr.yml b/config/locales/workbenches.fr.yml index 1cdc19a13..85c5259b8 100644 --- a/config/locales/workbenches.fr.yml +++ b/config/locales/workbenches.fr.yml @@ -10,3 +10,7 @@ fr: calendars: "Calendriers" see: "Voir la liste" no_content: "Aucun contenu pour le moment" + referential_count: + zero: "Aucun jeu de données à l'heure actuelle" + one: "1 jeu de données à l'heure actuelle" + other: "#{count} jeux de données à l'heure actuelle" diff --git a/lib/stif/permission_translator.rb b/lib/stif/permission_translator.rb index 7032f910a..afe69756e 100644 --- a/lib/stif/permission_translator.rb +++ b/lib/stif/permission_translator.rb @@ -1,7 +1,45 @@ module Stif module PermissionTranslator extend self + def translate(sso_extra_permissions) - %w{sessions:create} + sso_extra_permissions + .sort + .flat_map(&method(:extra_permission_translation)) + .uniq + end + + private + + def all_destructive_permissions + destructive_permissions_for( all_resources ) + end + + def all_resources + %w[ + access_points + connection_links calendars + footnotes + journey_patterns + referentials routes routing_constraint_zones + time_tables + vehicle_journeys + ] + end + + def destructive_permissions_for(models) + @__destructive_permissions_for__ ||= + models.product( %w{create destroy update} ).map{ |model_action| model_action.join('.') } + end + + def extra_permission_translation extra_permission + translation_table.fetch(extra_permission, []) + end + + def translation_table + { + "boiv:read-offer" => %w{sessions:create}, + "boiv:edit-offer" => all_destructive_permissions + %w{sessions:create}, + } end end end diff --git a/spec/controllers/devise/cas_sessions_controller_spec.rb b/spec/controllers/devise/cas_sessions_controller_spec.rb index 950d141fd..c82fd2cdb 100644 --- a/spec/controllers/devise/cas_sessions_controller_spec.rb +++ b/spec/controllers/devise/cas_sessions_controller_spec.rb @@ -1,25 +1,37 @@ RSpec.describe Devise::CasSessionsController, type: :controller do - login_user + before do + @user = signed_in_user + allow_any_instance_of(Warden::Proxy).to receive(:authenticate).and_return @user + allow_any_instance_of(Warden::Proxy).to receive(:authenticate!).and_return @user + @request.env["devise.mapping"] = Devise.mappings[:user] + end + context 'login is correctly redirected' do + let( :signed_in_user ){ build_stubbed :user } it 'to #service' do get :new - expect(response).to redirect_to(unauthenticated_root_path) + expect( response ).to be_redirect + expect( response.redirect_url ).to eq("http://stif-portail-dev.af83.priv/sessions/login?service=http%3A%2F%2Ftest.host%2Fusers%2Fservice") end end - context 'user does not have any boiv:.+ permission' do - xit 'cannot login and will be redirected to the login page, with a corresponding message' do + context 'user does not have permission sessions:create' do + let( :signed_in_user ){ build_stubbed :user } + + it 'cannot login and will be redirected to the login page, with a corresponding message' do get :service expect(controller).to set_flash[:alert].to(%r{IBOO}) - expect(response).to redirect_to("http://stif-portail-dev.af83.priv/sessions/login?service=http%3A%2F%2Ftest.host%2Fusers%2Fservice") + expect(response).to redirect_to "http://stif-portail-dev.af83.priv/sessions/logout?service=http%3A%2F%2Ftest.host%2Fusers%2Fservice" end end - context 'user does have a boiv:.+ permission' do + context 'user does have permission sessions:create' do + let( :signed_in_user ){ build_stubbed :allmighty_user } + it 'can login and will be redirected to the referentials page' do - @user.update_attribute :permissions, (@user.permissions << 'boiv:UnameIt') + @user.permissions << 'sessions:create' get :service expect(response).to redirect_to(authenticated_root_path) end diff --git a/spec/features/workbenches_spec.rb b/spec/features/workbenches_spec.rb index 9141b5673..536469a46 100644 --- a/spec/features/workbenches_spec.rb +++ b/spec/features/workbenches_spec.rb @@ -36,7 +36,7 @@ describe 'Workbenches', type: :feature do context 'without any filter' do it 'should have results' do - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to have_content(referential.name) expect(page).to have_content(other_referential.name) end @@ -45,7 +45,7 @@ describe 'Workbenches', type: :feature do context 'filter by organisation' do it 'should be possible to filter by organisation' do find("#q_organisation_name_eq_any_#{@user.organisation.name.parameterize.underscore}").set(true) - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to have_content(referential.name) expect(page).not_to have_content(other_referential.name) @@ -54,7 +54,7 @@ describe 'Workbenches', type: :feature do it 'should be possible to filter by multiple organisation' do find("#q_organisation_name_eq_any_#{@user.organisation.name.parameterize.underscore}").set(true) find("#q_organisation_name_eq_any_#{another_organisation.name.parameterize.underscore}").set(true) - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to have_content(referential.name) expect(page).to have_content(other_referential.name) @@ -63,7 +63,7 @@ describe 'Workbenches', type: :feature do it 'should keep filter value on submit' do box = "#q_organisation_name_eq_any_#{another_organisation.name.parameterize.underscore}" find(box).set(true) - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(find(box)).to be_checked end end @@ -73,7 +73,7 @@ describe 'Workbenches', type: :feature do other_referential.update_attribute(:archived_at, Date.today) find("#q_archived_at_not_null").set(true) - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to have_content(other_referential.name) expect(page).to_not have_content(referential.name) end @@ -83,7 +83,7 @@ describe 'Workbenches', type: :feature do find("#q_archived_at_not_null").set(true) find("#q_archived_at_null").set(true) - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to have_content(referential.name) expect(page).to have_content(other_referential.name) end @@ -92,14 +92,14 @@ describe 'Workbenches', type: :feature do other_referential.update_attribute(:archived_at, Date.today) find("#q_archived_at_null").set(true) - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to have_content(referential.name) expect(page).to_not have_content(other_referential.name) end it 'should keep filter value on submit' do find("#q_archived_at_null").set(true) - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(find("#q_archived_at_null")).to be_checked end end @@ -115,7 +115,7 @@ describe 'Workbenches', type: :feature do dates = referential.validity_period.to_a fill_validity_field dates[0], 'begin_gteq' fill_validity_field dates[1], 'end_lteq' - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to have_content(referential.name) expect(page).to_not have_content(other_referential.name) @@ -125,7 +125,7 @@ describe 'Workbenches', type: :feature do dates = referential.validity_period.to_a fill_validity_field dates[0], 'begin_gteq' fill_validity_field dates[1], 'end_lteq' - click_button 'Filtrer' + click_button I18n.t('actions.filter') find('a[href*="&sort=validity_period"]').click @@ -136,7 +136,7 @@ describe 'Workbenches', type: :feature do it 'should not show results for out off range' do fill_validity_field(Date.today - 2.year, 'begin_gteq') fill_validity_field(Date.today - 1.year, 'end_lteq') - click_button 'Filtrer' + click_button I18n.t('actions.filter') expect(page).to_not have_content(referential.name) expect(page).to_not have_content(other_referential.name) @@ -147,7 +147,7 @@ describe 'Workbenches', type: :feature do ['begin_gteq', 'end_lteq'].each_with_index do |field, index| fill_validity_field dates[index], field end - click_button 'Filtrer' + click_button I18n.t('actions.filter') ['begin_gteq', 'end_lteq'].each_with_index do |field, index| expect(find("#q_validity_period_#{field}_3i").value).to eq dates[index].day.to_s diff --git a/spec/javascripts/itineraries/reducers/stop_points_spec.js b/spec/javascripts/itineraries/reducers/stop_points_spec.js index 93fe85d36..0331a424c 100644 --- a/spec/javascripts/itineraries/reducers/stop_points_spec.js +++ b/spec/javascripts/itineraries/reducers/stop_points_spec.js @@ -16,6 +16,7 @@ describe('stops reducer', () => { { text: 'first', index: 0, + stoppoint_id: 72, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -27,6 +28,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -54,6 +56,7 @@ describe('stops reducer', () => { { text: 'first', index: 0, + stoppoint_id: 72, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -65,6 +68,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -99,6 +103,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 72, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -110,6 +115,7 @@ describe('stops reducer', () => { { text: 'first', index: 0, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -133,6 +139,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 72, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -144,6 +151,7 @@ describe('stops reducer', () => { { text: 'first', index: 0, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -156,28 +164,29 @@ describe('stops reducer', () => { ) }) - it('should handle DELETE_STOP', () => { - expect( - stopPointsReducer(state, { - type: 'DELETE_STOP', - index: 1 - }) - ).toEqual( - [ - { - text: 'first', - index: 0, - edit: false, - for_boarding: 'normal', - for_alighting: 'normal', - olMap: { - isOpened: false, - json: {} - } - } - ] - ) - }) + // it('should handle DELETE_STOP', () => { + // expect( + // stopPointsReducer(state, { + // type: 'DELETE_STOP', + // index: 1 + // }) + // ).toEqual( + // [ + // { + // text: 'first', + // index: 0, + // stoppoint_id: 72, + // edit: false, + // for_boarding: 'normal', + // for_alighting: 'normal', + // olMap: { + // isOpened: false, + // json: {} + // } + // } + // ] + // ) + // }) it('should handle UPDATE_INPUT_VALUE', () => { expect( @@ -205,8 +214,8 @@ describe('stops reducer', () => { text: 'new value', name: 'new', index: 0, + stoppoint_id: 72, edit: false, - stoppoint_id: '', stoparea_id: 1, for_boarding: 'normal', for_alighting: 'normal', @@ -226,6 +235,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -251,6 +261,7 @@ describe('stops reducer', () => { { text: 'first', index: 0, + stoppoint_id: 72, edit: false, for_boarding: 'prohibited', for_alighting: 'normal', @@ -262,6 +273,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -285,6 +297,7 @@ describe('stops reducer', () => { { text: 'first', index: 0, + stoppoint_id: 72, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -293,6 +306,7 @@ describe('stops reducer', () => { json: { text: 'first', index: 0, + stoppoint_id: 72, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -303,6 +317,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', @@ -326,6 +341,7 @@ describe('stops reducer', () => { { text: 'first', index: 0, + stoppoint_id: 72, edit: true, for_boarding: 'normal', for_alighting: 'normal', @@ -337,6 +353,7 @@ describe('stops reducer', () => { { text: 'second', index: 1, + stoppoint_id: 73, edit: false, for_boarding: 'normal', for_alighting: 'normal', diff --git a/spec/lib/stif/permission_translator_spec.rb b/spec/lib/stif/permission_translator_spec.rb index 3672c7937..1af21364c 100644 --- a/spec/lib/stif/permission_translator_spec.rb +++ b/spec/lib/stif/permission_translator_spec.rb @@ -1,10 +1,45 @@ RSpec.describe Stif::PermissionTranslator do - context "SSO Permission boiv:read:offer →" do + context "No SSO Permissions" do + it { expect(described_class.translate([])).to be_empty } + end + + context "SSO Permission boiv:read-offer →" do it "sessions:create only" do - expect( described_class.translate(%w{boiv:read:offer}) ).to eq(%w{sessions:create}) + expect( described_class.translate(%w{boiv:read-offer}) ).to eq(%w{sessions:create}) + end + + end + + context "SSO Permission boiv:edit-offer →" do + + it "all permissions" do + expect( described_class.translate(%w{boiv:edit-offer}) ).to eq(Support::Permissions.all_permissions) + end + + it "all permissions, no doubletons" do + expect( described_class.translate(%w{boiv:edit-offer boiv:read-offer}) ).to eq(Support::Permissions.all_permissions) end + it "all permissions, input order agnostic" do + expect( described_class.translate(%w{boiv:read-offer boiv:edit-offer}) ).to eq(Support::Permissions.all_permissions) + end + end + + context "SSO Permission ignores garbage (no injection) →" do + it "remains empty" do + expect( described_class.translate(%w{referentials.create}) ).to be_empty + end + + it "remains at boiv:read-offer level" do + expect( described_class.translate(%w{referentials.create boiv:read-offer calendars.delete}) ).to eq(%w{sessions:create}) + end + + it "does not add garbage or doubletons for boiv:edit-offer level" do + expect( + described_class.translate(%w{xxx boiv:read-offer lines.delete boiv:edit-offer footnotes.update}) + ).to eq(Support::Permissions.all_permissions) + end end end diff --git a/spec/mailers/calendar_mailer_spec.rb b/spec/mailers/calendar_mailer_spec.rb index 49cc3cce8..9a2076f64 100644 --- a/spec/mailers/calendar_mailer_spec.rb +++ b/spec/mailers/calendar_mailer_spec.rb @@ -5,7 +5,7 @@ RSpec.describe CalendarMailer, type: :mailer do shared_examples 'notify all user' do |type| let!(:user) { create(:user) } let(:calendar) { create(:calendar, shared: true) } - let(:email) { CalendarMailer.send(type, calendar, user) } + let(:email) { CalendarMailer.send(type, calendar.id, user.id) } it 'should deliver email to user' do expect(email).to deliver_to user.email diff --git a/spec/models/calendar_observer_spec.rb b/spec/models/calendar_observer_spec.rb index abb462d25..4fba02bef 100644 --- a/spec/models/calendar_observer_spec.rb +++ b/spec/models/calendar_observer_spec.rb @@ -12,14 +12,14 @@ RSpec.describe CalendarObserver, type: :observer do it 'should schedule mailer on calendar update' do calendar.name = 'edited_name' - expect(MailerJob).to receive(:perform_later).with 'CalendarMailer', 'updated', [calendar, user] + expect(MailerJob).to receive(:perform_later).with 'CalendarMailer', 'updated', [calendar.id, user.id] calendar.save end it 'should not schedule mailer for none shared calendar on update' do calendar = create(:calendar, shared: false) calendar.name = 'edited_name' - expect(MailerJob).to_not receive(:perform_later).with 'CalendarMailer', 'updated', [calendar, user] + expect(MailerJob).to_not receive(:perform_later).with 'CalendarMailer', 'updated', [calendar.id, user.id] calendar.save end end @@ -31,12 +31,12 @@ RSpec.describe CalendarObserver, type: :observer do end it 'should schedule mailer on calendar create' do - expect(MailerJob).to receive(:perform_later).with 'CalendarMailer', 'created', [anything, user] + expect(MailerJob).to receive(:perform_later).with 'CalendarMailer', 'created', [anything, user.id] build(:calendar, shared: true).save end it 'should not schedule mailer for none shared calendar on create' do - expect(MailerJob).to_not receive(:perform_later).with 'CalendarMailer', 'created', [anything, user] + expect(MailerJob).to_not receive(:perform_later).with 'CalendarMailer', 'created', [anything, user.id] build(:calendar, shared: false).save end end diff --git a/spec/models/chouette/journey_pattern_spec.rb b/spec/models/chouette/journey_pattern_spec.rb index 6601ed5f4..26d220056 100644 --- a/spec/models/chouette/journey_pattern_spec.rb +++ b/spec/models/chouette/journey_pattern_spec.rb @@ -2,30 +2,30 @@ 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 - - it 'should only validate on update' do - jp = build(:journey_pattern_common) - expect(jp).to be_valid - end - end + # 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 + # + # it 'should only validate on update' do + # jp = build(:journey_pattern_common) + # expect(jp).to be_valid + # end + # end describe "state_update" do def journey_pattern_to_state jp diff --git a/spec/models/chouette/line_spec.rb b/spec/models/chouette/line_spec.rb index 5a339e7ed..2e5882012 100644 --- a/spec/models/chouette/line_spec.rb +++ b/spec/models/chouette/line_spec.rb @@ -1,17 +1,12 @@ require 'spec_helper' describe Chouette::Line, :type => :model do - subject { create(:line) } - it { is_expected.to belong_to(:line_referential) } + it { should belong_to(:line_referential) } # it { is_expected.to validate_presence_of :network } # it { is_expected.to validate_presence_of :company } - - it { is_expected.to validate_presence_of :name } - - # it { should validate_presence_of :objectid } - it { is_expected.to validate_uniqueness_of :objectid } + it { should validate_presence_of :name } describe '#display_name' do it 'should display local_id, number, name and company name' do @@ -22,7 +17,7 @@ describe Chouette::Line, :type => :model do describe '#objectid' do subject { super().objectid } - it { is_expected.to be_kind_of(Chouette::NetexObjectId) } + it { is_expected.to be_kind_of(Chouette::StifCodifligneObjectid) } end # it { should validate_numericality_of :objectversion } diff --git a/spec/models/chouette/routing_constraint_zone_spec.rb b/spec/models/chouette/routing_constraint_zone_spec.rb index 87ee9e9ac..0165c369d 100644 --- a/spec/models/chouette/routing_constraint_zone_spec.rb +++ b/spec/models/chouette/routing_constraint_zone_spec.rb @@ -29,7 +29,7 @@ describe Chouette::RoutingConstraintZone, type: :model do }.to raise_error(ActiveRecord::RecordInvalid) end - it 'validates that not all stop points from the route are selected' do + xit 'validates that not all stop points from the route are selected' do routing_constraint_zone.stop_points = routing_constraint_zone.route.stop_points expect { routing_constraint_zone.save! diff --git a/spec/models/chouette/stop_area_spec.rb b/spec/models/chouette/stop_area_spec.rb index 1a2ff8ede..293ae5202 100644 --- a/spec/models/chouette/stop_area_spec.rb +++ b/spec/models/chouette/stop_area_spec.rb @@ -7,16 +7,15 @@ describe Chouette::StopArea, :type => :model do let!(:commercial_stop_point) { create :stop_area, :area_type => "lda" } let!(:stop_place) { create :stop_area, :area_type => "zdlp" } - # Refs #1627 - # describe '#objectid' do - # subject { super().objectid } - # it { is_expected.to be_kind_of(Chouette::ObjectId) } - # end - - it { is_expected.to belong_to(:stop_area_referential) } - it { is_expected.to validate_presence_of :name } - it { is_expected.to validate_numericality_of :latitude } - it { is_expected.to validate_numericality_of :longitude } + describe '#objectid' do + subject { super().objectid } + it { should be_kind_of(Chouette::StifReflexObjectid) } + end + + it { should belong_to(:stop_area_referential) } + it { should validate_presence_of :name } + it { should validate_numericality_of :latitude } + it { should validate_numericality_of :longitude } # describe ".latitude" do # it "should accept -90 value" do diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb index bd74a2d4c..304cb0184 100644 --- a/spec/models/chouette/time_table_spec.rb +++ b/spec/models/chouette/time_table_spec.rb @@ -14,6 +14,8 @@ describe Chouette::TimeTable, :type => :model do 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 } } + let(:dates) { another_tt.dates.map(&:date) } + let(:continuous_dates) { another_tt.continuous_dates.flatten.map(&:date) } # Make sur we don't have overlapping periods or dates before do @@ -22,12 +24,19 @@ describe Chouette::TimeTable, :type => :model do p.period_end = p.period_end + 1.year end another_tt.dates.each{| d| d.date = d.date + 1.year } + another_tt.save 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)) + expect(subject.dates.map(&:date)).to match_array(dates - continuous_dates) + end + + it 'should not merge continuous dates' do + subject.dates.clear + subject.merge!(another_tt) + expect(subject.dates.map(&:date)).not_to include(*continuous_dates) end it 'should merge periods' do @@ -50,28 +59,42 @@ describe Chouette::TimeTable, :type => :model do subject.merge!(another_tt) expect(subject.dates.map(&:date)).to include(another_tt.dates.last.date) end + + it 'should remove date in_out false if other tt doesnt have them' do + subject.dates.create(in_out: false, date: Date.today + 5.day + 1.year) + + expect { + subject.merge!(another_tt) + }.to change {subject.reload.excluded_days.count}.by(-1) + end end context "#merge! with calendar" do let(:calendar) { create(:calendar, date_ranges: [Date.today + 1.year..Date.tomorrow + 1.year]) } + let(:another_tt) { calendar.convert_to_time_table } + let(:dates) { subject.dates.map(&:date) } + let(:continuous_dates) { subject.continuous_dates.flatten.map(&:date) } 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) + subject.merge!(another_tt) + expect(subject.dates.map(&:date)).to match_array(dates - continuous_dates) + end + + it 'should not merge calendar continuous dates' do + subject.dates.clear + subject.merge!(another_tt) + expect(subject.dates.map(&:date)).not_to include(*continuous_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 @@ -1109,7 +1132,7 @@ end - describe "#optimize_periods" do + describe "#optimize_overlapping_periods" do before do subject.periods.clear subject.periods << Chouette::TimeTablePeriod.new( @@ -1127,7 +1150,7 @@ end subject.int_day_types = 4|8|16 end it "should return 2 ordered periods" do - periods = subject.optimize_periods + periods = subject.optimize_overlapping_periods expect(periods.size).to eq(2) expect(periods[0].period_start).to eq(Date.new(2014, 6, 1)) expect(periods[0].period_end).to eq(Date.new(2014, 6, 14)) diff --git a/spec/models/chouette/vehicle_journey_at_stop_spec.rb b/spec/models/chouette/vehicle_journey_at_stop_spec.rb new file mode 100644 index 000000000..d999ed1a8 --- /dev/null +++ b/spec/models/chouette/vehicle_journey_at_stop_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +RSpec.describe Chouette::VehicleJourneyAtStop, type: :model do + describe "#day_offset_outside_range?" do + let (:at_stop) { build_stubbed(:vehicle_journey_at_stop) } + + it "disallows negative offsets" do + expect(at_stop.day_offset_outside_range?(-1)).to be true + end + + it "disallows offsets greater than DAY_OFFSET_MAX" do + expect(at_stop.day_offset_outside_range?( + Chouette::VehicleJourneyAtStop::DAY_OFFSET_MAX + 1 + )).to be true + end + + it "allows offsets between 0 and DAY_OFFSET_MAX inclusive" do + expect(at_stop.day_offset_outside_range?( + Chouette::VehicleJourneyAtStop::DAY_OFFSET_MAX + )).to be false + end + + it "forces a nil offset to 0" do + expect(at_stop.day_offset_outside_range?(nil)).to be false + end + end + + describe "#validate" do + it "displays the proper error message when day offset exceeds the max" do + bad_offset = Chouette::VehicleJourneyAtStop::DAY_OFFSET_MAX + 1 + + at_stop = build_stubbed( + :vehicle_journey_at_stop, + arrival_day_offset: bad_offset, + departure_day_offset: bad_offset + ) + error_message = I18n.t( + 'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max', + local_id: at_stop.vehicle_journey.objectid.local_id, + max: bad_offset + ) + + at_stop.validate + + expect(at_stop.errors[:arrival_day_offset]).to include(error_message) + expect(at_stop.errors[:departure_day_offset]).to include(error_message) + end + end +end diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index c495becac..645513735 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -1,7 +1,24 @@ require 'spec_helper' describe Chouette::VehicleJourney, :type => :model do - describe "vjas_departure_time_must_be_before_next_stop_arrival_time" do + it "must be valid with an at-stop day offset of 1" do + vehicle_journey = create( + :vehicle_journey, + stop_arrival_time: '23:00:00', + stop_departure_time: '23:00:00' + ) + vehicle_journey.vehicle_journey_at_stops.last.update( + arrival_time: '00:30:00', + departure_time: '00:30:00', + arrival_day_offset: 1, + departure_day_offset: 1 + ) + + expect(vehicle_journey).to be_valid + end + + describe "vjas_departure_time_must_be_before_next_stop_arrival_time", + skip: "Validation currently commented out because it interferes with day offsets" do let(:vehicle_journey) { create :vehicle_journey } let(:vjas) { vehicle_journey.vehicle_journey_at_stops } diff --git a/spec/models/referential_spec.rb b/spec/models/referential_spec.rb index 2390cc470..53eaa60a3 100644 --- a/spec/models/referential_spec.rb +++ b/spec/models/referential_spec.rb @@ -27,11 +27,12 @@ describe Referential, :type => :model do context "Cloning referential" do let(:clone) do - Referential.new_from(ref, organisation: ref.organisation) + Referential.new_from(ref) end let(:saved_clone) do clone.tap do |clone| + clone.organisation = ref.organisation clone.metadatas.each do |metadata| metadata.periodes = metadata.periodes.map { |period| Range.new(period.end+1, period.end+10) } end diff --git a/spec/models/time_table_combination_spec.rb b/spec/models/time_table_combination_spec.rb index 3e60fa444..ee934f50d 100644 --- a/spec/models/time_table_combination_spec.rb +++ b/spec/models/time_table_combination_spec.rb @@ -8,30 +8,93 @@ describe TimeTableCombination, :type => :model do describe '#continuous_dates' do it 'should group continuous dates' do dates = source.dates.where(in_out: true) - expect(source.continuous_dates[0].count).to eq(dates.count) + expect(source.continuous_dates.flatten.count).to eq(dates.count) - # 6 more continuous date, 1 isolated date + # 6 more continuous dates, 2 isolated dates (10..15).each do |n| source.dates.create(date: Date.today + n.day, in_out: true) end - source.dates.create(date: Date.today + 1.year, in_out: true) + + (1..2).each do |n| + source.dates.create(date: Date.today + n.day + 1.year, in_out: true) + end + expect(source.reload.continuous_dates[1].count).to eq(6) - expect(source.reload.continuous_dates[2].count).to eq(1) + expect(source.reload.continuous_dates[2].count).to eq(2) end end describe '#convert_continuous_dates_to_periods' do it 'should convert continuous dates to periods' do + source.dates.clear + (10..12).each do |n| source.dates.create(date: Date.today + n.day, in_out: true) end - source.dates.create(date: Date.today + 1.year, in_out: true) + + (1..3).each do |n| + source.dates.create(date: Date.today + n.day + 1.year, in_out: true) + end expect { source.reload.convert_continuous_dates_to_periods }.to change {source.periods.count}.by(2) - expect(source.reload.dates.where(in_out: true).count).to eq(1) + expect(source.reload.dates.where(in_out: true).count).to eq(0) + end + end + + describe '#continuous_periods' do + it 'should group continuous periods' do + source.periods.clear + + start_date = Date.today + 1.year + end_date = start_date + 10 + + # 6 more continuous dates, 2 isolated dates + 0.upto(4) do |i| + source.periods.create(period_start: start_date, period_end: end_date) + start_date = end_date + 1 + end_date = start_date + 10 + end + + expect(source.reload.continuous_periods.flatten.count).to eq(5) + end + end + + describe '#convert_continuous_periods_into_one' do + it 'should convert continuous periods into one' do + source.periods.clear + + start_date = Date.today + 1.year + end_date = start_date + 10 + + # 6 more continuous dates, 2 isolated dates + 0.upto(4) do |i| + source.periods.create(period_start: start_date, period_end: end_date) + start_date = end_date + 1 + end_date = start_date + 10 + end + + expect { + source.reload.convert_continuous_periods_into_one + }.to change {source.periods.count}.by(-4) + end + end + + describe '#optimize_continuous_dates_and_periods' do + it 'should update period if timetable has in_date just before or after ' do + source.dates.clear + source.periods.clear + + source.periods.create(period_start: Date.today, period_end: Date.today + 10.day) + source.dates.create(date: Date.today - 1.day, in_out: true) + + expect { + source.periods = source.optimize_continuous_dates_and_periods + }.to change {source.dates.count}.by(-1) + + expect(source.reload.periods.first.period_start).to eq(Date.today - 1.day) end end @@ -129,4 +192,3 @@ describe TimeTableCombination, :type => :model do end end end - diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 6f98e5ce7..3a9ae37e9 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,6 +1,4 @@ -require 'spec_helper' - -describe User, :type => :model do +RSpec.describe User, :type => :model do # it { should validate_uniqueness_of :email } # it { should validate_presence_of :name } @@ -116,9 +114,11 @@ describe User, :type => :model do end context 'permissions' do + let( :all_permissions ){ Stif::PermissionTranslator.translate(%w{boiv:edit-offer}) } + it 'should give edit permissions to user if user has "edit offer" permission in portail' do User.portail_sync - expect(User.find_by(username: 'vlatka.pavisic').permissions).to include_all(User.edit_offer_permissions) + expect(User.find_by(username: 'vlatka.pavisic').permissions).to eq(all_permissions) expect(User.find_by(username: 'pierre.vabre').permissions).to be_empty end end diff --git a/spec/support/permissions.rb b/spec/support/permissions.rb index a13010f65..fcf9ae9c4 100644 --- a/spec/support/permissions.rb +++ b/spec/support/permissions.rb @@ -15,6 +15,7 @@ module Support %w[ access_points connection_links + calendars footnotes journey_patterns referentials |
