diff --git a/Gemfile b/Gemfile index 17d37eee9..8849da0e9 100644 --- a/Gemfile +++ b/Gemfile @@ -27,6 +27,9 @@ gem 'devise' # Gestion des comptes utilisateurs gem 'devise-async' gem 'dotenv-rails', require: 'dotenv/rails-now' # dotenv should always be loaded before rails gem 'flipflop' +gem 'flipper' +gem 'flipper-active_record' +gem 'flipper-ui' gem 'fog-openstack' gem 'font-awesome-rails' gem 'gon' diff --git a/Gemfile.lock b/Gemfile.lock index 7c9448de9..9bf075646 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -222,6 +222,15 @@ GEM ffi (1.9.25) flipflop (2.4.0) activesupport (>= 4.0) + flipper (0.16.2) + flipper-active_record (0.16.2) + activerecord (>= 3.2, < 6) + flipper (~> 0.16.2) + flipper-ui (0.16.2) + erubis (~> 2.7.0) + flipper (~> 0.16.2) + rack (>= 1.4, < 3) + rack-protection (>= 1.5.3, < 2.1.0) fog-core (2.1.2) builder excon (~> 0.58) @@ -725,6 +734,9 @@ DEPENDENCIES dotenv-rails factory_bot flipflop + flipper + flipper-active_record + flipper-ui fog-openstack font-awesome-rails gon diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8648c7b08..e70dadfda 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,7 +12,7 @@ class ApplicationController < ActionController::Base before_action :set_raven_context before_action :redirect_if_untrusted before_action :authorize_request_for_profiler - before_action :reject, if: -> { Flipflop.maintenance_mode? } + before_action :reject, if: -> { feature_enabled?(:maintenance_mode) } before_action :staging_authenticate before_action :set_active_storage_host @@ -28,7 +28,7 @@ class ApplicationController < ActionController::Base end def authorize_request_for_profiler - if Flipflop.mini_profiler_enabled? + if feature_enabled?(:mini_profiler) Rack::MiniProfiler.authorize_request end end @@ -77,6 +77,10 @@ class ApplicationController < ActionController::Base protected + def feature_enabled?(feature_name) + Flipper.enabled?(feature_name, current_user) + end + def authenticate_logged_user! if instructeur_signed_in? authenticate_instructeur! @@ -190,7 +194,7 @@ class ApplicationController < ActionController::Base def redirect_if_untrusted if instructeur_signed_in? && sensitive_path && - !Flipflop.bypass_email_login_token? && + !feature_enabled?(:instructeur_bypass_email_login_token) && !IPService.ip_trusted?(request.headers['X-Forwarded-For']) && !trusted_device? diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index dca392aa1..c213a0255 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -191,7 +191,7 @@ module Instructeurs end def telecharger_pjs - return head(:forbidden) if !Flipflop.download_as_zip_enabled? || !dossier.attachments_downloadable? + return head(:forbidden) if !feature_enabled?(:instructeur_download_as_zip) || !dossier.attachments_downloadable? files = ActiveStorage::DownloadableFile.create_list_from_dossier(dossier) diff --git a/app/controllers/manager/administrateurs_controller.rb b/app/controllers/manager/administrateurs_controller.rb index 74bd191ea..18e30f9a1 100644 --- a/app/controllers/manager/administrateurs_controller.rb +++ b/app/controllers/manager/administrateurs_controller.rb @@ -19,20 +19,6 @@ module Manager redirect_to manager_administrateur_path(params[:id]) end - def enable_feature - administrateur = Administrateur.find(params[:id]) - - params[:features].each do |key, enable| - if enable - administrateur.enable_feature(key.to_sym) - else - administrateur.disable_feature(key.to_sym) - end - end - - head :ok - end - def delete administrateur = Administrateur.find(params[:id]) diff --git a/app/controllers/manager/instructeurs_controller.rb b/app/controllers/manager/instructeurs_controller.rb index 89ed9bdc3..82b1e11eb 100644 --- a/app/controllers/manager/instructeurs_controller.rb +++ b/app/controllers/manager/instructeurs_controller.rb @@ -6,19 +6,5 @@ module Manager flash[:notice] = "Instructeur réinvité." redirect_to manager_instructeur_path(instructeur) end - - def enable_feature - instructeur = Instructeur.find(params[:id]) - - params[:features].each do |key, enable| - if enable - instructeur.enable_feature(key.to_sym) - else - instructeur.disable_feature(key.to_sym) - end - end - - head :ok - end end end diff --git a/app/controllers/manager/users_controller.rb b/app/controllers/manager/users_controller.rb index d655c552b..e80d22404 100644 --- a/app/controllers/manager/users_controller.rb +++ b/app/controllers/manager/users_controller.rb @@ -6,5 +6,19 @@ module Manager flash[:notice] = "L'email d'activation de votre compte a été renvoyé." redirect_to manager_user_path(user) end + + def enable_feature + user = User.find(params[:id]) + + params[:features].each do |key, enable| + if enable + Flipper.enable_actor(key.to_sym, user) + else + Flipper.disable_actor(key.to_sym, user) + end + end + + head :ok + end end end diff --git a/app/helpers/flipper_helper.rb b/app/helpers/flipper_helper.rb new file mode 100644 index 000000000..aa81299c5 --- /dev/null +++ b/app/helpers/flipper_helper.rb @@ -0,0 +1,5 @@ +module FlipperHelper + def feature_enabled?(feature_name) + Flipper.enabled?(feature_name, current_user) + end +end diff --git a/app/helpers/procedure_helper.rb b/app/helpers/procedure_helper.rb index 76ddd6760..52a834f16 100644 --- a/app/helpers/procedure_helper.rb +++ b/app/helpers/procedure_helper.rb @@ -50,7 +50,7 @@ module ProcedureHelper private TOGGLES = { - TypeDeChamp.type_champs.fetch(:integer_number) => :champ_integer_number? + TypeDeChamp.type_champs.fetch(:integer_number) => :administrateur_champ_integer_number } def types_de_champ_types @@ -58,7 +58,7 @@ module ProcedureHelper types_de_champ_types.select! do |tdc| toggle = TOGGLES[tdc.last] - toggle.blank? || Flipflop.send(toggle) + toggle.blank? || feature_enabled?(toggle) end types_de_champ_types diff --git a/app/lib/api_entreprise/api.rb b/app/lib/api_entreprise/api.rb index f90abd6e9..818d1e833 100644 --- a/app/lib/api_entreprise/api.rb +++ b/app/lib/api_entreprise/api.rb @@ -44,7 +44,7 @@ class ApiEntreprise::API def self.url(resource_name, siret_or_siren) base_url = [API_ENTREPRISE_URL, resource_name, siret_or_siren].join("/") - if Flipflop.insee_api_v3? + if Flipper.enabled?(:insee_api_v3) base_url += "?with_insee_v3=true" end diff --git a/app/lib/flipflop/strategies/user_preference_strategy.rb b/app/lib/flipflop/strategies/user_preference_strategy.rb deleted file mode 100644 index 1aa2c1165..000000000 --- a/app/lib/flipflop/strategies/user_preference_strategy.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Flipflop::Strategies - class UserPreferenceStrategy < AbstractStrategy - def self.default_description - "Allows configuration of features per user." - end - - def switchable? - false - end - - def enabled?(feature) - find_current_administrateur&.feature_enabled?(feature) || - find_current_instructeur&.feature_enabled?(feature) - end - - private - - def find_current_administrateur - administrateur_id = Current.administrateur&.id - if administrateur_id - Administrateur.find_by(id: administrateur_id) - end - end - - def find_current_instructeur - instructeur_id = Current.instructeur&.id - if instructeur_id - Instructeur.find_by(id: instructeur_id) - end - end - end -end diff --git a/app/models/administrateur.rb b/app/models/administrateur.rb index 89fbbecc0..738281e29 100644 --- a/app/models/administrateur.rb +++ b/app/models/administrateur.rb @@ -72,23 +72,6 @@ class Administrateur < ApplicationRecord administrateur end - def feature_enabled?(feature) - Flipflop.feature_set.feature(feature) - features[feature.to_s] - end - - def disable_feature(feature) - Flipflop.feature_set.feature(feature) - features.delete(feature.to_s) - save - end - - def enable_feature(feature) - Flipflop.feature_set.feature(feature) - features[feature.to_s] = true - save - end - def owns?(procedure) procedure.administrateurs.include?(self) end diff --git a/app/models/dossier_operation_log.rb b/app/models/dossier_operation_log.rb index a54f0718e..fbb3df559 100644 --- a/app/models/dossier_operation_log.rb +++ b/app/models/dossier_operation_log.rb @@ -66,7 +66,7 @@ class DossierOperationLog < ApplicationRecord def self.serialize_subject(subject) if subject.nil? nil - elsif !Flipflop.operation_log_serialize_subject? + elsif !Flipper.enabled?(:operation_log_serialize_subject) { id: subject.id } else case subject diff --git a/app/models/instructeur.rb b/app/models/instructeur.rb index bf849e4ba..08087b61e 100644 --- a/app/models/instructeur.rb +++ b/app/models/instructeur.rb @@ -180,23 +180,6 @@ class Instructeur < ApplicationRecord Follow.where(instructeur: self, dossier: dossier).update_all(attributes) end - def feature_enabled?(feature) - Flipflop.feature_set.feature(feature) - features[feature.to_s] - end - - def disable_feature(feature) - Flipflop.feature_set.feature(feature) - features.delete(feature.to_s) - save - end - - def enable_feature(feature) - Flipflop.feature_set.feature(feature) - features[feature.to_s] = true - save - end - def young_login_token? trusted_device_token = trusted_device_tokens.order(created_at: :desc).first trusted_device_token&.token_young? diff --git a/app/models/user.rb b/app/models/user.rb index 8a6fdd86e..1f5217767 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -87,6 +87,10 @@ class User < ApplicationRecord user end + def flipper_id + "User:#{id}" + end + private def link_invites! diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index bd2dd2af3..ea7475d17 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -1,8 +1,12 @@ class PiecesJustificativesService def self.liste_pieces_justificatives(dossier) - dossier.champs - .select { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative) } - .filter { |pj| pj.piece_justificative_file.attached? } + champs_blocs_repetables = dossier.champs + .select { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:repetition) } + .flat_map(&:champs) + + champs_pieces_justificatives_with_attachments( + champs_blocs_repetables + dossier.champs + ) end def self.pieces_justificatives_total_size(dossier) @@ -37,4 +41,12 @@ class PiecesJustificativesService } end end + + private + + def self.champs_pieces_justificatives_with_attachments(champs) + champs + .select { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative) } + .filter { |pj| pj.piece_justificative_file.attached? } + end end diff --git a/app/views/admin/procedures/_informations.html.haml b/app/views/admin/procedures/_informations.html.haml index 6d783feae..bcd59d4a8 100644 --- a/app/views/admin/procedures/_informations.html.haml +++ b/app/views/admin/procedures/_informations.html.haml @@ -125,7 +125,7 @@ .col-md-6 %h4 Options avancées - - if Flipflop.web_hook? + - if feature_enabled?(:administrateur_web_hook) %label{ for: :web_hook_url } Lien de rappel HTTP (webhook) = f.text_field :web_hook_url, class: 'form-control', placeholder: 'https://callback.exemple.fr/' %p.help-block diff --git a/app/views/fields/features_field/_show.html.haml b/app/views/fields/features_field/_show.html.haml index 6db607e55..221825c3e 100644 --- a/app/views/fields/features_field/_show.html.haml +++ b/app/views/fields/features_field/_show.html.haml @@ -1,14 +1,11 @@ :ruby - url = if field.resource.class.name == 'Instructeur' - enable_feature_manager_instructeur_path(field.resource.id) - else - enable_feature_manager_administrateur_path(field.resource.id) - end + group = field.resource.class.name.downcase + user = field.resource.user + url = enable_feature_manager_user_path(user) %table#features - - admin_features = Flipflop.feature_set.features.reject{ |f| f.group.try(:key) == :production } - - admin_features.each do |feature| + - Flipper.features.select { |feature| feature.key.start_with?("#{group}_") }.each do |feature| %tr - %td= feature.title + %td= feature %td - = check_box_tag "enable-feature", "enable", field.data[feature.name], data: { url: url, key: feature.key } + = check_box_tag "enable-feature", "enable", feature.enabled?(user), data: { url: url, key: feature.key } diff --git a/app/views/instructeurs/dossiers/_header_actions.html.haml b/app/views/instructeurs/dossiers/_header_actions.html.haml index 3cbaa91f4..27c428f59 100644 --- a/app/views/instructeurs/dossiers/_header_actions.html.haml +++ b/app/views/instructeurs/dossiers/_header_actions.html.haml @@ -7,7 +7,7 @@ %li = link_to "Uniquement cet onglet", "#", onclick: "window.print()", class: "menu-item menu-link" -- if Flipflop.download_as_zip_enabled? && !PiecesJustificativesService.liste_pieces_justificatives(dossier).empty? +- if feature_enabled?(:instructeur_download_as_zip) && !PiecesJustificativesService.liste_pieces_justificatives(dossier).empty? %span.dropdown.print-menu-opener %button.button.dropdown-button.icon-only %span.icon.attachment diff --git a/app/views/layouts/_pre_maintenance.html.haml b/app/views/layouts/_pre_maintenance.html.haml index 636556d16..c14ca1e61 100644 --- a/app/views/layouts/_pre_maintenance.html.haml +++ b/app/views/layouts/_pre_maintenance.html.haml @@ -1,4 +1,4 @@ -- if Flipflop.pre_maintenance_mode? +- if feature_enabled?(:pre_maintenance_mode) .maintenance %span Une opération de maintenance est prévue sur demarches-simplifiees.fr à 23 h 00. La plateforme sera inaccessible pendant une vingtaine de minutes. diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 456ebfb5c..7178d975f 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -21,7 +21,7 @@ = Gon::Base.render_data(camel_case: true, init: true, nonce: request.content_security_policy_nonce) - - if Flipflop.xray_enabled? + - if feature_enabled?(:xray) = stylesheet_link_tag :xray %body{ id: content_for(:page_id), class: browser.platform.ios? ? 'ios' : nil } @@ -39,7 +39,7 @@ - if content_for?(:footer) = content_for(:footer) - - if Flipflop.xray_enabled? + - if feature_enabled?(:xray) = javascript_include_tag :xray = yield :charts_js diff --git a/app/views/manager/application/_navigation.html.erb b/app/views/manager/application/_navigation.html.erb index 216e3fe4c..891658871 100644 --- a/app/views/manager/application/_navigation.html.erb +++ b/app/views/manager/application/_navigation.html.erb @@ -24,5 +24,5 @@ as defined by the routes in the `admin/` namespace