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