Extract poll actions from MastodonAPIController to PollController
This commit is contained in:
parent
703deb66d9
commit
36a34c36fe
11 changed files with 454 additions and 419 deletions
|
|
@ -75,4 +75,16 @@ defmodule Pleroma.Web.ControllerHelper do
|
|||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||
end
|
||||
end
|
||||
|
||||
def try_render(conn, target, params)
|
||||
when is_binary(target) do
|
||||
case render(conn, target, params) do
|
||||
nil -> render_error(conn, :not_implemented, "Can't display this activity")
|
||||
res -> res
|
||||
end
|
||||
end
|
||||
|
||||
def try_render(conn, _, _) do
|
||||
render_error(conn, :not_implemented, "Can't display this activity")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP
|
||||
|
|
@ -19,7 +18,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.AppView
|
||||
|
|
@ -117,56 +115,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
json(conn, mastodon_emoji)
|
||||
end
|
||||
|
||||
def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("poll.json", %{object: object, for: user})
|
||||
else
|
||||
error when is_nil(error) or error == false ->
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
end
|
||||
|
||||
defp get_cached_vote_or_vote(user, object, choices) do
|
||||
idempotency_key = "polls:#{user.id}:#{object.data["id"]}"
|
||||
|
||||
{_, res} =
|
||||
Cachex.fetch(:idempotency_cache, idempotency_key, fn _ ->
|
||||
case CommonAPI.vote(user, object, choices) do
|
||||
{:error, _message} = res -> {:ignore, res}
|
||||
res -> {:commit, res}
|
||||
end
|
||||
end)
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
|
||||
with %Object{} = object <- Object.get_by_id(id),
|
||||
true <- object.data["type"] == "Question",
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user),
|
||||
{:ok, _activities, object} <- get_cached_vote_or_vote(user, object, choices) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("poll.json", %{object: object, for: user})
|
||||
else
|
||||
nil ->
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
|
||||
false ->
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
def update_media(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"id" => id, "description" => description} = _
|
||||
|
|
@ -511,18 +459,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def try_render(conn, target, params)
|
||||
when is_binary(target) do
|
||||
case render(conn, target, params) do
|
||||
nil -> render_error(conn, :not_implemented, "Can't display this activity")
|
||||
res -> res
|
||||
end
|
||||
end
|
||||
|
||||
def try_render(conn, _, _) do
|
||||
render_error(conn, :not_implemented, "Can't display this activity")
|
||||
end
|
||||
|
||||
defp present?(nil), do: false
|
||||
defp present?(false), do: false
|
||||
defp present?(_), do: true
|
||||
|
|
|
|||
53
lib/pleroma/web/mastodon_api/controllers/poll_controller.ex
Normal file
53
lib/pleroma/web/mastodon_api/controllers/poll_controller.ex
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.PollController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [try_render: 3, json_response: 3]
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
@doc "GET /api/v1/polls/:id"
|
||||
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
try_render(conn, "show.json", %{object: object, for: user})
|
||||
else
|
||||
error when is_nil(error) or error == false ->
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/polls/:id/votes"
|
||||
def vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
|
||||
with %Object{data: %{"type" => "Question"}} = object <- Object.get_by_id(id),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user),
|
||||
{:ok, _activities, object} <- get_cached_vote_or_vote(user, object, choices) do
|
||||
try_render(conn, "show.json", %{object: object, for: user})
|
||||
else
|
||||
nil -> render_error(conn, :not_found, "Record not found")
|
||||
false -> render_error(conn, :not_found, "Record not found")
|
||||
{:error, message} -> json_response(conn, :unprocessable_entity, %{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
defp get_cached_vote_or_vote(user, object, choices) do
|
||||
idempotency_key = "polls:#{user.id}:#{object.data["id"]}"
|
||||
|
||||
Cachex.fetch!(:idempotency_cache, idempotency_key, fn ->
|
||||
case CommonAPI.vote(user, object, choices) do
|
||||
{:error, _message} = res -> {:ignore, res}
|
||||
res -> {:commit, res}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.MastodonAPI.MastodonAPIController, only: [try_render: 3]
|
||||
import Pleroma.Web.ControllerHelper, only: [try_render: 3]
|
||||
|
||||
require Ecto.Query
|
||||
|
||||
|
|
|
|||
74
lib/pleroma/web/mastodon_api/views/poll_view.ex
Normal file
74
lib/pleroma/web/mastodon_api/views/poll_view.ex
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.PollView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
|
||||
def render("show.json", %{object: object, multiple: multiple, options: options} = params) do
|
||||
{end_time, expired} = end_time_and_expired(object)
|
||||
{options, votes_count} = options_and_votes_count(options)
|
||||
|
||||
%{
|
||||
# Mastodon uses separate ids for polls, but an object can't have
|
||||
# more than one poll embedded so object id is fine
|
||||
id: to_string(object.id),
|
||||
expires_at: end_time,
|
||||
expired: expired,
|
||||
multiple: multiple,
|
||||
votes_count: votes_count,
|
||||
options: options,
|
||||
voted: voted?(params),
|
||||
emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
|
||||
}
|
||||
end
|
||||
|
||||
def render("show.json", %{object: object} = params) do
|
||||
case object.data do
|
||||
%{"anyOf" => options} when is_list(options) ->
|
||||
render(__MODULE__, "show.json", Map.merge(params, %{multiple: true, options: options}))
|
||||
|
||||
%{"oneOf" => options} when is_list(options) ->
|
||||
render(__MODULE__, "show.json", Map.merge(params, %{multiple: false, options: options}))
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp end_time_and_expired(object) do
|
||||
case object.data["closed"] || object.data["endTime"] do
|
||||
end_time when is_binary(end_time) ->
|
||||
end_time = NaiveDateTime.from_iso8601!(end_time)
|
||||
expired = NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) == :lt
|
||||
|
||||
{Utils.to_masto_date(end_time), expired}
|
||||
|
||||
_ ->
|
||||
{nil, false}
|
||||
end
|
||||
end
|
||||
|
||||
defp options_and_votes_count(options) do
|
||||
Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
|
||||
current_count = option["replies"]["totalItems"] || 0
|
||||
|
||||
{%{
|
||||
title: HTML.strip_tags(name),
|
||||
votes_count: current_count
|
||||
}, current_count + count}
|
||||
end)
|
||||
end
|
||||
|
||||
defp voted?(%{object: object} = opts) do
|
||||
if opts[:for] do
|
||||
existing_votes = Pleroma.Web.ActivityPub.Utils.get_existing_votes(opts[:for].ap_id, object)
|
||||
existing_votes != [] or opts[:for].ap_id == object.data["actor"]
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -18,6 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.PollView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
|
|
@ -277,7 +278,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
spoiler_text: summary_html,
|
||||
visibility: get_visibility(object),
|
||||
media_attachments: attachments,
|
||||
poll: render("poll.json", %{object: object, for: opts[:for]}),
|
||||
poll: render(PollView, "show.json", object: object, for: opts[:for]),
|
||||
mentions: mentions,
|
||||
tags: build_tags(tags),
|
||||
application: %{
|
||||
|
|
@ -389,75 +390,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
safe_render_many(opts.activities, StatusView, "listen.json", opts)
|
||||
end
|
||||
|
||||
def render("poll.json", %{object: object} = opts) do
|
||||
{multiple, options} =
|
||||
case object.data do
|
||||
%{"anyOf" => options} when is_list(options) -> {true, options}
|
||||
%{"oneOf" => options} when is_list(options) -> {false, options}
|
||||
_ -> {nil, nil}
|
||||
end
|
||||
|
||||
if options do
|
||||
{end_time, expired} =
|
||||
case object.data["closed"] || object.data["endTime"] do
|
||||
end_time when is_binary(end_time) ->
|
||||
end_time =
|
||||
(object.data["closed"] || object.data["endTime"])
|
||||
|> NaiveDateTime.from_iso8601!()
|
||||
|
||||
expired =
|
||||
end_time
|
||||
|> NaiveDateTime.compare(NaiveDateTime.utc_now())
|
||||
|> case do
|
||||
:lt -> true
|
||||
_ -> false
|
||||
end
|
||||
|
||||
end_time = Utils.to_masto_date(end_time)
|
||||
|
||||
{end_time, expired}
|
||||
|
||||
_ ->
|
||||
{nil, false}
|
||||
end
|
||||
|
||||
voted =
|
||||
if opts[:for] do
|
||||
existing_votes =
|
||||
Pleroma.Web.ActivityPub.Utils.get_existing_votes(opts[:for].ap_id, object)
|
||||
|
||||
existing_votes != [] or opts[:for].ap_id == object.data["actor"]
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
{options, votes_count} =
|
||||
Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
|
||||
current_count = option["replies"]["totalItems"] || 0
|
||||
|
||||
{%{
|
||||
title: HTML.strip_tags(name),
|
||||
votes_count: current_count
|
||||
}, current_count + count}
|
||||
end)
|
||||
|
||||
%{
|
||||
# Mastodon uses separate ids for polls, but an object can't have
|
||||
# more than one poll embedded so object id is fine
|
||||
id: to_string(object.id),
|
||||
expires_at: end_time,
|
||||
expired: expired,
|
||||
multiple: multiple,
|
||||
votes_count: votes_count,
|
||||
options: options,
|
||||
voted: voted,
|
||||
emojis: build_emojis(object.data["emoji"])
|
||||
}
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def render("context.json", %{activity: activity, activities: activities, user: user}) do
|
||||
%{ancestors: ancestors, descendants: descendants} =
|
||||
activities
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ defmodule Pleroma.Web.Router do
|
|||
put("/scheduled_statuses/:id", ScheduledActivityController, :update)
|
||||
delete("/scheduled_statuses/:id", ScheduledActivityController, :delete)
|
||||
|
||||
post("/polls/:id/votes", MastodonAPIController, :poll_vote)
|
||||
post("/polls/:id/votes", PollController, :vote)
|
||||
|
||||
post("/media", MastodonAPIController, :upload)
|
||||
put("/media/:id", MastodonAPIController, :update_media)
|
||||
|
|
@ -488,7 +488,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/statuses/:id", StatusController, :show)
|
||||
get("/statuses/:id/context", StatusController, :context)
|
||||
|
||||
get("/polls/:id", MastodonAPIController, :get_poll)
|
||||
get("/polls/:id", PollController, :show)
|
||||
|
||||
get("/accounts/:id/statuses", AccountController, :statuses)
|
||||
get("/accounts/:id/followers", AccountController, :followers)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue