WIP: post editing #103
7 changed files with 245 additions and 23 deletions
|
@ -425,4 +425,24 @@ def object_data_hashtags(%{"tag" => tags}) when is_list(tags) do
|
|||
end
|
||||
|
||||
def object_data_hashtags(_), do: []
|
||||
|
||||
def history_for(object) do
|
||||
with history <- Map.get(object, "formerRepresentations"),
|
||||
true <- is_map(history),
|
||||
"OrderedCollection" <- Map.get(history, "type"),
|
||||
true <- is_list(Map.get(history, "orderedItems")),
|
||||
true <- is_integer(Map.get(history, "totalItems")) do
|
||||
history
|
||||
else
|
||||
_ -> history_skeleton()
|
||||
end
|
||||
end
|
||||
|
||||
defp history_skeleton do
|
||||
%{
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => 0,
|
||||
"orderedItems" => []
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -415,26 +415,6 @@ defp handle_update_user(
|
|||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
defp history_for_object(object) do
|
||||
with history <- Map.get(object, "formerRepresentations"),
|
||||
true <- is_map(history),
|
||||
"OrderedCollection" <- Map.get(history, "type"),
|
||||
true <- is_list(Map.get(history, "orderedItems")),
|
||||
true <- is_integer(Map.get(history, "totalItems")) do
|
||||
history
|
||||
else
|
||||
_ -> history_skeleton()
|
||||
end
|
||||
end
|
||||
|
||||
defp history_skeleton do
|
||||
%{
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => 0,
|
||||
"orderedItems" => []
|
||||
}
|
||||
end
|
||||
|
||||
@updatable_object_types ["Note", "Question"]
|
||||
# We do not allow poll options to be changed, but the poll description can be.
|
||||
@updatable_fields [
|
||||
|
@ -473,7 +453,7 @@ defp maybe_update_history(updated_object, orig_object_data, updated) do
|
|||
else
|
||||
# Put edit history
|
||||
# Note that we may have got the edit history by first fetching the object
|
||||
history = history_for_object(orig_object_data)
|
||||
history = Object.history_for(orig_object_data)
|
||||
|
||||
latest_history_item =
|
||||
orig_object_data
|
||||
|
|
|
@ -6,9 +6,13 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.AccountOperation
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Attachment
|
||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||
|
@ -406,6 +410,29 @@ def bookmarks_operation do
|
|||
}
|
||||
end
|
||||
|
||||
def show_history_operation do
|
||||
%Operation{
|
||||
tags: ["Retrieve status history"],
|
||||
summary: "Status history",
|
||||
description: "View history of a status",
|
||||
operationId: "StatusController.show_history",
|
||||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
parameters: [
|
||||
id_param()
|
||||
],
|
||||
responses: %{
|
||||
200 => status_history_response(),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def show_source_operation do
|
||||
end
|
||||
|
||||
def update_operation do
|
||||
end
|
||||
|
||||
def array_of_statuses do
|
||||
%Schema{type: :array, items: Status, example: [Status.schema().example]}
|
||||
end
|
||||
|
@ -556,6 +583,61 @@ defp status_response do
|
|||
Operation.response("Status", "application/json", Status)
|
||||
end
|
||||
|
||||
defp status_history_response do
|
||||
Operation.response(
|
||||
"Status History",
|
||||
"application/json",
|
||||
%Schema{
|
||||
title: "Status history",
|
||||
description: "Response schema for history of a status",
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
account: %Schema{
|
||||
allOf: [Account],
|
||||
description: "The account that authored this status"
|
||||
},
|
||||
content: %Schema{
|
||||
type: :string,
|
||||
format: :html,
|
||||
description: "HTML-encoded status content"
|
||||
},
|
||||
sensitive: %Schema{
|
||||
type: :boolean,
|
||||
description: "Is this status marked as sensitive content?"
|
||||
},
|
||||
spoiler_text: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"Subject or summary line, below which status content is collapsed until expanded"
|
||||
},
|
||||
created_at: %Schema{
|
||||
type: :string,
|
||||
format: "date-time",
|
||||
description: "The date when this status was created"
|
||||
},
|
||||
media_attachments: %Schema{
|
||||
type: :array,
|
||||
items: Attachment,
|
||||
description: "Media that is attached to this status"
|
||||
},
|
||||
emojis: %Schema{
|
||||
type: :array,
|
||||
items: Emoji,
|
||||
description: "Custom emoji to be used when rendering status content"
|
||||
},
|
||||
poll: %Schema{
|
||||
allOf: [Poll],
|
||||
nullable: true,
|
||||
description: "The poll attached to the status"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
defp context do
|
||||
%Schema{
|
||||
title: "StatusContext",
|
||||
|
|
|
@ -37,7 +37,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
when action in [
|
||||
:index,
|
||||
:show,
|
||||
:context
|
||||
:context,
|
||||
:show_history,
|
||||
:show_source
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -48,7 +50,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
:create,
|
||||
:delete,
|
||||
:reblog,
|
||||
:unreblog
|
||||
:unreblog,
|
||||
:update
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -190,6 +193,29 @@ def create(%{assigns: %{user: _user}, body_params: %{media_ids: _} = params} = c
|
|||
create(%Plug.Conn{conn | body_params: params}, %{})
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id/history"
|
||||
def show_history(%{assigns: %{user: user}} = conn, %{id: id} = params) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
try_render(conn, "history.json",
|
||||
activity: activity,
|
||||
for: user,
|
||||
with_direct_conversation_id: true,
|
||||
with_muted: Map.get(params, :with_muted, false)
|
||||
)
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id/source"
|
||||
def show_source(%{assigns: %{user: _user}} = _conn, %{id: _id} = _params) do
|
||||
end
|
||||
|
||||
@doc "PUT /api/v1/statuses/:id"
|
||||
def update(%{assigns: %{user: _user}} = _conn, %{id: _id} = _params) do
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id"
|
||||
def show(%{assigns: %{user: user}} = conn, %{id: id} = params) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
|
|
|
@ -390,6 +390,71 @@ def render("show.json", _) do
|
|||
nil
|
||||
end
|
||||
|
||||
def render("history.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
hashtags = Object.hashtags(object)
|
||||
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
|
||||
past_history =
|
||||
Object.history_for(object.data)
|
||||
|> Map.get("orderedItems")
|
||||
|> Enum.map(&Map.put(&1, "id", object.data["id"]))
|
||||
|> Enum.map(&%Object{data: &1, id: object.id})
|
||||
|
||||
history = [object | past_history]
|
||||
|
||||
individual_opts =
|
||||
opts
|
||||
|> Map.put(:as, :object)
|
||||
|> Map.put(:user, user)
|
||||
|> Map.put(:hashtags, hashtags)
|
||||
|
||||
render_many(history, StatusView, "history_item.json", individual_opts)
|
||||
end
|
||||
|
||||
def render(
|
||||
"history_item.json",
|
||||
%{activity: activity, user: user, object: object, hashtags: hashtags} = opts
|
||||
) do
|
||||
sensitive = object.data["sensitive"] || Enum.member?(hashtags, "nsfw")
|
||||
|
||||
attachment_data = object.data["attachment"] || []
|
||||
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
||||
|
||||
created_at = Utils.to_masto_date(object.data["updated"] || object.data["published"])
|
||||
|
||||
content =
|
||||
object
|
||||
|> render_content()
|
||||
|
||||
content_html =
|
||||
content
|
||||
|> Activity.HTML.get_cached_scrubbed_html_for_activity(
|
||||
User.html_filter_policy(opts[:for]),
|
||||
activity,
|
||||
"mastoapi:content"
|
||||
)
|
||||
|
||||
summary = object.data["summary"] || ""
|
||||
|
||||
%{
|
||||
account:
|
||||
AccountView.render("show.json", %{
|
||||
user: user,
|
||||
for: opts[:for]
|
||||
}),
|
||||
content: content_html,
|
||||
sensitive: sensitive,
|
||||
spoiler_text: summary,
|
||||
created_at: created_at,
|
||||
media_attachments: attachments,
|
||||
emojis: build_emojis(object.data["emoji"]),
|
||||
poll: render(PollView, "show.json", object: object, for: opts[:for])
|
||||
}
|
||||
end
|
||||
|
||||
def render("card.json", %{rich_media: rich_media, page_url: page_url}) do
|
||||
page_url_data = URI.parse(page_url)
|
||||
|
||||
|
|
|
@ -537,6 +537,9 @@ defmodule Pleroma.Web.Router do
|
|||
get("/bookmarks", StatusController, :bookmarks)
|
||||
|
||||
post("/statuses", StatusController, :create)
|
||||
get("/statuses/:id/history", StatusController, :show_history)
|
||||
get("/statuses/:id/source", StatusController, :show_source)
|
||||
put("/statuses/:id", StatusController, :update)
|
||||
delete("/statuses/:id", StatusController, :delete)
|
||||
post("/statuses/:id/reblog", StatusController, :reblog)
|
||||
post("/statuses/:id/unreblog", StatusController, :unreblog)
|
||||
|
|
|
@ -2040,6 +2040,52 @@ test "posting a quote of a status that doesn't exist", %{conn: conn} do
|
|||
"quote_id" => "oops"
|
||||
})
|
||||
|> json_response_and_validate_schema(422)
|
||||
end
|
||||
end
|
||||
|
||||
describe "get status history" do
|
||||
setup do
|
||||
oauth_access(["read:statuses"])
|
||||
end
|
||||
|
||||
test "unedited post", %{conn: conn} do
|
||||
activity = insert(:note_activity)
|
||||
|
||||
conn = get(conn, "/api/v1/statuses/#{activity.id}/history")
|
||||
|
||||
assert [_] = json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
|
||||
test "edited post", %{conn: conn} do
|
||||
note =
|
||||
insert(
|
||||
:note,
|
||||
data: %{
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "mew mew 2",
|
||||
"summary" => "title 2"
|
||||
},
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "mew mew 1",
|
||||
"summary" => "title 1"
|
||||
}
|
||||
],
|
||||
"totalItems" => 2
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note)
|
||||
|
||||
conn = get(conn, "/api/v1/statuses/#{activity.id}/history")
|
||||
|
||||
assert [_, %{"spoiler_text" => "title 2"}, %{"spoiler_text" => "title 1"}] =
|
||||
json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue