From 4a614f6f4f6c14972182b761a94b4dc68a742a8c Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 4 Dec 2019 15:35:15 +0100 Subject: [PATCH 1/4] Add unpublished_at to procedures --- .../20191127113700_add_unpublished_at_to_procedures.rb | 5 +++++ db/schema.rb | 1 + 2 files changed, 6 insertions(+) create mode 100644 db/migrate/20191127113700_add_unpublished_at_to_procedures.rb diff --git a/db/migrate/20191127113700_add_unpublished_at_to_procedures.rb b/db/migrate/20191127113700_add_unpublished_at_to_procedures.rb new file mode 100644 index 000000000..368173813 --- /dev/null +++ b/db/migrate/20191127113700_add_unpublished_at_to_procedures.rb @@ -0,0 +1,5 @@ +class AddUnpublishedAtToProcedures < ActiveRecord::Migration[5.2] + def change + add_column :procedures, :unpublished_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index cb42fecf4..3ed58dc87 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -490,6 +490,7 @@ ActiveRecord::Schema.define(version: 2019_11_28_081324) do t.boolean "xlsx_export_queued" t.boolean "ods_export_queued" t.datetime "closed_at" + t.datetime "unpublished_at" t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state" t.index ["hidden_at"], name: "index_procedures_on_hidden_at" t.index ["parent_procedure_id"], name: "index_procedures_on_parent_procedure_id" From 2f060fc30a359424dbef6690158e1bb0ca67da69 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 4 Dec 2019 15:45:06 +0100 Subject: [PATCH 2/4] Add depubliee state to procedures --- .../admin/procedures_controller.rb | 2 +- .../instructeurs/procedures_controller.rb | 2 +- app/dashboards/procedure_dashboard.rb | 4 +- app/graphql/schema.graphql | 10 ++ app/graphql/types/demarche_type.rb | 1 + app/models/administrateur.rb | 2 +- app/models/dossier.rb | 2 +- app/models/procedure.rb | 29 ++-- app/views/admin/procedures/_list.html.haml | 4 +- .../instructeurs/procedures/_list.html.haml | 63 +++++++++ .../instructeurs/procedures/index.html.haml | 67 +--------- config/locales/fr.yml | 2 +- .../admin/procedures_controller_spec.rb | 4 +- spec/factories/procedure.rb | 8 ++ spec/features/admin/procedure_cloning_spec.rb | 2 +- spec/models/procedure_spec.rb | 124 ++++++++++++------ 16 files changed, 207 insertions(+), 119 deletions(-) create mode 100644 app/views/instructeurs/procedures/_list.html.haml diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 346f16ea7..47a86c99c 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -52,7 +52,7 @@ class Admin::ProceduresController < AdminController def destroy procedure = current_administrateur.procedures.find(params[:id]) - if procedure.publiee_ou_close? + if procedure.locked? return render json: {}, status: 401 end diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index 26e083e98..2af6b0ffe 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -10,7 +10,7 @@ module Instructeurs .procedures .with_attached_logo .includes(:defaut_groupe_instructeur) - .order(closed_at: :desc, archived_at: :desc, published_at: :desc, created_at: :desc) + .order(closed_at: :desc, archived_at: :desc, unpublished_at: :desc, published_at: :desc, created_at: :desc) dossiers = current_instructeur.dossiers.joins(:groupe_instructeur) @dossiers_count_per_procedure = dossiers.all_state.group('groupe_instructeurs.procedure_id').reorder(nil).count diff --git a/app/dashboards/procedure_dashboard.rb b/app/dashboards/procedure_dashboard.rb index a21b26b7a..fa9175e32 100644 --- a/app/dashboards/procedure_dashboard.rb +++ b/app/dashboards/procedure_dashboard.rb @@ -25,6 +25,7 @@ class ProcedureDashboard < Administrate::BaseDashboard for_individual: Field::Boolean, auto_archive_on: Field::DateTime, published_at: Field::DateTime, + unpublished_at: Field::DateTime, hidden_at: Field::DateTime, closed_at: Field::DateTime, whitelisted_at: Field::DateTime, @@ -48,7 +49,8 @@ class ProcedureDashboard < Administrate::BaseDashboard :libelle, :service, :dossiers, - :published_at + :published_at, + :unpublished_at ].freeze # SHOW_PAGE_ATTRIBUTES diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index e9ad183b8..2604850d6 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -209,6 +209,11 @@ type Demarche { """ dateCreation: ISO8601DateTime! + """ + Date de la dépublication. + """ + dateDepublication: ISO8601DateTime + """ Date de la dernière modification. """ @@ -309,6 +314,11 @@ enum DemarcheState { """ close + """ + Depubliee + """ + depubliee + """ Publiée """ diff --git a/app/graphql/types/demarche_type.rb b/app/graphql/types/demarche_type.rb index b1643d01a..3206b06d7 100644 --- a/app/graphql/types/demarche_type.rb +++ b/app/graphql/types/demarche_type.rb @@ -26,6 +26,7 @@ module Types field :date_creation, GraphQL::Types::ISO8601DateTime, "Date de la création.", null: false, method: :created_at field :date_publication, GraphQL::Types::ISO8601DateTime, "Date de la publication.", null: false, method: :published_at field :date_derniere_modification, GraphQL::Types::ISO8601DateTime, "Date de la dernière modification.", null: false, method: :updated_at + field :date_depublication, GraphQL::Types::ISO8601DateTime, "Date de la dépublication.", null: true, method: :unpublished_at field :date_fermeture, GraphQL::Types::ISO8601DateTime, "Date de la fermeture.", null: true, method: :closed_at field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false diff --git a/app/models/administrateur.rb b/app/models/administrateur.rb index 1e94b2004..0d38113c4 100644 --- a/app/models/administrateur.rb +++ b/app/models/administrateur.rb @@ -14,7 +14,7 @@ class Administrateur < ApplicationRecord before_validation -> { sanitize_email(:email) } scope :inactive, -> { joins(:user).where(users: { last_sign_in_at: nil }) } - scope :with_publiees_ou_closes, -> { joins(:procedures).where(procedures: { aasm_state: [:publiee, :archivee, :close] }) } + scope :with_publiees_ou_closes, -> { joins(:procedures).where(procedures: { aasm_state: [:publiee, :archivee, :close, :depubliee] }) } # validate :password_complexity, if: Proc.new { |a| Devise.password_length.include?(a.password.try(:size)) } diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 3075a1f32..43a89320f 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -267,7 +267,7 @@ class Dossier < ApplicationRecord end def can_transition_to_en_construction? - !procedure.close? && brouillon? + brouillon? && procedure.dossier_can_transition_to_en_construction? end def can_be_updated_by_user? diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 82120fe6b..0d993f9bc 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -46,8 +46,8 @@ class Procedure < ApplicationRecord default_scope { where(hidden_at: nil) } scope :brouillons, -> { where(aasm_state: :brouillon) } scope :publiees, -> { where(aasm_state: :publiee) } - scope :closes, -> { where(aasm_state: [:archivee, :close]) } - scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :archivee]) } + scope :closes, -> { where(aasm_state: [:archivee, :close, :depubliee]) } + scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :archivee, :depubliee]) } scope :by_libelle, -> { order(libelle: :asc) } scope :created_during, -> (range) { where(created_at: range) } scope :cloned_from_library, -> { where(cloned_from_library: true) } @@ -77,7 +77,7 @@ class Procedure < ApplicationRecord validates :lien_site_web, presence: true, if: :publiee? validate :validate_for_publication, on: :publication validate :check_juridique - validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,50}\z/ }, uniqueness: { scope: [:path, :closed_at, :archived_at, :hidden_at], case_sensitive: false } + validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,50}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false } # FIXME: remove duree_conservation_required flag once all procedures are converted to the new style validates :duree_conservation_dossiers_dans_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: MAX_DUREE_CONSERVATION }, if: :durees_conservation_required validates :duree_conservation_dossiers_hors_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :durees_conservation_required @@ -97,10 +97,12 @@ class Procedure < ApplicationRecord state :publiee state :close state :hidden + state :depubliee event :publish, before: :before_publish, after: :after_publish do transitions from: :brouillon, to: :publiee transitions from: :close, to: :publiee + transitions from: :depubliee, to: :publiee end event :close, after: :after_close do @@ -112,6 +114,10 @@ class Procedure < ApplicationRecord transitions from: :publiee, to: :hidden transitions from: :close, to: :hidden end + + event :unpublish, after: :after_unpublish do + transitions from: :publiee, to: :depubliee + end end def publish_or_reopen!(administrateur) @@ -122,7 +128,7 @@ class Procedure < ApplicationRecord other_procedure = other_procedure_with_path(path) if other_procedure.present? && administrateur.owns?(other_procedure) - other_procedure.close! + other_procedure.unpublish! end publish! @@ -282,15 +288,15 @@ class Procedure < ApplicationRecord end def locked? - publiee_ou_close? + publiee? || close? || depubliee? end def accepts_new_dossiers? - !close? + publiee? || brouillon? end - def publiee_ou_close? - publiee? || close? + def dossier_can_transition_to_en_construction? + accepts_new_dossiers? || depubliee? end def expose_legacy_carto_api? @@ -379,6 +385,7 @@ class Procedure < ApplicationRecord procedure.aasm_state = :brouillon procedure.archived_at = nil procedure.closed_at = nil + procedure.unpublished_at = nil procedure.published_at = nil procedure.lien_notice = nil @@ -615,7 +622,7 @@ class Procedure < ApplicationRecord end def before_publish - update!(archived_at: nil, closed_at: nil) + update!(archived_at: nil, closed_at: nil, unpublished_at: nil) end def after_publish @@ -635,6 +642,10 @@ class Procedure < ApplicationRecord purge_export_files end + def after_unpublish + update!(unpublished_at: Time.zone.now) + end + def update_juridique_required self.juridique_required ||= (cadre_juridique.present? || deliberation.attached?) true diff --git a/app/views/admin/procedures/_list.html.haml b/app/views/admin/procedures/_list.html.haml index d87130174..1828c02ff 100644 --- a/app/views/admin/procedures/_list.html.haml +++ b/app/views/admin/procedures/_list.html.haml @@ -19,13 +19,13 @@ %td.col-xs-6= link_to(procedure.libelle, admin_procedure_href) - if procedure.publiee? %td.procedure-lien= link_to(procedure_lien(procedure), procedure_lien(procedure)) - - if procedure.publiee_ou_close? + - if procedure.locked? %td= link_to(procedure.published_at.present? ? try_format_datetime(procedure.published_at) : "", admin_procedure_href) - else %td= link_to(try_format_datetime(procedure.created_at), admin_procedure_href) %td = link_to('Cloner', admin_procedure_clone_path(procedure.id), data: { method: :put }, class: 'btn-sm btn-primary clone-btn') - - if !procedure.publiee_ou_close? + - if !procedure.locked? = link_to('X', url_for(controller: 'admin/procedures', action: :destroy, id: procedure.id), data: { method: :delete, confirm: "Confirmez-vous la suppression de la démarche ? \n\n Attention : toute suppression est définitive et s’appliquera aux éventuels autres administrateurs de cette démarche !" }, class: 'btn-sm btn-danger') = smart_listing.paginate diff --git a/app/views/instructeurs/procedures/_list.html.haml b/app/views/instructeurs/procedures/_list.html.haml new file mode 100644 index 000000000..e8e6fb21f --- /dev/null +++ b/app/views/instructeurs/procedures/_list.html.haml @@ -0,0 +1,63 @@ +%ul.procedure-list + - procedures.each do |p| + %li.procedure-item.flex.align-start + = link_to(instructeur_procedure_path(p)) do + .flex + + .procedure-logo{ style: "background-image: url(#{p.logo_url})" } + + .procedure-details + %p.procedure-title + = procedure_libelle p + %ul.procedure-stats.flex + %li + %object + = link_to(instructeur_procedure_path(p, statut: 'a-suivre')) do + - a_suivre_count = dossiers_a_suivre_count_per_procedure[p.id] || 0 + .stats-number + = a_suivre_count + .stats-legend + à suivre + %li + %object + = link_to(instructeur_procedure_path(p, statut: 'suivis')) do + - if current_instructeur.procedures_with_notifications(:en_cours).include?(p) + %span.notifications{ 'aria-label': "notifications" } + - followed_count = followed_dossiers_count_per_procedure[p.id] || 0 + .stats-number + = followed_count + .stats-legend + = t('pluralize.followed', count: followed_count) + %li + %object + = link_to(instructeur_procedure_path(p, statut: 'traites')) do + - if current_instructeur.procedures_with_notifications(:termine).include?(p) + %span.notifications{ 'aria-label': "notifications" } + - termines_count = dossiers_termines_count_per_procedure[p.id] || 0 + .stats-number + = termines_count + .stats-legend + = t('pluralize.processed', count: termines_count) + %li + %object + = link_to(instructeur_procedure_path(p, statut: 'tous')) do + - dossier_count = dossiers_count_per_procedure[p.id] || 0 + .stats-number + = dossier_count + .stats-legend + = t('pluralize.case', count: dossier_count) + %li + %object + = link_to(instructeur_procedure_path(p, statut: 'archives')) do + - archived_count = dossiers_archived_count_per_procedure[p.id] || 0 + .stats-number + = archived_count + .stats-legend + = t('pluralize.archived', count: archived_count) + + - if p.close? + .procedure-status + %span.label Close + - elsif p.depubliee? + .procedure-status + %span.label Dépubliée diff --git a/app/views/instructeurs/procedures/index.html.haml b/app/views/instructeurs/procedures/index.html.haml index a45f8815f..93efd2ba2 100644 --- a/app/views/instructeurs/procedures/index.html.haml +++ b/app/views/instructeurs/procedures/index.html.haml @@ -3,64 +3,9 @@ .container %h1.page-title Démarches - %ul.procedure-list - - @procedures.each do |p| - %li.procedure-item.flex.align-start - = link_to(instructeur_procedure_path(p)) do - .flex - - .procedure-logo{ style: "background-image: url(#{p.logo_url})" } - - .procedure-details - %p.procedure-title - = procedure_libelle p - - %ul.procedure-stats.flex - %li - %object - = link_to(instructeur_procedure_path(p, statut: 'a-suivre')) do - - a_suivre_count = @dossiers_a_suivre_count_per_procedure[p.id] || 0 - .stats-number - = a_suivre_count - .stats-legend - à suivre - %li - %object - = link_to(instructeur_procedure_path(p, statut: 'suivis')) do - - if current_instructeur.procedures_with_notifications(:en_cours).include?(p) - %span.notifications{ 'aria-label': "notifications" } - - followed_count = @followed_dossiers_count_per_procedure[p.id] || 0 - .stats-number - = followed_count - .stats-legend - = t('pluralize.followed', count: followed_count) - %li - %object - = link_to(instructeur_procedure_path(p, statut: 'traites')) do - - if current_instructeur.procedures_with_notifications(:termine).include?(p) - %span.notifications{ 'aria-label': "notifications" } - - termines_count = @dossiers_termines_count_per_procedure[p.id] || 0 - .stats-number - = termines_count - .stats-legend - = t('pluralize.processed', count: termines_count) - %li - %object - = link_to(instructeur_procedure_path(p, statut: 'tous')) do - - dossier_count = @dossiers_count_per_procedure[p.id] || 0 - .stats-number - = dossier_count - .stats-legend - = t('pluralize.case', count: dossier_count) - %li - %object - = link_to(instructeur_procedure_path(p, statut: 'archives')) do - - archived_count = @dossiers_archived_count_per_procedure[p.id] || 0 - .stats-number - = archived_count - .stats-legend - = t('pluralize.archived', count: archived_count) - - - if p.close? - .procedure-status - %span.label Close + = render partial: 'instructeurs/procedures/list', locals: { procedures: @procedures, + dossiers_count_per_procedure: @dossiers_count_per_procedure, + dossiers_a_suivre_count_per_procedure: @dossiers_a_suivre_count_per_procedure, + dossiers_archived_count_per_procedure: @dossiers_archived_count_per_procedure, + dossiers_termines_count_per_procedure: @dossiers_termines_count_per_procedure, + followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure } diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 686df7f57..9481bd33e 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -164,7 +164,7 @@ fr: attributes: path: taken: est déjà utilisé par une démarche. Vous ne pouvez pas l’utiliser car il appartient à un autre administrateur. - taken_can_be_claimed: est identique à celui d’une autre de vos démarches publiées. Si vous publiez cette démarche, l’ancienne sera archivée et ne sera plus accessible au public. + taken_can_be_claimed: est identique à celui d’une autre de vos démarches publiées. Si vous publiez cette démarche, l’ancienne sera dépubliée et ne sera plus accessible au public. Les utilisateurs qui ont commencé un brouillon vont pouvoir le déposer. invalid: n'est pas valide. Il doit comporter au moins 3 caractères, au plus 50 caractères et seuls les caractères a-z, 0-9, '_' et '-' sont autorisés. errors: diff --git a/spec/controllers/admin/procedures_controller_spec.rb b/spec/controllers/admin/procedures_controller_spec.rb index c9d80263a..0ff35f9d8 100644 --- a/spec/controllers/admin/procedures_controller_spec.rb +++ b/spec/controllers/admin/procedures_controller_spec.rb @@ -184,8 +184,8 @@ describe Admin::ProceduresController, type: :controller do expect(flash[:notice]).to have_content 'Démarche publiée' end - it 'archive previous procedure' do - expect(procedure2.close?).to be_truthy + it 'depubliee previous procedure' do + expect(procedure2.depubliee?).to be_truthy end end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 1ca4d97fb..5dbfdaebc 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -168,6 +168,14 @@ FactoryBot.define do end end + trait :unpublished do + after(:build) do |procedure, _evaluator| + procedure.path = generate(:published_path) + procedure.publish! + procedure.unpublish! + end + end + trait :hidden do after(:build) do |procedure, _evaluator| procedure.path = generate(:published_path) diff --git a/spec/features/admin/procedure_cloning_spec.rb b/spec/features/admin/procedure_cloning_spec.rb index 74442d89f..2377e7980 100644 --- a/spec/features/admin/procedure_cloning_spec.rb +++ b/spec/features/admin/procedure_cloning_spec.rb @@ -29,7 +29,7 @@ feature 'As an administrateur I wanna clone a procedure', js: true do within '#publish-modal' do expect(find_field('procedure_path').value).to eq 'libelle-de-la-procedure' - expect(page).to have_text('ancienne sera archivée') + expect(page).to have_text('ancienne sera dépubliée') fill_in 'lien_site_web', with: 'http://some.website' click_on 'publish' end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 87649142a..099dd91a6 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -336,22 +336,6 @@ describe Procedure do end end - describe 'locked?' do - let(:procedure) { create(:procedure, aasm_state: aasm_state) } - - subject { procedure.locked? } - - context 'when procedure is in brouillon status' do - let(:aasm_state) { :brouillon } - it { is_expected.to be_falsey } - end - - context 'when procedure is in publiee status' do - let(:aasm_state) { :publiee } - it { is_expected.to be_truthy } - end - end - describe 'active' do let(:procedure) { create(:procedure) } subject { Procedure.active(procedure.id) } @@ -514,6 +498,7 @@ describe Procedure do it 'Not published nor closed' do expect(subject.closed_at).to be_nil expect(subject.published_at).to be_nil + expect(subject.unpublished_at).to be_nil expect(subject.aasm_state).to eq "brouillon" expect(subject.path).not_to be_nil end @@ -549,59 +534,122 @@ describe Procedure do let(:procedure) { create(:procedure, path: 'example-path') } let(:now) { Time.zone.now.beginning_of_minute } + after { Timecop.return } + + context "without parent procedure" do + before do + Timecop.freeze(now) + procedure.publish! + end + + it do + expect(procedure.closed_at).to be_nil + expect(procedure.published_at).to eq(now) + expect(Procedure.find_by(path: "example-path")).to eq(procedure) + expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs) + end + end + end + + describe "#publish_or_reopen!" do + let(:published_procedure) { create(:procedure, :published) } + let(:administrateur) { published_procedure.administrateurs.first } + + let(:procedure) { create(:procedure, administrateurs: [administrateur]) } + let(:now) { Time.zone.now.beginning_of_minute } + + context "without parent procedure" do + before do + Timecop.freeze(now) + procedure.path = published_procedure.path + procedure.publish_or_reopen!(administrateur) + end + + it do + expect(procedure.closed_at).to be_nil + expect(procedure.published_at).to eq(now) + end + end + end + + describe "#unpublish!" do + let(:procedure) { create(:procedure, :published) } + let(:now) { Time.zone.now.beginning_of_minute } + before do Timecop.freeze(now) - procedure.publish! + procedure.unpublish! end after { Timecop.return } - it { expect(procedure.closed_at).to eq(nil) } - it { expect(procedure.published_at).to eq(now) } - it { expect(Procedure.find_by(path: "example-path")).to eq(procedure) } - it { expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs) } + it { + expect(procedure.closed_at).to eq(nil) + expect(procedure.published_at).not_to be_nil + expect(procedure.unpublished_at).to eq(now) + } end describe "#brouillon?" do - let(:procedure_brouillon) { Procedure.new() } - let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) } - let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) } + let(:procedure_brouillon) { build(:procedure) } + let(:procedure_publiee) { build(:procedure, :published) } + let(:procedure_close) { build(:procedure, :closed) } + let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.brouillon?).to be_truthy } it { expect(procedure_publiee.brouillon?).to be_falsey } it { expect(procedure_close.brouillon?).to be_falsey } + it { expect(procedure_depubliee.brouillon?).to be_falsey } end describe "#publiee?" do - let(:procedure_brouillon) { Procedure.new() } - let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) } - let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) } + let(:procedure_brouillon) { build(:procedure) } + let(:procedure_publiee) { build(:procedure, :published) } + let(:procedure_close) { build(:procedure, :closed) } + let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.publiee?).to be_falsey } it { expect(procedure_publiee.publiee?).to be_truthy } it { expect(procedure_close.publiee?).to be_falsey } + it { expect(procedure_depubliee.publiee?).to be_falsey } end describe "#close?" do - let(:procedure_brouillon) { Procedure.new() } - let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) } - let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) } + let(:procedure_brouillon) { build(:procedure) } + let(:procedure_publiee) { build(:procedure, :published) } + let(:procedure_close) { build(:procedure, :closed) } + let(:procedure_depubliee) { build(:procedure, :unpublished) } it { expect(procedure_brouillon.close?).to be_falsey } it { expect(procedure_publiee.close?).to be_falsey } it { expect(procedure_close.close?).to be_truthy } + it { expect(procedure_depubliee.close?).to be_falsey } end - describe "#publiee_ou_close?" do - let(:procedure_brouillon) { Procedure.new() } - let(:procedure_publiee) { Procedure.new(aasm_state: :publiee, published_at: Time.zone.now) } - let(:procedure_close) { Procedure.new(aasm_state: :close, published_at: Time.zone.now, closed_at: Time.zone.now) } + describe "#depubliee?" do + let(:procedure_brouillon) { build(:procedure) } + let(:procedure_publiee) { build(:procedure, :published) } + let(:procedure_close) { build(:procedure, :closed) } + let(:procedure_depubliee) { build(:procedure, :unpublished) } - it { expect(procedure_brouillon.publiee_ou_close?).to be_falsey } - it { expect(procedure_publiee.publiee_ou_close?).to be_truthy } - it { expect(procedure_close.publiee_ou_close?).to be_truthy } + it { expect(procedure_brouillon.depubliee?).to be_falsey } + it { expect(procedure_publiee.depubliee?).to be_falsey } + it { expect(procedure_close.depubliee?).to be_falsey } + it { expect(procedure_depubliee.depubliee?).to be_truthy } end - describe 'archive' do + describe "#locked?" do + let(:procedure_brouillon) { build(:procedure) } + let(:procedure_publiee) { build(:procedure, :published) } + let(:procedure_close) { build(:procedure, :closed) } + let(:procedure_depubliee) { build(:procedure, :unpublished) } + + it { expect(procedure_brouillon.locked?).to be_falsey } + it { expect(procedure_publiee.locked?).to be_truthy } + it { expect(procedure_close.locked?).to be_truthy } + it { expect(procedure_depubliee.locked?).to be_truthy } + end + + describe 'close' do let(:procedure) { create(:procedure, :published) } let(:now) { Time.zone.now.beginning_of_minute } before do From ef76af55468c3c5fe4c95e8345a961d9f24929b3 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 4 Dec 2019 14:10:30 +0000 Subject: [PATCH 3/4] app: add a site-wide banner for the national strike --- app/views/layouts/_main_container.html.haml | 1 + app/views/layouts/_strike_banner.html.haml | 8 ++++++++ app/views/layouts/application.html.haml | 2 ++ 3 files changed, 11 insertions(+) create mode 100644 app/views/layouts/_strike_banner.html.haml diff --git a/app/views/layouts/_main_container.html.haml b/app/views/layouts/_main_container.html.haml index bd4d39896..f307e1eba 100644 --- a/app/views/layouts/_main_container.html.haml +++ b/app/views/layouts/_main_container.html.haml @@ -1,5 +1,6 @@ #main-container{ class: "col-xs-#{main_container_size}" } .row + = render partial: 'layouts/strike_banner' = render partial: 'layouts/outdated_browser_banner' = render partial: 'layouts/pre_maintenance' .row diff --git a/app/views/layouts/_strike_banner.html.haml b/app/views/layouts/_strike_banner.html.haml new file mode 100644 index 000000000..a585bb51e --- /dev/null +++ b/app/views/layouts/_strike_banner.html.haml @@ -0,0 +1,8 @@ +#strike-banner.site-banner + .container + .site-banner-icon ⚠️ + .site-banner-text + %strong + En raison d’une grève nationale interprofessionnelle, une partie des personnels ne travaille pas. + %br + Les délais de réponse aux questions techniques pourront être perturbés pendant les prochains jours. diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 8eb3e4004..54f753b3f 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -26,8 +26,10 @@ %body{ id: content_for(:page_id), class: browser.platform.ios? ? 'ios' : nil } .page-wrapper + = render partial: 'layouts/strike_banner' = render partial: "layouts/outdated_browser_banner" = render partial: 'layouts/pre_maintenance' + - if staging? #beta Env Test From c2882b6cc356ecfb74a86072916539c2a187acb8 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 4 Dec 2019 15:04:35 +0000 Subject: [PATCH 4/4] app: hide IE11 deprecation banner during the strike Having two banners appearing in a few days may be overwhelming for users. --- config/initializers/browser.rb | 1 + spec/features/outdated_browser_spec.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/config/initializers/browser.rb b/config/initializers/browser.rb index f9d22d40b..798e342ed 100644 --- a/config/initializers/browser.rb +++ b/config/initializers/browser.rb @@ -2,6 +2,7 @@ Browser.modern_rules.clear Browser.modern_rules << -> b { b.chrome? && b.version.to_i >= 50 && !b.platform.ios? } Browser.modern_rules << -> b { b.edge? && b.version.to_i >= 14 && !b.compatibility_view? } +Browser.modern_rules << -> b { b.ie? && b.version.to_i >= 11 && !b.compatibility_view? } Browser.modern_rules << -> b { b.firefox? && b.version.to_i >= 50 && !b.platform.ios? } Browser.modern_rules << -> b { b.opera? && b.version.to_i >= 40 } Browser.modern_rules << -> b { b.safari? && b.version.to_i >= 8 } diff --git a/spec/features/outdated_browser_spec.rb b/spec/features/outdated_browser_spec.rb index e6cb28efb..57fa4815a 100644 --- a/spec/features/outdated_browser_spec.rb +++ b/spec/features/outdated_browser_spec.rb @@ -3,29 +3,29 @@ require 'spec_helper' feature 'Outdated browsers support:' do context 'when the user browser is outdated' do before(:each) do - ie_11_user_agent = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko' - Capybara.page.driver.header('user-agent', ie_11_user_agent) + ie_10_user_agent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)' + Capybara.page.driver.header('user-agent', ie_10_user_agent) end scenario 'a banner is displayed' do visit new_user_session_path - expect(page).to have_content('Internet Explorer 11 est trop ancien') + expect(page).to have_content('Internet Explorer 10 est trop ancien') end scenario 'the banner can be dismissed' do visit new_user_session_path - expect(page).to have_content('Internet Explorer 11 est trop ancien') + expect(page).to have_content('Internet Explorer 10 est trop ancien') # The banner is hidden immediately within '#outdated-browser-banner' do click_on 'Ignorer' end - expect(page).not_to have_content('Internet Explorer 11 est trop ancien') + expect(page).not_to have_content('Internet Explorer 10 est trop ancien') expect(page).to have_current_path(new_user_session_path) # The banner is hidden after a refresh page.refresh - expect(page).not_to have_content('Internet Explorer 11 est trop ancien') + expect(page).not_to have_content('Internet Explorer 10 est trop ancien') end end end