Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/add notification after adding a proposal #620

Draft
wants to merge 12 commits into
base: develop
Choose a base branch
from
126 changes: 126 additions & 0 deletions app/commands/decidim/proposals/publish_proposal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# frozen_string_literal: true

module Decidim
module Proposals
# A command with all the business logic when a user publishes a draft proposal.
class PublishProposal < Decidim::Command
include Decidim::AnonymousProposals::AnonymousBehaviorCommandsConcern

# Public: Initializes the command.
#
# proposal - The proposal to publish.
# current_user - The current user.
# override: decidim-module-anonymous_proposals/app/commands/decidim/anonymous_proposals/publish_proposal_command_overrides.rb
def initialize(proposal, current_user)
@proposal = proposal
@is_anonymous = allow_anonymous_proposals? && (current_user.blank? || proposal.authored_by?(anonymous_group))
set_current_user(current_user)
end

# Executes the command. Broadcasts these events:
#
# - :ok when everything is valid and the proposal is published.
# - :invalid if the proposal's author is not the current user.
#
# Returns nothing.
def call
return broadcast(:invalid) unless @proposal.authored_by?(@current_user)

transaction do
publish_proposal
increment_scores
send_notification
send_notification_to_participatory_space
send_publication_notification
end

broadcast(:ok, @proposal)
end

private

# This will be the PaperTrail version that is
# shown in the version control feature (1 of 1)
#
# For an attribute to appear in the new version it has to be reset
# and reassigned, as PaperTrail only keeps track of object CHANGES.
def publish_proposal
title = reset(:title)
body = reset(:body)

Decidim.traceability.perform_action!(
"publish",
@proposal,
@current_user,
visibility: "public-only"
) do
@proposal.update title: title, body: body, published_at: Time.current
end
end

# Reset the attribute to an empty string and return the old value
def reset(attribute)
attribute_value = @proposal[attribute]
PaperTrail.request(enabled: false) do
# rubocop:disable Rails/SkipsModelValidations
@proposal.update_attribute attribute, ""
# rubocop:enable Rails/SkipsModelValidations
end
attribute_value
end

def send_notification
return if @proposal.coauthorships.empty?

Decidim::EventsManager.publish(
event: "decidim.events.proposals.proposal_published",
event_class: Decidim::Proposals::PublishProposalEvent,
resource: @proposal,
followers: coauthors_followers
)
end

def send_publication_notification
Decidim::EventsManager.publish(
event: "decidim.events.proposals.proposal_published_event",
event_class: Decidim::Proposals::ProposalPublishedEvent,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
event_class: Decidim::Proposals::ProposalPublishedEvent,
event_class: Decidim::Proposals::AuthorConfirmationProposalEvent,

resource: @proposal,
affected_users: [@proposal.creator_identity],
extra: { force_email: true },
force_send: true
)
end

def send_notification_to_participatory_space
Decidim::EventsManager.publish(
event: "decidim.events.proposals.proposal_published",
event_class: Decidim::Proposals::PublishProposalEvent,
resource: @proposal,
followers: @proposal.participatory_space.followers - coauthors_followers,
extra: {
participatory_space: true
}
)
end

def coauthors_followers
@coauthors_followers ||= @proposal.authors.flat_map(&:followers)
end

def increment_scores
@proposal.coauthorships.find_each do |coauthorship|
if coauthorship.user_group
Decidim::Gamification.increment_score(coauthorship.user_group, :proposals)
else
Decidim::Gamification.increment_score(coauthorship.author, :proposals)
end
end
end

# override: decidim-module-anonymous_proposals/app/commands/decidim/anonymous_proposals/publish_proposal_command_overrides.rb
def component
@component ||= @proposal.component
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def create
end
end
end

# Overridden because of a core bug when the command posts the "invalid"
# signal and when rendering the form.
def update_draft
Expand Down Expand Up @@ -203,6 +202,7 @@ def proposal_limit_reached?(form = form_proposal_params)
def current_user_proposals(form)
Decidim::Proposals::Proposal.from_author(current_user).where(component: form.current_component).except_withdrawn
end

end
end
end
Expand Down
10 changes: 10 additions & 0 deletions app/events/decidim/proposals/proposal_published_event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# app/events/decidim/proposals/proposal_published_event.rb
module Decidim
module Proposals
class ProposalPublishedEvent < Decidim::Events::SimpleEvent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class ProposalPublishedEvent < Decidim::Events::SimpleEvent
class AuthorConfirmationProposalEvent < Decidim::Events::SimpleEvent

To be easier to read, we should rename the class Event as follow and also rename the file to app/events/decidim/proposals/author_confirmation_proposal_event.rb

def resource_title
translated_attribute(resource.title)
end
end
end
end
5 changes: 5 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ en:
exports:
awesome_private_proposals: Proposals with private fields
proposal_comments: Comments
notifications:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English keys must match french keys

proposal_published:
title: "Your proposal %{proposal_title} has been published!"
body: "Your proposal is now live. View it here: %{proposal_link}"
subject: "Your proposal has been published!"
collaborative_drafts:
new:
add_file: Add file
Expand Down
11 changes: 11 additions & 0 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ fr:
new:
sign_in_disabled: Vous pouvez accéder avec un compte externe
events:
proposals:
proposal_published_event:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
proposal_published_event:
author_confirmation_proposal_event:

email_intro: 'que vous suivez, a publié une nouvelle proposition appelée "%{resource_title}". Découvrez-le et contribuez:'
email_outro: Vous avez reçu cette notification car vous suivez . Si vous souhaitez vous désabonner des notifications, connectez-vous à la plateforme, puis rendez-vous dans l'onglet “Mon compte” > “Paramètres des notifications”.
email_subject: Nouvelle proposition "%{resource_title}" publiée
notification_title: La proposition <a href="%{resource_path}">%{resource_title}</a> a été publiée.
budgets:
pending_order:
email_intro: Le vote sur le budget "%{resource_title}" n'est pas encore finalisé sur la concertation "%{participatory_space_title}".
Expand Down Expand Up @@ -158,6 +164,11 @@ fr:
collaborative_drafts_list: Accéder aux brouillons collaboratifs
new_proposal: Nouvelle proposition
view_proposal: Voir la proposition
notifications:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This locale key is no longer needed because it is called above with notification_title key

proposal_published:
title: "Votre proposition %{proposal_title} a bien été publiée!"
body: "Votre proposition est en ligne. Lien ici: %{proposal_link}"
subject: "Votre proposition est publiée!"
update:
error: Il y a eu une erreur lors de la mise à jour de la proposition.
success: Proposition mise à jour avec succès.
Expand Down
78 changes: 78 additions & 0 deletions spec/events/decidim/proposals/proposal_published_event_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# frozen_string_literal: true

require "spec_helper"

module Decidim
module Proposals
describe ProposalPublishedEvent do
let(:resource) { create :extended_proposal }
let(:participatory_process) { create :participatory_process, organization: organization }
let(:proposal_component) { create(:extended_proposal_component, participatory_space: participatory_process) }
let(:resource_title) { decidim_sanitize_translated(resource.title) }
let(:event_name) { "decidim.events.proposals.proposal_published" }

include_context "when a simple event"

it_behaves_like "a simple event"

describe "resource_text" do
it "returns the proposal body" do
expect(subject.resource_text).to eq(resource.body)
end
end

describe "email_subject" do
context "when resource title contains apostrophes" do
it "is generated correctly" do
expect(subject.email_subject).to eq("New proposal \"#{resource_title}\" by @#{author.nickname}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should match the expected translation

end
end

it "is generated correctly" do
expect(subject.email_subject).to eq("New proposal \"#{resource_title}\" by @#{author.nickname}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should match the expected translation

end
end

describe "email_intro" do
it "is generated correctly" do
expect(subject.email_intro)
.to eq("#{author.name} @#{author.nickname}, who you are following, has published a new proposal called \"#{resource_title}\". Check it out and contribute:")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should match the expected translation

end
end

describe "email_outro" do
it "is generated correctly" do
expect(subject.email_outro)
.to eq("You have received this notification because you are following @#{author.nickname}. You can stop receiving notifications following the previous link.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should match the expected translation

end
end

describe "notification_title" do
it "is generated correctly" do
expect(subject.notification_title)
.to include("The <a href=\"#{resource_path}\">#{resource_title}</a> proposal was published by ")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should match the expected translation


expect(subject.notification_title)
.to include("<a href=\"/profiles/#{author.nickname}\">#{author.name} @#{author.nickname}</a>.")
end
end

describe "translated notifications" do
let(:en_body) { "A nice proposal" }
let(:body) { { en: en_body, machine_translations: { ca: "Une belle idee" } } }
let(:resource) do
create :extended_proposal,
component: proposal_component,
title: { en: "A nice proposal", machine_translations: { ca: "Une belle idee" } },
body: body
end

let(:en_version) { subject.resource_text["en"] }
let(:machine_translated) { subject.resource_text["machine_translations"]["ca"] }
let(:translatable) { true }

it_behaves_like "a translated event"
end
end
end
end
Loading
Loading