+ def vote(user, %{data: %{"type" => "Question"}} = object, choices) do
+ with :ok <- validate_not_author(object, user),
+ :ok <- validate_existing_votes(user, object),
+ {:ok, options, choices} <- normalize_and_validate_choices(choices, object) do
+ answer_activities =
+ Enum.map(choices, fn index ->
+ answer_data = make_answer_data(user, object, Enum.at(options, index)["name"])
+
+ {:ok, activity} =
+ ActivityPub.create(%{
+ to: answer_data["to"],
+ actor: user,
+ context: object.data["context"],
+ object: answer_data,
+ additional: %{"cc" => answer_data["cc"]}
+ })
+
+ activity
+ end)
+
+ object = Object.get_cached_by_ap_id(object.data["id"])
+ {:ok, answer_activities, object}
+ end
+ end
+
+ defp validate_not_author(%{data: %{"actor" => ap_id}}, %{ap_id: ap_id}),
+ do: {:error, dgettext("errors", "Poll's author can't vote")}
+
+ defp validate_not_author(_, _), do: :ok
+
+ defp validate_existing_votes(%{ap_id: ap_id}, object) do
+ if Utils.get_existing_votes(ap_id, object) == [] do
+ :ok
+ else
+ {:error, dgettext("errors", "Already voted")}
+ end
+ end
+
+ defp get_options_and_max_count(%{data: %{"anyOf" => any_of}}), do: {any_of, Enum.count(any_of)}
+ defp get_options_and_max_count(%{data: %{"oneOf" => one_of}}), do: {one_of, 1}
+
+ defp normalize_and_validate_choices(choices, object) do
+ choices = Enum.map(choices, fn i -> if is_binary(i), do: String.to_integer(i), else: i end)
+ {options, max_count} = get_options_and_max_count(object)
+ count = Enum.count(options)
+
+ with {_, true} <- {:valid_choice, Enum.all?(choices, &(&1 < count))},
+ {_, true} <- {:count_check, Enum.count(choices) <= max_count} do
+ {:ok, options, choices}
+ else
+ {:valid_choice, _} -> {:error, dgettext("errors", "Invalid indices")}
+ {:count_check, _} -> {:error, dgettext("errors", "Too many choices")}
+ end
+ end
+
+ def public_announce?(_, %{"visibility" => visibility})
+ when visibility in ~w{public unlisted private direct},
+ do: visibility in ~w(public unlisted)
+
+ def public_announce?(object, _) do
+ Visibility.is_public?(object)
+ end
+
+ def get_visibility(_, _, %Participation{}), do: {"direct", "direct"}
+
+ def get_visibility(%{"visibility" => visibility}, in_reply_to, _)