From 1fe41e8ca7f1bd74c51acb7285700e47fca7d8cd Mon Sep 17 00:00:00 2001 From: Quentin Champenois <26109239+Quentinchampenois@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:42:26 +0100 Subject: [PATCH] feat: Allow to choose notification settings when attachment added (#627) * fix: Override Create attachment admin command * fix: Force email notification on attachment event * fix: Override attachment form * feat: Allow admins to toggle notification sending * refactor: Remove override and add extend * lint: Fix rubocop offenses --------- Co-authored-by: Lucie Grau <87868063+luciegrau@users.noreply.github.com> --- app/forms/decidim/admin/attachment_form.rb | 44 ++++++++ .../decidim/admin/attachments/_form.html.erb | 33 ++++++ config/application.rb | 23 ++-- config/locales/en.yml | 3 + config/locales/fr.yml | 3 + .../admin/create_attachment_extends.rb | 25 +++++ .../decidim/admin/create_attachment_spec.rb | 104 ++++++++++++++++++ 7 files changed, 227 insertions(+), 8 deletions(-) create mode 100644 app/forms/decidim/admin/attachment_form.rb create mode 100644 app/views/decidim/admin/attachments/_form.html.erb create mode 100644 lib/extends/commands/decidim/admin/create_attachment_extends.rb create mode 100644 spec/commands/decidim/admin/create_attachment_spec.rb diff --git a/app/forms/decidim/admin/attachment_form.rb b/app/forms/decidim/admin/attachment_form.rb new file mode 100644 index 0000000000..c0ab45b5a6 --- /dev/null +++ b/app/forms/decidim/admin/attachment_form.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Decidim + module Admin + # A form object used to create attachments in a participatory process. + # + class AttachmentForm < Form + include TranslatableAttributes + + attribute :file + translatable_attribute :title, String + translatable_attribute :description, String + attribute :weight, Integer, default: 0 + attribute :attachment_collection_id, Integer + attribute :send_notification_to_followers, Boolean, default: false + + mimic :attachment + + validates :file, presence: true, unless: :persisted? + validates :file, passthru: { to: Decidim::Attachment } + validates :title, :description, translatable_presence: true + validates :attachment_collection, presence: true, if: ->(form) { form.attachment_collection_id.present? } + validates :attachment_collection_id, inclusion: { in: :attachment_collection_ids }, allow_blank: true + + delegate :attached_to, to: :context, prefix: false + + alias organization current_organization + + def attachment_collections + @attachment_collections ||= attached_to.attachment_collections + end + + def attachment_collection + @attachment_collection ||= attachment_collections.find_by(id: attachment_collection_id) + end + + private + + def attachment_collection_ids + @attachment_collection_ids ||= attachment_collections.pluck(:id) + end + end + end +end diff --git a/app/views/decidim/admin/attachments/_form.html.erb b/app/views/decidim/admin/attachments/_form.html.erb new file mode 100644 index 0000000000..1f8a2230bc --- /dev/null +++ b/app/views/decidim/admin/attachments/_form.html.erb @@ -0,0 +1,33 @@ +
+
+

+ <%= title %> +

+
+ +
+
+ <%= form.translated :text_field, :title, autofocus: true %> +
+ +
+ <%= form.number_field :weight %> +
+ +
+ <%= form.translated :text_field, :description %> +
+ +
+ <%= form.select :attachment_collection_id, @form.attachment_collections.collect { |c| [translated_attribute(c.name), c.id] }, include_blank: true %> +
+ +
+ <%= form.upload :file, required: true %> +
+ +
+ <%= form.check_box :send_notification_to_followers, label: t(".send_notification_to_followers") %> +
+
+
diff --git a/config/application.rb b/config/application.rb index 1411333f2d..1e45491268 100644 --- a/config/application.rb +++ b/config/application.rb @@ -46,24 +46,31 @@ class Application < Rails::Application end config.after_initialize do + # Controllers require "extends/controllers/decidim/devise/sessions_controller_extends" require "extends/controllers/decidim/editor_images_controller_extends" + require "extends/controllers/decidim/proposals/proposals_controller_extends" + require "extends/controllers/decidim/newsletters_controller_extends" require "extends/controllers/decidim/admin/scopes_controller_extends" require "extends/controllers/decidim/scopes_controller_extends" - require "extends/services/decidim/iframe_disabler_extends" - require "extends/helpers/decidim/check_boxes_tree_helper_extends" - require "extends/helpers/decidim/icon_helper_extends" - require "extends/commands/decidim/initiatives/admin/update_initiative_answer_extends" require "extends/controllers/decidim/initiatives/committee_requests_controller_extends" + # Models require "extends/models/decidim/budgets/project_extends" require "extends/models/decidim/authorization_extends" + require "extends/models/decidim/decidim_awesome/proposal_extra_field_extends" + # Services + require "extends/services/decidim/iframe_disabler_extends" + # Helpers + require "extends/helpers/decidim/icon_helper_extends" + require "extends/helpers/decidim/check_boxes_tree_helper_extends" + # Forms + require "extends/forms/decidim/initiatives/initiative_form_extends" require "extends/forms/decidim/initiatives/admin/initiative_form_extends" + # Commands + require "extends/commands/decidim/initiatives/admin/update_initiative_answer_extends" require "extends/commands/decidim/budgets/admin/import_proposals_to_budgets_extends" - require "extends/controllers/decidim/newsletters_controller_extends" require "extends/commands/decidim/admin/destroy_participatory_space_private_user_extends" - require "extends/controllers/decidim/proposals/proposals_controller_extends" - require "extends/forms/decidim/initiatives/initiative_form_extends" - require "extends/models/decidim/decidim_awesome/proposal_extra_field_extends" + require "extends/commands/decidim/admin/create_attachment_extends" Decidim::GraphiQL::Rails.config.tap do |config| config.initial_query = "{\n deployment {\n version\n branch\n remote\n upToDate\n currentCommit\n latestCommit\n locallyModified\n }\n}".html_safe diff --git a/config/locales/en.yml b/config/locales/en.yml index beafaddd2b..89f03de8b5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -22,6 +22,9 @@ en: confirm_destroy: Confirm destroy destroy: Destroy edit: Edit + attachments: + form: + send_notification_to_followers: Send a notification to all the people following the consultation who have agreed to receive email notifications exports: export_as: "%{name} as %{export_format}" notice: Your export is currently in progress. You'll receive an email when it's complete. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index c5c91823bc..3bd7493ba8 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -22,6 +22,9 @@ fr: confirm_destroy: Confirmer la suppression destroy: Supprimer edit: Modifier + attachments: + form: + send_notification_to_followers: Envoyer une notification à toutes les personnes qui suivent la concertation ayant accepté de recevoir des notifications par mail exports: export_as: "%{name} au format %{export_format}" notice: Votre exportation est en cours. Vous recevrez un e-mail quand elle sera terminée. diff --git a/lib/extends/commands/decidim/admin/create_attachment_extends.rb b/lib/extends/commands/decidim/admin/create_attachment_extends.rb new file mode 100644 index 0000000000..ea1efbf28c --- /dev/null +++ b/lib/extends/commands/decidim/admin/create_attachment_extends.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "active_support/concern" + +module CreateAttachmentExtends + extend ActiveSupport::Concern + + included do + def notify_followers + return unless @attachment.attached_to.is_a?(Decidim::Followable) + return unless form.send_notification_to_followers + + Decidim::EventsManager.publish( + event: "decidim.events.attachments.attachment_created", + event_class: Decidim::AttachmentCreatedEvent, + resource: @attachment, + followers: @attachment.attached_to.followers, + extra: { force_email: true }, + force_send: true + ) + end + end +end + +Decidim::Admin::CreateAttachment.include(CreateAttachmentExtends) diff --git a/spec/commands/decidim/admin/create_attachment_spec.rb b/spec/commands/decidim/admin/create_attachment_spec.rb new file mode 100644 index 0000000000..35425848ae --- /dev/null +++ b/spec/commands/decidim/admin/create_attachment_spec.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::Admin + describe CreateAttachment do + subject { described_class.call(form, attached_to, user) } + let(:user) { create(:user) } + let(:send_notification) { true } + let(:form) do + instance_double( + AttachmentForm, + title: { + en: "An image", + ca: "Una imatge", + es: "Una imagen" + }, + description: { + en: "A city", + ca: "Una ciutat", + es: "Una ciudad" + }, + file: file, + attachment_collection: nil, + send_notification_to_followers: send_notification, + weight: 0 + ) + end + let(:file) { upload_test_file(Decidim::Dev.test_file("city.jpeg", "image/jpeg")) } + let(:attached_to) { create(:participatory_process) } + + describe "when valid" do + before do + allow(form).to receive(:invalid?).and_return(false) + end + + it "broadcasts :ok and creates the component" do + expect do + subject + end.to broadcast(:ok) + + expect(Decidim::Attachment.count).to eq(1) + end + + it "notifies the followers" do + follower = create(:user, organization: attached_to.organization) + create(:follow, followable: attached_to, user: follower) + + expect(Decidim::EventsManager) + .to receive(:publish) + .with( + event: "decidim.events.attachments.attachment_created", + event_class: Decidim::AttachmentCreatedEvent, + resource: kind_of(Decidim::Attachment), + followers: [follower], + extra: { force_email: true }, + force_send: true + ) + + subject + end + + context "when send notification option is false" do + let(:send_notification) { false } + + it "does not notify the followers" do + follower = create(:user, organization: attached_to.organization) + create(:follow, followable: attached_to, user: follower) + + expect(Decidim::EventsManager) + .not_to receive(:publish) + + subject + end + end + + it "traces the action", versioning: true do + expect(Decidim.traceability) + .to receive(:perform_action!) + .with(:create, Decidim::Attachment, user) + .and_call_original + + expect { subject }.to change(Decidim::ActionLog, :count) + action_log = Decidim::ActionLog.last + expect(action_log.action).to eq("create") + expect(action_log.version).to be_present + end + end + + describe "when invalid" do + before do + allow(form).to receive(:invalid?).and_return(true) + end + + it "broadcasts invalid" do + expect do + subject + end.to broadcast(:invalid) + + expect(Decidim::Attachment.count).to eq(0) + end + end + end +end