discourse/spec/services
Régis Hanol 5cef23ef63
FEATURE: award badges based on topic votes received (#39493)
Adds four tiered badges to discourse-topic-voting so topic authors are
recognized when their ideas get traction:

- Daydreamer (Bronze, 1 vote)
- Brainstormer (Silver, 5 votes)
- Innovator (Silver, 15 votes)
- Visionary (Gold, 25 votes)

Badges are multi-grant and tied to the qualifying topic's first post, so
a user earns each tier once per topic that reaches the threshold.
Self-votes (voting on your own topic) are excluded. All badges are
disabled by default; the Silver and Gold tiers (Brainstormer, Innovator,
Visionary) allow the badge to be used as a title when enabled.

<img width="287" height="216" alt="image"
src="https://github.com/user-attachments/assets/3a84c2b2-6157-4504-b5a5-45e7c660e90c"
/>


### How it is wired

- `Votes::Cast` enqueues a `BackfillBadges` job after a vote is cast.
- `TopicMerger.merge` and `VoteReclaim` also enqueue the job so merged
or reclaimed topics are re-evaluated immediately rather than having to
wait for the daily consistency pass.
- The job calls `BadgeGranter.backfill` scoped to the topic's first
post. Queries join `badge_posts`, which already filters out deleted and
unlisted topics, read-restricted categories, and categories with
`allow_badges` disabled.
- Each query returns `granted_at` as the timestamp of the Nth qualifying
vote (via `ROW_NUMBER()`), so every tier reflects when that threshold
was actually crossed rather than when the most recent vote landed.
- Revocation (vote removed, topic deleted, category changed) runs on the
daily full backfill via `auto_revoke`, consistent with how
discourse-solved handles the same pattern.

### Notification handling

To avoid "granted badge" notification avalanches when the feature is
first enabled on a community with years of existing votes, core now
exposes a `:badge_granter_suppress_notification` modifier. The plugin
registers it and suppresses notifications for its four badges when the
qualifying vote is older than 2 weeks. Combined with the per-tier
`granted_at`, this means only the tier the author just crossed produces
a notification; lower tiers whose threshold was reached long ago stay
silent.

Ref - t/182304
2026-04-28 10:53:06 +10:00
..
admin/search DEV: Refactor admin search index into a service (#38959) 2026-04-01 10:09:10 +10:00
admin_notices FEATURE: Add problem checks page to admin panel and allow ignoring problem checks (#39103) 2026-04-23 08:28:33 +08:00
categories FEATURE: Add "Ideas" category type for Topic Voting plugin (#38794) 2026-04-14 11:27:18 +02:00
category DEV: Refactor category hierarchical search (#37609) 2026-02-13 09:43:56 +01:00
discourse_id FEATURE: Discourse ID setting page (#36316) 2025-12-11 16:04:09 +01:00
flags DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
groups FIX: Logging hole in group user histories and convert group create to service (#37054) 2026-02-06 13:34:58 +10:00
nested_topic DEV: Fix a shoulda-matchers warning (#39429) 2026-04-22 11:26:07 +02:00
notification/action FEATURE: Notify admins of upcoming changes and log events (#37003) 2026-01-21 12:45:54 +10:00
notifications FEATURE: Consolidate link notifications (#26567) 2024-04-09 11:53:37 -06:00
problem_check DEV: Remove enable_upcoming_changes global setting gate (#38360) 2026-03-10 10:08:20 +10:00
site_setting FEATURE: better email subject lines (#36040) 2026-04-24 15:14:10 +04:00
tags FIX: don't mislabel allowed tags as disabled in composer tag search (#39300) 2026-04-16 09:01:51 +02:00
themes DEV: Don't use fab! for non-ActiveRecord model objects (#39272) 2026-04-15 00:52:34 +02:00
upcoming_changes PERF: Cache has_unseen_features? to avoid per-request git shell-outs (#39532) 2026-04-24 10:16:46 -07:00
user SECURITY: Enforce edit permissions and audit trail in TriggerPostAction 2026-03-19 15:21:28 +00:00
video_conversion FEATURE: Add opt-in IAM role assumption for S3 operations (#39174) 2026-04-15 04:29:50 +08:00
anonymous_shadow_creator_spec.rb UX: Improve naming for anonymous mode settings (#31832) 2025-03-21 04:54:06 +03:00
auto_silence_spec.rb FIX: Moderator notifications when new post auto-silences a user (#35403) 2025-10-15 16:07:56 +08:00
badge_granter_spec.rb FEATURE: award badges based on topic votes received (#39493) 2026-04-28 10:53:06 +10:00
base_bookmarkable_spec.rb
category_hashtag_data_source_spec.rb FIX: Use unaccent() for category name/slug search (#37622) 2026-02-12 08:43:23 +01:00
color_scheme_revisor_spec.rb FEATURE: Ability to set palettes as default on theme (#34480) 2025-08-27 09:09:34 +08:00
destroy_task_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
email_settings_exception_handler_spec.rb DEV: Remove IMAP support in Discourse (#37002) 2026-01-12 10:07:26 +10:00
email_settings_validator_spec.rb DEV: Remove IMAP support in Discourse (#37002) 2026-01-12 10:07:26 +10:00
email_style_updater_spec.rb
external_upload_manager_spec.rb FIX: Enforce content disposition on S3 presigned URLs (#38850) 2026-03-27 10:04:00 +08:00
flag_sockpuppets_spec.rb
group_action_logger_spec.rb PERF: Add Users to Group Improvements Pt. 2 (#38901) 2026-04-06 09:31:50 -05:00
group_mentions_updater_spec.rb
group_message_spec.rb FIX: crawler view with unicode usernames (#27051) 2024-05-16 17:11:24 +02:00
hashtag_autocomplete_service_spec.rb DEV: Move canonical tag routes to /tag/slug/id keeping /tag/name support (#37055) 2026-02-11 10:21:19 +08:00
heat_settings_updater_spec.rb
hub_push_notification_pusher_spec.rb DEV: Centralize push notification delivery into a single job (#39207) 2026-04-14 11:55:45 -03:00
inline_uploads_multisite_spec.rb
inline_uploads_spec.rb FIX: Escape markdown characters in upload filenames (#39133) 2026-04-14 10:37:41 +02:00
locale_normalizer_spec.rb FIX: Show localization for regionless locale if they exist (#33702) 2025-07-21 15:45:14 +08:00
notification_emailer_spec.rb FIX: Send notification emails for staff-coloured posts (#37712) 2026-02-11 13:26:27 +01:00
post_action_notifier_spec.rb
post_alerter_spec.rb FEATURE: better email subject lines (#36040) 2026-04-24 15:14:10 +04:00
post_bookmarkable_spec.rb DEV: Remove unnecessary rails_helper requiring (#26364) 2024-03-26 11:32:01 +01:00
post_owner_changer_spec.rb SECURITY: Ensure moderator can see post/topic before allowing them to change owner 2026-01-28 17:11:14 +00:00
problem_check_spec.rb FIX: prevent cleanup_trackers from unintentionally deleting all trackers (#37619) 2026-02-09 09:43:13 -03:00
push_notification_pusher_spec.rb DEV: Centralize push notification delivery into a single job (#39207) 2026-04-14 11:55:45 -03:00
random_topic_selector_spec.rb
search_indexer_spec.rb FEATURE: Add full-text search for chat messages (#34704) 2025-10-22 11:30:35 +02:00
sidebar_site_settings_backfiller_spec.rb DEV: Hand-pick Rails/WhereNot autofixes (#35117) 2025-10-03 13:29:22 +02:00
site_settings_spec.rb DEV: Refactor and add missing tests for upload type in objects schema (#36285) 2025-11-28 04:02:23 +08:00
staff_action_logger_spec.rb FEATURE: Automatic promotion of upcoming changes (#36211) 2025-12-08 15:49:34 +10:00
tag_hashtag_data_source_spec.rb FIX: Tag search 500 when content_localization is enabled (#37769) 2026-02-12 23:58:03 +08:00
tag_settings_updater_spec.rb FIX: Clean localized tag names (#38488) 2026-03-12 12:54:50 +08:00
theme_settings_migrations_runner_spec.rb DEV: Add category helper for theme migration (#38816) 2026-03-24 16:50:37 +08:00
themes_spec.rb DEV: Fix constant redefinition warnings when running specs (#29837) 2024-11-20 15:17:36 +11:00
topic_bookmarkable_spec.rb DEV: Remove unnecessary rails_helper requiring (#26364) 2024-03-26 11:32:01 +01:00
topic_status_updater_spec.rb DEV: Add automatically closed topics event trigger and adapt the automation plugin's auto tag_topic_script accordingly (#39235) 2026-04-14 14:50:36 -05:00
topic_timestamp_changer_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
trust_level_granter_spec.rb
user_activator_spec.rb DEV: Change authenticated_with_oauth to use key instead of string. (#38381) 2026-03-10 18:31:32 -03:00
user_anonymizer_spec.rb FIX: Block associated account emails on user delete and anonymize (#37638) 2026-02-09 18:26:35 +01:00
user_authenticator_spec.rb FIX: do not enforce 2fa when an account is created with OAuth (#28625) 2024-08-29 11:19:04 +10:00
user_destroyer_spec.rb FIX: Delete reviewable notes before reviewables during user deletion (#38709) 2026-03-19 16:12:53 +08:00
user_merger_spec.rb FIX: blocks post action merge for duplicated flags (#36265) 2025-11-26 16:17:57 -03:00
user_notification_schedule_processor_spec.rb
user_password_expirer_spec.rb DEV: Migrate user passwords data to UserPassword table (#28746) 2024-10-10 09:23:06 +08:00
user_silencer_spec.rb
user_stat_count_updater_spec.rb DEV: Don’t replace Rails logger in specs (#29721) 2024-11-13 08:47:39 +08:00
user_suspender_spec.rb SECURITY: Don't allow suspending staff users via other_user_ids param 2024-07-03 20:49:29 +08:00
user_updater_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
username_changer_spec.rb FIX: Preserve display name in quotes when using the rich text editor (#38078) 2026-02-26 21:04:07 +01:00
username_checker_service_spec.rb
wildcard_domain_checker_spec.rb
wildcard_url_checker_spec.rb
word_watcher_spec.rb FIX: unify watched word boundary regex across Ruby and JS engines (#37965) 2026-02-23 16:30:10 +01:00