diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/activity.ex | 8 | ||||
-rw-r--r-- | lib/pleroma/notification.ex | 36 | ||||
-rw-r--r-- | lib/pleroma/pagination.ex | 78 | ||||
-rw-r--r-- | lib/pleroma/user.ex | 35 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 46 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api.ex | 48 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 |
7 files changed, 169 insertions, 84 deletions
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index b8a3d3054..04c3bb673 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -22,6 +22,10 @@ defmodule Pleroma.Activity do "Like" => "favourite" } + @mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types, + into: %{}, + do: {v, k} + schema "activities" do field(:data, :map) field(:local, :boolean, default: true) @@ -127,6 +131,10 @@ defmodule Pleroma.Activity do def mastodon_notification_type(%Activity{}), do: nil + def from_mastodon_notification_type(type) do + Map.get(@mastodon_to_ap_notification_types, type) + end + def all_by_actor_and_id(actor, status_ids \\ []) def all_by_actor_and_id(_actor, []), do: [] diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 765191275..a98649b63 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Notification do alias Pleroma.Activity alias Pleroma.Notification + alias Pleroma.Pagination alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.CommonAPI @@ -28,36 +29,17 @@ defmodule Pleroma.Notification do |> cast(attrs, [:seen]) end - # TODO: Make generic and unify (see activity_pub.ex) - defp restrict_max(query, %{"max_id" => max_id}) do - from(activity in query, where: activity.id < ^max_id) + def for_user_query(user) do + Notification + |> where(user_id: ^user.id) + |> join(:inner, [n], activity in assoc(n, :activity)) + |> preload(:activity) end - defp restrict_max(query, _), do: query - - defp restrict_since(query, %{"since_id" => since_id}) do - from(activity in query, where: activity.id > ^since_id) - end - - defp restrict_since(query, _), do: query - def for_user(user, opts \\ %{}) do - query = - from( - n in Notification, - where: n.user_id == ^user.id, - order_by: [desc: n.id], - join: activity in assoc(n, :activity), - preload: [activity: activity], - limit: 20 - ) - - query = - query - |> restrict_since(opts) - |> restrict_max(opts) - - Repo.all(query) + user + |> for_user_query() + |> Pagination.fetch_paginated(opts) end def set_read_up_to(%{id: user_id} = _user, id) do diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex new file mode 100644 index 000000000..7c864deef --- /dev/null +++ b/lib/pleroma/pagination.ex @@ -0,0 +1,78 @@ +defmodule Pleroma.Pagination do + @moduledoc """ + Implements Mastodon-compatible pagination. + """ + + import Ecto.Query + import Ecto.Changeset + + alias Pleroma.Repo + + @default_limit 20 + + def fetch_paginated(query, params) do + options = cast_params(params) + + query + |> paginate(options) + |> Repo.all() + |> enforce_order(options) + end + + def paginate(query, options) do + query + |> restrict(:min_id, options) + |> restrict(:since_id, options) + |> restrict(:max_id, options) + |> restrict(:order, options) + |> restrict(:limit, options) + end + + defp cast_params(params) do + param_types = %{ + min_id: :string, + since_id: :string, + max_id: :string, + limit: :integer + } + + changeset = cast({%{}, param_types}, params, Map.keys(param_types)) + changeset.changes + end + + defp restrict(query, :min_id, %{min_id: min_id}) do + where(query, [q], q.id > ^min_id) + end + + defp restrict(query, :since_id, %{since_id: since_id}) do + where(query, [q], q.id > ^since_id) + end + + defp restrict(query, :max_id, %{max_id: max_id}) do + where(query, [q], q.id < ^max_id) + end + + defp restrict(query, :order, %{min_id: _}) do + order_by(query, [u], fragment("? asc nulls last", u.id)) + end + + defp restrict(query, :order, _options) do + order_by(query, [u], fragment("? desc nulls last", u.id)) + end + + defp restrict(query, :limit, options) do + limit = Map.get(options, :limit, @default_limit) + + query + |> limit(^limit) + end + + defp restrict(query, _, _), do: query + + defp enforce_order(result, %{min_id: _}) do + result + |> Enum.reverse() + end + + defp enforce_order(result, _), do: result +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 7a6675208..eb0933c85 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -104,9 +104,8 @@ defmodule Pleroma.User do "#{Web.base_url()}/users/#{nickname}" end - def ap_followers(%User{} = user) do - "#{ap_id(user)}/followers" - end + def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa + def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers" def user_info(%User{} = user) do oneself = if user.local, do: 1, else: 0 @@ -779,7 +778,7 @@ defmodule Pleroma.User do }) :: {:ok, [Pleroma.User.t()], number()} def search_for_admin(%{query: nil, local: local, page: page, page_size: page_size}) do query = - from(u in User, order_by: u.id) + from(u in User, order_by: u.nickname) |> maybe_local_user_query(local) paginated_query = @@ -795,34 +794,27 @@ defmodule Pleroma.User do @spec search_for_admin(%{ query: binary(), - admin: Pleroma.User.t(), local: boolean(), page: number(), page_size: number() }) :: {:ok, [Pleroma.User.t()], number()} def search_for_admin(%{ query: term, - admin: admin, local: local, page: page, page_size: page_size }) do - term = String.trim_leading(term, "@") + maybe_local_query = User |> maybe_local_user_query(local) - local_paginated_query = - User - |> maybe_local_user_query(local) - |> paginate(page, page_size) + search_query = from(u in maybe_local_query, where: ilike(u.nickname, ^"%#{term}%")) + count = search_query |> Repo.aggregate(:count, :id) - search_query = fts_search_subquery(term, local_paginated_query) - - count = - term - |> fts_search_subquery() - |> maybe_local_user_query(local) - |> Repo.aggregate(:count, :id) + results = + search_query + |> paginate(page, page_size) + |> Repo.all() - {:ok, do_search(search_query, admin), count} + {:ok, results, count} end def search(query, resolve \\ false, for_user \\ nil) do @@ -1165,9 +1157,12 @@ defmodule Pleroma.User do if !is_nil(user) and !User.needs_update?(user) do user else + # Whether to fetch initial posts for the user (if it's a new user & the fetching is enabled) + should_fetch_initial = is_nil(user) and Pleroma.Config.get([:fetch_initial_posts, :enabled]) + user = fetch_by_ap_id(ap_id) - if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do + if should_fetch_initial do with %User{} = user do {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user]) end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 8e4bf7b47..f733ae7e1 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -86,11 +86,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def fix_addressing_list(map, field) do - if is_binary(map[field]) do - map - |> Map.put(field, [map[field]]) - else - map + cond do + is_binary(map[field]) -> + Map.put(map, field, [map[field]]) + + is_nil(map[field]) -> + Map.put(map, field, []) + + true -> + map end end @@ -128,13 +132,42 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> fix_explicit_addressing(explicit_mentions) end + # if as:Public is addressed, then make sure the followers collection is also addressed + # so that the activities will be delivered to local users. + def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do + recipients = to ++ cc + + if followers_collection not in recipients do + cond do + "https://www.w3.org/ns/activitystreams#Public" in cc -> + to = to ++ [followers_collection] + Map.put(object, "to", to) + + "https://www.w3.org/ns/activitystreams#Public" in to -> + cc = cc ++ [followers_collection] + Map.put(object, "cc", cc) + + true -> + object + end + else + object + end + end + + def fix_implicit_addressing(object, _), do: object + def fix_addressing(object) do + %User{} = user = User.get_or_fetch_by_ap_id(object["actor"]) + followers_collection = User.ap_followers(user) + object |> fix_addressing_list("to") |> fix_addressing_list("cc") |> fix_addressing_list("bto") |> fix_addressing_list("bcc") |> fix_explicit_addressing + |> fix_implicit_addressing(followers_collection) end def fix_actor(%{"attributedTo" => actor} = object) do @@ -922,7 +955,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do defp strip_internal_tags(object), do: object defp user_upgrade_task(user) do - old_follower_address = User.ap_followers(user) + # we pass a fake user so that the followers collection is stripped away + old_follower_address = User.ap_followers(%User{nickname: user.nickname}) q = from( diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 54cb6c97a..08ea5f967 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -2,61 +2,49 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do import Ecto.Query import Ecto.Changeset - alias Pleroma.Repo + alias Pleroma.Activity + alias Pleroma.Notification + alias Pleroma.Pagination alias Pleroma.User - @default_limit 20 - def get_followers(user, params \\ %{}) do user |> User.get_followers_query() - |> paginate(params) - |> Repo.all() + |> Pagination.fetch_paginated(params) end def get_friends(user, params \\ %{}) do user |> User.get_friends_query() - |> paginate(params) - |> Repo.all() + |> Pagination.fetch_paginated(params) end - def paginate(query, params \\ %{}) do + def get_notifications(user, params \\ %{}) do options = cast_params(params) - query - |> restrict(:max_id, options) - |> restrict(:since_id, options) - |> restrict(:limit, options) - |> order_by([u], fragment("? desc nulls last", u.id)) + user + |> Notification.for_user_query() + |> restrict(:exclude_types, options) + |> Pagination.fetch_paginated(params) end - def cast_params(params) do + defp cast_params(params) do param_types = %{ - max_id: :string, - since_id: :string, - limit: :integer + exclude_types: {:array, :string} } changeset = cast({%{}, param_types}, params, Map.keys(param_types)) changeset.changes end - defp restrict(query, :max_id, %{max_id: max_id}) do - query - |> where([q], q.id < ^max_id) - end - - defp restrict(query, :since_id, %{since_id: since_id}) do - query - |> where([q], q.id > ^since_id) - end - - defp restrict(query, :limit, options) do - limit = Map.get(options, :limit, @default_limit) + defp restrict(query, :exclude_types, %{exclude_types: mastodon_types = [_ | _]}) do + ap_types = + mastodon_types + |> Enum.map(&Activity.from_mastodon_notification_type/1) + |> Enum.filter(& &1) query - |> limit(^limit) + |> where([q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data)) end defp restrict(query, _, _), do: query diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 952aa2453..2eb1da561 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -502,7 +502,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def notifications(%{assigns: %{user: user}} = conn, params) do - notifications = Notification.for_user(user, params) + notifications = MastodonAPI.get_notifications(user, params) conn |> add_link_headers(:notifications, notifications) |