Merge remote-tracking branch 'upstream/develop' into refactor/following-relationships

This commit is contained in:
Egor Kislitsyn 2019-10-24 14:42:14 +07:00
commit 4c1dd55c48
104 changed files with 2081 additions and 1368 deletions

View file

@ -5,6 +5,7 @@
defmodule Mix.Tasks.Pleroma.User do
use Mix.Task
import Mix.Pleroma
alias Ecto.Changeset
alias Pleroma.User
alias Pleroma.UserInviteToken
alias Pleroma.Web.OAuth
@ -109,10 +110,10 @@ defmodule Mix.Tasks.Pleroma.User do
start_pleroma()
with %User{} = user <- User.get_cached_by_nickname(nickname) do
{:ok, user} = User.deactivate(user, !user.info.deactivated)
{:ok, user} = User.deactivate(user, !user.deactivated)
shell_info(
"Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated"
"Activation status of #{nickname}: #{if(user.deactivated, do: "de", else: "")}activated"
)
else
_ ->
@ -340,7 +341,7 @@ defmodule Mix.Tasks.Pleroma.User do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
{:ok, user} = User.toggle_confirmation(user)
message = if user.info.confirmation_pending, do: "needs", else: "doesn't need"
message = if user.confirmation_pending, do: "needs", else: "doesn't need"
shell_info("#{nickname} #{message} confirmation.")
else
@ -364,23 +365,32 @@ defmodule Mix.Tasks.Pleroma.User do
end
defp set_moderator(user, value) do
{:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_moderator: value}))
{:ok, user} =
user
|> Changeset.change(%{is_moderator: value})
|> User.update_and_set_cache()
shell_info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
shell_info("Moderator status of #{user.nickname}: #{user.is_moderator}")
user
end
defp set_admin(user, value) do
{:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_admin: value}))
{:ok, user} =
user
|> Changeset.change(%{is_admin: value})
|> User.update_and_set_cache()
shell_info("Admin status of #{user.nickname}: #{user.info.is_admin}")
shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
user
end
defp set_locked(user, value) do
{:ok, user} = User.update_info(user, &User.Info.user_upgrade(&1, %{locked: value}))
{:ok, user} =
user
|> Changeset.change(%{locked: value})
|> User.update_and_set_cache()
shell_info("Locked status of #{user.nickname}: #{user.info.locked}")
shell_info("Locked status of #{user.nickname}: #{user.locked}")
user
end
end

View file

@ -17,7 +17,7 @@ defmodule Pleroma.Daemons.DigestEmailDaemon do
now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
from(u in inactive_users_query,
where: fragment(~s(? #> '{"email_notifications","digest"}' @> 'true'), u.info),
where: fragment(~s(? ->'digest' @> 'true'), u.email_notifications),
where: u.last_digest_emailed_at < datetime_add(^now, ^negative_interval, "day"),
select: u
)

View file

@ -72,7 +72,7 @@ defmodule Pleroma.Emails.UserEmail do
Endpoint,
:confirm_email,
user.id,
to_string(user.info.confirmation_token)
to_string(user.confirmation_token)
)
html_body = """

View file

@ -127,7 +127,7 @@ defmodule Pleroma.Formatter do
end
end
defp get_ap_id(%User{info: %{source_data: %{"url" => url}}}) when is_binary(url), do: url
defp get_ap_id(%User{source_data: %{"url" => url}}) when is_binary(url), do: url
defp get_ap_id(%User{ap_id: ap_id}), do: ap_id
defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname)

74
lib/pleroma/marker.ex Normal file
View file

@ -0,0 +1,74 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Marker do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias Ecto.Multi
alias Pleroma.Repo
alias Pleroma.User
@timelines ["notifications"]
schema "markers" do
field(:last_read_id, :string, default: "")
field(:timeline, :string, default: "")
field(:lock_version, :integer, default: 0)
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
timestamps()
end
def get_markers(user, timelines \\ []) do
Repo.all(get_query(user, timelines))
end
def upsert(%User{} = user, attrs) do
attrs
|> Map.take(@timelines)
|> Enum.reduce(Multi.new(), fn {timeline, timeline_attrs}, multi ->
marker =
user
|> get_marker(timeline)
|> changeset(timeline_attrs)
Multi.insert(multi, timeline, marker,
returning: true,
on_conflict: {:replace, [:last_read_id]},
conflict_target: [:user_id, :timeline]
)
end)
|> Repo.transaction()
end
defp get_marker(user, timeline) do
case Repo.find_resource(get_query(user, timeline)) do
{:ok, marker} -> %__MODULE__{marker | user: user}
_ -> %__MODULE__{timeline: timeline, user_id: user.id}
end
end
@doc false
defp changeset(marker, attrs) do
marker
|> cast(attrs, [:last_read_id])
|> validate_required([:user_id, :timeline, :last_read_id])
|> validate_inclusion(:timeline, @timelines)
end
defp by_timeline(query, timeline) do
from(m in query, where: m.timeline in ^List.wrap(timeline))
end
defp by_user_id(query, id), do: from(m in query, where: m.user_id == ^id)
defp get_query(user, timelines) do
__MODULE__
|> by_user_id(user.id)
|> by_timeline(timelines)
end
end

View file

@ -40,7 +40,7 @@ defmodule Pleroma.Notification do
|> where(
[n, a],
fragment(
"? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
"? not in (SELECT ap_id FROM users WHERE deactivated = 'true')",
a.actor
)
)
@ -55,21 +55,26 @@ defmodule Pleroma.Notification do
)
|> preload([n, a, o], activity: {a, object: o})
|> exclude_muted(user, opts)
|> exclude_blocked(user)
|> exclude_visibility(opts)
end
defp exclude_blocked(query, user) do
query
|> where([n, a], a.actor not in ^user.blocks)
|> where(
[n, a],
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks
)
end
defp exclude_muted(query, _, %{with_muted: true}) do
query
end
defp exclude_muted(query, user, _opts) do
query
|> where([n, a], a.actor not in ^user.info.muted_notifications)
|> where([n, a], a.actor not in ^user.info.blocks)
|> where(
[n, a],
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
)
|> where([n, a], a.actor not in ^user.muted_notifications)
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
)
@ -309,7 +314,7 @@ defmodule Pleroma.Notification do
def skip?(
:followers,
activity,
%{info: %{notification_settings: %{"followers" => false}}} = user
%{notification_settings: %{"followers" => false}} = user
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
@ -319,14 +324,14 @@ defmodule Pleroma.Notification do
def skip?(
:non_followers,
activity,
%{info: %{notification_settings: %{"non_followers" => false}}} = user
%{notification_settings: %{"non_followers" => false}} = user
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
!User.following?(follower, user)
end
def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
def skip?(:follows, activity, %{notification_settings: %{"follows" => false}} = user) do
actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor)
User.following?(user, followed)
@ -335,7 +340,7 @@ defmodule Pleroma.Notification do
def skip?(
:non_follows,
activity,
%{info: %{notification_settings: %{"non_follows" => false}}} = user
%{notification_settings: %{"non_follows" => false}} = user
) do
actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor)

View file

@ -19,7 +19,7 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
def call(%{params: %{"admin_token" => admin_token}} = conn, _) do
if secret_token() && admin_token == secret_token() do
conn
|> assign(:user, %User{info: %{is_admin: true}})
|> assign(:user, %User{is_admin: true})
else
conn
end

View file

@ -71,7 +71,7 @@ defmodule Pleroma.Plugs.OAuthPlug do
)
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
with %Token{user: %{info: %{deactivated: false} = _} = user} = token_record <- Repo.one(query) do
with %Token{user: %{deactivated: false} = user} = token_record <- Repo.one(query) do
{:ok, user, token_record}
end
end

View file

@ -10,7 +10,7 @@ defmodule Pleroma.Plugs.UserEnabledPlug do
options
end
def call(%{assigns: %{user: %User{info: %{deactivated: true}}}} = conn, _) do
def call(%{assigns: %{user: %User{deactivated: true}}} = conn, _) do
conn
|> assign(:user, nil)
end

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
options
end
def call(%{assigns: %{user: %User{info: %{is_admin: true}}}} = conn, _) do
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
conn
end

View file

@ -68,12 +68,7 @@ defmodule Pleroma.Stats do
domain_count = Enum.count(peers)
status_query =
from(u in User.Query.build(%{local: true}),
select: fragment("sum((?->>'note_count')::int)", u.info)
)
status_count = Repo.one(status_query)
status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count)
user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id)

File diff suppressed because it is too large Load diff

View file

@ -1,472 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Info do
use Ecto.Schema
import Ecto.Changeset
alias Pleroma.User.Info
@type t :: %__MODULE__{}
embedded_schema do
field(:banner, :map, default: %{})
field(:background, :map, default: %{})
field(:source_data, :map, default: %{})
field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0)
# Should be filled in only for remote users
field(:following_count, :integer, default: nil)
field(:locked, :boolean, default: false)
field(:confirmation_pending, :boolean, default: false)
field(:password_reset_pending, :boolean, default: false)
field(:confirmation_token, :string, default: nil)
field(:default_scope, :string, default: "public")
field(:blocks, {:array, :string}, default: [])
field(:domain_blocks, {:array, :string}, default: [])
field(:mutes, {:array, :string}, default: [])
field(:muted_reblogs, {:array, :string}, default: [])
field(:muted_notifications, {:array, :string}, default: [])
field(:subscribers, {:array, :string}, default: [])
field(:deactivated, :boolean, default: false)
field(:no_rich_text, :boolean, default: false)
field(:ap_enabled, :boolean, default: false)
field(:is_moderator, :boolean, default: false)
field(:is_admin, :boolean, default: false)
field(:show_role, :boolean, default: true)
field(:keys, :string, default: nil)
field(:settings, :map, default: nil)
field(:magic_key, :string, default: nil)
field(:uri, :string, default: nil)
field(:hide_followers_count, :boolean, default: false)
field(:hide_follows_count, :boolean, default: false)
field(:hide_followers, :boolean, default: false)
field(:hide_follows, :boolean, default: false)
field(:hide_favorites, :boolean, default: true)
field(:unread_conversation_count, :integer, default: 0)
field(:pinned_activities, {:array, :string}, default: [])
field(:email_notifications, :map, default: %{"digest" => false})
field(:mascot, :map, default: nil)
field(:emoji, {:array, :map}, default: [])
field(:pleroma_settings_store, :map, default: %{})
field(:fields, {:array, :map}, default: nil)
field(:raw_fields, {:array, :map}, default: [])
field(:discoverable, :boolean, default: false)
field(:notification_settings, :map,
default: %{
"followers" => true,
"follows" => true,
"non_follows" => true,
"non_followers" => true
}
)
field(:skip_thread_containment, :boolean, default: false)
# Found in the wild
# ap_id -> Where is this used?
# bio -> Where is this used?
# avatar -> Where is this used?
# fqn -> Where is this used?
# host -> Where is this used?
# subject _> Where is this used?
end
def set_activation_status(info, deactivated) do
params = %{deactivated: deactivated}
info
|> cast(params, [:deactivated])
|> validate_required([:deactivated])
end
def set_password_reset_pending(info, pending) do
params = %{password_reset_pending: pending}
info
|> cast(params, [:password_reset_pending])
|> validate_required([:password_reset_pending])
end
def update_notification_settings(info, settings) do
settings =
settings
|> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
|> Map.new()
notification_settings =
info.notification_settings
|> Map.merge(settings)
|> Map.take(["followers", "follows", "non_follows", "non_followers"])
params = %{notification_settings: notification_settings}
info
|> cast(params, [:notification_settings])
|> validate_required([:notification_settings])
end
@doc """
Update email notifications in the given User.Info struct.
Examples:
iex> update_email_notifications(%Pleroma.User.Info{email_notifications: %{"digest" => false}}, %{"digest" => true})
%Pleroma.User.Info{email_notifications: %{"digest" => true}}
"""
@spec update_email_notifications(t(), map()) :: Ecto.Changeset.t()
def update_email_notifications(info, settings) do
email_notifications =
info.email_notifications
|> Map.merge(settings)
|> Map.take(["digest"])
params = %{email_notifications: email_notifications}
fields = [:email_notifications]
info
|> cast(params, fields)
|> validate_required(fields)
end
def add_to_note_count(info, number) do
set_note_count(info, info.note_count + number)
end
def set_note_count(info, number) do
params = %{note_count: Enum.max([0, number])}
info
|> cast(params, [:note_count])
|> validate_required([:note_count])
end
def set_follower_count(info, number) do
params = %{follower_count: Enum.max([0, number])}
info
|> cast(params, [:follower_count])
|> validate_required([:follower_count])
end
def set_mutes(info, mutes) do
params = %{mutes: mutes}
info
|> cast(params, [:mutes])
|> validate_required([:mutes])
end
@spec set_notification_mutes(Changeset.t(), [String.t()], boolean()) :: Changeset.t()
def set_notification_mutes(changeset, muted_notifications, notifications?) do
if notifications? do
put_change(changeset, :muted_notifications, muted_notifications)
|> validate_required([:muted_notifications])
else
changeset
end
end
def set_blocks(info, blocks) do
params = %{blocks: blocks}
info
|> cast(params, [:blocks])
|> validate_required([:blocks])
end
def set_subscribers(info, subscribers) do
params = %{subscribers: subscribers}
info
|> cast(params, [:subscribers])
|> validate_required([:subscribers])
end
@spec add_to_mutes(Info.t(), String.t(), boolean()) :: Changeset.t()
def add_to_mutes(info, muted, notifications?) do
info
|> set_mutes(Enum.uniq([muted | info.mutes]))
|> set_notification_mutes(
Enum.uniq([muted | info.muted_notifications]),
notifications?
)
end
@spec remove_from_mutes(Info.t(), String.t()) :: Changeset.t()
def remove_from_mutes(info, muted) do
info
|> set_mutes(List.delete(info.mutes, muted))
|> set_notification_mutes(List.delete(info.muted_notifications, muted), true)
end
def add_to_block(info, blocked) do
set_blocks(info, Enum.uniq([blocked | info.blocks]))
end
def remove_from_block(info, blocked) do
set_blocks(info, List.delete(info.blocks, blocked))
end
def add_to_subscribers(info, subscribed) do
set_subscribers(info, Enum.uniq([subscribed | info.subscribers]))
end
def remove_from_subscribers(info, subscribed) do
set_subscribers(info, List.delete(info.subscribers, subscribed))
end
def set_domain_blocks(info, domain_blocks) do
params = %{domain_blocks: domain_blocks}
info
|> cast(params, [:domain_blocks])
|> validate_required([:domain_blocks])
end
def add_to_domain_block(info, domain_blocked) do
set_domain_blocks(info, Enum.uniq([domain_blocked | info.domain_blocks]))
end
def remove_from_domain_block(info, domain_blocked) do
set_domain_blocks(info, List.delete(info.domain_blocks, domain_blocked))
end
def set_keys(info, keys) do
params = %{keys: keys}
info
|> cast(params, [:keys])
|> validate_required([:keys])
end
def remote_user_creation(info, params) do
params =
if Map.has_key?(params, :fields) do
Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
else
params
end
info
|> cast(params, [
:ap_enabled,
:source_data,
:banner,
:locked,
:magic_key,
:uri,
:hide_followers,
:hide_follows,
:hide_followers_count,
:hide_follows_count,
:follower_count,
:fields,
:following_count,
:discoverable
])
|> validate_fields(true)
end
def user_upgrade(info, params, remote? \\ false) do
info
|> cast(params, [
:ap_enabled,
:source_data,
:banner,
:locked,
:magic_key,
:follower_count,
:following_count,
:hide_follows,
:fields,
:hide_followers,
:discoverable,
:hide_followers_count,
:hide_follows_count
])
|> validate_fields(remote?)
end
def profile_update(info, params) do
info
|> cast(params, [
:locked,
:no_rich_text,
:default_scope,
:banner,
:hide_follows,
:hide_followers,
:hide_followers_count,
:hide_follows_count,
:hide_favorites,
:background,
:show_role,
:skip_thread_containment,
:fields,
:raw_fields,
:pleroma_settings_store,
:discoverable
])
|> validate_fields()
end
def validate_fields(changeset, remote? \\ false) do
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
limit = Pleroma.Config.get([:instance, limit_name], 0)
changeset
|> validate_length(:fields, max: limit)
|> validate_change(:fields, fn :fields, fields ->
if Enum.all?(fields, &valid_field?/1) do
[]
else
[fields: "invalid"]
end
end)
end
defp valid_field?(%{"name" => name, "value" => value}) do
name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255)
value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255)
is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
String.length(value) <= value_limit
end
defp valid_field?(_), do: false
defp truncate_field(%{"name" => name, "value" => value}) do
{name, _chopped} =
String.split_at(name, Pleroma.Config.get([:instance, :account_field_name_length], 255))
{value, _chopped} =
String.split_at(value, Pleroma.Config.get([:instance, :account_field_value_length], 255))
%{"name" => name, "value" => value}
end
@spec confirmation_changeset(Info.t(), keyword()) :: Changeset.t()
def confirmation_changeset(info, opts) do
need_confirmation? = Keyword.get(opts, :need_confirmation)
params =
if need_confirmation? do
%{
confirmation_pending: true,
confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
}
else
%{
confirmation_pending: false,
confirmation_token: nil
}
end
cast(info, params, [:confirmation_pending, :confirmation_token])
end
def mastodon_settings_update(info, settings) do
params = %{settings: settings}
info
|> cast(params, [:settings])
|> validate_required([:settings])
end
def mascot_update(info, url) do
params = %{mascot: url}
info
|> cast(params, [:mascot])
|> validate_required([:mascot])
end
def set_source_data(info, source_data) do
params = %{source_data: source_data}
info
|> cast(params, [:source_data])
|> validate_required([:source_data])
end
def admin_api_update(info, params) do
info
|> cast(params, [
:is_moderator,
:is_admin,
:show_role
])
end
def add_pinnned_activity(info, %Pleroma.Activity{id: id}) do
if id not in info.pinned_activities do
max_pinned_statuses = Pleroma.Config.get([:instance, :max_pinned_statuses], 0)
params = %{pinned_activities: info.pinned_activities ++ [id]}
info
|> cast(params, [:pinned_activities])
|> validate_length(:pinned_activities,
max: max_pinned_statuses,
message: "You have already pinned the maximum number of statuses"
)
else
change(info)
end
end
def remove_pinnned_activity(info, %Pleroma.Activity{id: id}) do
params = %{pinned_activities: List.delete(info.pinned_activities, id)}
cast(info, params, [:pinned_activities])
end
def roles(%Info{is_moderator: is_moderator, is_admin: is_admin}) do
%{
admin: is_admin,
moderator: is_moderator
}
end
def add_reblog_mute(info, ap_id) do
params = %{muted_reblogs: info.muted_reblogs ++ [ap_id]}
cast(info, params, [:muted_reblogs])
end
def remove_reblog_mute(info, ap_id) do
params = %{muted_reblogs: List.delete(info.muted_reblogs, ap_id)}
cast(info, params, [:muted_reblogs])
end
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do
limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0)
attachment
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|> Enum.take(limit)
end
def fields(%{fields: nil}), do: []
def fields(%{fields: fields}), do: fields
def follow_information_update(info, params) do
info
|> cast(params, [
:hide_followers,
:hide_follows,
:follower_count,
:following_count,
:hide_followers_count,
:hide_follows_count
])
end
end

View file

@ -58,7 +58,6 @@ defmodule Pleroma.User.Query do
@ilike_criteria [:nickname, :name, :query]
@equal_criteria [:email]
@role_criteria [:is_admin, :is_moderator]
@contains_criteria [:ap_id, :nickname]
@spec build(criteria()) :: Query.t()
@ -102,15 +101,19 @@ defmodule Pleroma.User.Query do
Enum.reduce(tags, query, &prepare_tag_criteria/2)
end
defp compose_query({key, _}, query) when key in @role_criteria do
where(query, [u], fragment("(?->? @> 'true')", u.info, ^to_string(key)))
defp compose_query({:is_admin, _}, query) do
where(query, [u], u.is_admin)
end
defp compose_query({:is_moderator, _}, query) do
where(query, [u], u.is_moderator)
end
defp compose_query({:super_users, _}, query) do
where(
query,
[u],
fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info)
u.is_admin or u.is_moderator
)
end
@ -119,7 +122,13 @@ defmodule Pleroma.User.Query do
defp compose_query({:external, _}, query), do: location_query(query, false)
defp compose_query({:active, _}, query) do
where(query, [u], fragment("not (?->'deactivated' @> 'true')", u.info))
User.restrict_deactivated(query)
|> where([u], not is_nil(u.nickname))
end
defp compose_query({:legacy_active, _}, query) do
query
|> where([u], fragment("not (?->'deactivated' @> 'true')", u.info))
|> where([u], not is_nil(u.nickname))
end
@ -128,7 +137,7 @@ defmodule Pleroma.User.Query do
end
defp compose_query({:deactivated, true}, query) do
where(query, [u], fragment("?->'deactivated' @> 'true'", u.info))
where(query, [u], u.deactivated == ^true)
|> where([u], not is_nil(u.nickname))
end

View file

@ -108,14 +108,14 @@ defmodule Pleroma.User.Search do
defp base_query(_user, false), do: User
defp base_query(user, true), do: User.get_followers_query(user)
defp filter_blocked_user(query, %User{info: %{blocks: blocks}})
defp filter_blocked_user(query, %User{blocks: blocks})
when length(blocks) > 0 do
from(q in query, where: not (q.ap_id in ^blocks))
end
defp filter_blocked_user(query, _), do: query
defp filter_blocked_domains(query, %User{info: %{domain_blocks: domain_blocks}})
defp filter_blocked_domains(query, %User{domain_blocks: domain_blocks})
when length(domain_blocks) > 0 do
domains = Enum.join(domain_blocks, ",")

View file

@ -69,7 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp check_actor_is_active(actor) do
if not is_nil(actor) do
with user <- User.get_cached_by_ap_id(actor),
false <- user.info.deactivated do
false <- user.deactivated do
true
else
_e -> false
@ -248,8 +248,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
_ <- increase_replies_count_if_reply(create_data),
_ <- increase_poll_votes_if_vote(create_data),
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
# Changing note count prior to enqueuing federation task in order to avoid
# race conditions on updating user.info
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
@ -445,8 +443,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, activity} <- insert(data, local, false),
stream_out_participations(object, user),
_ <- decrease_replies_count_if_reply(object),
# Changing note count prior to enqueuing federation task in order to avoid
# race conditions on updating user.info
{:ok, _actor} <- decrease_note_count_if_public(user, object),
:ok <- maybe_federate(activity) do
{:ok, activity}
@ -662,7 +658,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_thread_visibility(
query,
%{"user" => %User{info: %{skip_thread_containment: true}}},
%{"user" => %User{skip_thread_containment: true}},
_
),
do: query
@ -700,7 +696,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Map.put("user", reading_user)
|> Map.put("actor_id", user.ap_id)
|> Map.put("whole_db", true)
|> Map.put("pinned_activity_ids", user.info.pinned_activities)
|> Map.put("pinned_activity_ids", user.pinned_activities)
recipients =
user_activities_recipients(%{
@ -861,8 +857,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
mutes = info.mutes
defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
mutes = user.mutes
query =
from([activity] in query,
@ -879,9 +875,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_muted(query, _), do: query
defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
blocks = info.blocks || []
domain_blocks = info.domain_blocks || []
defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
blocks = user.blocks || []
domain_blocks = user.domain_blocks || []
query =
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
@ -922,8 +918,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_pinned(query, _), do: query
defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
muted_reblogs = info.muted_reblogs || []
defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
muted_reblogs = user.muted_reblogs || []
from(
activity in query,
@ -1108,17 +1104,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
locked = data["manuallyApprovesFollowers"] || false
data = Transmogrifier.maybe_fix_user_object(data)
discoverable = data["discoverable"] || false
invisible = data["invisible"] || false
user_data = %{
ap_id: data["id"],
info: %{
ap_enabled: true,
source_data: data,
banner: banner,
fields: fields,
locked: locked,
discoverable: discoverable
},
ap_enabled: true,
source_data: data,
banner: banner,
fields: fields,
locked: locked,
discoverable: discoverable,
invisible: invisible,
avatar: avatar,
name: data["name"],
follower_address: data["followers"],
@ -1170,7 +1166,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
with {:enabled, true} <-
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
{:ok, info} <- fetch_follow_information_for_user(data) do
info = Map.merge(data.info, info)
info = Map.merge(data[:info] || %{}, info)
Map.put(data, :info, info)
else
{:enabled, false} ->

View file

@ -137,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
with %User{} = user <- User.get_cached_by_nickname(nickname),
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
{:show_follows, true} <-
{:show_follows, (for_user && for_user == user) || !user.info.hide_follows} do
{:show_follows, (for_user && for_user == user) || !user.hide_follows} do
{page, _} = Integer.parse(page)
conn
@ -174,7 +174,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
with %User{} = user <- User.get_cached_by_nickname(nickname),
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
{:show_followers, true} <-
{:show_followers, (for_user && for_user == user) || !user.info.hide_followers} do
{:show_followers, (for_user && for_user == user) || !user.hide_followers} do
{page, _} = Integer.parse(page)
conn
@ -387,7 +387,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def handle_user_activity(user, %{"type" => "Delete"} = params) do
with %Object{} = object <- Object.normalize(params["object"]),
true <- user.info.is_moderator || user.ap_id == object.data["actor"],
true <- user.is_moderator || user.ap_id == object.data["actor"],
{:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete}
else

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
# has the user successfully posted before?
defp old_user?(%User{} = u) do
u.info.note_count > 0 || u.info.follower_count > 0
u.note_count > 0 || u.follower_count > 0
end
# does the post contain links?

View file

@ -140,7 +140,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|> Enum.map(& &1.ap_id)
end
defp maybe_use_sharedinbox(%User{info: %{source_data: data}}),
defp maybe_use_sharedinbox(%User{source_data: data}),
do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
@doc """
@ -156,7 +156,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
"""
def determine_inbox(
%Activity{data: activity_data},
%User{info: %{source_data: data}} = user
%User{source_data: data} = user
) do
to = activity_data["to"] || []
cc = activity_data["cc"] || []
@ -190,12 +190,12 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
recipients
|> Enum.filter(&User.ap_enabled?/1)
|> Enum.map(fn %{info: %{source_data: data}} -> data["inbox"] end)
|> Enum.map(fn %{source_data: data} -> data["inbox"] end)
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|> Instances.filter_reachable()
|> Enum.each(fn {inbox, unreachable_since} ->
%User{ap_id: ap_id} =
Enum.find(recipients, fn %{info: %{source_data: data}} -> data["inbox"] == inbox end)
Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
# instance would only accept a first message for the first recipient and ignore the rest.

View file

@ -10,8 +10,12 @@ defmodule Pleroma.Web.ActivityPub.Relay do
require Logger
def get_actor do
"#{Pleroma.Web.Endpoint.url()}/relay"
|> User.get_or_create_service_actor_by_ap_id()
actor =
"#{Pleroma.Web.Endpoint.url()}/relay"
|> User.get_or_create_service_actor_by_ap_id()
{:ok, actor} = User.set_invisible(actor, true)
actor
end
@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}

View file

@ -599,13 +599,18 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
data,
_options
)
when object_type in ["Person", "Application", "Service", "Organization"] do
when object_type in [
"Person",
"Application",
"Service",
"Organization"
] do
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
banner = new_user_data[:info][:banner]
locked = new_user_data[:info][:locked] || false
attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || []
locked = new_user_data[:locked] || false
attachment = get_in(new_user_data, [:source_data, "attachment"]) || []
invisible = new_user_data[:invisible] || false
fields =
attachment
@ -614,8 +619,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
update_data =
new_user_data
|> Map.take([:name, :bio, :avatar])
|> Map.put(:info, %{banner: banner, locked: locked, fields: fields})
|> Map.take([:avatar, :banner, :bio, :name])
|> Map.put(:fields, fields)
|> Map.put(:locked, locked)
|> Map.put(:invisible, invisible)
actor
|> User.upgrade_changeset(update_data, true)
@ -982,7 +989,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Mention", "href" => ap_id, "name" => "@#{nickname}"}
end
def take_emoji_tags(%User{info: %{emoji: emoji} = _user_info} = _user) do
def take_emoji_tags(%User{emoji: emoji}) do
emoji
|> Enum.flat_map(&Map.to_list/1)
|> Enum.map(&build_emoji_tag/1)

View file

@ -49,26 +49,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def determine_explicit_mentions(_), do: []
@spec recipient_in_collection(any(), any()) :: boolean()
defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll
defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll
defp recipient_in_collection(_, _), do: false
@spec label_in_collection?(any(), any()) :: boolean()
defp label_in_collection?(ap_id, coll) when is_binary(coll), do: ap_id == coll
defp label_in_collection?(ap_id, coll) when is_list(coll), do: ap_id in coll
defp label_in_collection?(_, _), do: false
@spec label_in_message?(String.t(), map()) :: boolean()
def label_in_message?(label, params),
do:
[params["to"], params["cc"], params["bto"], params["bcc"]]
|> Enum.any?(&label_in_collection?(label, &1))
@spec unaddressed_message?(map()) :: boolean()
def unaddressed_message?(params),
do:
[params["to"], params["cc"], params["bto"], params["bcc"]]
|> Enum.all?(&is_nil(&1))
@spec recipient_in_message(User.t(), User.t(), map()) :: boolean()
def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params) do
addresses = [params["to"], params["cc"], params["bto"], params["bcc"]]
cond do
Enum.any?(addresses, &recipient_in_collection(ap_id, &1)) -> true
# if the message is unaddressed at all, then assume it is directly addressed
# to the recipient
Enum.all?(addresses, &is_nil(&1)) -> true
# if the message is sent from somebody the user is following, then assume it
# is addressed to the recipient
User.following?(recipient, actor) -> true
true -> false
end
end
def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params),
do:
label_in_message?(ap_id, params) || unaddressed_message?(params) ||
User.following?(recipient, actor)
defp extract_list(target) when is_binary(target), do: [target]
defp extract_list(lst) when is_list(lst), do: lst
@ -76,8 +78,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def maybe_splice_recipient(ap_id, params) do
need_splice? =
!recipient_in_collection(ap_id, params["to"]) &&
!recipient_in_collection(ap_id, params["cc"])
!label_in_collection?(ap_id, params["to"]) &&
!label_in_collection?(ap_id, params["cc"])
if need_splice? do
cc_list = extract_list(params["cc"])
@ -491,10 +493,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do
%Activity{data: %{"actor" => actor}},
object
) do
announcements = take_announcements(object)
unless actor |> User.get_cached_by_ap_id() |> User.invisible?() do
announcements = take_announcements(object)
with announcements <- Enum.uniq([actor | announcements]) do
update_element_in_object("announcement", announcements, object)
with announcements <- Enum.uniq([actor | announcements]) do
update_element_in_object("announcement", announcements, object)
end
else
{:ok, object}
end
end

View file

@ -55,7 +55,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"owner" => user.ap_id,
"publicKeyPem" => public_key
},
"endpoints" => endpoints
"endpoints" => endpoints,
"invisible" => User.invisible?(user)
}
|> Map.merge(Utils.make_json_ld_header())
end
@ -78,8 +79,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
emoji_tags = Transmogrifier.take_emoji_tags(user)
fields =
user.info
|> User.Info.fields()
user
|> User.fields()
|> Enum.map(fn %{"name" => name, "value" => value} ->
%{
"name" => Pleroma.HTML.strip_tags(name),
@ -99,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"name" => user.name,
"summary" => user.bio,
"url" => user.ap_id,
"manuallyApprovesFollowers" => user.info.locked,
"manuallyApprovesFollowers" => user.locked,
"publicKey" => %{
"id" => "#{user.ap_id}#main-key",
"owner" => user.ap_id,
@ -107,8 +108,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
},
"endpoints" => endpoints,
"attachment" => fields,
"tag" => (user.info.source_data["tag"] || []) ++ emoji_tags,
"discoverable" => user.info.discoverable
"tag" => (user.source_data["tag"] || []) ++ emoji_tags,
"discoverable" => user.discoverable
}
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
@ -116,8 +117,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
def render("following.json", %{user: user, page: page} = opts) do
showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
showing_count = showing_items || !user.info.hide_follows_count
showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows
showing_count = showing_items || !user.hide_follows_count
query = User.get_friends_query(user)
query = from(user in query, select: [:ap_id])
@ -135,8 +136,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
def render("following.json", %{user: user} = opts) do
showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_follows
showing_count = showing_items || !user.info.hide_follows_count
showing_items = (opts[:for] && opts[:for] == user) || !user.hide_follows
showing_count = showing_items || !user.hide_follows_count
query = User.get_friends_query(user)
query = from(user in query, select: [:ap_id])
@ -155,7 +156,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"totalItems" => total,
"first" =>
if showing_items do
collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
collection(following, "#{user.ap_id}/following", 1, !user.hide_follows)
else
"#{user.ap_id}/following?page=1"
end
@ -164,8 +165,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
def render("followers.json", %{user: user, page: page} = opts) do
showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
showing_count = showing_items || !user.info.hide_followers_count
showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers
showing_count = showing_items || !user.hide_followers_count
query = User.get_followers_query(user)
query = from(user in query, select: [:ap_id])
@ -183,8 +184,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
def render("followers.json", %{user: user} = opts) do
showing_items = (opts[:for] && opts[:for] == user) || !user.info.hide_followers
showing_count = showing_items || !user.info.hide_followers_count
showing_items = (opts[:for] && opts[:for] == user) || !user.hide_followers
showing_count = showing_items || !user.hide_followers_count
query = User.get_followers_query(user)
query = from(user in query, select: [:ap_id])

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Utils
require Pleroma.Constants
@ -15,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
def is_public?(%Object{data: data}), do: is_public?(data)
def is_public?(%Activity{data: data}), do: is_public?(data)
def is_public?(%{"directMessage" => true}), do: false
def is_public?(data), do: Pleroma.Constants.as_public() in (data["to"] ++ (data["cc"] || []))
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
def is_private?(activity) do
with false <- is_public?(activity),

View file

@ -51,8 +51,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
:tag_users,
:untag_users,
:right_add,
:right_delete,
:set_activation_status
:right_delete
]
)
@ -250,9 +249,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
user = User.get_cached_by_nickname(nickname)
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
{:ok, updated_user} = User.deactivate(user, !user.deactivated)
action = if user.info.deactivated, do: "activate", else: "deactivate"
action = if user.deactivated, do: "activate", else: "deactivate"
ModerationLog.insert_log(%{
actor: admin,
@ -364,11 +363,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
"nicknames" => nicknames
})
when permission_group in ["moderator", "admin"] do
info = Map.put(%{}, "is_" <> permission_group, true)
update = %{:"is_#{permission_group}" => true}
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
User.update_info(users, &User.Info.admin_api_update(&1, info))
for u <- users, do: User.admin_api_update(u, update)
ModerationLog.insert_log(%{
action: "grant",
@ -377,7 +376,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
permission: permission_group
})
json(conn, info)
json(conn, update)
end
def right_add_multiple(conn, _) do
@ -389,12 +388,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
"nickname" => nickname
})
when permission_group in ["moderator", "admin"] do
info = Map.put(%{}, "is_" <> permission_group, true)
fields = %{:"is_#{permission_group}" => true}
{:ok, user} =
nickname
|> User.get_cached_by_nickname()
|> User.update_info(&User.Info.admin_api_update(&1, info))
|> User.admin_api_update(fields)
ModerationLog.insert_log(%{
action: "grant",
@ -403,7 +402,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
permission: permission_group
})
json(conn, info)
json(conn, fields)
end
def right_add(conn, _) do
@ -415,8 +414,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn
|> json(%{
is_moderator: user.info.is_moderator,
is_admin: user.info.is_admin
is_moderator: user.is_moderator,
is_admin: user.is_admin
})
end
@ -429,11 +428,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
)
when permission_group in ["moderator", "admin"] do
with false <- Enum.member?(nicknames, admin_nickname) do
info = Map.put(%{}, "is_" <> permission_group, false)
update = %{:"is_#{permission_group}" => false}
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
User.update_info(users, &User.Info.admin_api_update(&1, info))
for u <- users, do: User.admin_api_update(u, update)
ModerationLog.insert_log(%{
action: "revoke",
@ -442,7 +441,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
permission: permission_group
})
json(conn, info)
json(conn, update)
else
_ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
end
@ -460,12 +459,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
}
)
when permission_group in ["moderator", "admin"] do
info = Map.put(%{}, "is_" <> permission_group, false)
fields = %{:"is_#{permission_group}" => false}
{:ok, user} =
nickname
|> User.get_cached_by_nickname()
|> User.update_info(&User.Info.admin_api_update(&1, info))
|> User.admin_api_update(fields)
ModerationLog.insert_log(%{
action: "revoke",
@ -474,7 +473,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
permission: permission_group
})
json(conn, info)
json(conn, fields)
end
def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do

View file

@ -7,7 +7,6 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.User.Info
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.MediaProxy
@ -34,9 +33,9 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
"avatar" => avatar,
"nickname" => user.nickname,
"display_name" => display_name,
"deactivated" => user.info.deactivated,
"deactivated" => user.deactivated,
"local" => user.local,
"roles" => Info.roles(user.info),
"roles" => User.roles(user),
"tags" => user.tags || []
}
end

View file

@ -266,10 +266,10 @@ defmodule Pleroma.Web.CommonAPI do
# Updates the emojis for a user based on their profile
def update(user) do
emoji = emoji_from_profile(user)
source_data = user.info |> Map.get(:source_data, %{}) |> Map.put("tag", emoji)
source_data = Map.put(user.source_data, "tag", emoji)
user =
case User.update_info(user, &User.Info.set_source_data(&1, source_data)) do
case User.update_source_data(user, source_data) do
{:ok, user} -> user
_ -> user
end
@ -290,20 +290,20 @@ defmodule Pleroma.Web.CommonAPI do
object: %Object{data: %{"type" => "Note"}}
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
true <- Visibility.is_public?(activity),
{:ok, _user} <- User.update_info(user, &User.Info.add_pinnned_activity(&1, activity)) do
{:ok, _user} <- User.add_pinnned_activity(user, activity) do
{:ok, activity}
else
{:error, %{changes: %{info: %{errors: [pinned_activities: {err, _}]}}}} -> {:error, err}
{:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err}
_ -> {:error, dgettext("errors", "Could not pin")}
end
end
def unpin(id_or_ap_id, user) do
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
{:ok, _user} <- User.update_info(user, &User.Info.remove_pinnned_activity(&1, activity)) do
{:ok, _user} <- User.remove_pinnned_activity(user, activity) do
{:ok, activity}
else
%{errors: [pinned_activities: {err, _}]} -> {:error, err}
{:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err}
_ -> {:error, dgettext("errors", "Could not unpin")}
end
end
@ -395,14 +395,14 @@ defmodule Pleroma.Web.CommonAPI do
defp set_visibility(activity, _), do: {:ok, activity}
def hide_reblogs(user, %{ap_id: ap_id} = _muted) do
if ap_id not in user.info.muted_reblogs do
User.update_info(user, &User.Info.add_reblog_mute(&1, ap_id))
if ap_id not in user.muted_reblogs do
User.add_reblog_mute(user, ap_id)
end
end
def show_reblogs(user, %{ap_id: ap_id} = _muted) do
if ap_id in user.info.muted_reblogs do
User.update_info(user, &User.Info.remove_reblog_mute(&1, ap_id))
if ap_id in user.muted_reblogs do
User.remove_reblog_mute(user, ap_id)
end
end
end

View file

@ -42,7 +42,7 @@ defmodule Pleroma.Web.MastoFEController do
@doc "PUT /api/web/settings"
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do
with {:ok, _} <- User.mastodon_settings_update(user, settings) do
json(conn, %{})
else
e ->

View file

@ -130,25 +130,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
user = original_user
user_params =
%{}
|> add_if_present(params, "display_name", :name)
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end)
|> add_if_present(params, "avatar", :avatar, fn value ->
with %Plug.Upload{} <- value,
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
{:ok, object.data}
end
end)
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
user_info_emojis =
user.info
|> Map.get(:emoji, [])
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
|> Enum.dedup()
params =
if Map.has_key?(params, "fields_attributes") do
Map.update!(params, "fields_attributes", fn fields ->
@ -160,7 +141,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
params
end
info_params =
user_params =
[
:no_rich_text,
:locked,
@ -176,15 +157,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|> Enum.reduce(%{}, fn key, acc ->
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
end)
|> add_if_present(params, "default_scope", :default_scope)
|> add_if_present(params, "fields_attributes", :fields, fn fields ->
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
{:ok, fields}
end)
|> add_if_present(params, "fields_attributes", :raw_fields)
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
{:ok, Map.merge(user.info.pleroma_settings_store, value)}
|> add_if_present(params, "display_name", :name)
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end)
|> add_if_present(params, "avatar", :avatar, fn value ->
with %Plug.Upload{} <- value,
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
{:ok, object.data}
end
end)
|> add_if_present(params, "header", :banner, fn value ->
with %Plug.Upload{} <- value,
@ -198,12 +177,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
{:ok, object.data}
end
end)
|> Map.put(:emoji, user_info_emojis)
|> add_if_present(params, "fields_attributes", :fields, fn fields ->
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
changeset =
{:ok, fields}
end)
|> add_if_present(params, "fields_attributes", :raw_fields)
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
{:ok, Map.merge(user.pleroma_settings_store, value)}
end)
|> add_if_present(params, "default_scope", :default_scope)
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
user_emojis =
user
|> User.update_changeset(user_params)
|> User.change_info(&User.Info.profile_update(&1, info_params))
|> Map.get(:emoji, [])
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
|> Enum.dedup()
user_params = Map.put(user_params, :emoji, user_emojis)
changeset = User.update_changeset(user, user_params)
with {:ok, user} <- User.update_and_set_cache(changeset) do
if original_user != user, do: CommonAPI.update(user)
@ -269,7 +263,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
followers =
cond do
for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params)
user.info.hide_followers -> []
user.hide_followers -> []
true -> MastodonAPI.get_followers(user, params)
end
@ -283,7 +277,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
followers =
cond do
for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params)
user.info.hide_follows -> []
user.hide_follows -> []
true -> MastodonAPI.get_friends(user, params)
end

View file

@ -21,8 +21,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
@doc "GET /api/v1/domain_blocks"
def index(%{assigns: %{user: %{info: info}}} = conn, _) do
json(conn, Map.get(info, :domain_blocks, []))
def index(%{assigns: %{user: user}} = conn, _) do
json(conn, Map.get(user, :domain_blocks, []))
end
@doc "POST /api/v1/domain_blocks"

View file

@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MarkerController do
use Pleroma.Web, :controller
alias Pleroma.Plugs.OAuthScopesPlug
plug(
OAuthScopesPlug,
%{scopes: ["read:statuses"]}
when action == :index
)
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
# GET /api/v1/markers
def index(%{assigns: %{user: user}} = conn, params) do
markers = Pleroma.Marker.get_markers(user, params["timeline"])
render(conn, "markers.json", %{markers: markers})
end
# POST /api/v1/markers
def upsert(%{assigns: %{user: user}} = conn, params) do
with {:ok, result} <- Pleroma.Marker.upsert(user, params),
markers <- Map.values(result) do
render(conn, "markers.json", %{markers: markers})
end
end
end

View file

@ -74,23 +74,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
user_info = User.get_cached_user_info(user)
following_count =
if !user.info.hide_follows_count or !user.info.hide_follows or opts[:for] == user do
if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
user_info.following_count
else
0
end
followers_count =
if !user.info.hide_followers_count or !user.info.hide_followers or opts[:for] == user do
if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do
user_info.follower_count
else
0
end
bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]
bot = (user.source_data["type"] || "Person") in ["Application", "Service"]
emojis =
(user.info.source_data["tag"] || [])
(user.source_data["tag"] || [])
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
%{
@ -102,8 +102,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end)
fields =
user.info
|> User.Info.fields()
user
|> User.fields()
|> Enum.map(fn %{"name" => name, "value" => value} ->
%{
"name" => Pleroma.HTML.strip_tags(name),
@ -111,23 +111,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
}
end)
raw_fields = Map.get(user.info, :raw_fields, [])
bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
relationship = render("relationship.json", %{user: opts[:for], target: user})
discoverable = user.info.discoverable
%{
id: to_string(user.id),
username: username_from_nickname(user.nickname),
acct: user.nickname,
display_name: display_name,
locked: user_info.locked,
locked: user.locked,
created_at: Utils.to_masto_date(user.inserted_at),
followers_count: followers_count,
following_count: following_count,
statuses_count: user_info.note_count,
statuses_count: user.note_count,
note: bio || "",
url: User.profile_url(user),
avatar: image,
@ -140,9 +136,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
source: %{
note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
sensitive: false,
fields: raw_fields,
fields: user.raw_fields,
pleroma: %{
discoverable: discoverable
discoverable: user.discoverable
}
},
@ -150,14 +146,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
pleroma: %{
confirmation_pending: user_info.confirmation_pending,
tags: user.tags,
hide_followers_count: user.info.hide_followers_count,
hide_follows_count: user.info.hide_follows_count,
hide_followers: user.info.hide_followers,
hide_follows: user.info.hide_follows,
hide_favorites: user.info.hide_favorites,
hide_followers_count: user.hide_followers_count,
hide_follows_count: user.hide_follows_count,
hide_followers: user.hide_followers,
hide_follows: user.hide_follows,
hide_favorites: user.hide_favorites,
relationship: relationship,
skip_thread_containment: user.info.skip_thread_containment,
background_image: image_url(user.info.background) |> MediaProxy.url()
skip_thread_containment: user.skip_thread_containment,
background_image: image_url(user.background) |> MediaProxy.url()
}
}
|> maybe_put_role(user, opts[:for])
@ -195,21 +191,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
data,
%User{id: user_id} = user,
%User{id: user_id},
user_info
_user_info
) do
data
|> Kernel.put_in([:source, :privacy], user_info.default_scope)
|> Kernel.put_in([:source, :pleroma, :show_role], user.info.show_role)
|> Kernel.put_in([:source, :pleroma, :no_rich_text], user.info.no_rich_text)
|> Kernel.put_in([:source, :privacy], user.default_scope)
|> Kernel.put_in([:source, :pleroma, :show_role], user.show_role)
|> Kernel.put_in([:source, :pleroma, :no_rich_text], user.no_rich_text)
end
defp maybe_put_settings(data, _, _, _), do: data
defp maybe_put_settings_store(data, %User{info: info, id: id}, %User{id: id}, %{
defp maybe_put_settings_store(data, %User{} = user, %User{}, %{
with_pleroma_settings: true
}) do
data
|> Kernel.put_in([:pleroma, :settings_store], info.pleroma_settings_store)
|> Kernel.put_in([:pleroma, :settings_store], user.pleroma_settings_store)
end
defp maybe_put_settings_store(data, _, _, _), do: data
@ -223,28 +219,28 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_chat_token(data, _, _, _), do: data
defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do
defp maybe_put_role(data, %User{show_role: true} = user, _) do
data
|> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator)
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
end
defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do
data
|> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator)
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
end
defp maybe_put_role(data, _, _), do: data
defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
Kernel.put_in(data, [:pleroma, :notification_settings], user.info.notification_settings)
Kernel.put_in(data, [:pleroma, :notification_settings], user.notification_settings)
end
defp maybe_put_notification_settings(data, _, _), do: data
defp maybe_put_activation_status(data, user, %User{info: %{is_admin: true}}) do
Kernel.put_in(data, [:pleroma, :deactivated], user.info.deactivated)
defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
end
defp maybe_put_activation_status(data, _, _), do: data
@ -253,7 +249,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
data
|> Kernel.put_in(
[:pleroma, :unread_conversation_count],
user.info.unread_conversation_count
user.unread_conversation_count
)
end

View file

@ -0,0 +1,17 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MarkerView do
use Pleroma.Web, :view
def render("markers.json", %{markers: markers}) do
Enum.reduce(markers, %{}, fn m, acc ->
Map.put_new(acc, m.timeline, %{
last_read_id: m.last_read_id,
version: m.lock_version,
updated_at: NaiveDateTime.to_iso8601(m.updated_at)
})
end)
end
end

View file

@ -498,6 +498,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp present?(false), do: false
defp present?(_), do: true
defp pinned?(%Activity{id: id}, %User{info: %{pinned_activities: pinned_activities}}),
defp pinned?(%Activity{id: id}, %User{pinned_activities: pinned_activities}),
do: id in pinned_activities
end

View file

@ -202,9 +202,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do
with {:ok, %User{} = user} <- Authenticator.get_user(conn),
{:ok, app} <- Token.Utils.fetch_app(conn),
{:auth_active, true} <- {:auth_active, User.auth_active?(user)},
{:user_active, true} <- {:user_active, !user.info.deactivated},
{:user_active, true} <- {:user_active, !user.deactivated},
{:password_reset_pending, false} <-
{:password_reset_pending, user.info.password_reset_pending},
{:password_reset_pending, user.password_reset_pending},
{:ok, scopes} <- validate_scopes(app, params),
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
{:ok, token} <- Token.exchange_token(app, auth) do

View file

@ -80,9 +80,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
@doc "PATCH /api/v1/pleroma/accounts/update_banner"
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
new_info = %{"banner" => %{}}
with {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
with {:ok, user} <- User.update_banner(user, %{}) do
CommonAPI.update(user)
json(conn, %{url: nil})
end
@ -90,8 +88,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
def update_banner(%{assigns: %{user: user}} = conn, params) do
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
new_info <- %{"banner" => object.data},
{:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
{:ok, user} <- User.update_banner(user, object.data) do
CommonAPI.update(user)
%{"url" => [%{"href" => href} | _]} = object.data
@ -101,17 +98,14 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
@doc "PATCH /api/v1/pleroma/accounts/update_background"
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
new_info = %{"background" => %{}}
with {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
with {:ok, _user} <- User.update_background(user, %{}) do
json(conn, %{url: nil})
end
end
def update_background(%{assigns: %{user: user}} = conn, params) do
with {:ok, object} <- ActivityPub.upload(params, type: :background),
new_info <- %{"background" => object.data},
{:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
{:ok, _user} <- User.update_background(user, object.data) do
%{"url" => [%{"href" => href} | _]} = object.data
json(conn, %{url: href})
@ -119,7 +113,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
end
@doc "GET /api/v1/pleroma/accounts/:id/favourites"
def favourites(%{assigns: %{account: %{info: %{hide_favorites: true}}}} = conn, _params) do
def favourites(%{assigns: %{account: %{hide_favorites: true}}} = conn, _params) do
render_error(conn, :forbidden, "Can't get favorites")
end

View file

@ -24,9 +24,7 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
# Reject if not an image
%{type: "image"} = attachment <- render_attachment(object) do
# Sure!
# Save to the user's info
{:ok, _user} = User.update_info(user, &User.Info.mascot_update(&1, attachment))
{:ok, _user} = User.mascot_update(user, attachment)
json(conn, attachment)
else

View file

@ -125,6 +125,10 @@ defmodule Pleroma.Web.Push.Impl do
end
end
def format_title(%{activity: %{data: %{"directMessage" => true}}}) do
"New Direct Message"
end
def format_title(%{activity: %{data: %{"type" => type}}}) do
case type do
"Create" -> "New Mention"

View file

@ -405,6 +405,9 @@ defmodule Pleroma.Web.Router do
get("/push/subscription", SubscriptionController, :get)
put("/push/subscription", SubscriptionController, :update)
delete("/push/subscription", SubscriptionController, :delete)
get("/markers", MarkerController, :index)
post("/markers", MarkerController, :upsert)
end
scope "/api/web", Pleroma.Web do

View file

@ -129,12 +129,12 @@ defmodule Pleroma.Web.Streamer.Worker do
end
defp should_send?(%User{} = user, %Activity{} = item) do
blocks = user.info.blocks || []
mutes = user.info.mutes || []
reblog_mutes = user.info.muted_reblogs || []
blocks = user.blocks || []
mutes = user.mutes || []
reblog_mutes = user.muted_reblogs || []
recipient_blocks = MapSet.new(blocks ++ mutes)
recipients = MapSet.new(item.recipients)
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
with parent when not is_nil(parent) <- Object.normalize(item),
true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
@ -212,7 +212,7 @@ defmodule Pleroma.Web.Streamer.Worker do
end
@spec thread_containment(Activity.t(), User.t()) :: boolean()
defp thread_containment(_activity, %User{info: %{skip_thread_containment: true}}), do: true
defp thread_containment(_activity, %User{skip_thread_containment: true}), do: true
defp thread_containment(activity, user) do
if Config.get([:instance, :skip_thread_containment]) do

View file

@ -20,11 +20,12 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
action_fallback(:errors)
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
new_info = [need_confirmation: false]
with %User{info: info} = user <- User.get_cached_by_id(uid),
true <- user.local and info.confirmation_pending and info.confirmation_token == token,
{:ok, _} <- User.update_info(user, &User.Info.confirmation_changeset(&1, new_info)) do
with %User{} = user <- User.get_cached_by_id(uid),
true <- user.local and user.confirmation_pending and user.confirmation_token == token,
{:ok, _} <-
user
|> User.confirmation_changeset(need_confirmation: false)
|> User.update_and_set_cache() do
redirect(conn, to: "/")
end
end

View file

@ -61,12 +61,12 @@ defmodule Pleroma.Web.MastoFEView do
},
poll_limits: Config.get([:instance, :poll_limits]),
rights: %{
delete_others_notice: present?(user.info.is_moderator),
admin: present?(user.info.is_admin)
delete_others_notice: present?(user.is_moderator),
admin: present?(user.is_admin)
},
compose: %{
me: "#{user.id}",
default_privacy: user.info.default_scope,
default_privacy: user.default_scope,
default_sensitive: false,
allow_content_types: Config.get([:instance, :allowed_post_formats])
},
@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastoFEView do
"video\/mp4"
]
},
settings: user.info.settings || @default_settings,
settings: user.settings || @default_settings,
push_subscription: nil,
accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),