diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/user.ex | 13 | ||||
-rw-r--r-- | lib/pleroma/user/query.ex | 5 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 5 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/side_effects.ex | 1 | ||||
-rw-r--r-- | lib/pleroma/web/api_spec/operations/directory_operation.ex | 41 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/directory_controller.ex | 82 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/account_view.ex | 1 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/instance_view.ex | 3 | ||||
-rw-r--r-- | lib/pleroma/web/router.ex | 2 |
9 files changed, 153 insertions, 0 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index c25023dc1..390de1e2d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -149,6 +149,7 @@ defmodule Pleroma.User do field(:disclose_client, :boolean, default: true) field(:pinned_objects, :map, default: %{}) field(:is_suggested, :boolean, default: false) + field(:last_status_at, :naive_datetime) embeds_one( :notification_settings, @@ -2499,4 +2500,16 @@ defmodule Pleroma.User do |> where([u], u.local == true) |> Repo.aggregate(:count) end + + def update_last_status_at(user) do + User + |> where(id: ^user.id) + |> update([u], set: [last_status_at: fragment("NOW()")]) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 6d4a4ead6..bf78cb32d 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -47,6 +47,7 @@ defmodule Pleroma.User.Query do is_admin: boolean(), is_moderator: boolean(), is_suggested: boolean(), + is_discoverable: boolean(), super_users: boolean(), invisible: boolean(), internal: boolean(), @@ -172,6 +173,10 @@ defmodule Pleroma.User.Query do where(query, [u], u.is_suggested == ^bool) end + defp compose_query({:is_discoverable, bool}, query) do + where(query, [u], u.is_discoverable == ^bool) + end + defp compose_query({:followers, %User{id: id}}, query) do query |> where([u], u.id != ^id) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8324ca22c..756096952 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -81,6 +81,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} end + def update_last_status_at_if_public(actor, object) do + if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor} + end + defp increase_replies_count_if_reply(%{ "object" => %{"inReplyTo" => reply_ap_id} = object, "type" => "Create" @@ -288,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do _ <- increase_replies_count_if_reply(create_data), {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), + {:ok, _actor} <- update_last_status_at_if_public(actor, activity), _ <- notify_and_stream(activity), :ok <- maybe_schedule_poll_notifications(activity), :ok <- maybe_federate(activity) do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index d55a4b340..39d37fbcb 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -199,6 +199,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, notifications} = Notification.create_notifications(activity, do_send: false) {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) + {:ok, _user} = ActivityPub.update_last_status_at_if_public(user, object) if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do Object.increase_replies_count(in_reply_to) diff --git a/lib/pleroma/web/api_spec/operations/directory_operation.ex b/lib/pleroma/web/api_spec/operations/directory_operation.ex new file mode 100644 index 000000000..9be965feb --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/directory_operation.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.DirectoryOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Directory"], + summary: "Profile directory", + operationId: "DirectoryController.index", + parameters: + [ + Operation.parameter( + :order, + :query, + :string, + "Order by recent activity or account creation", + required: nil + ), + Operation.parameter(:local, :query, BooleanLike, "Include local users only") + ] ++ pagination_params(), + responses: %{ + 200 => + Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex new file mode 100644 index 000000000..45ef227fb --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.DirectoryController do + use Pleroma.Web, :controller + + import Ecto.Query + alias Pleroma.Pagination + alias Pleroma.User + alias Pleroma.UserRelationship + alias Pleroma.Web.MastodonAPI.AccountView + + require Logger + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug(:skip_auth when action == "index") + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation + + @doc "GET /api/v1/directory" + def index(%{assigns: %{user: user}} = conn, params) do + with true <- Pleroma.Config.get([:instance, :profile_directory]) do + limit = Map.get(params, :limit, 20) |> min(80) + + users = + User.Query.build(%{is_discoverable: true, invisible: false, limit: limit}) + |> order_by_creation_date(params) + |> exclude_remote(params) + |> exclude_user(user) + |> exclude_relationships(user, [:block, :mute]) + |> Pagination.fetch_paginated(params, :offset) + + conn + |> put_view(AccountView) + |> render("index.json", for: user, users: users, as: :user) + else + _ -> json(conn, []) + end + end + + defp order_by_creation_date(query, %{order: "new"}) do + query + end + + defp order_by_creation_date(query, _params) do + query + |> order_by([u], desc_nulls_last: u.last_status_at) + end + + defp exclude_remote(query, %{local: true}) do + where(query, [u], u.local == true) + end + + defp exclude_remote(query, _params) do + query + end + + defp exclude_user(query, %User{id: user_id}) do + where(query, [u], u.id != ^user_id) + end + + defp exclude_user(query, _user) do + query + end + + defp exclude_relationships(query, %User{id: user_id}, relationship_types) do + query + |> join(:left, [u], r in UserRelationship, + as: :user_relationships, + on: + r.target_id == u.id and r.source_id == ^user_id and + r.relationship_type in ^relationship_types + ) + |> where([user_relationships: r], is_nil(r.target_id)) + end + + defp exclude_relationships(query, _user, _relationship_types) do + query + end +end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 3c8dd0353..4b15b1635 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -270,6 +270,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do actor_type: user.actor_type } }, + last_status_at: user.last_status_at, # Pleroma extensions # Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index ec7d150a9..7072d5d61 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -87,6 +87,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do "pleroma_chat_messages", if Config.get([:instance, :show_reactions]) do "exposable_reactions" + end, + if Config.get([:instance, :profile_directory]) do + "profile_directory" end ] |> Enum.filter(& &1) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 9ce35ad6b..e3659b87a 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -600,6 +600,8 @@ defmodule Pleroma.Web.Router do get("/timelines/tag/:tag", TimelineController, :hashtag) get("/polls/:id", PollController, :show) + + get("/directory", DirectoryController, :index) end scope "/api/v2", Pleroma.Web.MastodonAPI do |