Compare commits
8 commits
Author | SHA1 | Date | |
---|---|---|---|
|
1bc0eab396 | ||
|
802e8f0a87 | ||
|
5368c28c3d | ||
|
ce58a67d14 | ||
|
d90a04174c | ||
|
e1a1191ce6 | ||
|
f422b314a0 | ||
|
74c240ef66 |
33 changed files with 365 additions and 10 deletions
|
@ -0,0 +1,5 @@
|
||||||
|
class EditableChamp::ExpressionReguliereComponent < EditableChamp::EditableChampBaseComponent
|
||||||
|
def dsfr_input_classname
|
||||||
|
'fr-input'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
= @form.text_field(:value, input_opts(id: @champ.input_id, placeholder: @champ.expression_reguliere_exemple_text, required: @champ.required?, aria: { describedby: @champ.describedby_id }))
|
|
@ -40,6 +40,12 @@ fr:
|
||||||
update_condition: La condition du champ « %{label} » a été modifiée. La nouvelle condition est « %{to} ».
|
update_condition: La condition du champ « %{label} » a été modifiée. La nouvelle condition est « %{to} ».
|
||||||
update_character_limit: La limite de caractères du champ texte « %{label} » a été modifiée. La nouvelle limite est « %{to} ».
|
update_character_limit: La limite de caractères du champ texte « %{label} » a été modifiée. La nouvelle limite est « %{to} ».
|
||||||
remove_character_limit: La limite de caractères du champ texte « %{label} » a été supprimée.
|
remove_character_limit: La limite de caractères du champ texte « %{label} » a été supprimée.
|
||||||
|
remove_expression_reguliere: L’expression régulière du champ « %{label} » a été supprimée.
|
||||||
|
update_expression_reguliere: L’expression régulière du champ « %{label} » a été modifiée. La nouvelle expression est « %{to} ».
|
||||||
|
remove_expression_reguliere_exemple_text: L’exemple d’expression régulière du champ « %{label} » a été supprimé.
|
||||||
|
update_expression_reguliere_exemple_text: L’exemple d’expression régulière du champ « %{label} » a été modifié. Le nouvel exemple est « %{to} ».
|
||||||
|
remove_expression_reguliere_error_message: Le message d’erreur de l’expression régulière du champ « %{label} » a été supprimé.
|
||||||
|
update_expression_reguliere_error_message: Le message d’erreur de l’expression régulière du champ « %{label} » a été modifié. Le nouveau message est « %{to} ».
|
||||||
private:
|
private:
|
||||||
add: L’annotation privée « %{label} » a été ajoutée.
|
add: L’annotation privée « %{label} » a été ajoutée.
|
||||||
remove: L’annotation privée « %{label} » a été supprimée.
|
remove: L’annotation privée « %{label} » a été supprimée.
|
||||||
|
|
|
@ -141,6 +141,27 @@
|
||||||
- else
|
- else
|
||||||
- list.with_item do
|
- list.with_item do
|
||||||
= t(".#{prefix}.update_character_limit", label: change.label, to: change.to)
|
= t(".#{prefix}.update_character_limit", label: change.label, to: change.to)
|
||||||
|
- when :expression_reguliere
|
||||||
|
- if change.to.blank?
|
||||||
|
- list.with_item do
|
||||||
|
= t(".#{prefix}.remove_expression_reguliere", label: change.label, to: change.to)
|
||||||
|
- else
|
||||||
|
- list.with_item do
|
||||||
|
= t(".#{prefix}.update_expression_reguliere", label: change.label, to: change.to)
|
||||||
|
- when :expression_reguliere_exemple_text
|
||||||
|
- if change.to.blank?
|
||||||
|
- list.with_item do
|
||||||
|
= t(".#{prefix}.remove_expression_reguliere_exemple_text", label: change.label, to: change.to)
|
||||||
|
- else
|
||||||
|
- list.with_item do
|
||||||
|
= t(".#{prefix}.update_expression_reguliere_exemple_text", label: change.label, to: change.to)
|
||||||
|
- when :expression_reguliere_error_message
|
||||||
|
- if change.to.blank?
|
||||||
|
- list.with_item do
|
||||||
|
= t(".#{prefix}.remove_expression_reguliere_error_message", label: change.label, to: change.to)
|
||||||
|
- else
|
||||||
|
- list.with_item do
|
||||||
|
= t(".#{prefix}.update_expression_reguliere_error_message", label: change.label, to: change.to)
|
||||||
|
|
||||||
- if @public_move_changes.present?
|
- if @public_move_changes.present?
|
||||||
- list.with_item do
|
- list.with_item do
|
||||||
|
|
|
@ -13,3 +13,8 @@ fr:
|
||||||
character_limit:
|
character_limit:
|
||||||
unlimited: Pas de limite de caractères
|
unlimited: Pas de limite de caractères
|
||||||
limit: Limité à %{limit} caractères
|
limit: Limité à %{limit} caractères
|
||||||
|
expression_reguliere:
|
||||||
|
labels:
|
||||||
|
regex: Saisissez votre expression régulière, essayez-la sur https://rubular.com
|
||||||
|
valid_exemple: Exemple valide qui passe l'expression régulière
|
||||||
|
error_message: Message d'erreur à afficher à l'usager en cas de saisie invalide
|
||||||
|
|
|
@ -46,6 +46,23 @@
|
||||||
%p
|
%p
|
||||||
%small Nous numérotons automatiquement les titres lorsqu’aucun de vos titres ne commence par un chiffre.
|
%small Nous numérotons automatiquement les titres lorsqu’aucun de vos titres ne commence par un chiffre.
|
||||||
|
|
||||||
|
- if type_de_champ.expression_reguliere?
|
||||||
|
.cell.mt-1
|
||||||
|
= form.label :expression_reguliere, for: dom_id(type_de_champ, :expression_reguliere) do
|
||||||
|
= t('.expression_reguliere.labels.regex')
|
||||||
|
= form.text_field :expression_reguliere, class: "fr-input small-margin small", id: dom_id(type_de_champ, :expression_reguliere)
|
||||||
|
|
||||||
|
.cell.mt-1
|
||||||
|
= form.label :expression_reguliere_exemple_text, for: dom_id(type_de_champ, :expression_reguliere_exemple_text) do
|
||||||
|
= t('.expression_reguliere.labels.valid_exemple')
|
||||||
|
= form.text_field :expression_reguliere_exemple_text, class: "fr-input small-margin small", id: dom_id(type_de_champ, :expression_reguliere_exemple_text)
|
||||||
|
- if type_de_champ.invalid_regexp?
|
||||||
|
%p.fr-message.fr-message--error
|
||||||
|
= type_de_champ.errors[:expression_reguliere_exemple_text].join(", ")
|
||||||
|
.cell.mt-1
|
||||||
|
= form.label :expression_reguliere_error_message, for: dom_id(type_de_champ, :expression_reguliere_error_message) do
|
||||||
|
= t('.expression_reguliere.labels.error_message')
|
||||||
|
= form.text_field :expression_reguliere_error_message, class: "fr-input small-margin small", id: dom_id(type_de_champ, :expression_reguliere_error_message)
|
||||||
- if !type_de_champ.header_section? && !type_de_champ.titre_identite?
|
- if !type_de_champ.header_section? && !type_de_champ.titre_identite?
|
||||||
.cell.mt-1
|
.cell.mt-1
|
||||||
= form.label :description, "Description du champ (optionnel)", for: dom_id(type_de_champ, :description)
|
= form.label :description, "Description du champ (optionnel)", for: dom_id(type_de_champ, :description)
|
||||||
|
|
|
@ -15,6 +15,10 @@ class TypesDeChampEditor::ErrorsSummary < ApplicationComponent
|
||||||
@revision.errors.include?(:header_section)
|
@revision.errors.include?(:header_section)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expression_reguliere_errors?
|
||||||
|
@revision.errors.include?(:expression_reguliere)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def errors_for(key)
|
def errors_for(key)
|
||||||
|
|
|
@ -6,3 +6,7 @@ fr:
|
||||||
fix_header_section:
|
fix_header_section:
|
||||||
one: 'Le titre de section suivant est invalide, veuillez le corriger :'
|
one: 'Le titre de section suivant est invalide, veuillez le corriger :'
|
||||||
other: 'Les titres de section suivants sont invalides, veuillez les corriger :'
|
other: 'Les titres de section suivants sont invalides, veuillez les corriger :'
|
||||||
|
|
||||||
|
fix_expressions_regulieres:
|
||||||
|
one: "L'expression régulière suivante est invalide, veuillez la corriger :"
|
||||||
|
other: 'Les expressions régulières suivantes sont invalides, veuillez les corriger :'
|
||||||
|
|
|
@ -9,3 +9,7 @@
|
||||||
- if header_section_errors?
|
- if header_section_errors?
|
||||||
%p= t('.fix_header_section', count: errors_for(:header_section).size)
|
%p= t('.fix_header_section', count: errors_for(:header_section).size)
|
||||||
= error_message_for(:header_section)
|
= error_message_for(:header_section)
|
||||||
|
|
||||||
|
- if expression_reguliere_errors?
|
||||||
|
%p= t('.fix_expressions_regulieres', count: errors_for(:expression_reguliere).size)
|
||||||
|
= error_message_for(:expression_reguliere)
|
||||||
|
|
|
@ -130,6 +130,9 @@ module Administrateurs
|
||||||
:collapsible_explanation_text,
|
:collapsible_explanation_text,
|
||||||
:header_section_level,
|
:header_section_level,
|
||||||
:character_limit,
|
:character_limit,
|
||||||
|
:expression_reguliere,
|
||||||
|
:expression_reguliere_exemple_text,
|
||||||
|
:expression_reguliere_error_message,
|
||||||
editable_options: [
|
editable_options: [
|
||||||
:cadastres,
|
:cadastres,
|
||||||
:unesco,
|
:unesco,
|
||||||
|
|
|
@ -114,7 +114,8 @@ class API::V2::Schema < GraphQL::Schema
|
||||||
Types::Champs::Descriptor::TextareaChampDescriptorType,
|
Types::Champs::Descriptor::TextareaChampDescriptorType,
|
||||||
Types::Champs::Descriptor::TextChampDescriptorType,
|
Types::Champs::Descriptor::TextChampDescriptorType,
|
||||||
Types::Champs::Descriptor::TitreIdentiteChampDescriptorType,
|
Types::Champs::Descriptor::TitreIdentiteChampDescriptorType,
|
||||||
Types::Champs::Descriptor::YesNoChampDescriptorType
|
Types::Champs::Descriptor::YesNoChampDescriptorType,
|
||||||
|
Types::Champs::Descriptor::ExpressionReguliereChampDescriptorType
|
||||||
|
|
||||||
def self.unauthorized_object(error)
|
def self.unauthorized_object(error)
|
||||||
# Add a top-level error to the response instead of returning nil:
|
# Add a top-level error to the response instead of returning nil:
|
||||||
|
|
|
@ -2284,6 +2284,34 @@ type ExplicationChampDescriptor implements ChampDescriptor {
|
||||||
type: TypeDeChamp! @deprecated(reason: "Utilisez le champ `__typename` à la place.")
|
type: TypeDeChamp! @deprecated(reason: "Utilisez le champ `__typename` à la place.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExpressionReguliereChampDescriptor implements ChampDescriptor {
|
||||||
|
"""
|
||||||
|
Description des champs d’un bloc répétable.
|
||||||
|
"""
|
||||||
|
champDescriptors: [ChampDescriptor!] @deprecated(reason: "Utilisez le champ `RepetitionChampDescriptor.champ_descriptors` à la place.")
|
||||||
|
|
||||||
|
"""
|
||||||
|
Description du champ.
|
||||||
|
"""
|
||||||
|
description: String
|
||||||
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Est-ce que le champ est obligatoire ?
|
||||||
|
"""
|
||||||
|
required: Boolean!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Type de la valeur du champ.
|
||||||
|
"""
|
||||||
|
type: TypeDeChamp! @deprecated(reason: "Utilisez le champ `__typename` à la place.")
|
||||||
|
}
|
||||||
|
|
||||||
type File {
|
type File {
|
||||||
byteSize: Int! @deprecated(reason: "Utilisez le champ `byteSizeBigInt` à la place.")
|
byteSize: Int! @deprecated(reason: "Utilisez le champ `byteSizeBigInt` à la place.")
|
||||||
byteSizeBigInt: BigInt!
|
byteSizeBigInt: BigInt!
|
||||||
|
@ -3945,6 +3973,11 @@ enum TypeDeChamp {
|
||||||
"""
|
"""
|
||||||
explication
|
explication
|
||||||
|
|
||||||
|
"""
|
||||||
|
Expression régulière
|
||||||
|
"""
|
||||||
|
expression_reguliere
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Titre de section
|
Titre de section
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -96,6 +96,8 @@ module Types
|
||||||
Types::Champs::Descriptor::EpciChampDescriptorType
|
Types::Champs::Descriptor::EpciChampDescriptorType
|
||||||
when TypeDeChamp.type_champs.fetch(:cojo)
|
when TypeDeChamp.type_champs.fetch(:cojo)
|
||||||
Types::Champs::Descriptor::COJOChampDescriptorType
|
Types::Champs::Descriptor::COJOChampDescriptorType
|
||||||
|
when TypeDeChamp.type_champs.fetch(:expression_reguliere)
|
||||||
|
Types::Champs::Descriptor::ExpressionReguliereChampDescriptorType
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
module Types::Champs::Descriptor
|
||||||
|
class ExpressionReguliereChampDescriptorType < Types::BaseObject
|
||||||
|
implements Types::ChampDescriptorType
|
||||||
|
end
|
||||||
|
end
|
|
@ -58,6 +58,9 @@ class Champ < ApplicationRecord
|
||||||
:character_limit?,
|
:character_limit?,
|
||||||
:character_limit,
|
:character_limit,
|
||||||
:yes_no?,
|
:yes_no?,
|
||||||
|
:expression_reguliere,
|
||||||
|
:expression_reguliere_exemple_text,
|
||||||
|
:expression_reguliere_error_message,
|
||||||
to: :type_de_champ
|
to: :type_de_champ
|
||||||
|
|
||||||
delegate :to_typed_id, :to_typed_id_for_query, to: :type_de_champ, prefix: true
|
delegate :to_typed_id, :to_typed_id_for_query, to: :type_de_champ, prefix: true
|
||||||
|
|
3
app/models/champs/expression_reguliere_champ.rb
Normal file
3
app/models/champs/expression_reguliere_champ.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class Champs::ExpressionReguliereChamp < Champ
|
||||||
|
validates_with ExpressionReguliereValidator, if: -> { validation_context != :brouillon }
|
||||||
|
end
|
|
@ -19,6 +19,7 @@ class ProcedureRevision < ApplicationRecord
|
||||||
|
|
||||||
validate :conditions_are_valid?
|
validate :conditions_are_valid?
|
||||||
validate :header_sections_are_valid?
|
validate :header_sections_are_valid?
|
||||||
|
validate :expressions_regulieres_are_valid?
|
||||||
|
|
||||||
delegate :path, to: :procedure, prefix: true
|
delegate :path, to: :procedure, prefix: true
|
||||||
|
|
||||||
|
@ -375,6 +376,25 @@ class ProcedureRevision < ApplicationRecord
|
||||||
from_type_de_champ.character_limit,
|
from_type_de_champ.character_limit,
|
||||||
to_type_de_champ.character_limit)
|
to_type_de_champ.character_limit)
|
||||||
end
|
end
|
||||||
|
elsif to_type_de_champ.expression_reguliere?
|
||||||
|
if from_type_de_champ.expression_reguliere != to_type_de_champ.expression_reguliere
|
||||||
|
changes << ProcedureRevisionChange::UpdateChamp.new(from_type_de_champ,
|
||||||
|
:expression_reguliere,
|
||||||
|
from_type_de_champ.expression_reguliere,
|
||||||
|
to_type_de_champ.expression_reguliere)
|
||||||
|
end
|
||||||
|
if from_type_de_champ.expression_reguliere_exemple_text != to_type_de_champ.expression_reguliere_exemple_text
|
||||||
|
changes << ProcedureRevisionChange::UpdateChamp.new(from_type_de_champ,
|
||||||
|
:expression_reguliere_exemple_text,
|
||||||
|
from_type_de_champ.expression_reguliere_exemple_text,
|
||||||
|
to_type_de_champ.expression_reguliere_exemple_text)
|
||||||
|
end
|
||||||
|
if from_type_de_champ.expression_reguliere_error_message != to_type_de_champ.expression_reguliere_error_message
|
||||||
|
changes << ProcedureRevisionChange::UpdateChamp.new(from_type_de_champ,
|
||||||
|
:expression_reguliere_error_message,
|
||||||
|
from_type_de_champ.expression_reguliere_error_message,
|
||||||
|
to_type_de_champ.expression_reguliere_error_message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
changes
|
changes
|
||||||
end
|
end
|
||||||
|
@ -412,6 +432,16 @@ class ProcedureRevision < ApplicationRecord
|
||||||
repetition_tdcs_errors + root_tdcs_errors
|
repetition_tdcs_errors + root_tdcs_errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expressions_regulieres_are_valid?
|
||||||
|
types_de_champ_public.to_a
|
||||||
|
.flat_map { _1.repetition? ? children_of(_1) : _1 }
|
||||||
|
.each do |tdc|
|
||||||
|
if tdc.expression_reguliere? && tdc.invalid_regexp?
|
||||||
|
errors.add(:expression_reguliere, type_de_champ: tdc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def errors_for_header_sections_order(tdcs)
|
def errors_for_header_sections_order(tdcs)
|
||||||
tdcs
|
tdcs
|
||||||
.map.with_index
|
.map.with_index
|
||||||
|
|
|
@ -3,7 +3,8 @@ class TypeDeChamp < ApplicationRecord
|
||||||
|
|
||||||
FILE_MAX_SIZE = 200.megabytes
|
FILE_MAX_SIZE = 200.megabytes
|
||||||
FEATURE_FLAGS = {
|
FEATURE_FLAGS = {
|
||||||
cojo: :cojo_type_de_champ
|
cojo: :cojo_type_de_champ,
|
||||||
|
expression_reguliere: :expression_reguliere_type_de_champ
|
||||||
}
|
}
|
||||||
MINIMUM_TEXTAREA_CHARACTER_LIMIT_LENGTH = 400
|
MINIMUM_TEXTAREA_CHARACTER_LIMIT_LENGTH = 400
|
||||||
|
|
||||||
|
@ -55,7 +56,8 @@ class TypeDeChamp < ApplicationRecord
|
||||||
dgfip: REFERENTIEL_EXTERNE,
|
dgfip: REFERENTIEL_EXTERNE,
|
||||||
pole_emploi: REFERENTIEL_EXTERNE,
|
pole_emploi: REFERENTIEL_EXTERNE,
|
||||||
mesri: REFERENTIEL_EXTERNE,
|
mesri: REFERENTIEL_EXTERNE,
|
||||||
cojo: REFERENTIEL_EXTERNE
|
cojo: REFERENTIEL_EXTERNE,
|
||||||
|
expression_reguliere: STANDARD
|
||||||
}
|
}
|
||||||
|
|
||||||
enum type_champs: {
|
enum type_champs: {
|
||||||
|
@ -95,7 +97,8 @@ class TypeDeChamp < ApplicationRecord
|
||||||
pole_emploi: 'pole_emploi',
|
pole_emploi: 'pole_emploi',
|
||||||
mesri: 'mesri',
|
mesri: 'mesri',
|
||||||
epci: 'epci',
|
epci: 'epci',
|
||||||
cojo: 'cojo'
|
cojo: 'cojo',
|
||||||
|
expression_reguliere: 'expression_reguliere'
|
||||||
}
|
}
|
||||||
|
|
||||||
ROUTABLE_TYPES = [
|
ROUTABLE_TYPES = [
|
||||||
|
@ -116,6 +119,9 @@ class TypeDeChamp < ApplicationRecord
|
||||||
:drop_down_secondary_description,
|
:drop_down_secondary_description,
|
||||||
:drop_down_other,
|
:drop_down_other,
|
||||||
:character_limit,
|
:character_limit,
|
||||||
|
:expression_reguliere,
|
||||||
|
:expression_reguliere_exemple_text,
|
||||||
|
:expression_reguliere_error_message,
|
||||||
:collapsible_explanation_enabled,
|
:collapsible_explanation_enabled,
|
||||||
:collapsible_explanation_text,
|
:collapsible_explanation_text,
|
||||||
:header_section_level
|
:header_section_level
|
||||||
|
@ -184,6 +190,7 @@ class TypeDeChamp < ApplicationRecord
|
||||||
|
|
||||||
before_validation :check_mandatory
|
before_validation :check_mandatory
|
||||||
before_validation :normalize_libelle
|
before_validation :normalize_libelle
|
||||||
|
|
||||||
before_save :remove_piece_justificative_template, if: -> { type_champ_changed? }
|
before_save :remove_piece_justificative_template, if: -> { type_champ_changed? }
|
||||||
before_validation :remove_drop_down_list, if: -> { type_champ_changed? }
|
before_validation :remove_drop_down_list, if: -> { type_champ_changed? }
|
||||||
before_save :remove_block, if: -> { type_champ_changed? }
|
before_save :remove_block, if: -> { type_champ_changed? }
|
||||||
|
@ -414,6 +421,10 @@ class TypeDeChamp < ApplicationRecord
|
||||||
type_champ == TypeDeChamp.type_champs.fetch(:checkbox)
|
type_champ == TypeDeChamp.type_champs.fetch(:checkbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expression_reguliere?
|
||||||
|
type_champ == TypeDeChamp.type_champs.fetch(:expression_reguliere)
|
||||||
|
end
|
||||||
|
|
||||||
def public?
|
def public?
|
||||||
!private?
|
!private?
|
||||||
end
|
end
|
||||||
|
@ -604,6 +615,21 @@ class TypeDeChamp < ApplicationRecord
|
||||||
type_champ.in?(ROUTABLE_TYPES)
|
type_champ.in?(ROUTABLE_TYPES)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def invalid_regexp?
|
||||||
|
return false if expression_reguliere.blank?
|
||||||
|
return false if expression_reguliere_exemple_text.blank?
|
||||||
|
return false if expression_reguliere_exemple_text.match?(Regexp.new(expression_reguliere, timeout: 2.0))
|
||||||
|
|
||||||
|
self.errors.add(:expression_reguliere_exemple_text, I18n.t('errors.messages.mismatch_regexp'))
|
||||||
|
true
|
||||||
|
rescue RegexpError
|
||||||
|
self.errors.add(:expression_reguliere, I18n.t('errors.messages.syntax_error_regexp'))
|
||||||
|
true
|
||||||
|
rescue Regexp::TimeoutError
|
||||||
|
self.errors.add(:expression_reguliere, I18n.t('errors.messages.evil_regexp'))
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
DEFAULT_EMPTY = ['']
|
DEFAULT_EMPTY = ['']
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
class TypesDeChamp::ExpressionReguliereTypeDeChamp < TypesDeChamp::TypeDeChampBase
|
||||||
|
end
|
11
app/validators/expression_reguliere_validator.rb
Normal file
11
app/validators/expression_reguliere_validator.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class ExpressionReguliereValidator < ActiveModel::Validator
|
||||||
|
def validate(record)
|
||||||
|
if record.value.present?
|
||||||
|
if !record.value.match?(Regexp.new(record.expression_reguliere, timeout: 5.0))
|
||||||
|
record.errors.add(:value, :invalid_regexp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Regexp::TimeoutError
|
||||||
|
record.errors.add(:expression_reguliere, :evil_regexp)
|
||||||
|
end
|
||||||
|
end
|
|
@ -684,6 +684,10 @@ en:
|
||||||
procedure_archived:
|
procedure_archived:
|
||||||
with_service_and_phone_email: This procedure has been closed, it is no longer possible to submit a file. For more information, please contact the service %{service_name}, available at %{service_phone_number} or by email %{service_email}
|
with_service_and_phone_email: This procedure has been closed, it is no longer possible to submit a file. For more information, please contact the service %{service_name}, available at %{service_phone_number} or by email %{service_email}
|
||||||
with_organisation_only: This procedure has been closed, it is no longer possible to submit a file. For more information, please contact the organisation %{organisation_name}
|
with_organisation_only: This procedure has been closed, it is no longer possible to submit a file. For more information, please contact the organisation %{organisation_name}
|
||||||
|
evil_regexp: The regular expression you have entered is potentially dangerous and could lead to performance issues.
|
||||||
|
mismatch_regexp: The provided example must match the regular expression
|
||||||
|
syntax_error_regexp: The syntax of the regular expression is invalid
|
||||||
|
invalid_regexp: "%{expression_reguliere_error_message}"
|
||||||
# # procedure_not_draft: "This procedure is not a draft anymore."
|
# # procedure_not_draft: "This procedure is not a draft anymore."
|
||||||
# cadastres_empty:
|
# cadastres_empty:
|
||||||
# one: "Aucune parcelle cadastrale sur la zone sélectionnée"
|
# one: "Aucune parcelle cadastrale sur la zone sélectionnée"
|
||||||
|
|
|
@ -689,7 +689,10 @@ fr:
|
||||||
procedure_archived:
|
procedure_archived:
|
||||||
with_service_and_phone_email: Cette démarche en ligne a été close, il n’est plus possible de déposer de dossier. Pour plus d’informations veuillez contacter le service %{service_name} au %{service_phone_number} ou par email à %{service_email}
|
with_service_and_phone_email: Cette démarche en ligne a été close, il n’est plus possible de déposer de dossier. Pour plus d’informations veuillez contacter le service %{service_name} au %{service_phone_number} ou par email à %{service_email}
|
||||||
with_organisation_only: Cette démarche en ligne a été close, il n’est plus possible de déposer de dossier. Pour plus d’informations veuillez contacter le service %{organisation_name}
|
with_organisation_only: Cette démarche en ligne a été close, il n’est plus possible de déposer de dossier. Pour plus d’informations veuillez contacter le service %{organisation_name}
|
||||||
|
evil_regexp: L'expression régulière que vous avez entrée est potentiellement dangereuse et pourrait entraîner des problèmes de performance
|
||||||
|
mismatch_regexp: L'exemple doit correspondre à l'expression régulière fournie
|
||||||
|
syntax_error_regexp: La syntaxe de l'expression régulière n'est pas valide
|
||||||
|
invalid_regexp: "%{expression_reguliere_error_message}"
|
||||||
empty_repetition: '« %{value} » doit comporter au moins un champ répétable'
|
empty_repetition: '« %{value} » doit comporter au moins un champ répétable'
|
||||||
empty_drop_down: '« %{value} » doit comporter au moins un choix sélectionnable'
|
empty_drop_down: '« %{value} » doit comporter au moins un choix sélectionnable'
|
||||||
# procedure_not_draft: "Cette démarche n’est maintenant plus en brouillon."
|
# procedure_not_draft: "Cette démarche n’est maintenant plus en brouillon."
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
en:
|
||||||
|
activerecord:
|
||||||
|
errors:
|
||||||
|
models:
|
||||||
|
champs/expression_reguliere_champ:
|
||||||
|
attributes:
|
||||||
|
value:
|
||||||
|
invalid_regexp: does not match expected format
|
|
@ -0,0 +1,8 @@
|
||||||
|
fr:
|
||||||
|
activerecord:
|
||||||
|
errors:
|
||||||
|
models:
|
||||||
|
champs/expression_reguliere_champ:
|
||||||
|
attributes:
|
||||||
|
value:
|
||||||
|
invalid_regexp: ne correspond pas au format attendu
|
|
@ -55,6 +55,7 @@ en:
|
||||||
mesri: "Data from Ministère de l’Enseignement Supérieur, de la Recherche et de l’Innovation"
|
mesri: "Data from Ministère de l’Enseignement Supérieur, de la Recherche et de l’Innovation"
|
||||||
epci: "EPCI"
|
epci: "EPCI"
|
||||||
cojo: "Accreditation Paris 2024"
|
cojo: "Accreditation Paris 2024"
|
||||||
|
expression_reguliere: 'Regular expression'
|
||||||
errors:
|
errors:
|
||||||
type_de_champ:
|
type_de_champ:
|
||||||
attributes:
|
attributes:
|
||||||
|
|
|
@ -55,6 +55,7 @@ fr:
|
||||||
mesri: "Données du Ministère de l’Enseignement Supérieur, de la Recherche et de l’Innovation"
|
mesri: "Données du Ministère de l’Enseignement Supérieur, de la Recherche et de l’Innovation"
|
||||||
epci: "EPCI"
|
epci: "EPCI"
|
||||||
cojo: "Accréditation Paris 2024"
|
cojo: "Accréditation Paris 2024"
|
||||||
|
expression_reguliere: 'Expression régulière'
|
||||||
errors:
|
errors:
|
||||||
type_de_champ:
|
type_de_champ:
|
||||||
attributes:
|
attributes:
|
||||||
|
|
|
@ -243,6 +243,10 @@ FactoryBot.define do
|
||||||
type_de_champ { association :type_de_champ_cojo, procedure: dossier.procedure }
|
type_de_champ { association :type_de_champ_cojo, procedure: dossier.procedure }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :champ_expression_reguliere, class: 'Champs::ExpressionReguliereChamp' do
|
||||||
|
type_de_champ { association :type_de_champ_expression_reguliere, procedure: dossier.procedure }
|
||||||
|
end
|
||||||
|
|
||||||
factory :champ_repetition, class: 'Champs::RepetitionChamp' do
|
factory :champ_repetition, class: 'Champs::RepetitionChamp' do
|
||||||
type_de_champ { association :type_de_champ_repetition, procedure: dossier.procedure }
|
type_de_champ { association :type_de_champ_repetition, procedure: dossier.procedure }
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,9 @@ FactoryBot.define do
|
||||||
type_champ { TypeDeChamp.type_champs.fetch(:linked_drop_down_list) }
|
type_champ { TypeDeChamp.type_champs.fetch(:linked_drop_down_list) }
|
||||||
drop_down_list_value { "--primary--\nsecondary\n" }
|
drop_down_list_value { "--primary--\nsecondary\n" }
|
||||||
end
|
end
|
||||||
|
factory :type_de_champ_expression_reguliere do
|
||||||
|
type_champ { TypeDeChamp.type_champs.fetch(:expression_reguliere) }
|
||||||
|
end
|
||||||
factory :type_de_champ_pays do
|
factory :type_de_champ_pays do
|
||||||
type_champ { TypeDeChamp.type_champs.fetch(:pays) }
|
type_champ { TypeDeChamp.type_champs.fetch(:pays) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,9 +14,9 @@ describe '20220705164551_remove_unused_champs' do
|
||||||
|
|
||||||
describe 'remove_unused_champs' do
|
describe 'remove_unused_champs' do
|
||||||
it "with bad champs" do
|
it "with bad champs" do
|
||||||
expect(Champ.where(dossier: dossier).count).to eq(41)
|
expect(Champ.where(dossier: dossier).count).to eq(42)
|
||||||
run_task
|
run_task
|
||||||
expect(Champ.where(dossier: dossier).count).to eq(40)
|
expect(Champ.where(dossier: dossier).count).to eq(41)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1575,6 +1575,46 @@ describe Dossier, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#check_expressions_regulieres_champs" do
|
||||||
|
let(:procedure) { create(:procedure, types_de_champ_public: types_de_champ) }
|
||||||
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
let(:types_de_champ) { [type_de_champ] }
|
||||||
|
let(:type_de_champ) { { type: :expression_reguliere, expression_reguliere:, expression_reguliere_exemple_text: } }
|
||||||
|
|
||||||
|
context "with bad example" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "01234567" }
|
||||||
|
let(:expression_reguliere) { "[A-Z]+" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
champ = dossier.champs_public.first
|
||||||
|
champ.value = expression_reguliere_exemple_text
|
||||||
|
dossier.save
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have errors' do
|
||||||
|
expect(dossier.errors).not_to be_empty
|
||||||
|
expect(dossier.errors.full_messages.join(',')).to include("ne correspond pas au format attendu")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with good example" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "AZERTY" }
|
||||||
|
let(:expression_reguliere) { "[A-Z]+" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
champ = dossier.champs_public.first
|
||||||
|
champ.value = expression_reguliere_exemple_text
|
||||||
|
champ.save!
|
||||||
|
dossier.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not have errors' do
|
||||||
|
expect(errors).not_to be_empty
|
||||||
|
expect(errors.first).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'index_for_section_header' do
|
describe 'index_for_section_header' do
|
||||||
let(:procedure) { create(:procedure, types_de_champ_public: types_de_champ) }
|
let(:procedure) { create(:procedure, types_de_champ_public: types_de_champ) }
|
||||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
|
|
@ -908,6 +908,75 @@ describe ProcedureRevision do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "expressions_regulieres_are_valid" do
|
||||||
|
let(:procedure) do
|
||||||
|
create(:procedure).tap do |p|
|
||||||
|
p.draft_revision.add_type_de_champ(type_champ: :expression_reguliere, libelle: 'exemple', expression_reguliere:, expression_reguliere_exemple_text:)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
let(:draft_revision) { procedure.draft_revision }
|
||||||
|
|
||||||
|
subject do
|
||||||
|
draft_revision.save
|
||||||
|
draft_revision.errors
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When no regexp and no example" do
|
||||||
|
let(:expression_reguliere_exemple_text) { nil }
|
||||||
|
let(:expression_reguliere) { nil }
|
||||||
|
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When expression_reguliere but no example" do
|
||||||
|
let(:expression_reguliere) { "[A-Z]+" }
|
||||||
|
let(:expression_reguliere_exemple_text) { nil }
|
||||||
|
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When expression_reguliere and bad example" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "01234567" }
|
||||||
|
let(:expression_reguliere) { "[A-Z]+" }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When expression_reguliere and good example" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "A" }
|
||||||
|
let(:expression_reguliere) { "[A-Z]+" }
|
||||||
|
it { is_expected.to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When bad expression_reguliere" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "0123456789" }
|
||||||
|
let(:expression_reguliere) { "(" }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When repetition" do
|
||||||
|
let(:procedure) do
|
||||||
|
create(:procedure,
|
||||||
|
types_de_champ_public: [{ type: :repetition, children: [{ type: :expression_reguliere, expression_reguliere:, expression_reguliere_exemple_text: }] }])
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When bad expression_reguliere" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "0123456789" }
|
||||||
|
let(:expression_reguliere) { "(" }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_empty }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When expression_reguliere and bad example" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "01234567" }
|
||||||
|
let(:expression_reguliere) { "[A-Z]+" }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_empty }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "#dependent_conditions" do
|
describe "#dependent_conditions" do
|
||||||
include Logic
|
include Logic
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,31 @@ describe TypeDeChamp do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "validate_regexp" do
|
||||||
|
let(:tdc) { create(:type_de_champ_expression_reguliere, expression_reguliere:, expression_reguliere_exemple_text:) }
|
||||||
|
subject { tdc.invalid_regexp? }
|
||||||
|
|
||||||
|
context "expression_reguliere and bad example" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "01234567" }
|
||||||
|
let(:expression_reguliere) { "[A-Z]+" }
|
||||||
|
|
||||||
|
it "should add error message" do
|
||||||
|
expect(subject).to be_truthy
|
||||||
|
expect(tdc.errors.messages[:expression_reguliere_exemple_text]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Bad expression_reguliere" do
|
||||||
|
let(:expression_reguliere_exemple_text) { "0123456789" }
|
||||||
|
let(:expression_reguliere) { "(" }
|
||||||
|
|
||||||
|
it "should add error message" do
|
||||||
|
expect(subject).to be_truthy
|
||||||
|
expect(tdc.errors.messages[:expression_reguliere]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#drop_down_list_options' do
|
describe '#drop_down_list_options' do
|
||||||
let(:value) do
|
let(:value) do
|
||||||
<<~EOS
|
<<~EOS
|
||||||
|
|
|
@ -89,7 +89,8 @@ describe ProcedureExportService do
|
||||||
"epci",
|
"epci",
|
||||||
"epci (Code)",
|
"epci (Code)",
|
||||||
"epci (Département)",
|
"epci (Département)",
|
||||||
"cojo"
|
"cojo",
|
||||||
|
"expression_reguliere"
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -200,7 +201,8 @@ describe ProcedureExportService do
|
||||||
"epci",
|
"epci",
|
||||||
"epci (Code)",
|
"epci (Code)",
|
||||||
"epci (Département)",
|
"epci (Département)",
|
||||||
"cojo"
|
"cojo",
|
||||||
|
"expression_reguliere"
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -294,7 +296,8 @@ describe ProcedureExportService do
|
||||||
"epci",
|
"epci",
|
||||||
"epci (Code)",
|
"epci (Code)",
|
||||||
"epci (Département)",
|
"epci (Département)",
|
||||||
"cojo"
|
"cojo",
|
||||||
|
"expression_reguliere"
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue