forked from AkkomaGang/akkoma
Merge branch 'feature/pinned-posts' into 'develop'
Pinned Statuses Closes #440 See merge request pleroma/pleroma!636
This commit is contained in:
commit
7f5efddd6e
19 changed files with 370 additions and 14 deletions
|
@ -138,7 +138,8 @@
|
||||||
],
|
],
|
||||||
finmoji_enabled: true,
|
finmoji_enabled: true,
|
||||||
mrf_transparency: true,
|
mrf_transparency: true,
|
||||||
autofollowed_nicknames: []
|
autofollowed_nicknames: [],
|
||||||
|
max_pinned_statuses: 1
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
|
|
@ -32,7 +32,7 @@ This filter replaces the filename (not the path) of an upload. For complete obfu
|
||||||
|
|
||||||
## Pleroma.Mailer
|
## Pleroma.Mailer
|
||||||
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
|
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
|
||||||
* `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
|
* `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
|
||||||
|
|
||||||
An example for Sendgrid adapter:
|
An example for Sendgrid adapter:
|
||||||
|
|
||||||
|
@ -93,6 +93,7 @@ config :pleroma, Pleroma.Mailer,
|
||||||
* `always_show_subject_input`: When set to false, auto-hide the subject field when it's empty.
|
* `always_show_subject_input`: When set to false, auto-hide the subject field when it's empty.
|
||||||
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
|
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
|
||||||
older software for theses nicknames.
|
older software for theses nicknames.
|
||||||
|
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
||||||
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
||||||
|
|
||||||
## :logger
|
## :logger
|
||||||
|
|
|
@ -31,6 +31,7 @@ defmodule Pleroma.User.Info do
|
||||||
field(:hub, :string, default: nil)
|
field(:hub, :string, default: nil)
|
||||||
field(:salmon, :string, default: nil)
|
field(:salmon, :string, default: nil)
|
||||||
field(:hide_network, :boolean, default: false)
|
field(:hide_network, :boolean, default: false)
|
||||||
|
field(:pinned_activities, {:array, :integer}, default: [])
|
||||||
|
|
||||||
# Found in the wild
|
# Found in the wild
|
||||||
# ap_id -> Where is this used?
|
# ap_id -> Where is this used?
|
||||||
|
@ -196,4 +197,26 @@ def admin_api_update(info, params) do
|
||||||
:is_admin
|
:is_admin
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_pinnned_activity(info, %Pleroma.Activity{id: id}) do
|
||||||
|
if id not in info.pinned_activities do
|
||||||
|
max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0)
|
||||||
|
params = %{pinned_activities: info.pinned_activities ++ [id]}
|
||||||
|
|
||||||
|
info
|
||||||
|
|> cast(params, [:pinned_activities])
|
||||||
|
|> validate_length(:pinned_activities,
|
||||||
|
max: max_pinned_statuses,
|
||||||
|
message: "You have already pinned the maximum number of statuses"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
change(info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_pinnned_activity(info, %Pleroma.Activity{id: id}) do
|
||||||
|
params = %{pinned_activities: List.delete(info.pinned_activities, id)}
|
||||||
|
|
||||||
|
cast(info, params, [:pinned_activities])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -391,6 +391,7 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put("type", ["Create", "Announce"])
|
||||||
|> Map.put("actor_id", user.ap_id)
|
|> Map.put("actor_id", user.ap_id)
|
||||||
|> Map.put("whole_db", true)
|
|> Map.put("whole_db", true)
|
||||||
|
|> Map.put("pinned_activity_ids", user.info.pinned_activities)
|
||||||
|
|
||||||
recipients =
|
recipients =
|
||||||
if reading_user do
|
if reading_user do
|
||||||
|
@ -549,6 +550,12 @@ defp restrict_unlisted(query) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
|
||||||
|
from(activity in query, where: activity.id in ^ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_pinned(query, _), do: query
|
||||||
|
|
||||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
base_query =
|
base_query =
|
||||||
from(
|
from(
|
||||||
|
@ -573,6 +580,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_visibility(opts)
|
|> restrict_visibility(opts)
|
||||||
|> restrict_replies(opts)
|
|> restrict_replies(opts)
|
||||||
|> restrict_reblogs(opts)
|
|> restrict_reblogs(opts)
|
||||||
|
|> restrict_pinned(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities(recipients, opts \\ %{}) do
|
def fetch_activities(recipients, opts \\ %{}) do
|
||||||
|
|
|
@ -164,4 +164,48 @@ def update(user) do
|
||||||
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
|
||||||
|
with %Activity{
|
||||||
|
actor: ^user_ap_id,
|
||||||
|
data: %{
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => %{
|
||||||
|
"to" => object_to,
|
||||||
|
"type" => "Note"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
|
true <- Enum.member?(object_to, "https://www.w3.org/ns/activitystreams#Public"),
|
||||||
|
%{valid?: true} = info_changeset <-
|
||||||
|
Pleroma.User.Info.add_pinnned_activity(user.info, activity),
|
||||||
|
changeset <-
|
||||||
|
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
||||||
|
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
%{errors: [pinned_activities: {err, _}]} ->
|
||||||
|
{:error, err}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "Could not pin"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpin(id_or_ap_id, user) do
|
||||||
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
|
%{valid?: true} = info_changeset <-
|
||||||
|
Pleroma.User.Info.remove_pinnned_activity(user.info, activity),
|
||||||
|
changeset <-
|
||||||
|
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
||||||
|
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
%{errors: [pinned_activities: {err, _}]} ->
|
||||||
|
{:error, err}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "Could not unpin"}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -256,13 +256,7 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
with %User{} = user <- Repo.get(User, params["id"]) do
|
with %User{} = user <- Repo.get(User, params["id"]) do
|
||||||
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
|
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||||
activities =
|
|
||||||
if params["pinned"] == "true" do
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
ActivityPub.fetch_user_activities(user, reading_user, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(:user_statuses, activities, params["id"])
|
|> add_link_headers(:user_statuses, activities, params["id"])
|
||||||
|
@ -409,6 +403,27 @@ def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
else
|
||||||
|
{:error, reason} ->
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(:bad_request, Jason.encode!(%{"error" => reason}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def notifications(%{assigns: %{user: user}} = conn, params) do
|
def notifications(%{assigns: %{user: user}} = conn, params) do
|
||||||
notifications = Notification.for_user(user, params)
|
notifications = Notification.for_user(user, params)
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ def render(
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
favourited: false,
|
favourited: false,
|
||||||
muted: false,
|
muted: false,
|
||||||
|
pinned: pinned?(activity, user),
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
spoiler_text: "",
|
spoiler_text: "",
|
||||||
visibility: "public",
|
visibility: "public",
|
||||||
|
@ -142,6 +143,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
||||||
reblogged: present?(repeated),
|
reblogged: present?(repeated),
|
||||||
favourited: present?(favorited),
|
favourited: present?(favorited),
|
||||||
muted: false,
|
muted: false,
|
||||||
|
pinned: pinned?(activity, user),
|
||||||
sensitive: sensitive,
|
sensitive: sensitive,
|
||||||
spoiler_text: object["summary"] || "",
|
spoiler_text: object["summary"] || "",
|
||||||
visibility: get_visibility(object),
|
visibility: get_visibility(object),
|
||||||
|
@ -295,4 +297,7 @@ def build_emojis(emojis) do
|
||||||
defp present?(nil), do: false
|
defp present?(nil), do: false
|
||||||
defp present?(false), do: false
|
defp present?(false), do: false
|
||||||
defp present?(_), do: true
|
defp present?(_), do: true
|
||||||
|
|
||||||
|
defp pinned?(%Activity{id: id}, %User{info: %{pinned_activities: pinned_activities}}),
|
||||||
|
do: id in pinned_activities
|
||||||
end
|
end
|
||||||
|
|
|
@ -188,6 +188,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
|
post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
|
||||||
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
||||||
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
||||||
|
post("/statuses/:id/pin", MastodonAPIController, :pin_status)
|
||||||
|
post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
|
||||||
|
|
||||||
post("/notifications/clear", MastodonAPIController, :clear_notifications)
|
post("/notifications/clear", MastodonAPIController, :clear_notifications)
|
||||||
post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
|
post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
|
||||||
|
@ -353,6 +355,9 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet)
|
post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet)
|
||||||
post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post)
|
post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post)
|
||||||
|
|
||||||
|
post("/statuses/pin/:id", TwitterAPI.Controller, :pin)
|
||||||
|
post("/statuses/unpin/:id", TwitterAPI.Controller, :unpin)
|
||||||
|
|
||||||
get("/pleroma/friend_requests", TwitterAPI.Controller, :friend_requests)
|
get("/pleroma/friend_requests", TwitterAPI.Controller, :friend_requests)
|
||||||
post("/pleroma/friendships/approve", TwitterAPI.Controller, :approve_friend_request)
|
post("/pleroma/friendships/approve", TwitterAPI.Controller, :approve_friend_request)
|
||||||
post("/pleroma/friendships/deny", TwitterAPI.Controller, :deny_friend_request)
|
post("/pleroma/friendships/deny", TwitterAPI.Controller, :deny_friend_request)
|
||||||
|
|
|
@ -153,6 +153,7 @@ def to_map(
|
||||||
announcement_count = object["announcement_count"] || 0
|
announcement_count = object["announcement_count"] || 0
|
||||||
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
|
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
|
||||||
repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
|
repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
|
||||||
|
pinned = activity.id in user.info.pinned_activities
|
||||||
|
|
||||||
mentions = opts[:mentioned] || []
|
mentions = opts[:mentioned] || []
|
||||||
|
|
||||||
|
@ -204,6 +205,7 @@ def to_map(
|
||||||
"repeat_num" => announcement_count,
|
"repeat_num" => announcement_count,
|
||||||
"favorited" => to_boolean(favorited),
|
"favorited" => to_boolean(favorited),
|
||||||
"repeated" => to_boolean(repeated),
|
"repeated" => to_boolean(repeated),
|
||||||
|
"pinned" => pinned,
|
||||||
"external_url" => object["external_url"] || object["id"],
|
"external_url" => object["external_url"] || object["id"],
|
||||||
"tags" => tags,
|
"tags" => tags,
|
||||||
"activity_type" => "post",
|
"activity_type" => "post",
|
||||||
|
|
|
@ -82,6 +82,14 @@ def unrepeat(%User{} = user, ap_id_or_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pin(%User{} = user, ap_id_or_id) do
|
||||||
|
CommonAPI.pin(ap_id_or_id, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpin(%User{} = user, ap_id_or_id) do
|
||||||
|
CommonAPI.unpin(ap_id_or_id, user)
|
||||||
|
end
|
||||||
|
|
||||||
def fav(%User{} = user, ap_id_or_id) do
|
def fav(%User{} = user, ap_id_or_id) do
|
||||||
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
|
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
|
||||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||||
|
|
|
@ -375,6 +375,30 @@ def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
|
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||||
|
{:ok, activity} <- TwitterAPI.pin(user, id) do
|
||||||
|
conn
|
||||||
|
|> put_view(ActivityView)
|
||||||
|
|> render("activity.json", %{activity: activity, for: user})
|
||||||
|
else
|
||||||
|
{:error, message} -> bad_request_reply(conn, message)
|
||||||
|
err -> err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
|
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||||
|
{:ok, activity} <- TwitterAPI.unpin(user, id) do
|
||||||
|
conn
|
||||||
|
|> put_view(ActivityView)
|
||||||
|
|> render("activity.json", %{activity: activity, for: user})
|
||||||
|
else
|
||||||
|
{:error, message} -> bad_request_reply(conn, message)
|
||||||
|
err -> err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def register(conn, params) do
|
def register(conn, params) do
|
||||||
with {:ok, user} <- TwitterAPI.register_user(params) do
|
with {:ok, user} <- TwitterAPI.register_user(params) do
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -243,6 +243,7 @@ def render(
|
||||||
announcement_count = object["announcement_count"] || 0
|
announcement_count = object["announcement_count"] || 0
|
||||||
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
|
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
|
||||||
repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
|
repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
|
||||||
|
pinned = activity.id in user.info.pinned_activities
|
||||||
|
|
||||||
attentions =
|
attentions =
|
||||||
activity.recipients
|
activity.recipients
|
||||||
|
@ -302,6 +303,7 @@ def render(
|
||||||
"repeat_num" => announcement_count,
|
"repeat_num" => announcement_count,
|
||||||
"favorited" => !!favorited,
|
"favorited" => !!favorited,
|
||||||
"repeated" => !!repeated,
|
"repeated" => !!repeated,
|
||||||
|
"pinned" => pinned,
|
||||||
"external_url" => object["external_url"] || object["id"],
|
"external_url" => object["external_url"] || object["id"],
|
||||||
"tags" => tags,
|
"tags" => tags,
|
||||||
"activity_type" => "post",
|
"activity_type" => "post",
|
||||||
|
|
|
@ -637,6 +637,28 @@ test "it can fetch peertube videos" do
|
||||||
assert object
|
assert object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returned pinned statuses" do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
CommonAPI.pin(activity_one.id, user)
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
CommonAPI.pin(activity_two.id, user)
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
CommonAPI.pin(activity_three.id, user)
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
|
||||||
|
|
||||||
|
assert 3 = length(activities)
|
||||||
|
end
|
||||||
|
|
||||||
def data_uri do
|
def data_uri do
|
||||||
File.read!("test/fixtures/avatar_data_uri")
|
File.read!("test/fixtures/avatar_data_uri")
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.CommonAPI.Test do
|
defmodule Pleroma.Web.CommonAPI.Test do
|
||||||
|
@ -96,4 +96,42 @@ test "favoriting a status twice returns an error" do
|
||||||
{:error, _} = CommonAPI.favorite(activity.id, user)
|
{:error, _} = CommonAPI.favorite(activity.id, user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "pinned statuses" do
|
||||||
|
setup do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
[user: user, activity: activity]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pin status", %{user: user, activity: activity} do
|
||||||
|
assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only self-authored can be pinned", %{activity: activity} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "max pinned statuses", %{user: user, activity: activity_one} do
|
||||||
|
{:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
|
||||||
|
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
assert {:error, "You have already pinned the maximum number of statuses"} =
|
||||||
|
CommonAPI.pin(activity_two.id, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unpin status", %{user: user, activity: activity} do
|
||||||
|
{:ok, activity} = CommonAPI.pin(activity.id, user)
|
||||||
|
|
||||||
|
assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
||||||
|
@ -1471,4 +1471,84 @@ test "put settings", %{conn: conn} do
|
||||||
user = User.get_cached_by_ap_id(user.ap_id)
|
user = User.get_cached_by_ap_id(user.ap_id)
|
||||||
assert user.info.settings == %{"programming" => "socks"}
|
assert user.info.settings == %{"programming" => "socks"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "pinned statuses" do
|
||||||
|
setup do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
[user: user, activity: activity]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
|
||||||
|
{:ok, _} = CommonAPI.pin(activity.id, user)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
id_str = to_string(activity.id)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id_str, "pinned" => true}] = result
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pin status", %{conn: conn, user: user, activity: activity} do
|
||||||
|
id_str = to_string(activity.id)
|
||||||
|
|
||||||
|
assert %{"id" => ^id_str, "pinned" => true} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/pin")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id_str, "pinned" => true}] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unpin status", %{conn: conn, user: user, activity: activity} do
|
||||||
|
{:ok, _} = CommonAPI.pin(activity.id, user)
|
||||||
|
|
||||||
|
id_str = to_string(activity.id)
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
assert %{"id" => ^id_str, "pinned" => false} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/unpin")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert [] =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
|
||||||
|
{:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
id_str_one = to_string(activity_one.id)
|
||||||
|
|
||||||
|
assert %{"id" => ^id_str_one, "pinned" => true} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{id_str_one}/pin")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
assert %{"error" => "You have already pinned the maximum number of statuses"} =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{activity_two.id}/pin")
|
||||||
|
|> json_response(400)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -63,6 +63,7 @@ test "a note activity" do
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
favourited: false,
|
favourited: false,
|
||||||
muted: false,
|
muted: false,
|
||||||
|
pinned: false,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
spoiler_text: note.data["object"]["summary"],
|
spoiler_text: note.data["object"]["summary"],
|
||||||
visibility: "public",
|
visibility: "public",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
|
defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
|
||||||
|
@ -157,6 +157,7 @@ test "an activity" do
|
||||||
"repeat_num" => 3,
|
"repeat_num" => 3,
|
||||||
"favorited" => false,
|
"favorited" => false,
|
||||||
"repeated" => false,
|
"repeated" => false,
|
||||||
|
"pinned" => false,
|
||||||
"external_url" => "some url",
|
"external_url" => "some url",
|
||||||
"tags" => ["nsfw", "content", "mentioning"],
|
"tags" => ["nsfw", "content", "mentioning"],
|
||||||
"activity_type" => "post",
|
"activity_type" => "post",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
||||||
|
@ -1694,4 +1694,79 @@ test "it updates `data[name]` of referenced Object with provided value", %{
|
||||||
assert object.data["name"] == description
|
assert object.data["name"] == description
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "POST /api/statuses/user_timeline.json?user_id=:user_id&pinned=true" do
|
||||||
|
test "it returns a list of pinned statuses", %{conn: conn} do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
|
||||||
|
|
||||||
|
user = insert(:user, %{name: "egor"})
|
||||||
|
{:ok, %{id: activity_id}} = CommonAPI.post(user, %{"status" => "HI!!!"})
|
||||||
|
{:ok, _} = CommonAPI.pin(activity_id, user)
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> get("/api/statuses/user_timeline.json", %{user_id: user.id, pinned: true})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert length(resp) == 1
|
||||||
|
assert [%{"id" => ^activity_id, "pinned" => true}] = resp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/statuses/pin/:id" do
|
||||||
|
setup do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
|
||||||
|
[user: insert(:user)]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without valid credentials", %{conn: conn} do
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
conn = post(conn, "/api/statuses/pin/#{note_activity.id}.json")
|
||||||
|
assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with credentials", %{conn: conn, user: user} do
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "test!"})
|
||||||
|
|
||||||
|
request_path = "/api/statuses/pin/#{activity.id}.json"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> with_credentials(user.nickname, "test")
|
||||||
|
|> post(request_path)
|
||||||
|
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
assert json_response(response, 200) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/statuses/unpin/:id" do
|
||||||
|
setup do
|
||||||
|
Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
|
||||||
|
[user: insert(:user)]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without valid credentials", %{conn: conn} do
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
conn = post(conn, "/api/statuses/unpin/#{note_activity.id}.json")
|
||||||
|
assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with credentials", %{conn: conn, user: user} do
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "test!"})
|
||||||
|
{:ok, activity} = CommonAPI.pin(activity.id, user)
|
||||||
|
|
||||||
|
request_path = "/api/statuses/unpin/#{activity.id}.json"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> with_credentials(user.nickname, "test")
|
||||||
|
|> post(request_path)
|
||||||
|
|
||||||
|
user = refresh_record(user)
|
||||||
|
|
||||||
|
assert json_response(response, 200) == ActivityRepresenter.to_map(activity, %{user: user})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
|
defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
|
||||||
|
@ -136,6 +136,7 @@ test "a create activity with a note" do
|
||||||
"possibly_sensitive" => false,
|
"possibly_sensitive" => false,
|
||||||
"repeat_num" => 0,
|
"repeat_num" => 0,
|
||||||
"repeated" => false,
|
"repeated" => false,
|
||||||
|
"pinned" => false,
|
||||||
"statusnet_conversation_id" => convo_id,
|
"statusnet_conversation_id" => convo_id,
|
||||||
"summary" => "",
|
"summary" => "",
|
||||||
"summary_html" => "",
|
"summary_html" => "",
|
||||||
|
|
Loading…
Reference in a new issue