diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/user.ex | 45 | ||||
-rw-r--r-- | lib/pleroma/user/query.ex | 18 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 16 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/views/user_view.ex | 8 | ||||
-rw-r--r-- | lib/pleroma/web/api_spec/operations/account_operation.ex | 23 | ||||
-rw-r--r-- | lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex | 29 | ||||
-rw-r--r-- | lib/pleroma/web/api_spec/schemas/account.ex | 5 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/account_view.ex | 19 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/instance_view.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/pleroma_api/controllers/account_controller.ex | 19 | ||||
-rw-r--r-- | lib/pleroma/web/router.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api.ex | 1 |
13 files changed, 181 insertions, 12 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index ef5a02249..36177bda3 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -154,6 +154,8 @@ defmodule Pleroma.User do field(:pinned_objects, :map, default: %{}) field(:is_suggested, :boolean, default: false) field(:last_status_at, :naive_datetime) + field(:birthday, :date) + field(:show_birthday, :boolean, default: false) embeds_one( :notification_settings, @@ -470,7 +472,9 @@ defmodule Pleroma.User do :actor_type, :also_known_as, :accepts_chat_messages, - :pinned_objects + :pinned_objects, + :birthday, + :show_birthday ] ) |> cast(params, [:name], empty_values: []) @@ -531,9 +535,12 @@ defmodule Pleroma.User do :is_discoverable, :actor_type, :accepts_chat_messages, - :disclose_client + :disclose_client, + :birthday, + :show_birthday ] ) + |> validate_min_age() |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) @@ -738,7 +745,8 @@ defmodule Pleroma.User do :password_confirmation, :emoji, :accepts_chat_messages, - :registration_reason + :registration_reason, + :birthday ]) |> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_confirmation(:password) @@ -760,6 +768,8 @@ defmodule Pleroma.User do |> validate_length(:name, min: 1, max: name_limit) |> validate_length(:registration_reason, max: reason_limit) |> maybe_validate_required_email(opts[:external]) + |> maybe_validate_required_birthday + |> validate_min_age() |> put_password_hash |> put_ap_id() |> unique_constraint(:ap_id) @@ -776,6 +786,26 @@ defmodule Pleroma.User do end end + defp maybe_validate_required_birthday(changeset) do + if Config.get([:instance, :birthday_required]) do + validate_required(changeset, [:birthday]) + else + changeset + end + end + + defp validate_min_age(changeset) do + changeset + |> validate_change(:birthday, fn :birthday, birthday -> + valid? = + Date.utc_today() + |> Date.diff(birthday) >= + Config.get([:instance, :birthday_min_age]) + + if valid?, do: [], else: [birthday: "Invalid age"] + end) + end + defp put_ap_id(changeset) do ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)}) put_change(changeset, :ap_id, ap_id) @@ -2560,4 +2590,13 @@ defmodule Pleroma.User do _ -> {:error, user} end end + + def get_friends_birthdays_query(%User{} = user, day, month) do + User.Query.build(%{ + friends: user, + deactivated: false, + birthday_day: day, + birthday_month: month + }) + end end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index bf78cb32d..bd11d287c 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -59,7 +59,9 @@ defmodule Pleroma.User.Query do order_by: term(), select: term(), limit: pos_integer(), - actor_types: [String.t()] + actor_types: [String.t()], + birthday_day: pos_integer(), + birthday_month: pos_integer() } | map() @@ -230,6 +232,20 @@ defmodule Pleroma.User.Query do |> where([u], not like(u.nickname, "internal.%")) end + defp compose_query({:birthday_day, day}, query) do + query + |> where([u], u.show_birthday == true) + |> where([u], not is_nil(u.birthday)) + |> where([u], fragment("date_part('day', ?)", u.birthday) == ^day) + end + + defp compose_query({:birthday_month, month}, query) do + query + |> where([u], u.show_birthday == true) + |> where([u], not is_nil(u.birthday)) + |> where([u], fragment("date_part('month', ?)", u.birthday) == ^month) + end + defp compose_query(_unsupported_param, query), do: query defp location_query(query, local) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9ca44c532..e6475a2b7 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1501,6 +1501,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do nil end + birthday = + if is_binary(data["vcard:bday"]) do + case Date.from_iso8601(data["vcard:bday"]) do + {:ok, date} -> date + {:error, _} -> nil + end + else + nil + end + + show_birthday = !!birthday + user_data = %{ ap_id: data["id"], uri: get_actor_url(data["url"]), @@ -1523,7 +1535,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do inbox: data["inbox"], shared_inbox: shared_inbox, accepts_chat_messages: accepts_chat_messages, - pinned_objects: pinned_objects + pinned_objects: pinned_objects, + birthday: birthday, + show_birthday: show_birthday } # nickname can be nil because of virtual actors diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 344da19d3..5ed08d7f6 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -92,6 +92,11 @@ defmodule Pleroma.Web.ActivityPub.UserView do %{} end + birthday = + if user.show_birthday, + do: user.birthday, + else: nil + %{ "id" => user.ap_id, "type" => user.actor_type, @@ -116,7 +121,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do # Note: key name is indeed "discoverable" (not an error) "discoverable" => user.is_discoverable, "capabilities" => capabilities, - "alsoKnownAs" => user.also_known_as + "alsoKnownAs" => user.also_known_as, + "vcard:bday" => birthday } |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 768d3c720..03efa3c38 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -543,6 +543,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do type: :string, nullable: true, description: "Invite token required when the registrations aren't public" + }, + birthday: %Schema{ + type: :string, + nullable: true, + description: "User's birthday", + format: :date } }, example: %{ @@ -720,7 +726,18 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed." }, - actor_type: ActorType + actor_type: ActorType, + birthday: %Schema{ + type: :string, + nullable: true, + description: "User's birthday", + format: :date + }, + show_birthday: %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "User's birthday will be visible" + } }, example: %{ bot: false, @@ -740,7 +757,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do allow_following_move: false, also_known_as: ["https://foo.bar/users/foo"], discoverable: false, - actor_type: "Person" + actor_type: "Person", + show_birthday: false, + birthday: "2001-02-12" } } end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex index ed0db173e..23201a4ba 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do alias OpenApiSpex.Operation + alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.AccountOperation alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.ApiError @@ -112,6 +113,34 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do } end + def birthdays_operation do + %Operation{ + tags: ["Retrieve account information"], + summary: "Birthday reminders", + description: "Birthday reminders about users you follow.", + operationId: "PleromaAPI.AccountController.birthdays", + parameters: [ + Operation.parameter( + :day, + :query, + %Schema{type: :integer}, + "Day of users' birthdays" + ), + Operation.parameter( + :month, + :query, + %Schema{type: :integer}, + "Month of users' birthdays" + ) + ], + security: [%{"oAuth" => ["read:accounts"]}], + responses: %{ + 200 => + Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()) + } + } + end + defp id_param do Operation.parameter(:id, :path, FlakeID, "Account ID", example: "9umDrYheeY451cQnEe", diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index 548e70544..029c6f6cf 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -47,12 +47,14 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do description: "whether the user allows automatically follow moved following accounts" }, background_image: %Schema{type: :string, nullable: true, format: :uri}, + birthday: %Schema{type: :string, nullable: true, format: :date}, chat_token: %Schema{type: :string}, is_confirmed: %Schema{ type: :boolean, description: "whether the user account is waiting on email confirmation to be activated" }, + show_birthday: %Schema{type: :boolean, nullable: true}, hide_favorites: %Schema{type: :boolean}, hide_followers_count: %Schema{ type: :boolean, @@ -202,7 +204,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do }, "settings_store" => %{ "pleroma-fe" => %{} - } + }, + "birthday" => "2001-02-12" }, "source" => %{ "fields" => [], diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index a90833bf0..8e6d49168 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -191,7 +191,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :skip_thread_containment, :allow_following_move, :also_known_as, - :accepts_chat_messages + :accepts_chat_messages, + :show_birthday ] |> Enum.reduce(%{}, fn key, acc -> Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)}) @@ -219,6 +220,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do |> Maps.put_if_present(:is_locked, params[:locked]) # Note: param name is indeed :discoverable (not an error) |> Maps.put_if_present(:is_discoverable, params[:discoverable]) + |> Maps.put_if_present(:birthday, params[:birthday]) # What happens here: # diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index b964fdc54..e73d03f06 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -297,7 +297,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do skip_thread_containment: user.skip_thread_containment, background_image: image_url(user.background) |> MediaProxy.url(), accepts_chat_messages: user.accepts_chat_messages, - favicon: favicon + favicon: favicon, + birthday: user.birthday } } |> maybe_put_role(user, opts[:for]) @@ -311,6 +312,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do |> maybe_put_unread_conversation_count(user, opts[:for]) |> maybe_put_unread_notification_count(user, opts[:for]) |> maybe_put_email_address(user, opts[:for]) + |> maybe_show_birthday(user, opts[:for]) end defp username_from_nickname(string) when is_binary(string) do @@ -344,6 +346,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do |> Kernel.put_in([:source, :privacy], user.default_scope) |> Kernel.put_in([:source, :pleroma, :show_role], user.show_role) |> Kernel.put_in([:source, :pleroma, :no_rich_text], user.no_rich_text) + |> Kernel.put_in([:source, :pleroma, :show_birthday], user.show_birthday) end defp maybe_put_settings(data, _, _, _), do: data @@ -432,6 +435,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_email_address(data, _, _), do: data + defp maybe_show_birthday(data, %User{id: user_id} = user, %User{id: user_id}) do + data + |> Kernel.put_in([:pleroma, :birthday], user.birthday) + end + + defp maybe_show_birthday(data, %User{show_birthday: true} = user, _) do + data + |> Kernel.put_in([:pleroma, :birthday], user.birthday) + end + + defp maybe_show_birthday(data, _, _) do + data + end + defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil end diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index cbed5fba9..fa6c20a30 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -46,7 +46,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do federation: federation(), fields_limits: fields_limits(), post_formats: Config.get([:instance, :allowed_post_formats]), - privileged_staff: Config.get([:instance, :privileged_staff]) + privileged_staff: Config.get([:instance, :privileged_staff]), + birthday_required: Config.get([:instance, :birthday_required]), + birthday_min_age: Config.get([:instance, :birthday_min_age]) }, stats: %{mau: Pleroma.User.active_user_count()}, vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 66a8d1c1c..d78ebbe2e 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -51,6 +51,11 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do when action == :endorsements ) + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} when action == :birthdays + ) + plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend) plug( @@ -137,4 +142,18 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do {:error, message} -> json_response(conn, :forbidden, %{error: message}) end end + + @doc "GET /api/v1/pleroma/birthdays" + def birthdays(%{assigns: %{user: %User{} = user}} = conn, %{day: day, month: month} = _params) do + birthdays = + User.get_friends_birthdays_query(user, day, month) + |> Pleroma.Repo.all() + + conn + |> render("index.json", + for: user, + users: birthdays, + as: :user + ) + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 67c1a3e5c..26706a6b8 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -448,6 +448,8 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/subscribe", AccountController, :subscribe) post("/accounts/:id/unsubscribe", AccountController, :unsubscribe) + + get("/birthdays", AccountController, :birthdays) end post("/accounts/confirmation_resend", AccountController, :confirmation_resend) diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 76ca82d20..aa4dfb145 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -20,6 +20,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do |> Map.put(:name, Map.get(params, :fullname, params[:username])) |> Map.put(:password_confirmation, params[:password]) |> Map.put(:registration_reason, params[:reason]) + |> Map.put(:birthday, params[:birthday]) if Pleroma.Config.get([:instance, :registrations_open]) do create_user(params, opts) |