diff --git a/Gemfile b/Gemfile index 3c7f41916..2e10ff81d 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ gem 'anchored' gem 'bcrypt' gem 'bootsnap', '>= 1.4.4', require: false # Reduces boot times through caching; required in config/boot.rb gem 'browser' +gem 'charlock_holmes' gem 'chartkick' gem 'chunky_png' gem 'clamav-client', require: 'clamav/client' diff --git a/Gemfile.lock b/Gemfile.lock index eb21c74b0..5a1f455c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -155,6 +155,7 @@ GEM marcel (~> 1.0) nokogiri (~> 1.10, >= 1.10.4) rubyzip (>= 1.3.0, < 3) + charlock_holmes (0.7.7) chartkick (3.4.2) childprocess (3.0.0) choice (0.2.0) @@ -793,6 +794,7 @@ DEPENDENCIES capybara-email capybara-screenshot capybara-selenium + charlock_holmes chartkick chunky_png clamav-client diff --git a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb index ce51452e9..23086104f 100644 --- a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb +++ b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb @@ -170,7 +170,10 @@ module NewAdministrateur redirect_to admin_procedure_groupe_instructeurs_path(procedure) else - groupes_emails = CSV.new(group_csv_file.read.force_encoding("UTF-8"), headers: true, header_converters: :downcase) + file = group_csv_file.read + # find the original encoding to avoid encoding errors + base_encoding = CharlockHolmes::EncodingDetector.detect(file) + groupes_emails = CSV.new(file.encode("UTF-8", base_encoding[:encoding], invalid: :replace, replace: ""), headers: true, header_converters: :downcase) .map { |r| r.to_h.slice('groupe', 'email') } groupes_emails_has_keys = groupes_emails.first.has_key?("groupe") && groupes_emails.first.has_key?("email") diff --git a/app/javascript/components/shared/mapbox/MapStyleControl.jsx b/app/javascript/components/shared/mapbox/MapStyleControl.jsx index a94c07aa7..f6bc98a04 100644 --- a/app/javascript/components/shared/mapbox/MapStyleControl.jsx +++ b/app/javascript/components/shared/mapbox/MapStyleControl.jsx @@ -48,6 +48,9 @@ export function useMapStyle( const enabledLayers = Object.entries(layers).filter( ([, { enabled }]) => enabled ); + const layerIds = enabledLayers.map( + ([layer, { opacity }]) => `${layer}-${opacity}` + ); const style = useMemo( () => getMapStyle( @@ -57,13 +60,10 @@ export function useMapStyle( enabledLayers.map(([layer, { opacity }]) => [layer, opacity]) ) ), - [ - styleId, - enabledLayers.map(([layer, { opacity }]) => `${layer}-${opacity}`) - ] + [styleId, layerIds] ); - useEffect(() => onStyleChange(), [styleId, cadastreEnabled]); + useEffect(() => onStyleChange(), [styleId, layerIds, cadastreEnabled]); return { style, layers, setStyle, setLayerEnabled, setLayerOpacity }; } diff --git a/app/models/procedure_presentation.rb b/app/models/procedure_presentation.rb index 309b63d31..066ef42ca 100644 --- a/app/models/procedure_presentation.rb +++ b/app/models/procedure_presentation.rb @@ -22,6 +22,8 @@ class ProcedurePresentation < ApplicationRecord TYPE_DE_CHAMP = 'type_de_champ' TYPE_DE_CHAMP_PRIVATE = 'type_de_champ_private' + FILTERS_VALUE_MAX_LENGTH = 100 + belongs_to :assign_to, optional: false delegate :procedure, to: :assign_to @@ -30,6 +32,7 @@ class ProcedurePresentation < ApplicationRecord validate :check_allowed_sort_column validate :check_allowed_sort_order validate :check_allowed_filter_columns + validate :check_filters_max_length def fields fields = [ @@ -282,6 +285,14 @@ class ProcedurePresentation < ApplicationRecord end end + def check_filters_max_length + filters.values.flatten.each do |filter| + if filter['value']&.length.to_i > FILTERS_VALUE_MAX_LENGTH + errors.add(:filters, :too_long) + end + end + end + def field_hash(label, table, column) { 'label' => label, diff --git a/app/views/instructeurs/procedures/show.html.haml b/app/views/instructeurs/procedures/show.html.haml index 062af95c5..5e2553b7b 100644 --- a/app/views/instructeurs/procedures/show.html.haml +++ b/app/views/instructeurs/procedures/show.html.haml @@ -86,7 +86,7 @@ = select_tag :field, options_for_select(@displayed_fields_options) %br = label_tag :value, "Valeur" - = text_field_tag :value + = text_field_tag :value, nil, maxlength: ProcedurePresentation::FILTERS_VALUE_MAX_LENGTH = hidden_field_tag :statut, @statut %br = submit_tag "Ajouter le filtre", class: 'button' diff --git a/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb index d3308a54d..bc1bcf04e 100644 --- a/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb +++ b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb @@ -365,6 +365,16 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do it { expect(flash.alert).to eq("Import terminé. Cependant les emails suivants ne sont pas pris en compte: kara") } end + context 'when the content of csv contains special characters' do + let(:csv_file) { fixture_file_upload('spec/fixtures/files/groupe_avec_caracteres_speciaux.csv', 'text/csv') } + + before { subject } + + it { expect(procedure.groupe_instructeurs.pluck(:label)).to eq(["défaut", "Auvergne-Rhône-Alpes", "Vendée"]) } + it { expect(flash.notice).to be_present } + it { expect(flash.notice).to eq("La liste des instructeurs a été importée avec succès") } + end + context 'when the csv file length is more than 1 mo' do let(:csv_file) { fixture_file_upload('spec/fixtures/files/groupe-instructeur.csv', 'text/csv') } diff --git a/spec/fixtures/files/groupe_avec_caracteres_speciaux.csv b/spec/fixtures/files/groupe_avec_caracteres_speciaux.csv new file mode 100644 index 000000000..376e358f0 --- /dev/null +++ b/spec/fixtures/files/groupe_avec_caracteres_speciaux.csv @@ -0,0 +1,5 @@ +Email,Groupe +kara@beta.gouv.fr,Auvergne-Rhône-Alpes +camilya@beta.gouv.fr,Vendée +philippe@beta.gouv.fr,Auvergne-Rhône-Alpes +sim@beta.gouv.fr,Vendée \ No newline at end of file diff --git a/spec/models/procedure_presentation_spec.rb b/spec/models/procedure_presentation_spec.rb index 3be47afce..9eb2bd6f7 100644 --- a/spec/models/procedure_presentation_spec.rb +++ b/spec/models/procedure_presentation_spec.rb @@ -45,6 +45,7 @@ describe ProcedurePresentation do context 'of filters' do it { expect(build(:procedure_presentation, filters: { "suivis" => [{ "table" => "user", "column" => "reset_password_token", "order" => "asc" }] })).to be_invalid } + it { expect(build(:procedure_presentation, filters: { "suivis" => [{ "table" => "user", "column" => "email", "value" => "A" * 200 }] })).to be_invalid } end end