diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/user.ex | 24 | ||||
-rw-r--r-- | lib/pleroma/user/info.ex | 23 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 33 | ||||
-rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 44 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 29 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/status_view.ex | 5 | ||||
-rw-r--r-- | lib/pleroma/web/router.ex | 5 | ||||
-rw-r--r-- | lib/pleroma/web/twitter_api/representers/activity_representer.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api.ex | 8 | ||||
-rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api_controller.ex | 24 | ||||
-rw-r--r-- | lib/pleroma/web/twitter_api/views/activity_view.ex | 2 |
11 files changed, 174 insertions, 25 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 7c2849ce2..a49fa3fcd 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -247,10 +247,7 @@ defmodule Pleroma.User do ) |> Repo.all() - autofollowed_users - |> Enum.reduce({:ok, user}, fn other_user, {:ok, user} -> - follow(user, other_user) - end) + follow_all(user, autofollowed_users) end @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)" @@ -307,6 +304,25 @@ defmodule Pleroma.User do end end + @doc "A mass follow for local users. Ignores blocks and has no side effects" + @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()} + def follow_all(follower, followeds) do + following = + (follower.following ++ Enum.map(followeds, fn %{follower_address: fa} -> fa end)) + |> Enum.uniq() + + {:ok, follower} = + follower + |> follow_changeset(%{following: following}) + |> update_and_set_cache + + Enum.each(followeds, fn followed -> + update_follower_count(followed) + end) + + {:ok, follower} + end + def follow(%User{} = follower, %User{info: info} = followed) do user_config = Application.get_env(:pleroma, :user) deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 7c79dfcff..fb1791c20 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -31,6 +31,7 @@ defmodule Pleroma.User.Info do field(:hub, :string, default: nil) field(:salmon, :string, default: nil) field(:hide_network, :boolean, default: false) + field(:pinned_activities, {:array, :integer}, default: []) # Found in the wild # ap_id -> Where is this used? @@ -196,4 +197,26 @@ defmodule Pleroma.User.Info do :is_admin ]) 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 diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 4685f6d95..9c1eb377f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -364,21 +364,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @valid_visibilities ~w[direct unlisted public private] - defp restrict_visibility(query, %{visibility: "direct"}) do - public = "https://www.w3.org/ns/activitystreams#Public" + defp restrict_visibility(query, %{visibility: visibility}) + when visibility in @valid_visibilities do + query = + from( + a in query, + where: + fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility) + ) - from( - activity in query, - join: sender in User, - on: sender.ap_id == activity.actor, - # Are non-direct statuses with no to/cc possible? - where: - fragment( - "not (? && ?)", - [^public, sender.follower_address], - activity.recipients - ) - ) + Ecto.Adapters.SQL.to_sql(:all, Repo, query) + + query end defp restrict_visibility(_query, %{visibility: visibility}) @@ -394,6 +391,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Map.put("type", ["Create", "Announce"]) |> Map.put("actor_id", user.ap_id) |> Map.put("whole_db", true) + |> Map.put("pinned_activity_ids", user.info.pinned_activities) recipients = if reading_user do @@ -552,6 +550,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) 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 base_query = from( @@ -576,6 +580,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_visibility(opts) |> restrict_replies(opts) |> restrict_reblogs(opts) + |> restrict_pinned(opts) end def fetch_activities(recipients, opts \\ %{}) do diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index bb3c38f00..7ec6aa0ea 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -164,4 +164,48 @@ defmodule Pleroma.Web.CommonAPI do object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) }) 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 diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index f739e8f7d..e00a3fb87 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -256,13 +256,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def user_statuses(%{assigns: %{user: reading_user}} = conn, params) 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 = - if params["pinned"] == "true" do - [] - else - ActivityPub.fetch_user_activities(user, reading_user, params) - end + activities = ActivityPub.fetch_user_activities(user, reading_user, params) conn |> add_link_headers(:user_statuses, activities, params["id"]) @@ -409,6 +403,27 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do 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 notifications = Notification.for_user(user, params) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 8e8fa8121..db543ffe5 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -76,6 +76,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblogged: false, favourited: false, muted: false, + pinned: pinned?(activity, user), sensitive: false, spoiler_text: "", visibility: "public", @@ -142,6 +143,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblogged: present?(repeated), favourited: present?(favorited), muted: false, + pinned: pinned?(activity, user), sensitive: sensitive, spoiler_text: object["summary"] || "", visibility: get_visibility(object), @@ -295,4 +297,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do defp present?(nil), do: false defp present?(false), do: false defp present?(_), do: true + + defp pinned?(%Activity{id: id}, %User{info: %{pinned_activities: pinned_activities}}), + do: id in pinned_activities end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 8df45bf4d..a5f4d8126 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -188,6 +188,8 @@ defmodule Pleroma.Web.Router do post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status) post("/statuses/:id/favourite", MastodonAPIController, :fav_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/dismiss", MastodonAPIController, :dismiss_notification) @@ -353,6 +355,9 @@ defmodule Pleroma.Web.Router do post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet) 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) post("/pleroma/friendships/approve", TwitterAPI.Controller, :approve_friend_request) post("/pleroma/friendships/deny", TwitterAPI.Controller, :deny_friend_request) diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index 47154829d..4f8f228ab 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -153,6 +153,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do announcement_count = object["announcement_count"] || 0 favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) + pinned = activity.id in user.info.pinned_activities mentions = opts[:mentioned] || [] @@ -204,6 +205,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do "repeat_num" => announcement_count, "favorited" => to_boolean(favorited), "repeated" => to_boolean(repeated), + "pinned" => pinned, "external_url" => object["external_url"] || object["id"], "tags" => tags, "activity_type" => "post", diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index ecf81d492..7a63724f1 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -82,6 +82,14 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do 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 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 diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 1e04b8c4b..d5b9d2fb8 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -375,6 +375,30 @@ defmodule Pleroma.Web.TwitterAPI.Controller do 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 with {:ok, user} <- TwitterAPI.register_user(params) do conn diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index 5f4b74842..108e7bfc5 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -243,6 +243,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do announcement_count = object["announcement_count"] || 0 favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) + pinned = activity.id in user.info.pinned_activities attentions = activity.recipients @@ -302,6 +303,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do "repeat_num" => announcement_count, "favorited" => !!favorited, "repeated" => !!repeated, + "pinned" => pinned, "external_url" => object["external_url"] || object["id"], "tags" => tags, "activity_type" => "post", |