2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-06-06 11:04:56 -04:00
|
|
|
module DiscoursePoll
|
|
|
|
class PollsValidator
|
2018-12-31 04:48:30 -05:00
|
|
|
MAX_VALUE = 2_147_483_647
|
|
|
|
|
2016-06-06 11:04:56 -04:00
|
|
|
def initialize(post)
|
|
|
|
@post = post
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate_polls
|
|
|
|
polls = {}
|
|
|
|
|
2018-05-03 04:32:01 -04:00
|
|
|
DiscoursePoll::Poll
|
|
|
|
.extract(@post.raw, @post.topic_id, @post.user_id)
|
|
|
|
.each do |poll|
|
2018-12-31 04:48:30 -05:00
|
|
|
return false unless valid_arguments?(poll)
|
|
|
|
return false unless valid_numbers?(poll)
|
2016-06-06 11:04:56 -04:00
|
|
|
return false unless unique_poll_name?(polls, poll)
|
|
|
|
return false unless unique_options?(poll)
|
2020-01-03 09:09:36 -05:00
|
|
|
return false unless any_blank_options?(poll)
|
2020-02-05 09:03:27 -05:00
|
|
|
return false unless at_least_one_option?(poll)
|
2016-06-06 11:04:56 -04:00
|
|
|
return false unless valid_number_of_options?(poll)
|
|
|
|
return false unless valid_multiple_choice_settings?(poll)
|
|
|
|
polls[poll["name"]] = poll
|
|
|
|
end
|
|
|
|
|
|
|
|
polls
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2018-12-31 04:48:30 -05:00
|
|
|
def valid_arguments?(poll)
|
|
|
|
valid = true
|
|
|
|
|
|
|
|
if poll["type"].present? && !::Poll.types.has_key?(poll["type"])
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.invalid_argument", argument: "type", value: poll["type"]),
|
|
|
|
)
|
|
|
|
valid = false
|
|
|
|
end
|
|
|
|
|
|
|
|
if poll["status"].present? && !::Poll.statuses.has_key?(poll["status"])
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.invalid_argument", argument: "status", value: poll["status"]),
|
|
|
|
)
|
|
|
|
valid = false
|
|
|
|
end
|
|
|
|
|
|
|
|
if poll["results"].present? && !::Poll.results.has_key?(poll["results"])
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.invalid_argument", argument: "results", value: poll["results"]),
|
|
|
|
)
|
|
|
|
valid = false
|
|
|
|
end
|
|
|
|
|
|
|
|
valid
|
|
|
|
end
|
|
|
|
|
2016-06-06 11:04:56 -04:00
|
|
|
def unique_poll_name?(polls, poll)
|
|
|
|
if polls.has_key?(poll["name"])
|
|
|
|
if poll["name"] == ::DiscoursePoll::DEFAULT_POLL_NAME
|
|
|
|
@post.errors.add(:base, I18n.t("poll.multiple_polls_without_name"))
|
|
|
|
else
|
|
|
|
@post.errors.add(:base, I18n.t("poll.multiple_polls_with_same_name", name: poll["name"]))
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def unique_options?(poll)
|
|
|
|
if poll["options"].map { |o| o["id"] }.uniq.size != poll["options"].size
|
|
|
|
if poll["name"] == ::DiscoursePoll::DEFAULT_POLL_NAME
|
|
|
|
@post.errors.add(:base, I18n.t("poll.default_poll_must_have_different_options"))
|
|
|
|
else
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.named_poll_must_have_different_options", name: poll["name"]),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2020-01-03 09:09:36 -05:00
|
|
|
def any_blank_options?(poll)
|
|
|
|
if poll["options"].any? { |o| o["html"].blank? }
|
|
|
|
if poll["name"] == ::DiscoursePoll::DEFAULT_POLL_NAME
|
|
|
|
@post.errors.add(:base, I18n.t("poll.default_poll_must_not_have_any_empty_options"))
|
|
|
|
else
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.named_poll_must_not_have_any_empty_options", name: poll["name"]),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2020-02-05 09:03:27 -05:00
|
|
|
def at_least_one_option?(poll)
|
|
|
|
if poll["options"].size < 1
|
2016-06-06 11:04:56 -04:00
|
|
|
if poll["name"] == ::DiscoursePoll::DEFAULT_POLL_NAME
|
2020-02-05 09:03:27 -05:00
|
|
|
@post.errors.add(:base, I18n.t("poll.default_poll_must_have_at_least_1_option"))
|
2016-06-06 11:04:56 -04:00
|
|
|
else
|
2020-02-05 09:03:27 -05:00
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.named_poll_must_have_at_least_1_option", name: poll["name"]),
|
|
|
|
)
|
2016-06-06 11:04:56 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def valid_number_of_options?(poll)
|
|
|
|
if poll["options"].size > SiteSetting.poll_maximum_options
|
|
|
|
if poll["name"] == ::DiscoursePoll::DEFAULT_POLL_NAME
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t(
|
|
|
|
"poll.default_poll_must_have_less_options",
|
|
|
|
count: SiteSetting.poll_maximum_options,
|
2023-01-06 15:42:16 -05:00
|
|
|
),
|
2016-06-06 11:04:56 -04:00
|
|
|
)
|
|
|
|
else
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t(
|
|
|
|
"poll.named_poll_must_have_less_options",
|
|
|
|
name: poll["name"],
|
|
|
|
count: SiteSetting.poll_maximum_options,
|
2023-01-06 15:42:16 -05:00
|
|
|
),
|
2016-06-06 11:04:56 -04:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def valid_multiple_choice_settings?(poll)
|
|
|
|
if poll["type"] == "multiple"
|
2018-11-19 08:50:00 -05:00
|
|
|
options = poll["options"].size
|
2016-06-06 11:04:56 -04:00
|
|
|
min = (poll["min"].presence || 1).to_i
|
2018-11-19 08:50:00 -05:00
|
|
|
max = (poll["max"].presence || options).to_i
|
2016-06-06 11:04:56 -04:00
|
|
|
|
2018-11-19 08:50:00 -05:00
|
|
|
if min > max || min <= 0 || max <= 0 || max > options || min >= options
|
2016-06-06 11:04:56 -04:00
|
|
|
if poll["name"] == ::DiscoursePoll::DEFAULT_POLL_NAME
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.default_poll_with_multiple_choices_has_invalid_parameters"),
|
|
|
|
)
|
|
|
|
else
|
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t(
|
|
|
|
"poll.named_poll_with_multiple_choices_has_invalid_parameters",
|
|
|
|
name: poll["name"],
|
2023-01-06 15:42:16 -05:00
|
|
|
),
|
2016-06-06 11:04:56 -04:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
2018-12-31 04:48:30 -05:00
|
|
|
|
|
|
|
def valid_numbers?(poll)
|
|
|
|
return true if poll["type"] != "number"
|
|
|
|
|
|
|
|
valid = true
|
|
|
|
|
|
|
|
min = poll["min"].to_i
|
|
|
|
max = (poll["max"].presence || MAX_VALUE).to_i
|
|
|
|
step = (poll["step"].presence || 1).to_i
|
|
|
|
|
|
|
|
if min < 0
|
|
|
|
@post.errors.add(:base, "Min #{I18n.t("errors.messages.greater_than", count: 0)}")
|
|
|
|
valid = false
|
|
|
|
elsif min > MAX_VALUE
|
|
|
|
@post.errors.add(:base, "Min #{I18n.t("errors.messages.less_than", count: MAX_VALUE)}")
|
|
|
|
valid = false
|
|
|
|
end
|
|
|
|
|
|
|
|
if max < min
|
|
|
|
@post.errors.add(:base, "Max #{I18n.t("errors.messages.greater_than", count: "min")}")
|
|
|
|
valid = false
|
|
|
|
elsif max > MAX_VALUE
|
|
|
|
@post.errors.add(:base, "Max #{I18n.t("errors.messages.less_than", count: MAX_VALUE)}")
|
|
|
|
valid = false
|
|
|
|
end
|
|
|
|
|
|
|
|
if step <= 0
|
|
|
|
@post.errors.add(:base, "Step #{I18n.t("errors.messages.greater_than", count: 0)}")
|
|
|
|
valid = false
|
|
|
|
elsif ((max - min + 1) / step) < 2
|
|
|
|
if poll["name"] == ::DiscoursePoll::DEFAULT_POLL_NAME
|
2020-02-05 09:03:27 -05:00
|
|
|
@post.errors.add(:base, I18n.t("poll.default_poll_must_have_at_least_1_option"))
|
2018-12-31 04:48:30 -05:00
|
|
|
else
|
2020-02-05 09:03:27 -05:00
|
|
|
@post.errors.add(
|
|
|
|
:base,
|
|
|
|
I18n.t("poll.named_poll_must_have_at_least_1_option", name: poll["name"]),
|
|
|
|
)
|
2018-12-31 04:48:30 -05:00
|
|
|
end
|
|
|
|
valid = false
|
|
|
|
end
|
|
|
|
|
|
|
|
valid
|
|
|
|
end
|
2016-06-06 11:04:56 -04:00
|
|
|
end
|
|
|
|
end
|