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