Allow admin to declare whether to include the badge description in message (#20684)
* Allow admin to declare whether to include the badge description in badge achievement message * Update spec/services/badges/award_spec.rb Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Add proper acceptance to tests * Tweak tests * Switch to kwargs * Clean up test acceptance * Change interface * Update spec/requests/admin/badge_achievements_spec.rb Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update app/services/badges/award.rb Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Adjust interface --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
585bea212d
commit
78f9bec3e7
14 changed files with 108 additions and 28 deletions
|
|
@ -32,9 +32,14 @@ module Admin
|
|||
end
|
||||
|
||||
usernames = permitted_params[:usernames].downcase.split(/\s*,\s*/)
|
||||
include_default_description = permitted_params[:include_default_description] == "1"
|
||||
message = permitted_params[:message_markdown].presence ||
|
||||
I18n.t("admin.badge_achievements_controller.congrats", community: ::Settings::Community.community_name)
|
||||
BadgeAchievements::BadgeAwardWorker.perform_async(usernames, permitted_params[:badge], message)
|
||||
BadgeAchievements::BadgeAwardWorker
|
||||
.perform_async(usernames,
|
||||
permitted_params[:badge],
|
||||
message,
|
||||
include_default_description)
|
||||
|
||||
flash[:success] = I18n.t("admin.badge_achievements_controller.rewarded")
|
||||
redirect_to admin_badge_achievements_path
|
||||
|
|
@ -46,7 +51,7 @@ module Admin
|
|||
private
|
||||
|
||||
def permitted_params
|
||||
params.permit(:usernames, :badge, :message_markdown)
|
||||
params.permit(:usernames, :badge, :message_markdown, :include_default_description)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
module Badges
|
||||
class Award
|
||||
def self.call(user_relation, slug, message_markdown)
|
||||
def self.call(user_relation, slug, message_markdown, include_default_description: true)
|
||||
return unless (badge_id = Badge.id_for_slug(slug))
|
||||
|
||||
user_relation.find_each do |user|
|
||||
|
|
@ -9,6 +9,7 @@ module Badges
|
|||
achievement = user.badge_achievements.create(
|
||||
badge_id: badge_id,
|
||||
rewarding_context_message_markdown: message_markdown,
|
||||
include_default_description: include_default_description,
|
||||
)
|
||||
user.touch if achievement.persisted?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ module Badges
|
|||
return unless (badge_id = Badge.id_for_slug(BADGE_SLUG))
|
||||
|
||||
Article.joins(:user)
|
||||
.published
|
||||
.where("articles.published_at > ?", 1.week.ago)
|
||||
.where("articles.published_at < ?", 1.hour.ago)
|
||||
.where("articles.score >= ?", 0)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ module Notifications
|
|||
attr_reader :badge_achievement
|
||||
|
||||
def json_data
|
||||
description = badge_achievement.include_default_description ? badge_achievement.badge.description : nil
|
||||
{
|
||||
user: user_data(badge_achievement.user),
|
||||
badge_achievement: {
|
||||
|
|
@ -34,7 +35,7 @@ module Notifications
|
|||
rewarding_context_message: badge_achievement.rewarding_context_message,
|
||||
badge: {
|
||||
title: badge_achievement.badge.title,
|
||||
description: badge_achievement.badge.description,
|
||||
description: description,
|
||||
badge_image_url: badge_achievement.badge.badge_image_url,
|
||||
credits_awarded: badge_achievement.badge.credits_awarded
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,17 @@
|
|||
<div class="crayons-field">
|
||||
<%= f.label :message_markdown, "Override Default Message", class: "crayons-field__label" %>
|
||||
<p class="crayons-field__description">
|
||||
Supports Markdown
|
||||
Supports Markdown. This overrides the "message" sent in notifications and emails, it does not override the badge description.
|
||||
</p>
|
||||
<%= f.text_area :message_markdown, size: "40x3", class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div class="crayons-field crayons-field--checkbox pb-2 pl-1">
|
||||
<%= f.check_box :include_default_description, checked: true %>
|
||||
<%= f.label :include_default_description, "Include Default Description", class: "crayons-field__label pt-2" %>
|
||||
</div>
|
||||
<p class="crayons-field__description">
|
||||
If checked, the default badge <em>description</em> will be included above the message. If unchecked, only the message will be included.
|
||||
</p>
|
||||
<div>
|
||||
<%= f.submit "Award Badges", class: "c-btn c-btn--primary" %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,12 @@
|
|||
<p style="text-align:center;padding:20px 0px">
|
||||
<img src="<%= @badge.badge_image_url %>" style="height:200px" alt="<%= @badge.title %> badge" loading="lazy" />
|
||||
</p>
|
||||
<p>
|
||||
<%= @badge.description %>
|
||||
</p>
|
||||
|
||||
<% if @badge_achievement.include_default_description %>
|
||||
<p>
|
||||
<%= @badge.description %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% if @badge_achievement.rewarding_context_message.present? %>
|
||||
<p>
|
||||
|
|
@ -21,7 +24,7 @@
|
|||
<p>
|
||||
<a
|
||||
href="<%= user_url(@user) %>"
|
||||
style="font-weight:bold;color:white;background:#4e57ef;padding: 5px 10px;border-radius:3px;text-decoration:none">
|
||||
style="font-weight:bold;color:white;background:#4e57ef;padding: 12px 15px;border-radius:6px;text-decoration:none">
|
||||
Check out your profile
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Congratulations, <%= @user.name %>! You got the <%= @badge.title %> badge! Be sure to check out your profile.
|
||||
|
||||
<%= @badge.description %>
|
||||
<%= @badge.description if @badge_achievement.include_default_description %>
|
||||
|
||||
<%= strip_tags @badge_achievement.rewarding_context_message %>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
<h3 class="fw-normal fs-l s:fs-xl m:fs-2xl">
|
||||
<%= t("views.notifications.badge.heading_html", badge: tag.strong(sanitize(json_data["badge_achievement"]["badge"]["title"]), class: "fw-bold")) %>
|
||||
</h3>
|
||||
<p class="fs-l"><%= json_data["badge_achievement"]["badge"]["description"] %></p>
|
||||
<% if json_data["badge_achievement"]["badge"]["description"] %>
|
||||
<p class="fs-l"><%= json_data["badge_achievement"]["badge"]["description"] %></p>
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<div class="crayons-card crayons-card--secondary p-4 w-100 grid gap-2">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module BadgeAchievements
|
|||
|
||||
sidekiq_options queue: :high_priority, retry: 10
|
||||
|
||||
def perform(usernames, badge_slug, message)
|
||||
def perform(usernames, badge_slug, message, include_default_description = true) # rubocop:disable Style/OptionalBooleanParameter
|
||||
if (award_class = "Badges::#{badge_slug.classify}".safe_constantize)
|
||||
award_class.call
|
||||
else
|
||||
|
|
@ -12,6 +12,7 @@ module BadgeAchievements
|
|||
User.where(username: usernames),
|
||||
badge_slug,
|
||||
message,
|
||||
include_default_description: include_default_description,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
class AddIncludeBadgeDescriptionInEmail < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :badge_achievements, :include_default_description, :boolean, default: true, null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_02_12_142953) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_02_27_181740) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "citext"
|
||||
enable_extension "ltree"
|
||||
|
|
@ -200,6 +200,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_02_12_142953) do
|
|||
create_table "badge_achievements", force: :cascade do |t|
|
||||
t.bigint "badge_id", null: false
|
||||
t.datetime "created_at", precision: nil, null: false
|
||||
t.boolean "include_default_description", default: true, null: false
|
||||
t.bigint "rewarder_id"
|
||||
t.text "rewarding_context_message"
|
||||
t.text "rewarding_context_message_markdown"
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ RSpec.describe "/admin/content_manager/badge_achievements" do
|
|||
message_markdown: "you got a badge nice one"
|
||||
}
|
||||
expect(BadgeAchievements::BadgeAwardWorker).to have_received(:perform_async).with(
|
||||
usernames_array, badge.slug, "you got a badge nice one"
|
||||
usernames_array, badge.slug, "you got a badge nice one", false
|
||||
)
|
||||
expect(request.flash[:success]).to include("Badges are being rewarded. The task will finish shortly.")
|
||||
end
|
||||
|
|
@ -60,7 +60,10 @@ RSpec.describe "/admin/content_manager/badge_achievements" do
|
|||
message_markdown: "Hinder me? Thou fool. No living man may hinder me!"
|
||||
}
|
||||
expect(BadgeAchievements::BadgeAwardWorker).to have_received(:perform_async).with(
|
||||
usernames_array, badge.slug, "Hinder me? Thou fool. No living man may hinder me!"
|
||||
usernames_array,
|
||||
badge.slug,
|
||||
"Hinder me? Thou fool. No living man may hinder me!",
|
||||
false,
|
||||
)
|
||||
expect(request.flash[:success]).to include("Badges are being rewarded. The task will finish shortly.")
|
||||
end
|
||||
|
|
@ -72,9 +75,43 @@ RSpec.describe "/admin/content_manager/badge_achievements" do
|
|||
usernames: usernames_string,
|
||||
message_markdown: ""
|
||||
}
|
||||
expect(BadgeAchievements::BadgeAwardWorker).to have_received(:perform_async).with(usernames_array,
|
||||
badge.slug,
|
||||
expected_message)
|
||||
expect(BadgeAchievements::BadgeAwardWorker)
|
||||
.to have_received(:perform_async).with(usernames_array,
|
||||
badge.slug,
|
||||
expected_message,
|
||||
false)
|
||||
expect(request.flash[:success]).to include("Badges are being rewarded. The task will finish shortly.")
|
||||
end
|
||||
|
||||
it "includes default description if passed as true" do
|
||||
allow(BadgeAchievements::BadgeAwardWorker).to receive(:perform_async)
|
||||
post admin_badge_achievements_award_badges_path, params: {
|
||||
badge: badge.slug,
|
||||
usernames: usernames_string,
|
||||
message_markdown: "",
|
||||
include_default_description: "1"
|
||||
}
|
||||
expect(BadgeAchievements::BadgeAwardWorker)
|
||||
.to have_received(:perform_async).with(usernames_array,
|
||||
badge.slug,
|
||||
expected_message,
|
||||
true)
|
||||
expect(request.flash[:success]).to include("Badges are being rewarded. The task will finish shortly.")
|
||||
end
|
||||
|
||||
it "does not include default description if passed as false" do
|
||||
allow(BadgeAchievements::BadgeAwardWorker).to receive(:perform_async)
|
||||
post admin_badge_achievements_award_badges_path, params: {
|
||||
badge: badge.slug,
|
||||
usernames: usernames_string,
|
||||
message_markdown: "",
|
||||
include_default_description: "0"
|
||||
}
|
||||
expect(BadgeAchievements::BadgeAwardWorker)
|
||||
.to have_received(:perform_async).with(usernames_array,
|
||||
badge.slug,
|
||||
expected_message,
|
||||
false)
|
||||
expect(request.flash[:success]).to include("Badges are being rewarded. The task will finish shortly.")
|
||||
end
|
||||
|
||||
|
|
@ -83,8 +120,11 @@ RSpec.describe "/admin/content_manager/badge_achievements" do
|
|||
usernames: usernames_string,
|
||||
message_markdown: ""
|
||||
}
|
||||
expect(BadgeAchievements::BadgeAwardWorker).not_to have_received(:perform_async).with(usernames_array,
|
||||
badge.slug, "")
|
||||
expect(BadgeAchievements::BadgeAwardWorker)
|
||||
.not_to have_received(:perform_async).with(usernames_array,
|
||||
badge.slug,
|
||||
"",
|
||||
false)
|
||||
end
|
||||
|
||||
it "does not award a badge if the username provided is not lowercase" do
|
||||
|
|
@ -93,8 +133,11 @@ RSpec.describe "/admin/content_manager/badge_achievements" do
|
|||
usernames: user.username.upcase,
|
||||
message_markdown: ""
|
||||
}
|
||||
expect(BadgeAchievements::BadgeAwardWorker).not_to have_received(:perform_async).with(user.username.upcase,
|
||||
badge.slug, "")
|
||||
expect(BadgeAchievements::BadgeAwardWorker)
|
||||
.not_to have_received(:perform_async).with(user.username.upcase,
|
||||
badge.slug,
|
||||
"",
|
||||
false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,21 +8,31 @@ RSpec.describe Badges::Award, type: :service do
|
|||
|
||||
it "awards badges" do
|
||||
expect do
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!")
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!", include_default_description: true)
|
||||
end.to change(BadgeAchievement, :count).by(2)
|
||||
end
|
||||
|
||||
it "creates correct badge achievements" do
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!")
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!", include_default_description: true)
|
||||
expect(user.badge_achievements.pluck(:badge_id)).to eq([badge.id])
|
||||
expect(user2.badge_achievements.pluck(:rewarding_context_message_markdown))
|
||||
.to eq(["Congrats on a badge!"])
|
||||
end
|
||||
|
||||
it "creates correct badge achievements without default description" do
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!", include_default_description: false)
|
||||
expect(user.badge_achievements.pluck(:include_default_description)).to eq([false])
|
||||
end
|
||||
|
||||
it "creates correct badge achievements with default description" do
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!", include_default_description: true)
|
||||
expect(user.badge_achievements.pluck(:include_default_description)).to eq([true])
|
||||
end
|
||||
|
||||
it "doesn't award badges to spam accounts" do
|
||||
spammer = create(:user, username: "spam_account")
|
||||
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!")
|
||||
described_class.call(User.all, "one-year-club", "Congrats on a badge!", include_default_description: true)
|
||||
expect(user.badge_achievements.any?).to be true
|
||||
expect(spammer.badge_achievements.any?).to be false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,16 +15,16 @@ RSpec.describe BadgeAchievements::BadgeAwardWorker, type: :worker do
|
|||
it "sends badge email" do
|
||||
relation = User.none
|
||||
allow(User).to receive(:where).with(username: ["jess"]).and_return(relation)
|
||||
worker.perform(["jess"], "test", "yo")
|
||||
worker.perform(["jess"], "test", "yo", false)
|
||||
|
||||
expect(Badges::Award).to have_received(:call).with(relation, "test", "yo")
|
||||
expect(Badges::Award).to have_received(:call).with(relation, "test", "yo", include_default_description: false)
|
||||
end
|
||||
end
|
||||
|
||||
context "with predefined badge_slug" do
|
||||
it "sends badge email" do
|
||||
allow(Badges::AwardYearlyClub).to receive(:call)
|
||||
worker.perform([], "award_yearly_club", "")
|
||||
worker.perform([], "award_yearly_club", "", true)
|
||||
|
||||
expect(Badges::AwardYearlyClub).to have_received(:call)
|
||||
expect(Badges::Award).not_to have_received(:call)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue