logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

user.ex (83008B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.User do
  5. use Ecto.Schema
  6. import Ecto.Changeset
  7. import Ecto.Query
  8. import Ecto, only: [assoc: 2]
  9. alias Ecto.Multi
  10. alias Pleroma.Activity
  11. alias Pleroma.Config
  12. alias Pleroma.Conversation.Participation
  13. alias Pleroma.Delivery
  14. alias Pleroma.EctoType.ActivityPub.ObjectValidators
  15. alias Pleroma.Emoji
  16. alias Pleroma.FollowingRelationship
  17. alias Pleroma.Formatter
  18. alias Pleroma.HTML
  19. alias Pleroma.Keys
  20. alias Pleroma.MFA
  21. alias Pleroma.Notification
  22. alias Pleroma.Object
  23. alias Pleroma.Registration
  24. alias Pleroma.Repo
  25. alias Pleroma.User
  26. alias Pleroma.UserRelationship
  27. alias Pleroma.Web.ActivityPub.ActivityPub
  28. alias Pleroma.Web.ActivityPub.Builder
  29. alias Pleroma.Web.ActivityPub.Pipeline
  30. alias Pleroma.Web.ActivityPub.Utils
  31. alias Pleroma.Web.CommonAPI
  32. alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
  33. alias Pleroma.Web.Endpoint
  34. alias Pleroma.Web.OAuth
  35. alias Pleroma.Web.RelMe
  36. alias Pleroma.Workers.BackgroundWorker
  37. require Logger
  38. require Pleroma.Constants
  39. @type t :: %__MODULE__{}
  40. @type account_status ::
  41. :active
  42. | :deactivated
  43. | :password_reset_pending
  44. | :confirmation_pending
  45. | :approval_pending
  46. @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
  47. # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
  48. @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
  49. @strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
  50. @extended_local_nickname_regex ~r/^[a-zA-Z\d_-]+$/
  51. # AP ID user relationships (blocks, mutes etc.)
  52. # Format: [rel_type: [outgoing_rel: :outgoing_rel_target, incoming_rel: :incoming_rel_source]]
  53. @user_relationships_config [
  54. block: [
  55. blocker_blocks: :blocked_users,
  56. blockee_blocks: :blocker_users
  57. ],
  58. mute: [
  59. muter_mutes: :muted_users,
  60. mutee_mutes: :muter_users
  61. ],
  62. reblog_mute: [
  63. reblog_muter_mutes: :reblog_muted_users,
  64. reblog_mutee_mutes: :reblog_muter_users
  65. ],
  66. notification_mute: [
  67. notification_muter_mutes: :notification_muted_users,
  68. notification_mutee_mutes: :notification_muter_users
  69. ],
  70. # Note: `inverse_subscription` relationship is inverse: subscriber acts as relationship target
  71. inverse_subscription: [
  72. subscribee_subscriptions: :subscriber_users,
  73. subscriber_subscriptions: :subscribee_users
  74. ],
  75. endorsement: [
  76. endorser_endorsements: :endorsed_users,
  77. endorsee_endorsements: :endorser_users
  78. ]
  79. ]
  80. @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
  81. schema "users" do
  82. field(:bio, :string, default: "")
  83. field(:raw_bio, :string)
  84. field(:email, :string)
  85. field(:name, :string)
  86. field(:nickname, :string)
  87. field(:password_hash, :string)
  88. field(:password, :string, virtual: true)
  89. field(:password_confirmation, :string, virtual: true)
  90. field(:keys, :string)
  91. field(:public_key, :string)
  92. field(:ap_id, :string)
  93. field(:avatar, :map, default: %{})
  94. field(:local, :boolean, default: true)
  95. field(:follower_address, :string)
  96. field(:following_address, :string)
  97. field(:featured_address, :string)
  98. field(:search_rank, :float, virtual: true)
  99. field(:search_type, :integer, virtual: true)
  100. field(:tags, {:array, :string}, default: [])
  101. field(:last_refreshed_at, :naive_datetime_usec)
  102. field(:last_digest_emailed_at, :naive_datetime)
  103. field(:banner, :map, default: %{})
  104. field(:background, :map, default: %{})
  105. field(:note_count, :integer, default: 0)
  106. field(:follower_count, :integer, default: 0)
  107. field(:following_count, :integer, default: 0)
  108. field(:is_locked, :boolean, default: false)
  109. field(:is_confirmed, :boolean, default: true)
  110. field(:password_reset_pending, :boolean, default: false)
  111. field(:is_approved, :boolean, default: true)
  112. field(:registration_reason, :string, default: nil)
  113. field(:confirmation_token, :string, default: nil)
  114. field(:default_scope, :string, default: "public")
  115. field(:domain_blocks, {:array, :string}, default: [])
  116. field(:is_active, :boolean, default: true)
  117. field(:no_rich_text, :boolean, default: false)
  118. field(:is_moderator, :boolean, default: false)
  119. field(:is_admin, :boolean, default: false)
  120. field(:show_role, :boolean, default: true)
  121. field(:uri, ObjectValidators.Uri, default: nil)
  122. field(:hide_followers_count, :boolean, default: false)
  123. field(:hide_follows_count, :boolean, default: false)
  124. field(:hide_followers, :boolean, default: false)
  125. field(:hide_follows, :boolean, default: false)
  126. field(:hide_favorites, :boolean, default: true)
  127. field(:email_notifications, :map, default: %{"digest" => false})
  128. field(:mascot, :map, default: nil)
  129. field(:emoji, :map, default: %{})
  130. field(:pleroma_settings_store, :map, default: %{})
  131. field(:fields, {:array, :map}, default: [])
  132. field(:raw_fields, {:array, :map}, default: [])
  133. field(:is_discoverable, :boolean, default: false)
  134. field(:invisible, :boolean, default: false)
  135. field(:allow_following_move, :boolean, default: true)
  136. field(:skip_thread_containment, :boolean, default: false)
  137. field(:actor_type, :string, default: "Person")
  138. field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
  139. field(:inbox, :string)
  140. field(:shared_inbox, :string)
  141. field(:accepts_chat_messages, :boolean, default: nil)
  142. field(:last_active_at, :naive_datetime)
  143. field(:disclose_client, :boolean, default: true)
  144. field(:pinned_objects, :map, default: %{})
  145. field(:is_suggested, :boolean, default: false)
  146. field(:last_status_at, :naive_datetime)
  147. field(:birthday, :date)
  148. field(:show_birthday, :boolean, default: false)
  149. field(:language, :string)
  150. embeds_one(
  151. :notification_settings,
  152. Pleroma.User.NotificationSetting,
  153. on_replace: :update
  154. )
  155. has_many(:notifications, Notification)
  156. has_many(:registrations, Registration)
  157. has_many(:deliveries, Delivery)
  158. has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
  159. has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
  160. for {relationship_type,
  161. [
  162. {outgoing_relation, outgoing_relation_target},
  163. {incoming_relation, incoming_relation_source}
  164. ]} <- @user_relationships_config do
  165. # Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
  166. # :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements
  167. has_many(outgoing_relation, UserRelationship,
  168. foreign_key: :source_id,
  169. where: [relationship_type: relationship_type]
  170. )
  171. # Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
  172. # :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements
  173. has_many(incoming_relation, UserRelationship,
  174. foreign_key: :target_id,
  175. where: [relationship_type: relationship_type]
  176. )
  177. # Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
  178. # :notification_muted_users, :subscriber_users, :endorsed_users
  179. has_many(outgoing_relation_target, through: [outgoing_relation, :target])
  180. # Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
  181. # :notification_muter_users, :subscribee_users, :endorser_users
  182. has_many(incoming_relation_source, through: [incoming_relation, :source])
  183. end
  184. # `:blocks` is deprecated (replaced with `blocked_users` relation)
  185. field(:blocks, {:array, :string}, default: [])
  186. # `:mutes` is deprecated (replaced with `muted_users` relation)
  187. field(:mutes, {:array, :string}, default: [])
  188. # `:muted_reblogs` is deprecated (replaced with `reblog_muted_users` relation)
  189. field(:muted_reblogs, {:array, :string}, default: [])
  190. # `:muted_notifications` is deprecated (replaced with `notification_muted_users` relation)
  191. field(:muted_notifications, {:array, :string}, default: [])
  192. # `:subscribers` is deprecated (replaced with `subscriber_users` relation)
  193. field(:subscribers, {:array, :string}, default: [])
  194. embeds_one(
  195. :multi_factor_authentication_settings,
  196. MFA.Settings,
  197. on_replace: :delete
  198. )
  199. timestamps()
  200. end
  201. for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
  202. @user_relationships_config do
  203. # `def blocked_users_relation/2`, `def muted_users_relation/2`,
  204. # `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
  205. # `def subscriber_users/2`, `def endorsed_users_relation/2`
  206. def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
  207. target_users_query = assoc(user, unquote(outgoing_relation_target))
  208. if restrict_deactivated? do
  209. target_users_query
  210. |> User.Query.build(%{deactivated: false})
  211. else
  212. target_users_query
  213. end
  214. end
  215. # `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
  216. # `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2`
  217. def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
  218. __MODULE__
  219. |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
  220. user,
  221. restrict_deactivated?
  222. ])
  223. |> Repo.all()
  224. end
  225. # `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
  226. # `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`,
  227. # `def endorsed_users_ap_ids/2`
  228. def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
  229. __MODULE__
  230. |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
  231. user,
  232. restrict_deactivated?
  233. ])
  234. |> select([u], u.ap_id)
  235. |> Repo.all()
  236. end
  237. end
  238. def cached_blocked_users_ap_ids(user) do
  239. @cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ ->
  240. blocked_users_ap_ids(user)
  241. end)
  242. end
  243. def cached_muted_users_ap_ids(user) do
  244. @cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ ->
  245. muted_users_ap_ids(user)
  246. end)
  247. end
  248. defdelegate following_count(user), to: FollowingRelationship
  249. defdelegate following(user), to: FollowingRelationship
  250. defdelegate following?(follower, followed), to: FollowingRelationship
  251. defdelegate following_ap_ids(user), to: FollowingRelationship
  252. defdelegate get_follow_requests(user), to: FollowingRelationship
  253. defdelegate search(query, opts \\ []), to: User.Search
  254. @doc """
  255. Dumps Flake Id to SQL-compatible format (16-byte UUID).
  256. E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>>
  257. """
  258. def binary_id(source_id) when is_binary(source_id) do
  259. with {:ok, dumped_id} <- FlakeId.Ecto.CompatType.dump(source_id) do
  260. dumped_id
  261. else
  262. _ -> source_id
  263. end
  264. end
  265. def binary_id(source_ids) when is_list(source_ids) do
  266. Enum.map(source_ids, &binary_id/1)
  267. end
  268. def binary_id(%User{} = user), do: binary_id(user.id)
  269. @doc "Returns status account"
  270. @spec account_status(User.t()) :: account_status()
  271. def account_status(%User{is_active: false}), do: :deactivated
  272. def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
  273. def account_status(%User{local: true, is_approved: false}), do: :approval_pending
  274. def account_status(%User{local: true, is_confirmed: false}), do: :confirmation_pending
  275. def account_status(%User{}), do: :active
  276. @spec visible_for(User.t(), User.t() | nil) ::
  277. :visible
  278. | :invisible
  279. | :restricted_unauthenticated
  280. | :deactivated
  281. | :confirmation_pending
  282. def visible_for(user, for_user \\ nil)
  283. def visible_for(%User{invisible: true}, _), do: :invisible
  284. def visible_for(%User{id: user_id}, %User{id: user_id}), do: :visible
  285. def visible_for(%User{} = user, nil) do
  286. if restrict_unauthenticated?(user) do
  287. :restrict_unauthenticated
  288. else
  289. visible_account_status(user)
  290. end
  291. end
  292. def visible_for(%User{} = user, for_user) do
  293. if privileged?(for_user, :users_manage_activation_state) do
  294. :visible
  295. else
  296. visible_account_status(user)
  297. end
  298. end
  299. def visible_for(_, _), do: :invisible
  300. defp restrict_unauthenticated?(%User{local: true}) do
  301. Config.restrict_unauthenticated_access?(:profiles, :local)
  302. end
  303. defp restrict_unauthenticated?(%User{local: _}) do
  304. Config.restrict_unauthenticated_access?(:profiles, :remote)
  305. end
  306. defp visible_account_status(user) do
  307. status = account_status(user)
  308. if status in [:active, :password_reset_pending] do
  309. :visible
  310. else
  311. status
  312. end
  313. end
  314. @spec privileged?(User.t(), atom()) :: boolean()
  315. def privileged?(%User{is_admin: false, is_moderator: false}, _), do: false
  316. def privileged?(
  317. %User{local: true, is_admin: is_admin, is_moderator: is_moderator},
  318. privilege_tag
  319. ),
  320. do:
  321. privileged_for?(privilege_tag, is_admin, :admin_privileges) or
  322. privileged_for?(privilege_tag, is_moderator, :moderator_privileges)
  323. def privileged?(_, _), do: false
  324. defp privileged_for?(privilege_tag, true, config_role_key),
  325. do: privilege_tag in Config.get([:instance, config_role_key])
  326. defp privileged_for?(_, _, _), do: false
  327. @spec privileges(User.t()) :: [atom()]
  328. def privileges(%User{local: false}) do
  329. []
  330. end
  331. def privileges(%User{is_moderator: false, is_admin: false}) do
  332. []
  333. end
  334. def privileges(%User{local: true, is_moderator: true, is_admin: true}) do
  335. (Config.get([:instance, :moderator_privileges]) ++ Config.get([:instance, :admin_privileges]))
  336. |> Enum.uniq()
  337. end
  338. def privileges(%User{local: true, is_moderator: true, is_admin: false}) do
  339. Config.get([:instance, :moderator_privileges])
  340. end
  341. def privileges(%User{local: true, is_moderator: false, is_admin: true}) do
  342. Config.get([:instance, :admin_privileges])
  343. end
  344. @spec invisible?(User.t()) :: boolean()
  345. def invisible?(%User{invisible: true}), do: true
  346. def invisible?(_), do: false
  347. def avatar_url(user, options \\ []) do
  348. case user.avatar do
  349. %{"url" => [%{"href" => href} | _]} ->
  350. href
  351. _ ->
  352. unless options[:no_default] do
  353. Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png")
  354. end
  355. end
  356. end
  357. def banner_url(user, options \\ []) do
  358. case user.banner do
  359. %{"url" => [%{"href" => href} | _]} -> href
  360. _ -> !options[:no_default] && "#{Endpoint.url()}/images/banner.png"
  361. end
  362. end
  363. # Should probably be renamed or removed
  364. @spec ap_id(User.t()) :: String.t()
  365. def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
  366. @spec ap_followers(User.t()) :: String.t()
  367. def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
  368. def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
  369. @spec ap_following(User.t()) :: String.t()
  370. def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
  371. def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
  372. @spec ap_featured_collection(User.t()) :: String.t()
  373. def ap_featured_collection(%User{featured_address: fa}) when is_binary(fa), do: fa
  374. def ap_featured_collection(%User{} = user), do: "#{ap_id(user)}/collections/featured"
  375. defp truncate_fields_param(params) do
  376. if Map.has_key?(params, :fields) do
  377. Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
  378. else
  379. params
  380. end
  381. end
  382. defp truncate_if_exists(params, key, max_length) do
  383. if Map.has_key?(params, key) and is_binary(params[key]) do
  384. {value, _chopped} = String.split_at(params[key], max_length)
  385. Map.put(params, key, value)
  386. else
  387. params
  388. end
  389. end
  390. defp fix_follower_address(%{follower_address: _, following_address: _} = params), do: params
  391. defp fix_follower_address(%{nickname: nickname} = params),
  392. do: Map.put(params, :follower_address, ap_followers(%User{nickname: nickname}))
  393. defp fix_follower_address(params), do: params
  394. def remote_user_changeset(struct \\ %User{local: false}, params) do
  395. bio_limit = Config.get([:instance, :user_bio_length], 5000)
  396. name_limit = Config.get([:instance, :user_name_length], 100)
  397. name =
  398. case params[:name] do
  399. name when is_binary(name) and byte_size(name) > 0 -> name
  400. _ -> params[:nickname]
  401. end
  402. params =
  403. params
  404. |> Map.put(:name, name)
  405. |> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now())
  406. |> truncate_if_exists(:name, name_limit)
  407. |> truncate_if_exists(:bio, bio_limit)
  408. |> truncate_fields_param()
  409. |> fix_follower_address()
  410. struct
  411. |> cast(
  412. params,
  413. [
  414. :bio,
  415. :emoji,
  416. :ap_id,
  417. :inbox,
  418. :shared_inbox,
  419. :nickname,
  420. :public_key,
  421. :avatar,
  422. :banner,
  423. :is_locked,
  424. :last_refreshed_at,
  425. :uri,
  426. :follower_address,
  427. :following_address,
  428. :featured_address,
  429. :hide_followers,
  430. :hide_follows,
  431. :hide_followers_count,
  432. :hide_follows_count,
  433. :follower_count,
  434. :fields,
  435. :following_count,
  436. :is_discoverable,
  437. :invisible,
  438. :actor_type,
  439. :also_known_as,
  440. :accepts_chat_messages,
  441. :pinned_objects,
  442. :birthday,
  443. :show_birthday
  444. ]
  445. )
  446. |> cast(params, [:name], empty_values: [])
  447. |> validate_required([:ap_id])
  448. |> validate_required([:name], trim: false)
  449. |> unique_constraint(:nickname)
  450. |> validate_format(:nickname, @email_regex)
  451. |> validate_length(:bio, max: bio_limit)
  452. |> validate_length(:name, max: name_limit)
  453. |> validate_fields(true)
  454. |> validate_non_local()
  455. end
  456. defp validate_non_local(cng) do
  457. local? = get_field(cng, :local)
  458. if local? do
  459. cng
  460. |> add_error(:local, "User is local, can't update with this changeset.")
  461. else
  462. cng
  463. end
  464. end
  465. def update_changeset(struct, params \\ %{}) do
  466. bio_limit = Config.get([:instance, :user_bio_length], 5000)
  467. name_limit = Config.get([:instance, :user_name_length], 100)
  468. struct
  469. |> cast(
  470. params,
  471. [
  472. :bio,
  473. :raw_bio,
  474. :name,
  475. :emoji,
  476. :avatar,
  477. :public_key,
  478. :inbox,
  479. :shared_inbox,
  480. :is_locked,
  481. :no_rich_text,
  482. :default_scope,
  483. :banner,
  484. :hide_follows,
  485. :hide_followers,
  486. :hide_followers_count,
  487. :hide_follows_count,
  488. :hide_favorites,
  489. :allow_following_move,
  490. :also_known_as,
  491. :background,
  492. :show_role,
  493. :skip_thread_containment,
  494. :fields,
  495. :raw_fields,
  496. :pleroma_settings_store,
  497. :is_discoverable,
  498. :actor_type,
  499. :accepts_chat_messages,
  500. :disclose_client,
  501. :birthday,
  502. :show_birthday
  503. ]
  504. )
  505. |> validate_min_age()
  506. |> unique_constraint(:nickname)
  507. |> validate_format(:nickname, local_nickname_regex())
  508. |> validate_length(:bio, max: bio_limit)
  509. |> validate_length(:name, min: 1, max: name_limit)
  510. |> validate_inclusion(:actor_type, Pleroma.Constants.allowed_user_actor_types())
  511. |> put_fields()
  512. |> put_emoji()
  513. |> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
  514. |> put_change_if_present(:avatar, &put_upload(&1, :avatar))
  515. |> put_change_if_present(:banner, &put_upload(&1, :banner))
  516. |> put_change_if_present(:background, &put_upload(&1, :background))
  517. |> put_change_if_present(
  518. :pleroma_settings_store,
  519. &{:ok, Map.merge(struct.pleroma_settings_store, &1)}
  520. )
  521. |> validate_fields(false)
  522. end
  523. defp put_fields(changeset) do
  524. if raw_fields = get_change(changeset, :raw_fields) do
  525. raw_fields =
  526. raw_fields
  527. |> Enum.filter(fn %{"name" => n} -> n != "" end)
  528. fields =
  529. raw_fields
  530. |> Enum.map(fn f -> Map.update!(f, "value", &parse_fields(&1)) end)
  531. changeset
  532. |> put_change(:raw_fields, raw_fields)
  533. |> put_change(:fields, fields)
  534. else
  535. changeset
  536. end
  537. end
  538. defp parse_fields(value) do
  539. value
  540. |> Formatter.linkify(mentions_format: :full)
  541. |> elem(0)
  542. end
  543. defp put_emoji(changeset) do
  544. emojified_fields = [:bio, :name, :raw_fields]
  545. if Enum.any?(changeset.changes, fn {k, _} -> k in emojified_fields end) do
  546. bio = Emoji.Formatter.get_emoji_map(get_field(changeset, :bio))
  547. name = Emoji.Formatter.get_emoji_map(get_field(changeset, :name))
  548. emoji = Map.merge(bio, name)
  549. emoji =
  550. changeset
  551. |> get_field(:raw_fields)
  552. |> Enum.reduce(emoji, fn x, acc ->
  553. Map.merge(acc, Emoji.Formatter.get_emoji_map(x["name"] <> x["value"]))
  554. end)
  555. put_change(changeset, :emoji, emoji)
  556. else
  557. changeset
  558. end
  559. end
  560. defp put_change_if_present(changeset, map_field, value_function) do
  561. with {:ok, value} <- fetch_change(changeset, map_field),
  562. {:ok, new_value} <- value_function.(value) do
  563. put_change(changeset, map_field, new_value)
  564. else
  565. {:error, :file_too_large} ->
  566. Ecto.Changeset.validate_change(changeset, map_field, fn map_field, _value ->
  567. [{map_field, "file is too large"}]
  568. end)
  569. _ ->
  570. changeset
  571. end
  572. end
  573. defp put_upload(value, type) do
  574. with %Plug.Upload{} <- value,
  575. {:ok, object} <- ActivityPub.upload(value, type: type) do
  576. {:ok, object.data}
  577. end
  578. end
  579. def update_as_admin_changeset(struct, params) do
  580. struct
  581. |> update_changeset(params)
  582. |> cast(params, [:email])
  583. |> delete_change(:also_known_as)
  584. |> unique_constraint(:email)
  585. |> validate_format(:email, @email_regex)
  586. |> validate_inclusion(:actor_type, ["Person", "Service"])
  587. end
  588. @spec update_as_admin(User.t(), map()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  589. def update_as_admin(user, params) do
  590. params = Map.put(params, "password_confirmation", params["password"])
  591. changeset = update_as_admin_changeset(user, params)
  592. if params["password"] do
  593. reset_password(user, changeset, params)
  594. else
  595. User.update_and_set_cache(changeset)
  596. end
  597. end
  598. def password_update_changeset(struct, params) do
  599. struct
  600. |> cast(params, [:password, :password_confirmation])
  601. |> validate_required([:password, :password_confirmation])
  602. |> validate_confirmation(:password)
  603. |> put_password_hash()
  604. |> put_change(:password_reset_pending, false)
  605. end
  606. @spec reset_password(User.t(), map()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  607. def reset_password(%User{} = user, params) do
  608. reset_password(user, user, params)
  609. end
  610. def reset_password(%User{id: user_id} = user, struct, params) do
  611. multi =
  612. Multi.new()
  613. |> Multi.update(:user, password_update_changeset(struct, params))
  614. |> Multi.delete_all(:tokens, OAuth.Token.Query.get_by_user(user_id))
  615. |> Multi.delete_all(:auth, OAuth.Authorization.delete_by_user_query(user))
  616. case Repo.transaction(multi) do
  617. {:ok, %{user: user} = _} -> set_cache(user)
  618. {:error, _, changeset, _} -> {:error, changeset}
  619. end
  620. end
  621. def update_password_reset_pending(user, value) do
  622. user
  623. |> change()
  624. |> put_change(:password_reset_pending, value)
  625. |> update_and_set_cache()
  626. end
  627. def force_password_reset_async(user) do
  628. BackgroundWorker.enqueue("force_password_reset", %{"user_id" => user.id})
  629. end
  630. @spec force_password_reset(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  631. def force_password_reset(user), do: update_password_reset_pending(user, true)
  632. # Used to auto-register LDAP accounts which won't have a password hash stored locally
  633. def register_changeset_ldap(struct, params = %{password: password})
  634. when is_nil(password) do
  635. params = Map.put_new(params, :accepts_chat_messages, true)
  636. params =
  637. if Map.has_key?(params, :email) do
  638. Map.put_new(params, :email, params[:email])
  639. else
  640. params
  641. end
  642. struct
  643. |> cast(params, [
  644. :name,
  645. :nickname,
  646. :email,
  647. :accepts_chat_messages
  648. ])
  649. |> validate_required([:name, :nickname])
  650. |> unique_constraint(:nickname)
  651. |> validate_not_restricted_nickname(:nickname)
  652. |> validate_format(:nickname, local_nickname_regex())
  653. |> put_ap_id()
  654. |> unique_constraint(:ap_id)
  655. |> put_following_and_follower_and_featured_address()
  656. |> put_private_key()
  657. end
  658. def register_changeset(struct, params \\ %{}, opts \\ []) do
  659. bio_limit = Config.get([:instance, :user_bio_length], 5000)
  660. name_limit = Config.get([:instance, :user_name_length], 100)
  661. reason_limit = Config.get([:instance, :registration_reason_length], 500)
  662. params = Map.put_new(params, :accepts_chat_messages, true)
  663. confirmed? =
  664. if is_nil(opts[:confirmed]) do
  665. !Config.get([:instance, :account_activation_required])
  666. else
  667. opts[:confirmed]
  668. end
  669. approved? =
  670. if is_nil(opts[:approved]) do
  671. !Config.get([:instance, :account_approval_required])
  672. else
  673. opts[:approved]
  674. end
  675. struct
  676. |> confirmation_changeset(set_confirmation: confirmed?)
  677. |> approval_changeset(set_approval: approved?)
  678. |> cast(params, [
  679. :bio,
  680. :raw_bio,
  681. :email,
  682. :name,
  683. :nickname,
  684. :password,
  685. :password_confirmation,
  686. :emoji,
  687. :accepts_chat_messages,
  688. :registration_reason,
  689. :birthday,
  690. :language
  691. ])
  692. |> validate_required([:name, :nickname, :password, :password_confirmation])
  693. |> validate_confirmation(:password)
  694. |> unique_constraint(:email)
  695. |> validate_format(:email, @email_regex)
  696. |> validate_email_not_in_blacklisted_domain(:email)
  697. |> unique_constraint(:nickname)
  698. |> validate_not_restricted_nickname(:nickname)
  699. |> validate_format(:nickname, local_nickname_regex())
  700. |> validate_length(:bio, max: bio_limit)
  701. |> validate_length(:name, min: 1, max: name_limit)
  702. |> validate_length(:registration_reason, max: reason_limit)
  703. |> maybe_validate_required_email(opts[:external])
  704. |> maybe_validate_required_birthday
  705. |> validate_min_age()
  706. |> put_password_hash
  707. |> put_ap_id()
  708. |> unique_constraint(:ap_id)
  709. |> put_following_and_follower_and_featured_address()
  710. |> put_private_key()
  711. end
  712. def validate_not_restricted_nickname(changeset, field) do
  713. validate_change(changeset, field, fn _, value ->
  714. valid? =
  715. Config.get([User, :restricted_nicknames])
  716. |> Enum.all?(fn restricted_nickname ->
  717. String.downcase(value) != String.downcase(restricted_nickname)
  718. end)
  719. if valid?, do: [], else: [nickname: "Invalid nickname"]
  720. end)
  721. end
  722. def validate_email_not_in_blacklisted_domain(changeset, field) do
  723. validate_change(changeset, field, fn _, value ->
  724. valid? =
  725. Config.get([User, :email_blacklist])
  726. |> Enum.all?(fn blacklisted_domain ->
  727. blacklisted_domain_downcase = String.downcase(blacklisted_domain)
  728. !String.ends_with?(String.downcase(value), [
  729. "@" <> blacklisted_domain_downcase,
  730. "." <> blacklisted_domain_downcase
  731. ])
  732. end)
  733. if valid?, do: [], else: [email: "Invalid email"]
  734. end)
  735. end
  736. def maybe_validate_required_email(changeset, true), do: changeset
  737. def maybe_validate_required_email(changeset, _) do
  738. if Config.get([:instance, :account_activation_required]) do
  739. validate_required(changeset, [:email])
  740. else
  741. changeset
  742. end
  743. end
  744. defp maybe_validate_required_birthday(changeset) do
  745. if Config.get([:instance, :birthday_required]) do
  746. validate_required(changeset, [:birthday])
  747. else
  748. changeset
  749. end
  750. end
  751. defp validate_min_age(changeset) do
  752. changeset
  753. |> validate_change(:birthday, fn :birthday, birthday ->
  754. valid? =
  755. Date.utc_today()
  756. |> Date.diff(birthday) >=
  757. Config.get([:instance, :birthday_min_age])
  758. if valid?, do: [], else: [birthday: "Invalid age"]
  759. end)
  760. end
  761. defp put_ap_id(changeset) do
  762. ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
  763. put_change(changeset, :ap_id, ap_id)
  764. end
  765. defp put_following_and_follower_and_featured_address(changeset) do
  766. user = %User{nickname: get_field(changeset, :nickname)}
  767. followers = ap_followers(user)
  768. following = ap_following(user)
  769. featured = ap_featured_collection(user)
  770. changeset
  771. |> put_change(:follower_address, followers)
  772. |> put_change(:following_address, following)
  773. |> put_change(:featured_address, featured)
  774. end
  775. defp put_private_key(changeset) do
  776. {:ok, pem} = Keys.generate_rsa_pem()
  777. put_change(changeset, :keys, pem)
  778. end
  779. defp autofollow_users(user) do
  780. candidates = Config.get([:instance, :autofollowed_nicknames])
  781. autofollowed_users =
  782. User.Query.build(%{nickname: candidates, local: true, is_active: true})
  783. |> Repo.all()
  784. follow_all(user, autofollowed_users)
  785. end
  786. defp autofollowing_users(user) do
  787. candidates = Config.get([:instance, :autofollowing_nicknames])
  788. User.Query.build(%{nickname: candidates, local: true, deactivated: false})
  789. |> Repo.all()
  790. |> Enum.each(&follow(&1, user, :follow_accept))
  791. {:ok, :success}
  792. end
  793. @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
  794. def register(%Ecto.Changeset{} = changeset) do
  795. with {:ok, user} <- Repo.insert(changeset) do
  796. post_register_action(user)
  797. end
  798. end
  799. def post_register_action(%User{is_confirmed: false} = user) do
  800. with {:ok, _} <- maybe_send_confirmation_email(user) do
  801. {:ok, user}
  802. end
  803. end
  804. def post_register_action(%User{is_approved: false} = user) do
  805. with {:ok, _} <- send_user_approval_email(user),
  806. {:ok, _} <- send_admin_approval_emails(user) do
  807. {:ok, user}
  808. end
  809. end
  810. def post_register_action(%User{is_approved: true, is_confirmed: true} = user) do
  811. with {:ok, user} <- autofollow_users(user),
  812. {:ok, _} <- autofollowing_users(user),
  813. {:ok, user} <- set_cache(user),
  814. {:ok, _} <- maybe_send_registration_email(user),
  815. {:ok, _} <- maybe_send_welcome_email(user),
  816. {:ok, _} <- maybe_send_welcome_message(user),
  817. {:ok, _} <- maybe_send_welcome_chat_message(user) do
  818. {:ok, user}
  819. end
  820. end
  821. defp send_user_approval_email(%User{email: email} = user) when is_binary(email) do
  822. user
  823. |> Pleroma.Emails.UserEmail.approval_pending_email()
  824. |> Pleroma.Emails.Mailer.deliver_async()
  825. {:ok, :enqueued}
  826. end
  827. defp send_user_approval_email(_user) do
  828. {:ok, :skipped}
  829. end
  830. defp send_admin_approval_emails(user) do
  831. all_superusers()
  832. |> Enum.filter(fn user -> not is_nil(user.email) end)
  833. |> Enum.each(fn superuser ->
  834. superuser
  835. |> Pleroma.Emails.AdminEmail.new_unapproved_registration(user)
  836. |> Pleroma.Emails.Mailer.deliver_async()
  837. end)
  838. {:ok, :enqueued}
  839. end
  840. defp maybe_send_welcome_message(user) do
  841. if User.WelcomeMessage.enabled?() do
  842. User.WelcomeMessage.post_message(user)
  843. {:ok, :enqueued}
  844. else
  845. {:ok, :noop}
  846. end
  847. end
  848. defp maybe_send_welcome_chat_message(user) do
  849. if User.WelcomeChatMessage.enabled?() do
  850. User.WelcomeChatMessage.post_message(user)
  851. {:ok, :enqueued}
  852. else
  853. {:ok, :noop}
  854. end
  855. end
  856. defp maybe_send_welcome_email(%User{email: email} = user) when is_binary(email) do
  857. if User.WelcomeEmail.enabled?() do
  858. User.WelcomeEmail.send_email(user)
  859. {:ok, :enqueued}
  860. else
  861. {:ok, :noop}
  862. end
  863. end
  864. defp maybe_send_welcome_email(_), do: {:ok, :noop}
  865. @spec maybe_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop}
  866. def maybe_send_confirmation_email(%User{is_confirmed: false, email: email} = user)
  867. when is_binary(email) do
  868. if Config.get([:instance, :account_activation_required]) do
  869. send_confirmation_email(user)
  870. {:ok, :enqueued}
  871. else
  872. {:ok, :noop}
  873. end
  874. end
  875. def maybe_send_confirmation_email(_), do: {:ok, :noop}
  876. @spec send_confirmation_email(User.t()) :: User.t()
  877. def send_confirmation_email(%User{} = user) do
  878. user
  879. |> Pleroma.Emails.UserEmail.account_confirmation_email()
  880. |> Pleroma.Emails.Mailer.deliver_async()
  881. user
  882. end
  883. @spec maybe_send_registration_email(User.t()) :: {:ok, :enqueued | :noop}
  884. defp maybe_send_registration_email(%User{email: email} = user) when is_binary(email) do
  885. with false <- User.WelcomeEmail.enabled?(),
  886. false <- Config.get([:instance, :account_activation_required], false),
  887. false <- Config.get([:instance, :account_approval_required], false) do
  888. user
  889. |> Pleroma.Emails.UserEmail.successful_registration_email()
  890. |> Pleroma.Emails.Mailer.deliver_async()
  891. {:ok, :enqueued}
  892. else
  893. _ ->
  894. {:ok, :noop}
  895. end
  896. end
  897. defp maybe_send_registration_email(_), do: {:ok, :noop}
  898. def needs_update?(%User{local: true}), do: false
  899. def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
  900. def needs_update?(%User{local: false} = user) do
  901. NaiveDateTime.diff(NaiveDateTime.utc_now(), user.last_refreshed_at) >= 86_400
  902. end
  903. def needs_update?(_), do: true
  904. @spec maybe_direct_follow(User.t(), User.t()) ::
  905. {:ok, User.t(), User.t()} | {:error, String.t()}
  906. # "Locked" (self-locked) users demand explicit authorization of follow requests
  907. def maybe_direct_follow(%User{} = follower, %User{local: true, is_locked: true} = followed) do
  908. follow(follower, followed, :follow_pending)
  909. end
  910. def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
  911. follow(follower, followed)
  912. end
  913. def maybe_direct_follow(%User{} = follower, %User{} = followed) do
  914. {:ok, follower, followed}
  915. end
  916. @doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
  917. @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
  918. def follow_all(follower, followeds) do
  919. followeds
  920. |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
  921. |> Enum.each(&follow(follower, &1, :follow_accept))
  922. set_cache(follower)
  923. end
  924. def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
  925. deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
  926. cond do
  927. not followed.is_active ->
  928. {:error, "Could not follow user: #{followed.nickname} is deactivated."}
  929. deny_follow_blocked and blocks?(followed, follower) ->
  930. {:error, "Could not follow user: #{followed.nickname} blocked you."}
  931. true ->
  932. FollowingRelationship.follow(follower, followed, state)
  933. end
  934. end
  935. def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do
  936. {:error, "Not subscribed!"}
  937. end
  938. @spec unfollow(User.t(), User.t()) :: {:ok, User.t(), Activity.t()} | {:error, String.t()}
  939. def unfollow(%User{} = follower, %User{} = followed) do
  940. case do_unfollow(follower, followed) do
  941. {:ok, follower, followed} ->
  942. {:ok, follower, Utils.fetch_latest_follow(follower, followed)}
  943. error ->
  944. error
  945. end
  946. end
  947. @spec do_unfollow(User.t(), User.t()) :: {:ok, User.t(), User.t()} | {:error, String.t()}
  948. defp do_unfollow(%User{} = follower, %User{} = followed) do
  949. case get_follow_state(follower, followed) do
  950. state when state in [:follow_pending, :follow_accept] ->
  951. FollowingRelationship.unfollow(follower, followed)
  952. nil ->
  953. {:error, "Not subscribed!"}
  954. end
  955. end
  956. @doc "Returns follow state as Pleroma.FollowingRelationship.State value"
  957. def get_follow_state(%User{} = follower, %User{} = following) do
  958. following_relationship = FollowingRelationship.get(follower, following)
  959. get_follow_state(follower, following, following_relationship)
  960. end
  961. def get_follow_state(
  962. %User{} = follower,
  963. %User{} = following,
  964. following_relationship
  965. ) do
  966. case {following_relationship, following.local} do
  967. {nil, false} ->
  968. case Utils.fetch_latest_follow(follower, following) do
  969. %Activity{data: %{"state" => state}} when state in ["pending", "accept"] ->
  970. FollowingRelationship.state_to_enum(state)
  971. _ ->
  972. nil
  973. end
  974. {%{state: state}, _} ->
  975. state
  976. {nil, _} ->
  977. nil
  978. end
  979. end
  980. def locked?(%User{} = user) do
  981. user.is_locked || false
  982. end
  983. def get_by_id(id) do
  984. Repo.get_by(User, id: id)
  985. end
  986. def get_by_ap_id(ap_id) do
  987. Repo.get_by(User, ap_id: ap_id)
  988. end
  989. def get_by_uri(uri) do
  990. Repo.get_by(User, uri: uri)
  991. end
  992. def get_all_by_ap_id(ap_ids) do
  993. from(u in __MODULE__,
  994. where: u.ap_id in ^ap_ids
  995. )
  996. |> Repo.all()
  997. end
  998. def get_all_by_ids(ids) do
  999. from(u in __MODULE__, where: u.id in ^ids)
  1000. |> Repo.all()
  1001. end
  1002. # This is mostly an SPC migration fix. This guesses the user nickname by taking the last part
  1003. # of the ap_id and the domain and tries to get that user
  1004. def get_by_guessed_nickname(ap_id) do
  1005. domain = URI.parse(ap_id).host
  1006. name = List.last(String.split(ap_id, "/"))
  1007. nickname = "#{name}@#{domain}"
  1008. get_cached_by_nickname(nickname)
  1009. end
  1010. def set_cache({:ok, user}), do: set_cache(user)
  1011. def set_cache({:error, err}), do: {:error, err}
  1012. def set_cache(%User{} = user) do
  1013. @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
  1014. @cachex.put(:user_cache, "nickname:#{user.nickname}", user)
  1015. @cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user))
  1016. {:ok, user}
  1017. end
  1018. def update_and_set_cache(struct, params) do
  1019. struct
  1020. |> update_changeset(params)
  1021. |> update_and_set_cache()
  1022. end
  1023. def update_and_set_cache(changeset) do
  1024. with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
  1025. set_cache(user)
  1026. end
  1027. end
  1028. def get_user_friends_ap_ids(user) do
  1029. from(u in User.get_friends_query(user), select: u.ap_id)
  1030. |> Repo.all()
  1031. end
  1032. @spec get_cached_user_friends_ap_ids(User.t()) :: [String.t()]
  1033. def get_cached_user_friends_ap_ids(user) do
  1034. @cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ ->
  1035. get_user_friends_ap_ids(user)
  1036. end)
  1037. end
  1038. def invalidate_cache(user) do
  1039. @cachex.del(:user_cache, "ap_id:#{user.ap_id}")
  1040. @cachex.del(:user_cache, "nickname:#{user.nickname}")
  1041. @cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}")
  1042. @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
  1043. @cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}")
  1044. end
  1045. @spec get_cached_by_ap_id(String.t()) :: User.t() | nil
  1046. def get_cached_by_ap_id(ap_id) do
  1047. key = "ap_id:#{ap_id}"
  1048. with {:ok, nil} <- @cachex.get(:user_cache, key),
  1049. user when not is_nil(user) <- get_by_ap_id(ap_id),
  1050. {:ok, true} <- @cachex.put(:user_cache, key, user) do
  1051. user
  1052. else
  1053. {:ok, user} -> user
  1054. nil -> nil
  1055. end
  1056. end
  1057. def get_cached_by_id(id) do
  1058. key = "id:#{id}"
  1059. ap_id =
  1060. @cachex.fetch!(:user_cache, key, fn _ ->
  1061. user = get_by_id(id)
  1062. if user do
  1063. @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
  1064. {:commit, user.ap_id}
  1065. else
  1066. {:ignore, ""}
  1067. end
  1068. end)
  1069. get_cached_by_ap_id(ap_id)
  1070. end
  1071. def get_cached_by_nickname(nickname) do
  1072. key = "nickname:#{nickname}"
  1073. @cachex.fetch!(:user_cache, key, fn _ ->
  1074. case get_or_fetch_by_nickname(nickname) do
  1075. {:ok, user} -> {:commit, user}
  1076. {:error, _error} -> {:ignore, nil}
  1077. end
  1078. end)
  1079. end
  1080. def get_cached_by_nickname_or_id(nickname_or_id, opts \\ []) do
  1081. restrict_to_local = Config.get([:instance, :limit_to_local_content])
  1082. cond do
  1083. is_integer(nickname_or_id) or FlakeId.flake_id?(nickname_or_id) ->
  1084. get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id)
  1085. restrict_to_local == false or not String.contains?(nickname_or_id, "@") ->
  1086. get_cached_by_nickname(nickname_or_id)
  1087. restrict_to_local == :unauthenticated and match?(%User{}, opts[:for]) ->
  1088. get_cached_by_nickname(nickname_or_id)
  1089. true ->
  1090. nil
  1091. end
  1092. end
  1093. @spec get_by_nickname(String.t()) :: User.t() | nil
  1094. def get_by_nickname(nickname) do
  1095. Repo.get_by(User, nickname: nickname) ||
  1096. if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
  1097. Repo.get_by(User, nickname: local_nickname(nickname))
  1098. end
  1099. end
  1100. def get_by_email(email), do: Repo.get_by(User, email: email)
  1101. def get_by_nickname_or_email(nickname_or_email) do
  1102. get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
  1103. end
  1104. def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
  1105. def get_or_fetch_by_nickname(nickname) do
  1106. with %User{} = user <- get_by_nickname(nickname) do
  1107. {:ok, user}
  1108. else
  1109. _e ->
  1110. with [_nick, _domain] <- String.split(nickname, "@"),
  1111. {:ok, user} <- fetch_by_nickname(nickname) do
  1112. {:ok, user}
  1113. else
  1114. _e -> {:error, "not found " <> nickname}
  1115. end
  1116. end
  1117. end
  1118. @spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
  1119. def get_followers_query(%User{} = user, nil) do
  1120. User.Query.build(%{followers: user, is_active: true})
  1121. end
  1122. def get_followers_query(%User{} = user, page) do
  1123. user
  1124. |> get_followers_query(nil)
  1125. |> User.Query.paginate(page, 20)
  1126. end
  1127. @spec get_followers_query(User.t()) :: Ecto.Query.t()
  1128. def get_followers_query(%User{} = user), do: get_followers_query(user, nil)
  1129. @spec get_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
  1130. def get_followers(%User{} = user, page \\ nil) do
  1131. user
  1132. |> get_followers_query(page)
  1133. |> Repo.all()
  1134. end
  1135. @spec get_external_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
  1136. def get_external_followers(%User{} = user, page \\ nil) do
  1137. user
  1138. |> get_followers_query(page)
  1139. |> User.Query.build(%{external: true})
  1140. |> Repo.all()
  1141. end
  1142. def get_followers_ids(%User{} = user, page \\ nil) do
  1143. user
  1144. |> get_followers_query(page)
  1145. |> select([u], u.id)
  1146. |> Repo.all()
  1147. end
  1148. @spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
  1149. def get_friends_query(%User{} = user, nil) do
  1150. User.Query.build(%{friends: user, deactivated: false})
  1151. end
  1152. def get_friends_query(%User{} = user, page) do
  1153. user
  1154. |> get_friends_query(nil)
  1155. |> User.Query.paginate(page, 20)
  1156. end
  1157. @spec get_friends_query(User.t()) :: Ecto.Query.t()
  1158. def get_friends_query(%User{} = user), do: get_friends_query(user, nil)
  1159. def get_friends(%User{} = user, page \\ nil) do
  1160. user
  1161. |> get_friends_query(page)
  1162. |> Repo.all()
  1163. end
  1164. def get_friends_ap_ids(%User{} = user) do
  1165. user
  1166. |> get_friends_query(nil)
  1167. |> select([u], u.ap_id)
  1168. |> Repo.all()
  1169. end
  1170. def get_friends_ids(%User{} = user, page \\ nil) do
  1171. user
  1172. |> get_friends_query(page)
  1173. |> select([u], u.id)
  1174. |> Repo.all()
  1175. end
  1176. def increase_note_count(%User{} = user) do
  1177. User
  1178. |> where(id: ^user.id)
  1179. |> update([u], inc: [note_count: 1])
  1180. |> select([u], u)
  1181. |> Repo.update_all([])
  1182. |> case do
  1183. {1, [user]} -> set_cache(user)
  1184. _ -> {:error, user}
  1185. end
  1186. end
  1187. def decrease_note_count(%User{} = user) do
  1188. User
  1189. |> where(id: ^user.id)
  1190. |> update([u],
  1191. set: [
  1192. note_count: fragment("greatest(0, note_count - 1)")
  1193. ]
  1194. )
  1195. |> select([u], u)
  1196. |> Repo.update_all([])
  1197. |> case do
  1198. {1, [user]} -> set_cache(user)
  1199. _ -> {:error, user}
  1200. end
  1201. end
  1202. def update_note_count(%User{} = user, note_count \\ nil) do
  1203. note_count =
  1204. note_count ||
  1205. from(
  1206. a in Object,
  1207. where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
  1208. select: count(a.id)
  1209. )
  1210. |> Repo.one()
  1211. user
  1212. |> cast(%{note_count: note_count}, [:note_count])
  1213. |> update_and_set_cache()
  1214. end
  1215. @spec maybe_fetch_follow_information(User.t()) :: User.t()
  1216. def maybe_fetch_follow_information(user) do
  1217. with {:ok, user} <- fetch_follow_information(user) do
  1218. user
  1219. else
  1220. e ->
  1221. Logger.error("Follower/Following counter update for #{user.ap_id} failed.\n#{inspect(e)}")
  1222. user
  1223. end
  1224. end
  1225. def fetch_follow_information(user) do
  1226. with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
  1227. user
  1228. |> follow_information_changeset(info)
  1229. |> update_and_set_cache()
  1230. end
  1231. end
  1232. defp follow_information_changeset(user, params) do
  1233. user
  1234. |> cast(params, [
  1235. :hide_followers,
  1236. :hide_follows,
  1237. :follower_count,
  1238. :following_count,
  1239. :hide_followers_count,
  1240. :hide_follows_count
  1241. ])
  1242. end
  1243. @spec update_follower_count(User.t()) :: {:ok, User.t()}
  1244. def update_follower_count(%User{} = user) do
  1245. if user.local or !Config.get([:instance, :external_user_synchronization]) do
  1246. follower_count = FollowingRelationship.follower_count(user)
  1247. user
  1248. |> follow_information_changeset(%{follower_count: follower_count})
  1249. |> update_and_set_cache
  1250. else
  1251. {:ok, maybe_fetch_follow_information(user)}
  1252. end
  1253. end
  1254. @spec update_following_count(User.t()) :: {:ok, User.t()}
  1255. def update_following_count(%User{local: false} = user) do
  1256. if Config.get([:instance, :external_user_synchronization]) do
  1257. {:ok, maybe_fetch_follow_information(user)}
  1258. else
  1259. {:ok, user}
  1260. end
  1261. end
  1262. def update_following_count(%User{local: true} = user) do
  1263. following_count = FollowingRelationship.following_count(user)
  1264. user
  1265. |> follow_information_changeset(%{following_count: following_count})
  1266. |> update_and_set_cache()
  1267. end
  1268. @spec get_users_from_set([String.t()], keyword()) :: [User.t()]
  1269. def get_users_from_set(ap_ids, opts \\ []) do
  1270. local_only = Keyword.get(opts, :local_only, true)
  1271. criteria = %{ap_id: ap_ids, is_active: true}
  1272. criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria
  1273. User.Query.build(criteria)
  1274. |> Repo.all()
  1275. end
  1276. @spec get_recipients_from_activity(Activity.t()) :: [User.t()]
  1277. def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do
  1278. to = [actor | to]
  1279. query = User.Query.build(%{recipients_from_activity: to, local: true, is_active: true})
  1280. query
  1281. |> Repo.all()
  1282. end
  1283. @spec mute(User.t(), User.t(), map()) ::
  1284. {:ok, list(UserRelationship.t())} | {:error, String.t()}
  1285. def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do
  1286. notifications? = Map.get(params, :notifications, true)
  1287. duration = Map.get(params, :duration, 0)
  1288. expires_at =
  1289. if duration > 0 do
  1290. DateTime.utc_now()
  1291. |> DateTime.add(duration)
  1292. else
  1293. nil
  1294. end
  1295. with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee, expires_at),
  1296. {:ok, user_notification_mute} <-
  1297. (notifications? &&
  1298. UserRelationship.create_notification_mute(
  1299. muter,
  1300. mutee,
  1301. expires_at
  1302. )) ||
  1303. {:ok, nil} do
  1304. if duration > 0 do
  1305. Pleroma.Workers.MuteExpireWorker.enqueue(
  1306. "unmute_user",
  1307. %{"muter_id" => muter.id, "mutee_id" => mutee.id},
  1308. scheduled_at: expires_at
  1309. )
  1310. end
  1311. @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
  1312. {:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
  1313. end
  1314. end
  1315. def unmute(%User{} = muter, %User{} = mutee) do
  1316. with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee),
  1317. {:ok, user_notification_mute} <-
  1318. UserRelationship.delete_notification_mute(muter, mutee) do
  1319. @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
  1320. {:ok, [user_mute, user_notification_mute]}
  1321. end
  1322. end
  1323. def unmute(muter_id, mutee_id) do
  1324. with {:muter, %User{} = muter} <- {:muter, User.get_by_id(muter_id)},
  1325. {:mutee, %User{} = mutee} <- {:mutee, User.get_by_id(mutee_id)} do
  1326. unmute(muter, mutee)
  1327. else
  1328. {who, result} = error ->
  1329. Logger.warning(
  1330. "User.unmute/2 failed. #{who}: #{result}, muter_id: #{muter_id}, mutee_id: #{mutee_id}"
  1331. )
  1332. {:error, error}
  1333. end
  1334. end
  1335. def subscribe(%User{} = subscriber, %User{} = target) do
  1336. deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
  1337. if blocks?(target, subscriber) and deny_follow_blocked do
  1338. {:error, "Could not subscribe: #{target.nickname} is blocking you"}
  1339. else
  1340. # Note: the relationship is inverse: subscriber acts as relationship target
  1341. UserRelationship.create_inverse_subscription(target, subscriber)
  1342. end
  1343. end
  1344. def subscribe(%User{} = subscriber, %{ap_id: ap_id}) do
  1345. with %User{} = subscribee <- get_cached_by_ap_id(ap_id) do
  1346. subscribe(subscriber, subscribee)
  1347. end
  1348. end
  1349. def unsubscribe(%User{} = unsubscriber, %User{} = target) do
  1350. # Note: the relationship is inverse: subscriber acts as relationship target
  1351. UserRelationship.delete_inverse_subscription(target, unsubscriber)
  1352. end
  1353. def unsubscribe(%User{} = unsubscriber, %{ap_id: ap_id}) do
  1354. with %User{} = user <- get_cached_by_ap_id(ap_id) do
  1355. unsubscribe(unsubscriber, user)
  1356. end
  1357. end
  1358. def block(%User{} = blocker, %User{} = blocked) do
  1359. # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
  1360. blocker =
  1361. if following?(blocker, blocked) do
  1362. {:ok, blocker, _} = unfollow(blocker, blocked)
  1363. blocker
  1364. else
  1365. blocker
  1366. end
  1367. # clear any requested follows from both sides as well
  1368. blocked =
  1369. case CommonAPI.reject_follow_request(blocked, blocker) do
  1370. {:ok, %User{} = updated_blocked} -> updated_blocked
  1371. nil -> blocked
  1372. end
  1373. blocker =
  1374. case CommonAPI.reject_follow_request(blocker, blocked) do
  1375. {:ok, %User{} = updated_blocker} -> updated_blocker
  1376. nil -> blocker
  1377. end
  1378. unsubscribe(blocked, blocker)
  1379. unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true)
  1380. if unfollowing_blocked && following?(blocked, blocker), do: unfollow(blocked, blocker)
  1381. {:ok, blocker} = update_follower_count(blocker)
  1382. {:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
  1383. add_to_block(blocker, blocked)
  1384. end
  1385. # helper to handle the block given only an actor's AP id
  1386. def block(%User{} = blocker, %{ap_id: ap_id}) do
  1387. block(blocker, get_cached_by_ap_id(ap_id))
  1388. end
  1389. def unblock(%User{} = blocker, %User{} = blocked) do
  1390. remove_from_block(blocker, blocked)
  1391. end
  1392. # helper to handle the block given only an actor's AP id
  1393. def unblock(%User{} = blocker, %{ap_id: ap_id}) do
  1394. unblock(blocker, get_cached_by_ap_id(ap_id))
  1395. end
  1396. def endorse(%User{} = endorser, %User{} = target) do
  1397. with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
  1398. endorsed_users <-
  1399. User.endorsed_users_relation(endorser)
  1400. |> Repo.aggregate(:count, :id) do
  1401. cond do
  1402. endorsed_users >= max_endorsed_users ->
  1403. {:error, "You have already pinned the maximum number of users"}
  1404. not following?(endorser, target) ->
  1405. {:error, "Could not endorse: You are not following #{target.nickname}"}
  1406. true ->
  1407. UserRelationship.create_endorsement(endorser, target)
  1408. end
  1409. end
  1410. end
  1411. def endorse(%User{} = endorser, %{ap_id: ap_id}) do
  1412. with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do
  1413. endorse(endorser, endorsed)
  1414. end
  1415. end
  1416. def unendorse(%User{} = unendorser, %User{} = target) do
  1417. UserRelationship.delete_endorsement(unendorser, target)
  1418. end
  1419. def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do
  1420. with %User{} = user <- get_cached_by_ap_id(ap_id) do
  1421. unendorse(unendorser, user)
  1422. end
  1423. end
  1424. def mutes?(nil, _), do: false
  1425. def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
  1426. def mutes_user?(%User{} = user, %User{} = target) do
  1427. UserRelationship.mute_exists?(user, target)
  1428. end
  1429. @spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
  1430. def muted_notifications?(nil, _), do: false
  1431. def muted_notifications?(%User{} = user, %User{} = target),
  1432. do: UserRelationship.notification_mute_exists?(user, target)
  1433. def blocks?(nil, _), do: false
  1434. def blocks?(%User{} = user, %User{} = target) do
  1435. blocks_user?(user, target) ||
  1436. (blocks_domain?(user, target) and not User.following?(user, target))
  1437. end
  1438. def blocks_user?(%User{} = user, %User{} = target) do
  1439. UserRelationship.block_exists?(user, target)
  1440. end
  1441. def blocks_user?(_, _), do: false
  1442. def blocks_domain?(%User{} = user, %User{} = target) do
  1443. domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
  1444. %{host: host} = URI.parse(target.ap_id)
  1445. Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
  1446. end
  1447. def blocks_domain?(_, _), do: false
  1448. def subscribed_to?(%User{} = user, %User{} = target) do
  1449. # Note: the relationship is inverse: subscriber acts as relationship target
  1450. UserRelationship.inverse_subscription_exists?(target, user)
  1451. end
  1452. def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
  1453. with %User{} = target <- get_cached_by_ap_id(ap_id) do
  1454. subscribed_to?(user, target)
  1455. end
  1456. end
  1457. def endorses?(%User{} = user, %User{} = target) do
  1458. UserRelationship.endorsement_exists?(user, target)
  1459. end
  1460. @doc """
  1461. Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
  1462. E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
  1463. """
  1464. @spec outgoing_relationships_ap_ids(User.t(), list(atom())) :: %{atom() => list(String.t())}
  1465. def outgoing_relationships_ap_ids(_user, []), do: %{}
  1466. def outgoing_relationships_ap_ids(nil, _relationship_types), do: %{}
  1467. def outgoing_relationships_ap_ids(%User{} = user, relationship_types)
  1468. when is_list(relationship_types) do
  1469. db_result =
  1470. user
  1471. |> assoc(:outgoing_relationships)
  1472. |> join(:inner, [user_rel], u in assoc(user_rel, :target))
  1473. |> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
  1474. |> select([user_rel, u], [user_rel.relationship_type, fragment("array_agg(?)", u.ap_id)])
  1475. |> group_by([user_rel, u], user_rel.relationship_type)
  1476. |> Repo.all()
  1477. |> Enum.into(%{}, fn [k, v] -> {k, v} end)
  1478. Enum.into(
  1479. relationship_types,
  1480. %{},
  1481. fn rel_type -> {rel_type, db_result[rel_type] || []} end
  1482. )
  1483. end
  1484. def incoming_relationships_ungrouped_ap_ids(user, relationship_types, ap_ids \\ nil)
  1485. def incoming_relationships_ungrouped_ap_ids(_user, [], _ap_ids), do: []
  1486. def incoming_relationships_ungrouped_ap_ids(nil, _relationship_types, _ap_ids), do: []
  1487. def incoming_relationships_ungrouped_ap_ids(%User{} = user, relationship_types, ap_ids)
  1488. when is_list(relationship_types) do
  1489. user
  1490. |> assoc(:incoming_relationships)
  1491. |> join(:inner, [user_rel], u in assoc(user_rel, :source))
  1492. |> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
  1493. |> maybe_filter_on_ap_id(ap_ids)
  1494. |> select([user_rel, u], u.ap_id)
  1495. |> distinct(true)
  1496. |> Repo.all()
  1497. end
  1498. defp maybe_filter_on_ap_id(query, ap_ids) when is_list(ap_ids) do
  1499. where(query, [user_rel, u], u.ap_id in ^ap_ids)
  1500. end
  1501. defp maybe_filter_on_ap_id(query, _ap_ids), do: query
  1502. def set_activation_async(user, status \\ true) do
  1503. BackgroundWorker.enqueue("user_activation", %{"user_id" => user.id, "status" => status})
  1504. end
  1505. @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  1506. def set_activation(users, status) when is_list(users) do
  1507. Repo.transaction(fn ->
  1508. for user <- users do
  1509. {:ok, user} = set_activation(user, status)
  1510. user
  1511. end
  1512. end)
  1513. end
  1514. @spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  1515. def set_activation(%User{} = user, status) do
  1516. with {:ok, user} <- set_activation_status(user, status) do
  1517. user
  1518. |> get_followers()
  1519. |> Enum.filter(& &1.local)
  1520. |> Enum.each(&set_cache(update_following_count(&1)))
  1521. # Only update local user counts, remote will be update during the next pull.
  1522. user
  1523. |> get_friends()
  1524. |> Enum.filter(& &1.local)
  1525. |> Enum.each(&do_unfollow(user, &1))
  1526. {:ok, user}
  1527. end
  1528. end
  1529. def approve(users) when is_list(users) do
  1530. Repo.transaction(fn ->
  1531. Enum.map(users, fn user ->
  1532. with {:ok, user} <- approve(user), do: user
  1533. end)
  1534. end)
  1535. end
  1536. def approve(%User{is_approved: false} = user) do
  1537. with chg <- change(user, is_approved: true),
  1538. {:ok, user} <- update_and_set_cache(chg) do
  1539. post_register_action(user)
  1540. {:ok, user}
  1541. end
  1542. end
  1543. def approve(%User{} = user), do: {:ok, user}
  1544. def confirm(users) when is_list(users) do
  1545. Repo.transaction(fn ->
  1546. Enum.map(users, fn user ->
  1547. with {:ok, user} <- confirm(user), do: user
  1548. end)
  1549. end)
  1550. end
  1551. def confirm(%User{is_confirmed: false} = user) do
  1552. with chg <- confirmation_changeset(user, set_confirmation: true),
  1553. {:ok, user} <- update_and_set_cache(chg) do
  1554. post_register_action(user)
  1555. {:ok, user}
  1556. end
  1557. end
  1558. def confirm(%User{} = user), do: {:ok, user}
  1559. def set_suggestion(users, is_suggested) when is_list(users) do
  1560. Repo.transaction(fn ->
  1561. Enum.map(users, fn user ->
  1562. with {:ok, user} <- set_suggestion(user, is_suggested), do: user
  1563. end)
  1564. end)
  1565. end
  1566. def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
  1567. def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
  1568. user
  1569. |> change(is_suggested: is_suggested)
  1570. |> update_and_set_cache()
  1571. end
  1572. def update_notification_settings(%User{} = user, settings) do
  1573. user
  1574. |> cast(%{notification_settings: settings}, [])
  1575. |> cast_embed(:notification_settings)
  1576. |> validate_required([:notification_settings])
  1577. |> update_and_set_cache()
  1578. end
  1579. @spec purge_user_changeset(User.t()) :: Ecto.Changeset.t()
  1580. def purge_user_changeset(user) do
  1581. # "Right to be forgotten"
  1582. # https://gdpr.eu/right-to-be-forgotten/
  1583. change(user, %{
  1584. bio: "",
  1585. raw_bio: nil,
  1586. email: nil,
  1587. name: nil,
  1588. password_hash: nil,
  1589. avatar: %{},
  1590. tags: [],
  1591. last_refreshed_at: nil,
  1592. last_digest_emailed_at: nil,
  1593. banner: %{},
  1594. background: %{},
  1595. note_count: 0,
  1596. follower_count: 0,
  1597. following_count: 0,
  1598. is_locked: false,
  1599. password_reset_pending: false,
  1600. registration_reason: nil,
  1601. confirmation_token: nil,
  1602. domain_blocks: [],
  1603. is_active: false,
  1604. is_moderator: false,
  1605. is_admin: false,
  1606. mascot: nil,
  1607. emoji: %{},
  1608. pleroma_settings_store: %{},
  1609. fields: [],
  1610. raw_fields: [],
  1611. is_discoverable: false,
  1612. also_known_as: []
  1613. # id: preserved
  1614. # ap_id: preserved
  1615. # nickname: preserved
  1616. })
  1617. end
  1618. # Purge doesn't delete the user from the database.
  1619. # It just nulls all its fields and deactivates it.
  1620. # See `User.purge_user_changeset/1` above.
  1621. defp purge(%User{} = user) do
  1622. user
  1623. |> purge_user_changeset()
  1624. |> update_and_set_cache()
  1625. end
  1626. def delete(users) when is_list(users) do
  1627. for user <- users, do: delete(user)
  1628. end
  1629. def delete(%User{} = user) do
  1630. # Purge the user immediately
  1631. purge(user)
  1632. BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
  1633. end
  1634. # *Actually* delete the user from the DB
  1635. defp delete_from_db(%User{} = user) do
  1636. invalidate_cache(user)
  1637. Repo.delete(user)
  1638. end
  1639. # If the user never finalized their account, it's safe to delete them.
  1640. defp maybe_delete_from_db(%User{local: true, is_confirmed: false} = user),
  1641. do: delete_from_db(user)
  1642. defp maybe_delete_from_db(%User{local: true, is_approved: false} = user),
  1643. do: delete_from_db(user)
  1644. defp maybe_delete_from_db(user), do: {:ok, user}
  1645. def perform(:force_password_reset, user), do: force_password_reset(user)
  1646. @spec perform(atom(), User.t()) :: {:ok, User.t()}
  1647. def perform(:delete, %User{} = user) do
  1648. # Purge the user again, in case perform/2 is called directly
  1649. purge(user)
  1650. # Remove all relationships
  1651. user
  1652. |> get_followers()
  1653. |> Enum.each(fn follower ->
  1654. ActivityPub.unfollow(follower, user)
  1655. unfollow(follower, user)
  1656. end)
  1657. user
  1658. |> get_friends()
  1659. |> Enum.each(fn followed ->
  1660. ActivityPub.unfollow(user, followed)
  1661. unfollow(user, followed)
  1662. end)
  1663. delete_user_activities(user)
  1664. delete_notifications_from_user_activities(user)
  1665. delete_outgoing_pending_follow_requests(user)
  1666. maybe_delete_from_db(user)
  1667. end
  1668. def perform(:set_activation_async, user, status), do: set_activation(user, status)
  1669. @spec external_users_query() :: Ecto.Query.t()
  1670. def external_users_query do
  1671. User.Query.build(%{
  1672. external: true,
  1673. active: true,
  1674. order_by: :id
  1675. })
  1676. end
  1677. @spec external_users(keyword()) :: [User.t()]
  1678. def external_users(opts \\ []) do
  1679. query =
  1680. external_users_query()
  1681. |> select([u], struct(u, [:id, :ap_id]))
  1682. query =
  1683. if opts[:max_id],
  1684. do: where(query, [u], u.id > ^opts[:max_id]),
  1685. else: query
  1686. query =
  1687. if opts[:limit],
  1688. do: limit(query, ^opts[:limit]),
  1689. else: query
  1690. Repo.all(query)
  1691. end
  1692. def delete_notifications_from_user_activities(%User{ap_id: ap_id}) do
  1693. Notification
  1694. |> join(:inner, [n], activity in assoc(n, :activity))
  1695. |> where([n, a], fragment("? = ?", a.actor, ^ap_id))
  1696. |> Repo.delete_all()
  1697. end
  1698. def delete_user_activities(%User{ap_id: ap_id} = user) do
  1699. ap_id
  1700. |> Activity.Queries.by_actor()
  1701. |> Repo.chunk_stream(50, :batches)
  1702. |> Stream.each(fn activities ->
  1703. Enum.each(activities, fn activity -> delete_activity(activity, user) end)
  1704. end)
  1705. |> Stream.run()
  1706. end
  1707. defp delete_activity(%{data: %{"type" => "Create", "object" => object}} = activity, user) do
  1708. with {_, %Object{}} <- {:find_object, Object.get_by_ap_id(object)},
  1709. {:ok, delete_data, _} <- Builder.delete(user, object) do
  1710. Pipeline.common_pipeline(delete_data, local: user.local)
  1711. else
  1712. {:find_object, nil} ->
  1713. # We have the create activity, but not the object, it was probably pruned.
  1714. # Insert a tombstone and try again
  1715. with {:ok, tombstone_data, _} <- Builder.tombstone(user.ap_id, object),
  1716. {:ok, _tombstone} <- Object.create(tombstone_data) do
  1717. delete_activity(activity, user)
  1718. end
  1719. e ->
  1720. Logger.error("Could not delete #{object} created by #{activity.data["ap_id"]}")
  1721. Logger.error("Error: #{inspect(e)}")
  1722. end
  1723. end
  1724. defp delete_activity(%{data: %{"type" => type}} = activity, user)
  1725. when type in ["Like", "Announce"] do
  1726. {:ok, undo, _} = Builder.undo(user, activity)
  1727. Pipeline.common_pipeline(undo, local: user.local)
  1728. end
  1729. defp delete_activity(_activity, _user), do: "Doing nothing"
  1730. defp delete_outgoing_pending_follow_requests(user) do
  1731. user
  1732. |> FollowingRelationship.outgoing_pending_follow_requests_query()
  1733. |> Repo.delete_all()
  1734. end
  1735. def html_filter_policy(%User{no_rich_text: true}) do
  1736. Pleroma.HTML.Scrubber.TwitterText
  1737. end
  1738. def html_filter_policy(_), do: Config.get([:markup, :scrub_policy])
  1739. def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)
  1740. def get_or_fetch_by_ap_id(ap_id) do
  1741. cached_user = get_cached_by_ap_id(ap_id)
  1742. maybe_fetched_user = needs_update?(cached_user) && fetch_by_ap_id(ap_id)
  1743. case {cached_user, maybe_fetched_user} do
  1744. {_, {:ok, %User{} = user}} ->
  1745. {:ok, user}
  1746. {%User{} = user, _} ->
  1747. {:ok, user}
  1748. _ ->
  1749. {:error, :not_found}
  1750. end
  1751. end
  1752. @doc """
  1753. Creates an internal service actor by URI if missing.
  1754. Optionally takes nickname for addressing.
  1755. """
  1756. @spec get_or_create_service_actor_by_ap_id(String.t(), String.t()) :: User.t() | nil
  1757. def get_or_create_service_actor_by_ap_id(uri, nickname) do
  1758. {_, user} =
  1759. case get_cached_by_ap_id(uri) do
  1760. nil ->
  1761. with {:error, %{errors: errors}} <- create_service_actor(uri, nickname) do
  1762. Logger.error("Cannot create service actor: #{uri}/.\n#{inspect(errors)}")
  1763. {:error, nil}
  1764. end
  1765. %User{invisible: false} = user ->
  1766. set_invisible(user)
  1767. user ->
  1768. {:ok, user}
  1769. end
  1770. user
  1771. end
  1772. @spec set_invisible(User.t()) :: {:ok, User.t()}
  1773. defp set_invisible(user) do
  1774. user
  1775. |> change(%{invisible: true})
  1776. |> update_and_set_cache()
  1777. end
  1778. @spec create_service_actor(String.t(), String.t()) ::
  1779. {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  1780. defp create_service_actor(uri, nickname) do
  1781. %User{
  1782. invisible: true,
  1783. local: true,
  1784. ap_id: uri,
  1785. nickname: nickname,
  1786. follower_address: uri <> "/followers"
  1787. }
  1788. |> change
  1789. |> put_private_key()
  1790. |> unique_constraint(:nickname)
  1791. |> Repo.insert()
  1792. |> set_cache()
  1793. end
  1794. def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do
  1795. key =
  1796. public_key_pem
  1797. |> :public_key.pem_decode()
  1798. |> hd()
  1799. |> :public_key.pem_entry_decode()
  1800. {:ok, key}
  1801. end
  1802. def public_key(_), do: {:error, "key not found"}
  1803. def get_public_key_for_ap_id(ap_id) do
  1804. with %User{} = user <- get_cached_by_ap_id(ap_id),
  1805. {:ok, public_key} <- public_key(user) do
  1806. {:ok, public_key}
  1807. else
  1808. _ -> :error
  1809. end
  1810. end
  1811. @doc "Gets or fetch a user by uri or nickname."
  1812. @spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
  1813. def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
  1814. def get_or_fetch("https://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
  1815. def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
  1816. # wait a period of time and return newest version of the User structs
  1817. # this is because we have synchronous follow APIs and need to simulate them
  1818. # with an async handshake
  1819. def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
  1820. with %User{} = a <- get_cached_by_id(a.id),
  1821. %User{} = b <- get_cached_by_id(b.id) do
  1822. {:ok, a, b}
  1823. else
  1824. nil -> :error
  1825. end
  1826. end
  1827. def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
  1828. with :ok <- :timer.sleep(timeout),
  1829. %User{} = a <- get_cached_by_id(a.id),
  1830. %User{} = b <- get_cached_by_id(b.id) do
  1831. {:ok, a, b}
  1832. else
  1833. nil -> :error
  1834. end
  1835. end
  1836. def parse_bio(bio) when is_binary(bio) and bio != "" do
  1837. bio
  1838. |> CommonUtils.format_input("text/plain", mentions_format: :full)
  1839. |> elem(0)
  1840. end
  1841. def parse_bio(_), do: ""
  1842. def parse_bio(bio, user) when is_binary(bio) and bio != "" do
  1843. # TODO: get profile URLs other than user.ap_id
  1844. profile_urls = [user.ap_id]
  1845. bio
  1846. |> CommonUtils.format_input("text/plain",
  1847. mentions_format: :full,
  1848. rel: &RelMe.maybe_put_rel_me(&1, profile_urls)
  1849. )
  1850. |> elem(0)
  1851. end
  1852. def parse_bio(_, _), do: ""
  1853. def tag(user_identifiers, tags) when is_list(user_identifiers) do
  1854. Repo.transaction(fn ->
  1855. for user_identifier <- user_identifiers, do: tag(user_identifier, tags)
  1856. end)
  1857. end
  1858. def tag(nickname, tags) when is_binary(nickname),
  1859. do: tag(get_by_nickname(nickname), tags)
  1860. def tag(%User{} = user, tags),
  1861. do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
  1862. def untag(user_identifiers, tags) when is_list(user_identifiers) do
  1863. Repo.transaction(fn ->
  1864. for user_identifier <- user_identifiers, do: untag(user_identifier, tags)
  1865. end)
  1866. end
  1867. def untag(nickname, tags) when is_binary(nickname),
  1868. do: untag(get_by_nickname(nickname), tags)
  1869. def untag(%User{} = user, tags),
  1870. do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
  1871. defp update_tags(%User{} = user, new_tags) do
  1872. {:ok, updated_user} =
  1873. user
  1874. |> change(%{tags: new_tags})
  1875. |> update_and_set_cache()
  1876. updated_user
  1877. end
  1878. defp normalize_tags(tags) do
  1879. [tags]
  1880. |> List.flatten()
  1881. |> Enum.map(&String.downcase/1)
  1882. end
  1883. defp local_nickname_regex do
  1884. if Config.get([:instance, :extended_nickname_format]) do
  1885. @extended_local_nickname_regex
  1886. else
  1887. @strict_local_nickname_regex
  1888. end
  1889. end
  1890. def local_nickname(nickname_or_mention) do
  1891. nickname_or_mention
  1892. |> full_nickname()
  1893. |> String.split("@")
  1894. |> hd()
  1895. end
  1896. def full_nickname(%User{} = user) do
  1897. if String.contains?(user.nickname, "@") do
  1898. user.nickname
  1899. else
  1900. host = Pleroma.Web.WebFinger.host()
  1901. user.nickname <> "@" <> host
  1902. end
  1903. end
  1904. def full_nickname(nickname_or_mention),
  1905. do: String.trim_leading(nickname_or_mention, "@")
  1906. def error_user(ap_id) do
  1907. %User{
  1908. name: ap_id,
  1909. ap_id: ap_id,
  1910. nickname: "erroruser@example.com",
  1911. inserted_at: NaiveDateTime.utc_now()
  1912. }
  1913. end
  1914. @spec all_superusers() :: [User.t()]
  1915. def all_superusers do
  1916. User.Query.build(%{super_users: true, local: true, is_active: true})
  1917. |> Repo.all()
  1918. end
  1919. @spec all_users_with_privilege(atom()) :: [User.t()]
  1920. def all_users_with_privilege(privilege) do
  1921. User.Query.build(%{is_privileged: privilege}) |> Repo.all()
  1922. end
  1923. def muting_reblogs?(%User{} = user, %User{} = target) do
  1924. UserRelationship.reblog_mute_exists?(user, target)
  1925. end
  1926. def showing_reblogs?(%User{} = user, %User{} = target) do
  1927. not muting_reblogs?(user, target)
  1928. end
  1929. @doc """
  1930. The function returns a query to get users with no activity for given interval of days.
  1931. Inactive users are those who didn't read any notification, or had any activity where
  1932. the user is the activity's actor, during `inactivity_threshold` days.
  1933. Deactivated users will not appear in this list.
  1934. ## Examples
  1935. iex> Pleroma.User.list_inactive_users()
  1936. %Ecto.Query{}
  1937. """
  1938. @spec list_inactive_users_query(integer()) :: Ecto.Query.t()
  1939. def list_inactive_users_query(inactivity_threshold \\ 7) do
  1940. negative_inactivity_threshold = -inactivity_threshold
  1941. now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
  1942. # Subqueries are not supported in `where` clauses, join gets too complicated.
  1943. has_read_notifications =
  1944. from(n in Pleroma.Notification,
  1945. where: n.seen == true,
  1946. group_by: n.id,
  1947. having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
  1948. select: n.user_id
  1949. )
  1950. |> Pleroma.Repo.all()
  1951. from(u in Pleroma.User,
  1952. left_join: a in Pleroma.Activity,
  1953. on: u.ap_id == a.actor,
  1954. where: not is_nil(u.nickname),
  1955. where: u.is_active == ^true,
  1956. where: u.id not in ^has_read_notifications,
  1957. group_by: u.id,
  1958. having:
  1959. max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
  1960. is_nil(max(a.inserted_at))
  1961. )
  1962. end
  1963. @doc """
  1964. Enable or disable email notifications for user
  1965. ## Examples
  1966. iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => false}}, "digest", true)
  1967. Pleroma.User{email_notifications: %{"digest" => true}}
  1968. iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => true}}, "digest", false)
  1969. Pleroma.User{email_notifications: %{"digest" => false}}
  1970. """
  1971. @spec switch_email_notifications(t(), String.t(), boolean()) ::
  1972. {:ok, t()} | {:error, Ecto.Changeset.t()}
  1973. def switch_email_notifications(user, type, status) do
  1974. User.update_email_notifications(user, %{type => status})
  1975. end
  1976. @doc """
  1977. Set `last_digest_emailed_at` value for the user to current time
  1978. """
  1979. @spec touch_last_digest_emailed_at(t()) :: t()
  1980. def touch_last_digest_emailed_at(user) do
  1981. now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
  1982. {:ok, updated_user} =
  1983. user
  1984. |> change(%{last_digest_emailed_at: now})
  1985. |> update_and_set_cache()
  1986. updated_user
  1987. end
  1988. @spec set_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  1989. def set_confirmation(%User{} = user, bool) do
  1990. user
  1991. |> confirmation_changeset(set_confirmation: bool)
  1992. |> update_and_set_cache()
  1993. end
  1994. def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
  1995. mascot
  1996. end
  1997. def get_mascot(%{mascot: mascot}) when is_nil(mascot) do
  1998. # use instance-default
  1999. config = Config.get([:assets, :mascots])
  2000. default_mascot = Config.get([:assets, :default_mascot])
  2001. mascot = Keyword.get(config, default_mascot)
  2002. %{
  2003. "id" => "default-mascot",
  2004. "url" => mascot[:url],
  2005. "preview_url" => mascot[:url],
  2006. "pleroma" => %{
  2007. "mime_type" => mascot[:mime_type]
  2008. }
  2009. }
  2010. end
  2011. def get_ap_ids_by_nicknames(nicknames) do
  2012. from(u in User,
  2013. where: u.nickname in ^nicknames,
  2014. order_by: fragment("array_position(?, ?)", ^nicknames, u.nickname),
  2015. select: u.ap_id
  2016. )
  2017. |> Repo.all()
  2018. end
  2019. defp put_password_hash(
  2020. %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
  2021. ) do
  2022. change(changeset, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
  2023. end
  2024. defp put_password_hash(changeset), do: changeset
  2025. def internal?(%User{nickname: nil}), do: true
  2026. def internal?(%User{local: true, nickname: "internal." <> _}), do: true
  2027. def internal?(_), do: false
  2028. # A hack because user delete activities have a fake id for whatever reason
  2029. # TODO: Get rid of this
  2030. def get_delivered_users_by_object_id("pleroma:fake_object_id"), do: []
  2031. def get_delivered_users_by_object_id(object_id) do
  2032. from(u in User,
  2033. inner_join: delivery in assoc(u, :deliveries),
  2034. where: delivery.object_id == ^object_id
  2035. )
  2036. |> Repo.all()
  2037. end
  2038. def change_email(user, email) do
  2039. user
  2040. |> cast(%{email: email}, [:email])
  2041. |> maybe_validate_required_email(false)
  2042. |> unique_constraint(:email)
  2043. |> validate_format(:email, @email_regex)
  2044. |> update_and_set_cache()
  2045. end
  2046. def alias_users(user) do
  2047. user.also_known_as
  2048. |> Enum.map(&User.get_cached_by_ap_id/1)
  2049. |> Enum.filter(fn user -> user != nil end)
  2050. end
  2051. def add_alias(user, new_alias_user) do
  2052. current_aliases = user.also_known_as || []
  2053. new_alias_ap_id = new_alias_user.ap_id
  2054. if new_alias_ap_id in current_aliases do
  2055. {:ok, user}
  2056. else
  2057. user
  2058. |> cast(%{also_known_as: current_aliases ++ [new_alias_ap_id]}, [:also_known_as])
  2059. |> update_and_set_cache()
  2060. end
  2061. end
  2062. def delete_alias(user, alias_user) do
  2063. current_aliases = user.also_known_as || []
  2064. alias_ap_id = alias_user.ap_id
  2065. if alias_ap_id in current_aliases do
  2066. user
  2067. |> cast(%{also_known_as: current_aliases -- [alias_ap_id]}, [:also_known_as])
  2068. |> update_and_set_cache()
  2069. else
  2070. {:error, :no_such_alias}
  2071. end
  2072. end
  2073. # Internal function; public one is `deactivate/2`
  2074. defp set_activation_status(user, status) do
  2075. user
  2076. |> cast(%{is_active: status}, [:is_active])
  2077. |> update_and_set_cache()
  2078. end
  2079. def update_banner(user, banner) do
  2080. user
  2081. |> cast(%{banner: banner}, [:banner])
  2082. |> update_and_set_cache()
  2083. end
  2084. def update_background(user, background) do
  2085. user
  2086. |> cast(%{background: background}, [:background])
  2087. |> update_and_set_cache()
  2088. end
  2089. def validate_fields(changeset, remote? \\ false) do
  2090. limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
  2091. limit = Config.get([:instance, limit_name], 0)
  2092. changeset
  2093. |> validate_length(:fields, max: limit)
  2094. |> validate_change(:fields, fn :fields, fields ->
  2095. if Enum.all?(fields, &valid_field?/1) do
  2096. []
  2097. else
  2098. [fields: "invalid"]
  2099. end
  2100. end)
  2101. end
  2102. defp valid_field?(%{"name" => name, "value" => value}) do
  2103. name_limit = Config.get([:instance, :account_field_name_length], 255)
  2104. value_limit = Config.get([:instance, :account_field_value_length], 255)
  2105. is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
  2106. String.length(value) <= value_limit
  2107. end
  2108. defp valid_field?(_), do: false
  2109. defp truncate_field(%{"name" => name, "value" => value}) do
  2110. {name, _chopped} =
  2111. String.split_at(name, Config.get([:instance, :account_field_name_length], 255))
  2112. {value, _chopped} =
  2113. String.split_at(value, Config.get([:instance, :account_field_value_length], 255))
  2114. %{"name" => name, "value" => value}
  2115. end
  2116. def admin_api_update(user, params) do
  2117. user
  2118. |> cast(params, [
  2119. :is_moderator,
  2120. :is_admin,
  2121. :show_role
  2122. ])
  2123. |> update_and_set_cache()
  2124. end
  2125. @doc "Signs user out of all applications"
  2126. def global_sign_out(user) do
  2127. OAuth.Authorization.delete_user_authorizations(user)
  2128. OAuth.Token.delete_user_tokens(user)
  2129. end
  2130. def mascot_update(user, url) do
  2131. user
  2132. |> cast(%{mascot: url}, [:mascot])
  2133. |> validate_required([:mascot])
  2134. |> update_and_set_cache()
  2135. end
  2136. @spec confirmation_changeset(User.t(), keyword()) :: Ecto.Changeset.t()
  2137. def confirmation_changeset(user, set_confirmation: confirmed?) do
  2138. params =
  2139. if confirmed? do
  2140. %{
  2141. is_confirmed: true,
  2142. confirmation_token: nil
  2143. }
  2144. else
  2145. %{
  2146. is_confirmed: false,
  2147. confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
  2148. }
  2149. end
  2150. cast(user, params, [:is_confirmed, :confirmation_token])
  2151. end
  2152. @spec approval_changeset(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
  2153. def approval_changeset(changeset, set_approval: approved?) do
  2154. cast(changeset, %{is_approved: approved?}, [:is_approved])
  2155. end
  2156. @spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}
  2157. def add_pinned_object_id(%User{} = user, object_id) do
  2158. if !user.pinned_objects[object_id] do
  2159. params = %{pinned_objects: Map.put(user.pinned_objects, object_id, NaiveDateTime.utc_now())}
  2160. user
  2161. |> cast(params, [:pinned_objects])
  2162. |> validate_change(:pinned_objects, fn :pinned_objects, pinned_objects ->
  2163. max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
  2164. if Enum.count(pinned_objects) <= max_pinned_statuses do
  2165. []
  2166. else
  2167. [pinned_objects: "You have already pinned the maximum number of statuses"]
  2168. end
  2169. end)
  2170. else
  2171. change(user)
  2172. end
  2173. |> update_and_set_cache()
  2174. end
  2175. @spec remove_pinned_object_id(User.t(), String.t()) :: {:ok, t()} | {:error, term()}
  2176. def remove_pinned_object_id(%User{} = user, object_id) do
  2177. user
  2178. |> cast(
  2179. %{pinned_objects: Map.delete(user.pinned_objects, object_id)},
  2180. [:pinned_objects]
  2181. )
  2182. |> update_and_set_cache()
  2183. end
  2184. def update_email_notifications(user, settings) do
  2185. email_notifications =
  2186. user.email_notifications
  2187. |> Map.merge(settings)
  2188. |> Map.take(["digest"])
  2189. params = %{email_notifications: email_notifications}
  2190. fields = [:email_notifications]
  2191. user
  2192. |> cast(params, fields)
  2193. |> validate_required(fields)
  2194. |> update_and_set_cache()
  2195. end
  2196. defp set_domain_blocks(user, domain_blocks) do
  2197. params = %{domain_blocks: domain_blocks}
  2198. user
  2199. |> cast(params, [:domain_blocks])
  2200. |> validate_required([:domain_blocks])
  2201. |> update_and_set_cache()
  2202. end
  2203. def block_domain(user, domain_blocked) do
  2204. set_domain_blocks(user, Enum.uniq([domain_blocked | user.domain_blocks]))
  2205. end
  2206. def unblock_domain(user, domain_blocked) do
  2207. set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
  2208. end
  2209. @spec add_to_block(User.t(), User.t()) ::
  2210. {:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
  2211. defp add_to_block(%User{} = user, %User{} = blocked) do
  2212. with {:ok, relationship} <- UserRelationship.create_block(user, blocked) do
  2213. @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
  2214. {:ok, relationship}
  2215. end
  2216. end
  2217. @spec add_to_block(User.t(), User.t()) ::
  2218. {:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
  2219. defp remove_from_block(%User{} = user, %User{} = blocked) do
  2220. with {:ok, relationship} <- UserRelationship.delete_block(user, blocked) do
  2221. @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
  2222. {:ok, relationship}
  2223. end
  2224. end
  2225. def set_invisible(user, invisible) do
  2226. params = %{invisible: invisible}
  2227. user
  2228. |> cast(params, [:invisible])
  2229. |> validate_required([:invisible])
  2230. |> update_and_set_cache()
  2231. end
  2232. def sanitize_html(%User{} = user) do
  2233. sanitize_html(user, nil)
  2234. end
  2235. # User data that mastodon isn't filtering (treated as plaintext):
  2236. # - field name
  2237. # - display name
  2238. def sanitize_html(%User{} = user, filter) do
  2239. fields =
  2240. Enum.map(user.fields, fn %{"name" => name, "value" => value} ->
  2241. %{
  2242. "name" => name,
  2243. "value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
  2244. }
  2245. end)
  2246. user
  2247. |> Map.put(:bio, HTML.filter_tags(user.bio, filter))
  2248. |> Map.put(:fields, fields)
  2249. end
  2250. def get_host(%User{ap_id: ap_id} = _user) do
  2251. URI.parse(ap_id).host
  2252. end
  2253. def update_last_active_at(%__MODULE__{local: true} = user) do
  2254. user
  2255. |> cast(%{last_active_at: NaiveDateTime.utc_now()}, [:last_active_at])
  2256. |> update_and_set_cache()
  2257. end
  2258. def update_last_active_at(user), do: user
  2259. def active_user_count(days \\ 30) do
  2260. active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
  2261. __MODULE__
  2262. |> where([u], u.last_active_at >= ^active_after)
  2263. |> where([u], u.local == true)
  2264. |> Repo.aggregate(:count)
  2265. end
  2266. def update_last_status_at(user) do
  2267. User
  2268. |> where(id: ^user.id)
  2269. |> update([u], set: [last_status_at: fragment("NOW()")])
  2270. |> select([u], u)
  2271. |> Repo.update_all([])
  2272. |> case do
  2273. {1, [user]} -> set_cache(user)
  2274. _ -> {:error, user}
  2275. end
  2276. end
  2277. def get_friends_birthdays_query(%User{} = user, day, month) do
  2278. User.Query.build(%{
  2279. friends: user,
  2280. deactivated: false,
  2281. birthday_day: day,
  2282. birthday_month: month
  2283. })
  2284. end
  2285. end