diff --git a/Gemfile b/Gemfile
index 044088d47..d629ba148 100644
--- a/Gemfile
+++ b/Gemfile
@@ -4,6 +4,7 @@ gem 'aasm'
gem 'actiontext', git: 'https://github.com/kobaltz/actiontext.git', branch: 'archive', require: 'action_text' # Port of ActionText to Rails 5
gem 'active_link_to' # Automatically set a class on active links
gem 'active_model_serializers'
+gem 'active_storage_validations'
gem 'activestorage-openstack'
gem 'administrate'
gem 'after_party'
diff --git a/Gemfile.lock b/Gemfile.lock
index 1c4931188..5d391447f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -20,25 +20,25 @@ GEM
specs:
aasm (5.0.1)
concurrent-ruby (~> 1.0)
- actioncable (5.2.3)
- actionpack (= 5.2.3)
+ actioncable (5.2.4.1)
+ actionpack (= 5.2.4.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailer (5.2.3)
- actionpack (= 5.2.3)
- actionview (= 5.2.3)
- activejob (= 5.2.3)
+ actionmailer (5.2.4.1)
+ actionpack (= 5.2.4.1)
+ actionview (= 5.2.4.1)
+ activejob (= 5.2.4.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (5.2.3)
- actionview (= 5.2.3)
- activesupport (= 5.2.3)
- rack (~> 2.0)
+ actionpack (5.2.4.1)
+ actionview (= 5.2.4.1)
+ activesupport (= 5.2.4.1)
+ rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.2.3)
- activesupport (= 5.2.3)
+ actionview (5.2.4.1)
+ activesupport (= 5.2.4.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@@ -46,30 +46,32 @@ GEM
active_link_to (1.0.5)
actionpack
addressable
- active_model_serializers (0.10.8)
- actionpack (>= 4.1, < 6)
- activemodel (>= 4.1, < 6)
+ active_model_serializers (0.10.10)
+ actionpack (>= 4.1, < 6.1)
+ activemodel (>= 4.1, < 6.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
+ active_storage_validations (0.8.7)
+ rails (>= 5.2.0)
activejob (5.2.3)
activesupport (= 5.2.3)
globalid (>= 0.3.6)
- activemodel (5.2.3)
- activesupport (= 5.2.3)
- activerecord (5.2.3)
- activemodel (= 5.2.3)
- activesupport (= 5.2.3)
+ activemodel (5.2.4.1)
+ activesupport (= 5.2.4.1)
+ activerecord (5.2.4.1)
+ activemodel (= 5.2.4.1)
+ activesupport (= 5.2.4.1)
arel (>= 9.0)
- activestorage (5.2.3)
- actionpack (= 5.2.3)
- activerecord (= 5.2.3)
+ activestorage (5.2.4.1)
+ actionpack (= 5.2.4.1)
+ activerecord (= 5.2.4.1)
marcel (~> 0.3.1)
- activestorage-openstack (1.2.0)
+ activestorage-openstack (1.3.0)
fog-openstack (~> 1.0)
marcel
mime-types
rails (>= 5.2.2)
- activesupport (5.2.3)
+ activesupport (5.2.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@@ -151,7 +153,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
- concurrent-ruby (1.1.5)
+ concurrent-ruby (1.1.6)
connection_pool (2.2.2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
@@ -203,25 +205,25 @@ GEM
ethon (0.11.0)
ffi (>= 1.3.0)
eventmachine (1.2.7)
- excon (0.71.0)
+ excon (0.72.0)
execjs (2.7.0)
factory_bot (4.11.1)
activesupport (>= 3.0.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
ffi (1.9.25)
- 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)
+ flipper (0.17.2)
+ flipper-active_record (0.17.2)
+ activerecord (>= 4.2, < 7)
+ flipper (~> 0.17.2)
+ flipper-ui (0.17.2)
+ erubi (>= 1.0.0, < 2.0.0)
+ flipper (~> 0.17.2)
rack (>= 1.4, < 3)
rack-protection (>= 1.5.3, < 2.1.0)
- fog-core (2.1.2)
+ fog-core (2.2.0)
builder
- excon (~> 0.58)
+ excon (~> 0.71)
formatador (~> 0.2)
mime-types
fog-json (1.2.0)
@@ -237,25 +239,26 @@ GEM
geocoder (1.6.0)
globalid (0.4.2)
activesupport (>= 4.2.0)
- gon (6.2.1)
- actionpack (>= 3.0)
+ gon (6.3.2)
+ actionpack (>= 3.0.20)
+ i18n (>= 0.7)
multi_json
request_store (>= 1.0)
graphiql-rails (1.7.0)
railties
sprockets-rails
- graphql (1.9.16)
- graphql-batch (0.4.1)
+ graphql (1.10.3)
+ graphql-batch (0.4.2)
graphql (>= 1.3, < 2)
promise.rb (~> 0.7.2)
- graphql-rails_logger (1.2.0)
- actionpack (~> 5.0)
- activesupport (~> 5.0)
- railties (~> 5.0)
+ graphql-rails_logger (1.2.2)
+ actionpack (> 5.0)
+ activesupport (> 5.0)
+ railties (> 5.0)
rouge (~> 3.0)
- graphql-schema_comparator (0.6.1)
+ graphql-schema_comparator (1.0.0)
bundler (>= 1.14)
- graphql (~> 1.6)
+ graphql (~> 1.10)
thor (>= 0.19, < 2.0)
groupdate (4.1.1)
activesupport (>= 4.2)
@@ -320,25 +323,25 @@ GEM
activesupport (>= 4.2)
aes_key_wrap
bindata
- jsonapi-renderer (0.2.0)
+ jsonapi-renderer (0.2.2)
jwt (2.1.0)
- kaminari (1.1.1)
+ kaminari (1.2.0)
activesupport (>= 4.1.0)
- kaminari-actionview (= 1.1.1)
- kaminari-activerecord (= 1.1.1)
- kaminari-core (= 1.1.1)
- kaminari-actionview (1.1.1)
+ kaminari-actionview (= 1.2.0)
+ kaminari-activerecord (= 1.2.0)
+ kaminari-core (= 1.2.0)
+ kaminari-actionview (1.2.0)
actionview
- kaminari-core (= 1.1.1)
- kaminari-activerecord (1.1.1)
+ kaminari-core (= 1.2.0)
+ kaminari-activerecord (1.2.0)
activerecord
- kaminari-core (= 1.1.1)
- kaminari-core (1.1.1)
+ kaminari-core (= 1.2.0)
+ kaminari-core (1.2.0)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.7.0)
launchy (~> 2.2)
- letter_opener_web (1.3.4)
+ letter_opener_web (1.4.0)
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
@@ -365,10 +368,10 @@ GEM
marcel (0.3.3)
mimemagic (~> 0.3.2)
method_source (0.9.2)
- mime-types (3.3)
+ mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.1009)
- mimemagic (0.3.3)
+ mimemagic (0.3.4)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.0)
@@ -448,11 +451,11 @@ GEM
pry-byebug (3.6.0)
byebug (~> 10.0)
pry (~> 0.10)
- public_suffix (4.0.1)
+ public_suffix (4.0.3)
puma (3.12.2)
pundit (2.0.1)
activesupport (>= 3.0.0)
- rack (2.1.2)
+ rack (2.2.2)
rack-attack (6.0.0)
rack (>= 1.0, < 3)
rack-mini-profiler (1.0.1)
@@ -469,18 +472,18 @@ GEM
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
- rails (5.2.3)
- actioncable (= 5.2.3)
- actionmailer (= 5.2.3)
- actionpack (= 5.2.3)
- actionview (= 5.2.3)
- activejob (= 5.2.3)
- activemodel (= 5.2.3)
- activerecord (= 5.2.3)
- activestorage (= 5.2.3)
- activesupport (= 5.2.3)
+ rails (5.2.4.1)
+ actioncable (= 5.2.4.1)
+ actionmailer (= 5.2.4.1)
+ actionpack (= 5.2.4.1)
+ actionview (= 5.2.4.1)
+ activejob (= 5.2.4.1)
+ activemodel (= 5.2.4.1)
+ activerecord (= 5.2.4.1)
+ activestorage (= 5.2.4.1)
+ activesupport (= 5.2.4.1)
bundler (>= 1.3.0)
- railties (= 5.2.3)
+ railties (= 5.2.4.1)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4)
actionpack (>= 5.0.1.x)
@@ -494,9 +497,9 @@ GEM
rails-i18n (5.1.2)
i18n (>= 0.7, < 2)
railties (>= 5.0, < 6)
- railties (5.2.3)
- actionpack (= 5.2.3)
- activesupport (= 5.2.3)
+ railties (5.2.4.1)
+ actionpack (= 5.2.4.1)
+ activesupport (= 5.2.4.1)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
@@ -513,7 +516,7 @@ GEM
railties (>= 3.2)
tilt
regexp_parser (1.6.0)
- request_store (1.4.1)
+ request_store (1.5.0)
rack (>= 1.4)
responders (3.0.0)
actionpack (>= 5.0)
@@ -529,7 +532,7 @@ GEM
activesupport (>= 3.0)
builder (>= 3.0)
rubyzip (>= 1.0)
- rouge (3.9.0)
+ rouge (3.16.0)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
@@ -713,6 +716,7 @@ DEPENDENCIES
actiontext!
active_link_to
active_model_serializers
+ active_storage_validations
activestorage-openstack
administrate
after_party
diff --git a/app/controllers/admin/attestation_templates_controller.rb b/app/controllers/admin/attestation_templates_controller.rb
index f50cba788..d1eff3ba5 100644
--- a/app/controllers/admin/attestation_templates_controller.rb
+++ b/app/controllers/admin/attestation_templates_controller.rb
@@ -40,9 +40,16 @@ class Admin::AttestationTemplatesController < AdminController
end
def preview
- @attestation = (@procedure.attestation_template || AttestationTemplate.new).render_attributes_for(activated_attestation_params)
+ attestation = (@procedure.attestation_template || AttestationTemplate.new)
+ attestation.assign_attributes(activated_attestation_params)
- render 'admin/attestation_templates/show', formats: [:pdf]
+ if attestation.valid?
+ @attestation = attestation.render_attributes_for(activated_attestation_params)
+
+ render 'admin/attestation_templates/show', formats: [:pdf]
+ else
+ flash.alert = attestation_template.errors.full_messages.join('
')
+ end
end
def delete_logo
diff --git a/app/mailers/dossier_mailer.rb b/app/mailers/dossier_mailer.rb
index ce7d05964..c2294ee67 100644
--- a/app/mailers/dossier_mailer.rb
+++ b/app/mailers/dossier_mailer.rb
@@ -90,4 +90,12 @@ class DossierMailer < ApplicationMailer
mail(to: user.email, subject: @subject)
end
+
+ def notify_groupe_instructeur_changed(instructeur, dossier)
+ @subject = "Un dossier a changé de groupe instructeur"
+ @dossier_id = dossier.id
+ @demarche = dossier.procedure.libelle
+
+ mail(from: NO_REPLY_EMAIL, to: instructeur.email, subject: @subject)
+ end
end
diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb
index d61ebc1fb..69056789b 100644
--- a/app/models/attestation_template.rb
+++ b/app/models/attestation_template.rb
@@ -11,6 +11,9 @@ class AttestationTemplate < ApplicationRecord
validates :footer, length: { maximum: 190 }
+ validates :logo, content_type: [:png, :jpg, :jpeg]
+ validates :signature, content_type: [:png, :jpg, :jpeg]
+
DOSSIER_STATE = Dossier.states.fetch(:accepte)
def attestation_for(dossier)
diff --git a/app/models/dossier.rb b/app/models/dossier.rb
index a03338b4e..9c25ec40f 100644
--- a/app/models/dossier.rb
+++ b/app/models/dossier.rb
@@ -213,6 +213,7 @@ class Dossier < ApplicationRecord
before_save :build_default_champs, if: Proc.new { groupe_instructeur_id_was.nil? }
before_save :update_search_terms
+ after_save :unfollow_stale_instructeurs
after_save :send_dossier_received
after_save :send_web_hook
after_create :send_draft_notification_email
@@ -645,6 +646,18 @@ class Dossier < ApplicationRecord
end
end
+ def unfollow_stale_instructeurs
+ if saved_change_to_groupe_instructeur_id? && saved_change_to_groupe_instructeur_id[0].present?
+ followers_instructeurs.each do |instructeur|
+ if instructeur.groupe_instructeurs.exclude?(groupe_instructeur)
+ instructeur.unfollow(self)
+ DossierMailer.notify_groupe_instructeur_changed(instructeur, self).deliver_later
+ end
+ end
+ log_dossier_operation(user, :changer_groupe_instructeur, self)
+ end
+ end
+
def self.send_brouillon_expiration_notices
brouillons = Dossier
.brouillon_close_to_expiration
diff --git a/app/models/dossier_operation_log.rb b/app/models/dossier_operation_log.rb
index fbb3df559..bfad08b97 100644
--- a/app/models/dossier_operation_log.rb
+++ b/app/models/dossier_operation_log.rb
@@ -1,5 +1,6 @@
class DossierOperationLog < ApplicationRecord
enum operation: {
+ changer_groupe_instructeur: 'changer_groupe_instructeur',
passer_en_instruction: 'passer_en_instruction',
repasser_en_construction: 'repasser_en_construction',
repasser_en_instruction: 'repasser_en_instruction',
diff --git a/app/models/procedure_presentation.rb b/app/models/procedure_presentation.rb
index 75872f175..944b2a07b 100644
--- a/app/models/procedure_presentation.rb
+++ b/app/models/procedure_presentation.rb
@@ -75,7 +75,6 @@ class ProcedurePresentation < ApplicationRecord
end
def sorted_ids(dossiers, instructeur)
- dossiers.each { |dossier| assert_matching_procedure(dossier) }
table, column, order = sort.values_at('table', 'column', 'order')
case table
@@ -110,7 +109,6 @@ class ProcedurePresentation < ApplicationRecord
end
def filtered_ids(dossiers, statut)
- dossiers.each { |dossier| assert_matching_procedure(dossier) }
filters[statut].group_by { |filter| filter.values_at('table', 'column') } .map do |(table, column), filters|
values = filters.pluck('value')
case table
diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml
index 4239d89e7..548afc3aa 100644
--- a/app/views/admin/attestation_templates/edit.html.haml
+++ b/app/views/admin/attestation_templates/edit.html.haml
@@ -23,7 +23,7 @@
= f.label :logo, "Logo de l'attestation"
- if @attestation_template.logo.attached?
= link_to 'Supprimer le logo', admin_procedure_attestation_template_logo_path(@procedure), method: :delete
- = f.file_field :logo, accept: 'image/png, image/jpg, image/jpeg'
+ = f.file_field :logo, accept: 'image/png,image/jpg,image/jpeg'
%p.help-block
Fichier accepté : JPG / JPEG / PNG
%br
@@ -87,3 +87,4 @@
- else
- save_data = @procedure.locked? ? { toggle: :tooltip, confirm: "Attention: les modifications n'affecteront pas les attestations déjà délivrées." } : nil
%button.btn.btn-success{ data: save_data } Enregistrer
+
diff --git a/app/views/dossier_mailer/notify_groupe_instructeur_changed.html.haml b/app/views/dossier_mailer/notify_groupe_instructeur_changed.html.haml
new file mode 100644
index 000000000..753568e40
--- /dev/null
+++ b/app/views/dossier_mailer/notify_groupe_instructeur_changed.html.haml
@@ -0,0 +1,9 @@
+- content_for(:title, "#{@subject}")
+
+%p
+ = "Vous suiviez jusqu'à maintenant le dossier n°#{@dossier_id} de la démarche #{@demarche}."
+ L'usager a modifié le groupe de routage. Son dossier appartient maintenant à un groupe instructeur dont vous ne faites pas partie.
+%p
+ Suite à cette modification, vous ne suivez plus ce dossier.
+
+= render partial: "layouts/mailers/signature"
diff --git a/spec/controllers/admin/attestation_templates_controller_spec.rb b/spec/controllers/admin/attestation_templates_controller_spec.rb
index 11c9c094c..0cfb06397 100644
--- a/spec/controllers/admin/attestation_templates_controller_spec.rb
+++ b/spec/controllers/admin/attestation_templates_controller_spec.rb
@@ -26,11 +26,12 @@ describe Admin::AttestationTemplatesController, type: :controller do
procedure_id: procedure.id,
attestation_template: upload_params
}
+ procedure.reload
end
context 'with an interlaced png' do
let(:upload_params) { { logo: interlaced_logo } }
- it { expect(assigns(:attestation)[:logo].read).to eq(uninterlaced_logo.read) }
+ it { expect(procedure.attestation_template.logo.download).to eq(uninterlaced_logo.read) }
end
context 'if an attestation template does not exist on the procedure' do
diff --git a/spec/fixtures/files/beta-gouv.gif b/spec/fixtures/files/beta-gouv.gif
new file mode 100644
index 000000000..72b151b65
Binary files /dev/null and b/spec/fixtures/files/beta-gouv.gif differ
diff --git a/spec/fixtures/files/french-flag.gif b/spec/fixtures/files/french-flag.gif
new file mode 100644
index 000000000..475ccb23e
Binary files /dev/null and b/spec/fixtures/files/french-flag.gif differ
diff --git a/spec/mailers/dossier_mailer_spec.rb b/spec/mailers/dossier_mailer_spec.rb
index b7b50111c..c18b2ebb4 100644
--- a/spec/mailers/dossier_mailer_spec.rb
+++ b/spec/mailers/dossier_mailer_spec.rb
@@ -154,4 +154,16 @@ RSpec.describe DossierMailer, type: :mailer do
it { expect(subject.body).to include("PDF") }
it { expect(subject.body).to include("Vous avez un mois pour traiter le dossier.") }
end
+
+ describe '.notify_groupe_instructeur_changed_to_instructeur' do
+ let(:dossier) { create(:dossier) }
+ let(:instructeur) { create(:instructeur) }
+
+ subject { described_class.notify_groupe_instructeur_changed(instructeur, dossier) }
+
+ it { expect(subject.subject).to eq("Un dossier a changé de groupe instructeur") }
+ it { expect(subject.body).to include("n°#{dossier.id}") }
+ it { expect(subject.body).to include(dossier.procedure.libelle) }
+ it { expect(subject.body).to include("Suite à cette modification, vous ne suivez plus ce dossier.") }
+ end
end
diff --git a/spec/models/attestation_template_spec.rb b/spec/models/attestation_template_spec.rb
index 8cd9c6990..4b03779f3 100644
--- a/spec/models/attestation_template_spec.rb
+++ b/spec/models/attestation_template_spec.rb
@@ -87,6 +87,25 @@ describe AttestationTemplate, type: :model do
end
end
+ describe 'invalidate attestation if images attachments are not valid' do
+ before do
+ @logo = Rack::Test::UploadedFile.new('spec/fixtures/files/french-flag.gif', 'image/gif')
+ @signature = Rack::Test::UploadedFile.new('spec/fixtures/files/beta-gouv.gif', 'image/gif')
+ end
+
+ after do
+ subject.destroy
+ end
+
+ let(:attestation_template) { AttestationTemplate.create(attributes) }
+ subject { attestation_template.dup }
+
+ context 'with an attestation which has gif files' do
+ let(:attributes) { { title: 't', body: 'b', footer: 'f', activated: true, logo: @logo, signature: @signature } }
+ it { is_expected.not_to be_valid }
+ end
+ end
+
describe 'attestation_for' do
let(:procedure) do
create(:procedure,
diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb
index 5136380e3..dbdc9ac69 100644
--- a/spec/models/dossier_spec.rb
+++ b/spec/models/dossier_spec.rb
@@ -398,6 +398,35 @@ describe Dossier do
it { is_expected.to match([dossier3, dossier4, dossier2]) }
end
+ describe "#unfollow_stale_instructeurs" do
+ let(:procedure) { create(:procedure) }
+ let(:instructeur) { create(:instructeur) }
+ let(:new_groupe_instructeur) { create(:groupe_instructeur) }
+ let(:instructeur2) { create(:instructeur, groupe_instructeurs: [procedure.defaut_groupe_instructeur, new_groupe_instructeur]) }
+ let(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction)) }
+ let(:last_operation) { DossierOperationLog.last }
+
+ before do
+ allow(DossierMailer).to receive(:notify_groupe_instructeur_changed).and_return(double(deliver_later: nil))
+ end
+
+ it "unfollows stale instructeurs when groupe instructeur change" do
+ instructeur.follow(dossier)
+ instructeur2.follow(dossier)
+ dossier.reload.update(groupe_instructeur: new_groupe_instructeur)
+
+ expect(dossier.reload.followers_instructeurs).not_to include(instructeur)
+ expect(dossier.reload.followers_instructeurs).to include(instructeur2)
+
+ expect(DossierMailer).to have_received(:notify_groupe_instructeur_changed).with(instructeur, dossier)
+ expect(DossierMailer).not_to have_received(:notify_groupe_instructeur_changed).with(instructeur2, dossier)
+
+ expect(last_operation.operation).to eq("changer_groupe_instructeur")
+ expect(last_operation.dossier).to eq(dossier)
+ expect(last_operation.automatic_operation?).to be_falsey
+ end
+ end
+
describe "#send_dossier_received" do
let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction)) }