logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

info.ex (13421B)


      1 # Pleroma: A lightweight social networking server
      2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
      3 # SPDX-License-Identifier: AGPL-3.0-only
      4 
      5 defmodule Pleroma.User.Info do
      6   use Ecto.Schema
      7   import Ecto.Changeset
      8 
      9   alias Pleroma.User.Info
     10 
     11   @type t :: %__MODULE__{}
     12 
     13   embedded_schema do
     14     field(:banner, :map, default: %{})
     15     field(:background, :map, default: %{})
     16     field(:source_data, :map, default: %{})
     17     field(:note_count, :integer, default: 0)
     18     field(:follower_count, :integer, default: 0)
     19     # Should be filled in only for remote users
     20     field(:following_count, :integer, default: nil)
     21     field(:locked, :boolean, default: false)
     22     field(:confirmation_pending, :boolean, default: false)
     23     field(:password_reset_pending, :boolean, default: false)
     24     field(:confirmation_token, :string, default: nil)
     25     field(:default_scope, :string, default: "public")
     26     field(:blocks, {:array, :string}, default: [])
     27     field(:domain_blocks, {:array, :string}, default: [])
     28     field(:mutes, {:array, :string}, default: [])
     29     field(:muted_reblogs, {:array, :string}, default: [])
     30     field(:muted_notifications, {:array, :string}, default: [])
     31     field(:subscribers, {:array, :string}, default: [])
     32     field(:deactivated, :boolean, default: false)
     33     field(:no_rich_text, :boolean, default: false)
     34     field(:ap_enabled, :boolean, default: false)
     35     field(:is_moderator, :boolean, default: false)
     36     field(:is_admin, :boolean, default: false)
     37     field(:show_role, :boolean, default: true)
     38     field(:keys, :string, default: nil)
     39     field(:settings, :map, default: nil)
     40     field(:magic_key, :string, default: nil)
     41     field(:uri, :string, default: nil)
     42     field(:topic, :string, default: nil)
     43     field(:hub, :string, default: nil)
     44     field(:salmon, :string, default: nil)
     45     field(:hide_followers_count, :boolean, default: false)
     46     field(:hide_follows_count, :boolean, default: false)
     47     field(:hide_followers, :boolean, default: false)
     48     field(:hide_follows, :boolean, default: false)
     49     field(:hide_favorites, :boolean, default: true)
     50     field(:unread_conversation_count, :integer, default: 0)
     51     field(:pinned_activities, {:array, :string}, default: [])
     52     field(:email_notifications, :map, default: %{"digest" => false})
     53     field(:mascot, :map, default: nil)
     54     field(:emoji, {:array, :map}, default: [])
     55     field(:pleroma_settings_store, :map, default: %{})
     56     field(:fields, {:array, :map}, default: nil)
     57     field(:raw_fields, {:array, :map}, default: [])
     58     field(:discoverable, :boolean, default: false)
     59 
     60     field(:notification_settings, :map,
     61       default: %{
     62         "followers" => true,
     63         "follows" => true,
     64         "non_follows" => true,
     65         "non_followers" => true
     66       }
     67     )
     68 
     69     field(:skip_thread_containment, :boolean, default: false)
     70 
     71     # Found in the wild
     72     # ap_id -> Where is this used?
     73     # bio -> Where is this used?
     74     # avatar -> Where is this used?
     75     # fqn -> Where is this used?
     76     # host -> Where is this used?
     77     # subject _> Where is this used?
     78   end
     79 
     80   def set_activation_status(info, deactivated) do
     81     params = %{deactivated: deactivated}
     82 
     83     info
     84     |> cast(params, [:deactivated])
     85     |> validate_required([:deactivated])
     86   end
     87 
     88   def set_password_reset_pending(info, pending) do
     89     params = %{password_reset_pending: pending}
     90 
     91     info
     92     |> cast(params, [:password_reset_pending])
     93     |> validate_required([:password_reset_pending])
     94   end
     95 
     96   def update_notification_settings(info, settings) do
     97     settings =
     98       settings
     99       |> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
    100       |> Map.new()
    101 
    102     notification_settings =
    103       info.notification_settings
    104       |> Map.merge(settings)
    105       |> Map.take(["followers", "follows", "non_follows", "non_followers"])
    106 
    107     params = %{notification_settings: notification_settings}
    108 
    109     info
    110     |> cast(params, [:notification_settings])
    111     |> validate_required([:notification_settings])
    112   end
    113 
    114   @doc """
    115   Update email notifications in the given User.Info struct.
    116 
    117   Examples:
    118 
    119       iex> update_email_notifications(%Pleroma.User.Info{email_notifications: %{"digest" => false}}, %{"digest" => true})
    120       %Pleroma.User.Info{email_notifications: %{"digest" => true}}
    121 
    122   """
    123   @spec update_email_notifications(t(), map()) :: Ecto.Changeset.t()
    124   def update_email_notifications(info, settings) do
    125     email_notifications =
    126       info.email_notifications
    127       |> Map.merge(settings)
    128       |> Map.take(["digest"])
    129 
    130     params = %{email_notifications: email_notifications}
    131     fields = [:email_notifications]
    132 
    133     info
    134     |> cast(params, fields)
    135     |> validate_required(fields)
    136   end
    137 
    138   def add_to_note_count(info, number) do
    139     set_note_count(info, info.note_count + number)
    140   end
    141 
    142   def set_note_count(info, number) do
    143     params = %{note_count: Enum.max([0, number])}
    144 
    145     info
    146     |> cast(params, [:note_count])
    147     |> validate_required([:note_count])
    148   end
    149 
    150   def set_follower_count(info, number) do
    151     params = %{follower_count: Enum.max([0, number])}
    152 
    153     info
    154     |> cast(params, [:follower_count])
    155     |> validate_required([:follower_count])
    156   end
    157 
    158   def set_mutes(info, mutes) do
    159     params = %{mutes: mutes}
    160 
    161     info
    162     |> cast(params, [:mutes])
    163     |> validate_required([:mutes])
    164   end
    165 
    166   @spec set_notification_mutes(Changeset.t(), [String.t()], boolean()) :: Changeset.t()
    167   def set_notification_mutes(changeset, muted_notifications, notifications?) do
    168     if notifications? do
    169       put_change(changeset, :muted_notifications, muted_notifications)
    170       |> validate_required([:muted_notifications])
    171     else
    172       changeset
    173     end
    174   end
    175 
    176   def set_blocks(info, blocks) do
    177     params = %{blocks: blocks}
    178 
    179     info
    180     |> cast(params, [:blocks])
    181     |> validate_required([:blocks])
    182   end
    183 
    184   def set_subscribers(info, subscribers) do
    185     params = %{subscribers: subscribers}
    186 
    187     info
    188     |> cast(params, [:subscribers])
    189     |> validate_required([:subscribers])
    190   end
    191 
    192   @spec add_to_mutes(Info.t(), String.t(), boolean()) :: Changeset.t()
    193   def add_to_mutes(info, muted, notifications?) do
    194     info
    195     |> set_mutes(Enum.uniq([muted | info.mutes]))
    196     |> set_notification_mutes(
    197       Enum.uniq([muted | info.muted_notifications]),
    198       notifications?
    199     )
    200   end
    201 
    202   @spec remove_from_mutes(Info.t(), String.t()) :: Changeset.t()
    203   def remove_from_mutes(info, muted) do
    204     info
    205     |> set_mutes(List.delete(info.mutes, muted))
    206     |> set_notification_mutes(List.delete(info.muted_notifications, muted), true)
    207   end
    208 
    209   def add_to_block(info, blocked) do
    210     set_blocks(info, Enum.uniq([blocked | info.blocks]))
    211   end
    212 
    213   def remove_from_block(info, blocked) do
    214     set_blocks(info, List.delete(info.blocks, blocked))
    215   end
    216 
    217   def add_to_subscribers(info, subscribed) do
    218     set_subscribers(info, Enum.uniq([subscribed | info.subscribers]))
    219   end
    220 
    221   def remove_from_subscribers(info, subscribed) do
    222     set_subscribers(info, List.delete(info.subscribers, subscribed))
    223   end
    224 
    225   def set_domain_blocks(info, domain_blocks) do
    226     params = %{domain_blocks: domain_blocks}
    227 
    228     info
    229     |> cast(params, [:domain_blocks])
    230     |> validate_required([:domain_blocks])
    231   end
    232 
    233   def add_to_domain_block(info, domain_blocked) do
    234     set_domain_blocks(info, Enum.uniq([domain_blocked | info.domain_blocks]))
    235   end
    236 
    237   def remove_from_domain_block(info, domain_blocked) do
    238     set_domain_blocks(info, List.delete(info.domain_blocks, domain_blocked))
    239   end
    240 
    241   def set_keys(info, keys) do
    242     params = %{keys: keys}
    243 
    244     info
    245     |> cast(params, [:keys])
    246     |> validate_required([:keys])
    247   end
    248 
    249   def remote_user_creation(info, params) do
    250     params =
    251       if Map.has_key?(params, :fields) do
    252         Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
    253       else
    254         params
    255       end
    256 
    257     info
    258     |> cast(params, [
    259       :ap_enabled,
    260       :source_data,
    261       :banner,
    262       :locked,
    263       :magic_key,
    264       :uri,
    265       :hub,
    266       :topic,
    267       :salmon,
    268       :hide_followers,
    269       :hide_follows,
    270       :hide_followers_count,
    271       :hide_follows_count,
    272       :follower_count,
    273       :fields,
    274       :following_count,
    275       :discoverable
    276     ])
    277     |> validate_fields(true)
    278   end
    279 
    280   def user_upgrade(info, params, remote? \\ false) do
    281     info
    282     |> cast(params, [
    283       :ap_enabled,
    284       :source_data,
    285       :banner,
    286       :locked,
    287       :magic_key,
    288       :follower_count,
    289       :following_count,
    290       :hide_follows,
    291       :fields,
    292       :hide_followers,
    293       :discoverable,
    294       :hide_followers_count,
    295       :hide_follows_count
    296     ])
    297     |> validate_fields(remote?)
    298   end
    299 
    300   def profile_update(info, params) do
    301     info
    302     |> cast(params, [
    303       :locked,
    304       :no_rich_text,
    305       :default_scope,
    306       :banner,
    307       :hide_follows,
    308       :hide_followers,
    309       :hide_followers_count,
    310       :hide_follows_count,
    311       :hide_favorites,
    312       :background,
    313       :show_role,
    314       :skip_thread_containment,
    315       :fields,
    316       :raw_fields,
    317       :pleroma_settings_store,
    318       :discoverable
    319     ])
    320     |> validate_fields()
    321   end
    322 
    323   def validate_fields(changeset, remote? \\ false) do
    324     limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
    325     limit = Pleroma.Config.get([:instance, limit_name], 0)
    326 
    327     changeset
    328     |> validate_length(:fields, max: limit)
    329     |> validate_change(:fields, fn :fields, fields ->
    330       if Enum.all?(fields, &valid_field?/1) do
    331         []
    332       else
    333         [fields: "invalid"]
    334       end
    335     end)
    336   end
    337 
    338   defp valid_field?(%{"name" => name, "value" => value}) do
    339     name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255)
    340     value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255)
    341 
    342     is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
    343       String.length(value) <= value_limit
    344   end
    345 
    346   defp valid_field?(_), do: false
    347 
    348   defp truncate_field(%{"name" => name, "value" => value}) do
    349     {name, _chopped} =
    350       String.split_at(name, Pleroma.Config.get([:instance, :account_field_name_length], 255))
    351 
    352     {value, _chopped} =
    353       String.split_at(value, Pleroma.Config.get([:instance, :account_field_value_length], 255))
    354 
    355     %{"name" => name, "value" => value}
    356   end
    357 
    358   @spec confirmation_changeset(Info.t(), keyword()) :: Changeset.t()
    359   def confirmation_changeset(info, opts) do
    360     need_confirmation? = Keyword.get(opts, :need_confirmation)
    361 
    362     params =
    363       if need_confirmation? do
    364         %{
    365           confirmation_pending: true,
    366           confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
    367         }
    368       else
    369         %{
    370           confirmation_pending: false,
    371           confirmation_token: nil
    372         }
    373       end
    374 
    375     cast(info, params, [:confirmation_pending, :confirmation_token])
    376   end
    377 
    378   def mastodon_settings_update(info, settings) do
    379     params = %{settings: settings}
    380 
    381     info
    382     |> cast(params, [:settings])
    383     |> validate_required([:settings])
    384   end
    385 
    386   def mascot_update(info, url) do
    387     params = %{mascot: url}
    388 
    389     info
    390     |> cast(params, [:mascot])
    391     |> validate_required([:mascot])
    392   end
    393 
    394   def set_source_data(info, source_data) do
    395     params = %{source_data: source_data}
    396 
    397     info
    398     |> cast(params, [:source_data])
    399     |> validate_required([:source_data])
    400   end
    401 
    402   def admin_api_update(info, params) do
    403     info
    404     |> cast(params, [
    405       :is_moderator,
    406       :is_admin,
    407       :show_role
    408     ])
    409   end
    410 
    411   def add_pinnned_activity(info, %Pleroma.Activity{id: id}) do
    412     if id not in info.pinned_activities do
    413       max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0)
    414       params = %{pinned_activities: info.pinned_activities ++ [id]}
    415 
    416       info
    417       |> cast(params, [:pinned_activities])
    418       |> validate_length(:pinned_activities,
    419         max: max_pinned_statuses,
    420         message: "You have already pinned the maximum number of statuses"
    421       )
    422     else
    423       change(info)
    424     end
    425   end
    426 
    427   def remove_pinnned_activity(info, %Pleroma.Activity{id: id}) do
    428     params = %{pinned_activities: List.delete(info.pinned_activities, id)}
    429 
    430     cast(info, params, [:pinned_activities])
    431   end
    432 
    433   def roles(%Info{is_moderator: is_moderator, is_admin: is_admin}) do
    434     %{
    435       admin: is_admin,
    436       moderator: is_moderator
    437     }
    438   end
    439 
    440   def add_reblog_mute(info, ap_id) do
    441     params = %{muted_reblogs: info.muted_reblogs ++ [ap_id]}
    442 
    443     cast(info, params, [:muted_reblogs])
    444   end
    445 
    446   def remove_reblog_mute(info, ap_id) do
    447     params = %{muted_reblogs: List.delete(info.muted_reblogs, ap_id)}
    448 
    449     cast(info, params, [:muted_reblogs])
    450   end
    451 
    452   # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
    453   # For example: [{"name": "Pronoun", "value": "she/her"}, …]
    454   def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do
    455     limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0)
    456 
    457     attachment
    458     |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
    459     |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
    460     |> Enum.take(limit)
    461   end
    462 
    463   def fields(%{fields: nil}), do: []
    464 
    465   def fields(%{fields: fields}), do: fields
    466 
    467   def follow_information_update(info, params) do
    468     info
    469     |> cast(params, [
    470       :hide_followers,
    471       :hide_follows,
    472       :follower_count,
    473       :following_count,
    474       :hide_followers_count,
    475       :hide_follows_count
    476     ])
    477   end
    478 end