0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-14 13:58:53 +08:00
discourse/plugins/poll/app/serializers/poll_serializer.rb
Gabriel Grubba 1e48a5d535
FIX: Respect poll result visibility for ranked-choice outcomes (#40578)
Ranked-choice polls always serialized `ranked_choice_outcome` (the
computed winner and round-by-round activity) regardless of the poll's
`results` setting, unlike vote counts and voter lists, which are gated
by `Poll#can_see_results?`.


The outcome was therefore displayed to users who should not yet see
results on `on_vote`, `on_close`, and `staff_only` polls — through the
topic view, the vote/remove-vote responses, and the MessageBus
broadcast.

- Gate `ranked_choice_outcome` in `PollSerializer` behind
`can_see_results?`, matching the other result fields.
- Stop manually re-appending the outcome in `vote`/`remove_vote` so the
serializer's gating is authoritative.
- Serialize the MessageBus payload as an anonymous user so the
topic-wide broadcast only carries data any viewer may see, matching
`PollsUpdater.publish_changes`.
- Don't render an empty results panel to a non-staff voter on a
`staff_only` poll; show the ballot and the staff-only notice instead.



Relates to /t/-/185090
And Patch 998

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 12:04:38 -03:00

88 lines
1.6 KiB
Ruby
Vendored

# frozen_string_literal: true
class PollSerializer < ApplicationSerializer
attributes :id,
:name,
:type,
:status,
:public,
:dynamic,
:results,
:min,
:max,
:step,
:options,
:voters,
:close,
:preloaded_voters,
:chart_type,
:groups,
:title,
:ranked_choice_outcome
def public
true
end
def include_public?
object.everyone?
end
def include_min?
object.min.present? && (object.number? || object.multiple?)
end
def include_max?
object.max.present? && (object.number? || object.multiple?)
end
def include_step?
object.step.present? && object.number?
end
def include_groups?
groups.present?
end
def options
can_see_results = object.can_see_results?(scope.user)
object.poll_options.map do |option|
PollOptionSerializer.new(
option,
root: false,
scope: {
can_see_results: can_see_results,
},
).as_json
end
end
def voters
object.voters_count + object.anonymous_voters.to_i
end
def close
object.close_at
end
def include_close?
object.close_at.present?
end
def preloaded_voters
DiscoursePoll::Poll.serialized_voters(object)
end
def include_preloaded_voters?
object.can_see_voters?(scope.user)
end
def include_ranked_choice_outcome?
object.ranked_choice? && object.can_see_results?(scope.user)
end
def ranked_choice_outcome
DiscoursePoll::RankedChoice.outcome(object.id)
end
end