Skip to content

Commit

Permalink
backport: Answer Initiatives Notifications (#464)
Browse files Browse the repository at this point in the history
* backport: Add initiative answers notifications

* lint: Rubocop fixes

* fix: Add some tests and fix i18n

* fix: Delete factory_bot_spec as we didn't do any customization of the factories

* fix: Add new tests
  • Loading branch information
AyakorK authored and moustachu committed Dec 13, 2023
1 parent 2c218e2 commit 658e598
Show file tree
Hide file tree
Showing 12 changed files with 400 additions and 9 deletions.
8 changes: 8 additions & 0 deletions app/events/decidim/initiatives/answer_initiative_event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen-string_literal: true

module Decidim
module Initiatives
class AnswerInitiativeEvent < Decidim::Events::SimpleEvent
end
end
end
2 changes: 2 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class Application < Rails::Application
require "extends/controllers/decidim/devise/sessions_controller_extends"
require "extends/controllers/decidim/editor_images_controller_extends"
require "extends/services/decidim/iframe_disabler_extends"
require "extends/helpers/decidim/icon_helper_extends"
require "extends/commands/decidim/initiatives/admin/update_initiative_answer_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
Expand Down
2 changes: 2 additions & 0 deletions config/i18n-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,6 @@ ignore_unused:
- decidim.proposals.collaborative_drafts.new.*
- decidim.admin.menu.admin_accountability
- decidim.anonymous_user
- decidim.events.initiatives.initiative_answered.*
- decidim.initiatives.pages.home.highlighted_initiatives.*

6 changes: 6 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ en:
email_outro: You have received this notification because you are participating in "%{participatory_space_title}"
email_subject: Your vote is still pending in %{participatory_space_title}
notification_title: The vote on budget <a href="%{resource_path}">%{resource_title}</a> is still waiting for your confirmation in %{participatory_space_title}
initiatives:
initiative_answered:
email_intro: The initiative "%{resource_title}" has been answered.
email_outro: You have received this notification because you are following the initiative "%{resource_title}".
email_subject: Initiative "%{resource_title}" has been answered
notification_title: The initiative <a href="%{resource_path}">%{resource_title}</a> has been answered.
users:
user_officialized:
email_intro: Participant %{name} (%{nickname}) has been officialized.
Expand Down
6 changes: 6 additions & 0 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ fr:
email_outro: Vous avez reçu cette notification parce que vous avez commencé à voter sur la concertation "%{participatory_space_title}"
email_subject: Votre vote est toujours en attente sur la concertation %{participatory_space_title}
notification_title: Votre vote pour le budget <a href="%{resource_path}">%{resource_title}</a> attend d'être finalisé sur la concertation %{participatory_space_title}
initiatives:
initiative_answered:
email_intro: La pétition "%{resource_title}" a reçu une réponse.
email_outro: Vous avez reçu cette notification parce que vous suivez la pétition "%{resource_title}".
email_subject: La pétition "%{resource_title}" a reçu une réponse.
notification_title: La pétition <a href="%{resource_path}">%{resource_title}</a> a reçu une réponse.
users:
user_officialized:
email_intro: Le participant %{name} (%{nickname}) a été officialisé.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

module UpdateInitiativeAnswerExtends
def call
return broadcast(:invalid) if form.invalid?

@initiative = Decidim.traceability.update!(
initiative,
current_user,
attributes
)
notify_initiative_is_extended if @notify_extended
notify_initiative_is_answered if @notify_answered
broadcast(:ok, initiative)
rescue ActiveRecord::RecordInvalid
broadcast(:invalid, initiative)
end

private

def attributes
attrs = {
answer: form.answer,
answer_url: form.answer_url
}

attrs[:answered_at] = Time.current if form.answer.present?

if form.signature_dates_required?
attrs[:signature_start_date] = form.signature_start_date
attrs[:signature_end_date] = form.signature_end_date

if initiative.published? && form.signature_end_date != initiative.signature_end_date &&
form.signature_end_date > initiative.signature_end_date
@notify_extended = true
end
end

@notify_answered = form.answer != initiative.answer && !form.answer.values.all?(&:blank?)

attrs
end

def notify_initiative_is_answered
Decidim::EventsManager.publish(
event: "decidim.events.initiatives.initiative_answered",
event_class: Decidim::Initiatives::AnswerInitiativeEvent,
resource: initiative,
followers: initiative.followers
)
end
end

Decidim::Initiatives::Admin::UpdateInitiativeAnswer.class_eval do
prepend UpdateInitiativeAnswerExtends
end
23 changes: 23 additions & 0 deletions lib/extends/helpers/decidim/icon_helper_extends.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module IconHelperExtends
def resource_icon(resource, options = {})
if resource.instance_of?(Decidim::Initiative)
icon "initiatives", options
elsif resource.instance_of?(Decidim::Comments::Comment)
icon "comment-square", options
elsif resource.respond_to?(:component) && resource.component
component_icon(resource.component, options)
elsif resource.respond_to?(:manifest) && resource.manifest
manifest_icon(resource.manifest, options)
elsif resource.is_a?(Decidim::User)
icon "person", options
else
icon "bell", options
end
end
end

Decidim::IconHelper.module_eval do
prepend(IconHelperExtends)
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# frozen_string_literal: true

require "spec_helper"

module Decidim
module Initiatives
module Admin
describe UpdateInitiativeAnswer do
let(:form_klass) { Decidim::Initiatives::Admin::InitiativeAnswerForm }

context "when valid data" do
it_behaves_like "update an initiative answer" do
context "when the user is an admin" do
let!(:current_user) { create(:user, :admin, organization: initiative.organization) }
let!(:follower) { create(:user, organization: organization) }
let!(:follow) { create(:follow, followable: initiative, user: follower) }

it "notifies the followers for extension and answer" do
expect(Decidim::EventsManager)
.to receive(:publish)
.with(
event: "decidim.events.initiatives.initiative_extended",
event_class: Decidim::Initiatives::ExtendInitiativeEvent,
resource: initiative,
followers: [follower]
)
.ordered
expect(Decidim::EventsManager)
.to receive(:publish)
.with(
event: "decidim.events.initiatives.initiative_answered",
event_class: Decidim::Initiatives::AnswerInitiativeEvent,
resource: initiative,
followers: [follower]
)
.ordered

command.call
end

context "when the signature end time is not modified" do
let(:signature_end_date) { initiative.signature_end_date }

it "doesn't notify the followers" do
expect(Decidim::EventsManager).not_to receive(:publish).with(
event: "decidim.events.initiatives.initiative_extended",
event_class: Decidim::Initiatives::ExtendInitiativeEvent,
resource: initiative,
followers: [follower]
)

command.call
end
end
end
end
end

context "when validation failure" do
let(:organization) { create(:organization) }
let!(:initiative) { create(:initiative, organization: organization) }
let!(:form) do
form_klass
.from_model(initiative)
.with_context(current_organization: organization, initiative: initiative)
end

let(:command) { described_class.new(initiative, form, initiative.author) }

it "broadcasts invalid" do
expect(initiative).to receive(:valid?)
.at_least(:once)
.and_return(false)
expect { command.call }.to broadcast :invalid
end
end
end
end
end
end
1 change: 1 addition & 0 deletions spec/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
require "decidim/verifications/test/factories"
require "decidim/forms/test/factories"
require "decidim/surveys/test/factories"
require "decidim/initiatives/test/factories"
9 changes: 0 additions & 9 deletions spec/factory_bot_spec.rb

This file was deleted.

131 changes: 131 additions & 0 deletions spec/helpers/icon_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# frozen_string_literal: true

require "spec_helper"

module Decidim
describe IconHelper do
describe "#component_icon" do
let(:component) do
create(:component, manifest_name: :dummy)
end

describe "when the component has no icon" do
before do
allow(component.manifest).to receive(:icon).and_return(nil)
end

it "returns a fallback" do
result = helper.component_icon(component)
expect(result).to include("question-mark")
end
end

describe "when the component has icon" do
it "returns the icon" do
result = helper.component_icon(component)
expect(result).to eq <<~SVG.strip
<svg class="icon external-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36.02 36.02"><circle cx="18.01" cy="18.01" r="15.75" fill="none" stroke="#2ecc71" stroke-width="4"/><circle cx="18.01" cy="18.01" r="11.25" fill="none" stroke="#08BCD0" stroke-width="4"/></svg>
SVG
end

context "with role attribute specified" do
it "implements role attribute" do
result = helper.component_icon(component, role: "img")
expect(result).to eq <<~SVG.strip
<svg class="icon external-icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36.02 36.02"><circle cx="18.01" cy="18.01" r="15.75" fill="none" stroke="#2ecc71" stroke-width="4"/><circle cx="18.01" cy="18.01" r="11.25" fill="none" stroke="#08BCD0" stroke-width="4"/></svg>
SVG
end
end

context "with no role attribute specified" do
it "doesn't implement role attribute" do
result = helper.component_icon(component)
expect(result).to eq <<~SVG.strip
<svg class="icon external-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36.02 36.02"><circle cx="18.01" cy="18.01" r="15.75" fill="none" stroke="#2ecc71" stroke-width="4"/><circle cx="18.01" cy="18.01" r="11.25" fill="none" stroke="#08BCD0" stroke-width="4"/></svg>
SVG
end
end
end

describe "resource_icon" do
let(:result) { helper.resource_icon(resource) }

context "when it has a component" do
let(:resource) { build :dummy_resource }

it "renders the component icon" do
expect(helper).to receive(:component_icon).with(resource.component, {})

result
end
end

context "when it has a manifest" do
let(:resource) { build(:component, manifest_name: :dummy) }

it "renders the manifest icon" do
expect(helper).to receive(:manifest_icon).with(resource.manifest, {})

result
end
end

context "when it is a user" do
let(:resource) { build :user }

it "renders a person icon" do
expect(result).to include("svg#icon-person")
end
end

context "when the resource component and manifest are nil" do
let(:resource) { build :dummy_resource }

before do
allow(resource).to receive(:component).and_return(nil)
end

it "renders a generic icon" do
expect(result).to include("svg#icon-bell")
end
end

context "when the manifest icon is nil" do
let(:resource) { build(:component, manifest_name: :dummy) }

before do
allow(resource.manifest).to receive(:icon).and_return(nil)
end

it "renders a generic icon" do
expect(result).to include("svg#icon-question-mark")
end
end

context "when the resource is a comment" do
let(:resource) { build :comment }

it "renders a comment icon" do
expect(result).to include("svg#icon-comment-square")
end
end

context "when the resource is an initiative" do
let(:resource) { build :initiative }

it "renders an initiative icon" do
expect(result).to include("svg#icon-initiatives")
end
end

context "and in other cases" do
let(:resource) { "Something" }

it "renders a generic icon" do
expect(result).to include("svg#icon-bell")
end
end
end
end
end
end
Loading

0 comments on commit 658e598

Please sign in to comment.