Merge branch 'develop' into feature/788-separate-email-addresses
This commit is contained in:
commit
fe511a6c65
29 changed files with 1527 additions and 142 deletions
|
|
@ -7,6 +7,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
import Ecto.Changeset
|
||||
alias Mix.Tasks.Pleroma.Common
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
|
||||
@shortdoc "Manages Pleroma users"
|
||||
@moduledoc """
|
||||
|
|
@ -26,7 +27,19 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
|
||||
## Generate an invite link.
|
||||
|
||||
mix pleroma.user invite
|
||||
mix pleroma.user invite [OPTION...]
|
||||
|
||||
Options:
|
||||
- `--expires_at DATE` - last day on which token is active (e.g. "2019-04-05")
|
||||
- `--max_use NUMBER` - maximum numbers of token uses
|
||||
|
||||
## List generated invites
|
||||
|
||||
mix pleroma.user invites
|
||||
|
||||
## Revoke invite
|
||||
|
||||
mix pleroma.user revoke_invite TOKEN OR TOKEN_ID
|
||||
|
||||
## Delete the user's account.
|
||||
|
||||
|
|
@ -287,23 +300,79 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def run(["invite"]) do
|
||||
def run(["invite" | rest]) do
|
||||
{options, [], []} =
|
||||
OptionParser.parse(rest,
|
||||
strict: [
|
||||
expires_at: :string,
|
||||
max_use: :integer
|
||||
]
|
||||
)
|
||||
|
||||
options =
|
||||
options
|
||||
|> Keyword.update(:expires_at, {:ok, nil}, fn
|
||||
nil -> {:ok, nil}
|
||||
val -> Date.from_iso8601(val)
|
||||
end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
Common.start_pleroma()
|
||||
|
||||
with {:ok, token} <- Pleroma.UserInviteToken.create_token() do
|
||||
Mix.shell().info("Generated user invite token")
|
||||
with {:ok, val} <- options[:expires_at],
|
||||
options = Map.put(options, :expires_at, val),
|
||||
{:ok, invite} <- UserInviteToken.create_invite(options) do
|
||||
Mix.shell().info(
|
||||
"Generated user invite token " <> String.replace(invite.invite_type, "_", " ")
|
||||
)
|
||||
|
||||
url =
|
||||
Pleroma.Web.Router.Helpers.redirect_url(
|
||||
Pleroma.Web.Endpoint,
|
||||
:registration_page,
|
||||
token.token
|
||||
invite.token
|
||||
)
|
||||
|
||||
IO.puts(url)
|
||||
else
|
||||
_ ->
|
||||
Mix.shell().error("Could not create invite token.")
|
||||
error ->
|
||||
Mix.shell().error("Could not create invite token: #{inspect(error)}")
|
||||
end
|
||||
end
|
||||
|
||||
def run(["invites"]) do
|
||||
Common.start_pleroma()
|
||||
|
||||
Mix.shell().info("Invites list:")
|
||||
|
||||
UserInviteToken.list_invites()
|
||||
|> Enum.each(fn invite ->
|
||||
expire_info =
|
||||
with expires_at when not is_nil(expires_at) <- invite.expires_at do
|
||||
" | Expires at: #{Date.to_string(expires_at)}"
|
||||
end
|
||||
|
||||
using_info =
|
||||
with max_use when not is_nil(max_use) <- invite.max_use do
|
||||
" | Max use: #{max_use} Left use: #{max_use - invite.uses}"
|
||||
end
|
||||
|
||||
Mix.shell().info(
|
||||
"ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{
|
||||
invite.used
|
||||
}#{expire_info}#{using_info}"
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["revoke_invite", token]) do
|
||||
Common.start_pleroma()
|
||||
|
||||
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
||||
{:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do
|
||||
Mix.shell().info("Invite for token #{token} was revoked.")
|
||||
else
|
||||
_ -> Mix.shell().error("No invite found with token #{token}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -122,13 +122,7 @@ defmodule Pleroma.Notification do
|
|||
|
||||
# TODO move to sql, too.
|
||||
def create_notification(%Activity{} = activity, %User{} = user) do
|
||||
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
|
||||
CommonAPI.thread_muted?(user, activity) or user.ap_id == activity.data["actor"] or
|
||||
(activity.data["type"] == "Follow" and
|
||||
Enum.any?(Notification.for_user(user), fn notif ->
|
||||
notif.activity.data["type"] == "Follow" and
|
||||
notif.activity.data["actor"] == activity.data["actor"]
|
||||
end)) do
|
||||
unless skip?(activity, user) do
|
||||
notification = %Notification{user_id: user.id, activity: activity}
|
||||
{:ok, notification} = Repo.insert(notification)
|
||||
Pleroma.Web.Streamer.stream("user", notification)
|
||||
|
|
@ -148,10 +142,66 @@ defmodule Pleroma.Notification do
|
|||
[]
|
||||
|> Utils.maybe_notify_to_recipients(activity)
|
||||
|> Utils.maybe_notify_mentioned_recipients(activity)
|
||||
|> Utils.maybe_notify_subscribers(activity)
|
||||
|> Enum.uniq()
|
||||
|
||||
User.get_users_from_set(recipients, local_only)
|
||||
end
|
||||
|
||||
def get_notified_from_activity(_, _local_only), do: []
|
||||
|
||||
def skip?(activity, user) do
|
||||
[:self, :blocked, :local, :muted, :followers, :follows, :recently_followed]
|
||||
|> Enum.any?(&skip?(&1, activity, user))
|
||||
end
|
||||
|
||||
def skip?(:self, activity, user) do
|
||||
activity.data["actor"] == user.ap_id
|
||||
end
|
||||
|
||||
def skip?(:blocked, activity, user) do
|
||||
actor = activity.data["actor"]
|
||||
User.blocks?(user, %{ap_id: actor})
|
||||
end
|
||||
|
||||
def skip?(:local, %{local: true}, %{info: %{notification_settings: %{"local" => false}}}),
|
||||
do: true
|
||||
|
||||
def skip?(:local, %{local: false}, %{info: %{notification_settings: %{"remote" => false}}}),
|
||||
do: true
|
||||
|
||||
def skip?(:muted, activity, user) do
|
||||
actor = activity.data["actor"]
|
||||
|
||||
User.mutes?(user, %{ap_id: actor}) or
|
||||
CommonAPI.thread_muted?(user, activity)
|
||||
end
|
||||
|
||||
def skip?(
|
||||
:followers,
|
||||
activity,
|
||||
%{info: %{notification_settings: %{"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
|
||||
actor = activity.data["actor"]
|
||||
followed = User.get_by_ap_id(actor)
|
||||
User.following?(user, followed)
|
||||
end
|
||||
|
||||
def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do
|
||||
actor = activity.data["actor"]
|
||||
|
||||
Notification.for_user(user)
|
||||
|> Enum.any?(fn
|
||||
%{activity: %{data: %{"type" => "Follow", "actor" => ^actor}}} -> true
|
||||
_ -> false
|
||||
end)
|
||||
end
|
||||
|
||||
def skip?(_, _, _), do: false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -931,6 +931,38 @@ defmodule Pleroma.User do
|
|||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def subscribe(subscriber, %{ap_id: ap_id}) do
|
||||
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
|
||||
|
||||
with %User{} = subscribed <- get_cached_by_ap_id(ap_id) do
|
||||
blocked = blocks?(subscribed, subscriber) and deny_follow_blocked
|
||||
|
||||
if blocked do
|
||||
{:error, "Could not subscribe: #{subscribed.nickname} is blocking you"}
|
||||
else
|
||||
info_cng =
|
||||
subscribed.info
|
||||
|> User.Info.add_to_subscribers(subscriber.ap_id)
|
||||
|
||||
change(subscribed)
|
||||
|> put_embed(:info, info_cng)
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unsubscribe(unsubscriber, %{ap_id: ap_id}) do
|
||||
with %User{} = user <- get_cached_by_ap_id(ap_id) do
|
||||
info_cng =
|
||||
user.info
|
||||
|> User.Info.remove_from_subscribers(unsubscriber.ap_id)
|
||||
|
||||
change(user)
|
||||
|> put_embed(:info, info_cng)
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
end
|
||||
|
||||
def block(blocker, %User{ap_id: ap_id} = blocked) do
|
||||
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
|
||||
blocker =
|
||||
|
|
@ -941,6 +973,14 @@ defmodule Pleroma.User do
|
|||
blocker
|
||||
end
|
||||
|
||||
blocker =
|
||||
if subscribed_to?(blocked, blocker) do
|
||||
{:ok, blocker} = unsubscribe(blocked, blocker)
|
||||
blocker
|
||||
else
|
||||
blocker
|
||||
end
|
||||
|
||||
if following?(blocked, blocker) do
|
||||
unfollow(blocked, blocker)
|
||||
end
|
||||
|
|
@ -989,12 +1029,21 @@ defmodule Pleroma.User do
|
|||
end)
|
||||
end
|
||||
|
||||
def subscribed_to?(user, %{ap_id: ap_id}) do
|
||||
with %User{} = target <- User.get_by_ap_id(ap_id) do
|
||||
Enum.member?(target.info.subscribers, user.ap_id)
|
||||
end
|
||||
end
|
||||
|
||||
def muted_users(user),
|
||||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes))
|
||||
|
||||
def blocked_users(user),
|
||||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks))
|
||||
|
||||
def subscribers(user),
|
||||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.subscribers))
|
||||
|
||||
def block_domain(user, domain) do
|
||||
info_cng =
|
||||
user.info
|
||||
|
|
@ -1092,6 +1141,14 @@ defmodule Pleroma.User do
|
|||
update_and_set_cache(cng)
|
||||
end
|
||||
|
||||
def update_notification_settings(%User{} = user, settings \\ %{}) do
|
||||
info_changeset = User.Info.update_notification_settings(user.info, settings)
|
||||
|
||||
change(user)
|
||||
|> put_embed(:info, info_changeset)
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def delete(%User{} = user) do
|
||||
{:ok, user} = User.deactivate(user)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ defmodule Pleroma.User.Info do
|
|||
field(:domain_blocks, {:array, :string}, default: [])
|
||||
field(:mutes, {:array, :string}, default: [])
|
||||
field(:muted_reblogs, {: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)
|
||||
|
|
@ -40,6 +41,10 @@ defmodule Pleroma.User.Info do
|
|||
field(:pinned_activities, {:array, :string}, default: [])
|
||||
field(:flavour, :string, default: nil)
|
||||
|
||||
field(:notification_settings, :map,
|
||||
default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
|
||||
)
|
||||
|
||||
# Found in the wild
|
||||
# ap_id -> Where is this used?
|
||||
# bio -> Where is this used?
|
||||
|
|
@ -57,6 +62,19 @@ defmodule Pleroma.User.Info do
|
|||
|> validate_required([:deactivated])
|
||||
end
|
||||
|
||||
def update_notification_settings(info, settings) do
|
||||
notification_settings =
|
||||
info.notification_settings
|
||||
|> Map.merge(settings)
|
||||
|> Map.take(["remote", "local", "followers", "follows"])
|
||||
|
||||
params = %{notification_settings: notification_settings}
|
||||
|
||||
info
|
||||
|> cast(params, [:notification_settings])
|
||||
|> validate_required([:notification_settings])
|
||||
end
|
||||
|
||||
def add_to_note_count(info, number) do
|
||||
set_note_count(info, info.note_count + number)
|
||||
end
|
||||
|
|
@ -93,6 +111,14 @@ defmodule Pleroma.User.Info do
|
|||
|> validate_required([:blocks])
|
||||
end
|
||||
|
||||
def set_subscribers(info, subscribers) do
|
||||
params = %{subscribers: subscribers}
|
||||
|
||||
info
|
||||
|> cast(params, [:subscribers])
|
||||
|> validate_required([:subscribers])
|
||||
end
|
||||
|
||||
def add_to_mutes(info, muted) do
|
||||
set_mutes(info, Enum.uniq([muted | info.mutes]))
|
||||
end
|
||||
|
|
@ -109,6 +135,14 @@ defmodule Pleroma.User.Info 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}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,40 +6,119 @@ defmodule Pleroma.UserInviteToken do
|
|||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
import Ecto.Query
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.UserInviteToken
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
@type token :: String.t()
|
||||
|
||||
schema "user_invite_tokens" do
|
||||
field(:token, :string)
|
||||
field(:used, :boolean, default: false)
|
||||
field(:max_use, :integer)
|
||||
field(:expires_at, :date)
|
||||
field(:uses, :integer, default: 0)
|
||||
field(:invite_type, :string)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def create_token do
|
||||
@spec create_invite(map()) :: UserInviteToken.t()
|
||||
def create_invite(params \\ %{}) do
|
||||
%UserInviteToken{}
|
||||
|> cast(params, [:max_use, :expires_at])
|
||||
|> add_token()
|
||||
|> assign_type()
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
defp add_token(changeset) do
|
||||
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
|
||||
|
||||
token = %UserInviteToken{
|
||||
used: false,
|
||||
token: token
|
||||
}
|
||||
|
||||
Repo.insert(token)
|
||||
put_change(changeset, :token, token)
|
||||
end
|
||||
|
||||
def used_changeset(struct) do
|
||||
struct
|
||||
|> cast(%{}, [])
|
||||
|> put_change(:used, true)
|
||||
defp assign_type(%{changes: %{max_use: _max_use, expires_at: _expires_at}} = changeset) do
|
||||
put_change(changeset, :invite_type, "reusable_date_limited")
|
||||
end
|
||||
|
||||
def mark_as_used(token) do
|
||||
with %{used: false} = token <- Repo.get_by(UserInviteToken, %{token: token}),
|
||||
{:ok, token} <- Repo.update(used_changeset(token)) do
|
||||
{:ok, token}
|
||||
else
|
||||
_e -> {:error, token}
|
||||
defp assign_type(%{changes: %{expires_at: _expires_at}} = changeset) do
|
||||
put_change(changeset, :invite_type, "date_limited")
|
||||
end
|
||||
|
||||
defp assign_type(%{changes: %{max_use: _max_use}} = changeset) do
|
||||
put_change(changeset, :invite_type, "reusable")
|
||||
end
|
||||
|
||||
defp assign_type(changeset), do: put_change(changeset, :invite_type, "one_time")
|
||||
|
||||
@spec list_invites() :: [UserInviteToken.t()]
|
||||
def list_invites do
|
||||
query = from(u in UserInviteToken, order_by: u.id)
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
@spec update_invite!(UserInviteToken.t(), map()) :: UserInviteToken.t() | no_return()
|
||||
def update_invite!(invite, changes) do
|
||||
change(invite, changes) |> Repo.update!()
|
||||
end
|
||||
|
||||
@spec update_invite(UserInviteToken.t(), map()) ::
|
||||
{:ok, UserInviteToken.t()} | {:error, Changeset.t()}
|
||||
def update_invite(invite, changes) do
|
||||
change(invite, changes) |> Repo.update()
|
||||
end
|
||||
|
||||
@spec find_by_token!(token()) :: UserInviteToken.t() | no_return()
|
||||
def find_by_token!(token), do: Repo.get_by!(UserInviteToken, token: token)
|
||||
|
||||
@spec find_by_token(token()) :: {:ok, UserInviteToken.t()} | nil
|
||||
def find_by_token(token) do
|
||||
with invite <- Repo.get_by(UserInviteToken, token: token) do
|
||||
{:ok, invite}
|
||||
end
|
||||
end
|
||||
|
||||
@spec valid_invite?(UserInviteToken.t()) :: boolean()
|
||||
def valid_invite?(%{invite_type: "one_time"} = invite) do
|
||||
not invite.used
|
||||
end
|
||||
|
||||
def valid_invite?(%{invite_type: "date_limited"} = invite) do
|
||||
not_overdue_date?(invite) and not invite.used
|
||||
end
|
||||
|
||||
def valid_invite?(%{invite_type: "reusable"} = invite) do
|
||||
invite.uses < invite.max_use and not invite.used
|
||||
end
|
||||
|
||||
def valid_invite?(%{invite_type: "reusable_date_limited"} = invite) do
|
||||
not_overdue_date?(invite) and invite.uses < invite.max_use and not invite.used
|
||||
end
|
||||
|
||||
defp not_overdue_date?(%{expires_at: expires_at}) do
|
||||
Date.compare(Date.utc_today(), expires_at) in [:lt, :eq]
|
||||
end
|
||||
|
||||
@spec update_usage!(UserInviteToken.t()) :: nil | UserInviteToken.t() | no_return()
|
||||
def update_usage!(%{invite_type: "date_limited"}), do: nil
|
||||
|
||||
def update_usage!(%{invite_type: "one_time"} = invite),
|
||||
do: update_invite!(invite, %{used: true})
|
||||
|
||||
def update_usage!(%{invite_type: invite_type} = invite)
|
||||
when invite_type == "reusable" or invite_type == "reusable_date_limited" do
|
||||
changes = %{
|
||||
uses: invite.uses + 1
|
||||
}
|
||||
|
||||
changes =
|
||||
if changes.uses >= invite.max_use do
|
||||
Map.put(changes, :used, true)
|
||||
else
|
||||
changes
|
||||
end
|
||||
|
||||
update_invite!(invite, changes)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -83,6 +83,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|> fix_content_map
|
||||
|> fix_likes
|
||||
|> fix_addressing
|
||||
|> fix_summary
|
||||
end
|
||||
|
||||
def fix_summary(%{"summary" => nil} = object) do
|
||||
object
|
||||
|> Map.put("summary", "")
|
||||
end
|
||||
|
||||
def fix_summary(%{"summary" => _} = object) do
|
||||
# summary is present, nothing to do
|
||||
object
|
||||
end
|
||||
|
||||
def fix_summary(object) do
|
||||
object
|
||||
|> Map.put("summary", "")
|
||||
end
|
||||
|
||||
def fix_addressing_list(map, field) do
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
use Pleroma.Web, :controller
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.AdminAPI.Search
|
||||
|
|
@ -235,7 +236,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
with true <-
|
||||
Pleroma.Config.get([:instance, :invites_enabled]) &&
|
||||
!Pleroma.Config.get([:instance, :registrations_open]),
|
||||
{:ok, invite_token} <- Pleroma.UserInviteToken.create_token(),
|
||||
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
||||
email <-
|
||||
Pleroma.UserEmail.user_invitation_email(user, invite_token, email, params["name"]),
|
||||
{:ok, _} <- Pleroma.Mailer.deliver(email) do
|
||||
|
|
@ -244,11 +245,29 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
|
||||
@doc "Get a account registeration invite token (base64 string)"
|
||||
def get_invite_token(conn, _params) do
|
||||
{:ok, token} = Pleroma.UserInviteToken.create_token()
|
||||
def get_invite_token(conn, params) do
|
||||
options = params["invite"] || %{}
|
||||
{:ok, invite} = UserInviteToken.create_invite(options)
|
||||
|
||||
conn
|
||||
|> json(token.token)
|
||||
|> json(invite.token)
|
||||
end
|
||||
|
||||
@doc "Get list of created invites"
|
||||
def invites(conn, _params) do
|
||||
invites = UserInviteToken.list_invites()
|
||||
|
||||
conn
|
||||
|> json(AccountView.render("invites.json", %{invites: invites}))
|
||||
end
|
||||
|
||||
@doc "Revokes invite by token"
|
||||
def revoke_invite(conn, %{"token" => token}) do
|
||||
invite = UserInviteToken.find_by_token!(token)
|
||||
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true})
|
||||
|
||||
conn
|
||||
|> json(AccountView.render("invite.json", %{invite: updated_invite}))
|
||||
end
|
||||
|
||||
@doc "Get a password reset token (base64 string) for given nickname"
|
||||
|
|
|
|||
|
|
@ -26,4 +26,22 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
"tags" => user.tags || []
|
||||
}
|
||||
end
|
||||
|
||||
def render("invite.json", %{invite: invite}) do
|
||||
%{
|
||||
"id" => invite.id,
|
||||
"token" => invite.token,
|
||||
"used" => invite.used,
|
||||
"expires_at" => invite.expires_at,
|
||||
"uses" => invite.uses,
|
||||
"max_use" => invite.max_use,
|
||||
"invite_type" => invite.invite_type
|
||||
}
|
||||
end
|
||||
|
||||
def render("invites.json", %{invites: invites}) do
|
||||
%{
|
||||
invites: render_many(invites, AccountView, "invite.json", as: :invite)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
|
|
@ -335,6 +336,24 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|
||||
def maybe_notify_mentioned_recipients(recipients, _), do: recipients
|
||||
|
||||
def maybe_notify_subscribers(
|
||||
recipients,
|
||||
%Activity{data: %{"actor" => actor, "type" => type}} = activity
|
||||
)
|
||||
when type == "Create" do
|
||||
with %User{} = user <- User.get_cached_by_ap_id(actor) do
|
||||
subscriber_ids =
|
||||
user
|
||||
|> User.subscribers()
|
||||
|> Enum.filter(&Visibility.visible_for_user?(activity, &1))
|
||||
|> Enum.map(& &1.ap_id)
|
||||
|
||||
recipients ++ subscriber_ids
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_notify_subscribers(recipients, _), do: recipients
|
||||
|
||||
def maybe_extract_mentions(%{"tag" => tag}) do
|
||||
tag
|
||||
|> Enum.filter(fn x -> is_map(x) end)
|
||||
|
|
|
|||
|
|
@ -931,6 +931,34 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
json(conn, %{})
|
||||
end
|
||||
|
||||
def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %User{} = subscription_target <- User.get_cached_by_id(id),
|
||||
{:ok, subscription_target} = User.subscribe(user, subscription_target) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("relationship.json", %{user: user, target: subscription_target})
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(403, Jason.encode!(%{"error" => message}))
|
||||
end
|
||||
end
|
||||
|
||||
def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %User{} = subscription_target <- User.get_cached_by_id(id),
|
||||
{:ok, subscription_target} = User.unsubscribe(user, subscription_target) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("relationship.json", %{user: user, target: subscription_target})
|
||||
else
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(403, Jason.encode!(%{"error" => message}))
|
||||
end
|
||||
end
|
||||
|
||||
def status_search(user, query) do
|
||||
fetched =
|
||||
if Regex.match?(~r/https?:/, query) do
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
blocking: User.blocks?(user, target),
|
||||
muting: User.mutes?(user, target),
|
||||
muting_notifications: false,
|
||||
subscribing: User.subscribed_to?(user, target),
|
||||
requested: requested,
|
||||
domain_blocking: false,
|
||||
showing_reblogs: User.showing_reblogs?(user, target),
|
||||
|
|
@ -117,13 +118,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
},
|
||||
|
||||
# Pleroma extension
|
||||
pleroma: %{
|
||||
confirmation_pending: user_info.confirmation_pending,
|
||||
tags: user.tags,
|
||||
is_moderator: user.info.is_moderator,
|
||||
is_admin: user.info.is_admin,
|
||||
relationship: relationship
|
||||
}
|
||||
pleroma:
|
||||
%{
|
||||
confirmation_pending: user_info.confirmation_pending,
|
||||
tags: user.tags,
|
||||
is_moderator: user.info.is_moderator,
|
||||
is_admin: user.info.is_admin,
|
||||
relationship: relationship
|
||||
}
|
||||
|> with_notification_settings(user, opts[:for])
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -132,4 +135,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
end
|
||||
|
||||
defp username_from_nickname(_), do: nil
|
||||
|
||||
defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||
Map.put(data, :notification_settings, user.info.notification_settings)
|
||||
end
|
||||
|
||||
defp with_notification_settings(data, _, _), do: data
|
||||
end
|
||||
|
|
|
|||
|
|
@ -168,6 +168,8 @@ defmodule Pleroma.Web.Router do
|
|||
delete("/relay", AdminAPIController, :relay_unfollow)
|
||||
|
||||
get("/invite_token", AdminAPIController, :get_invite_token)
|
||||
get("/invites", AdminAPIController, :invites)
|
||||
post("/revoke_invite", AdminAPIController, :revoke_invite)
|
||||
post("/email_invite", AdminAPIController, :email_invite)
|
||||
|
||||
get("/password_reset", AdminAPIController, :get_password_reset)
|
||||
|
|
@ -193,6 +195,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
post("/change_password", UtilController, :change_password)
|
||||
post("/delete_account", UtilController, :delete_account)
|
||||
put("/notification_settings", UtilController, :update_notificaton_settings)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
@ -336,6 +339,9 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
post("/domain_blocks", MastodonAPIController, :block_domain)
|
||||
delete("/domain_blocks", MastodonAPIController, :unblock_domain)
|
||||
|
||||
post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe)
|
||||
post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
|
|||
|
|
@ -286,12 +286,19 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
emoji =
|
||||
Emoji.get_all()
|
||||
|> Enum.map(fn {short_code, path, tags} ->
|
||||
%{short_code => %{image_url: path, tags: String.split(tags, ",")}}
|
||||
{short_code, %{image_url: path, tags: String.split(tags, ",")}}
|
||||
end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
json(conn, emoji)
|
||||
end
|
||||
|
||||
def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
|
||||
with {:ok, _} <- User.update_notification_settings(user, params) do
|
||||
json(conn, %{status: "success"})
|
||||
end
|
||||
end
|
||||
|
||||
def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
|
||||
follow_import(conn, %{"list" => File.read!(listfile.path)})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
end
|
||||
|
||||
def register_user(params) do
|
||||
token_string = params["token"]
|
||||
token = params["token"]
|
||||
|
||||
params = %{
|
||||
nickname: params["nickname"],
|
||||
|
|
@ -163,36 +163,49 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
|
||||
else
|
||||
registrations_open = Pleroma.Config.get([:instance, :registrations_open])
|
||||
registration_process(registrations_open, params, token)
|
||||
end
|
||||
end
|
||||
|
||||
# no need to query DB if registration is open
|
||||
token =
|
||||
unless registrations_open || is_nil(token_string) do
|
||||
Repo.get_by(UserInviteToken, %{token: token_string})
|
||||
end
|
||||
|
||||
cond do
|
||||
registrations_open || (!is_nil(token) && !token.used) ->
|
||||
changeset = User.register_changeset(%User{}, params)
|
||||
|
||||
with {:ok, user} <- User.register(changeset) do
|
||||
!registrations_open && UserInviteToken.mark_as_used(token.token)
|
||||
|
||||
{:ok, user}
|
||||
else
|
||||
{:error, changeset} ->
|
||||
errors =
|
||||
Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|
||||
|> Jason.encode!()
|
||||
|
||||
{:error, %{error: errors}}
|
||||
end
|
||||
|
||||
!registrations_open && is_nil(token) ->
|
||||
{:error, "Invalid token"}
|
||||
|
||||
!registrations_open && token.used ->
|
||||
{:error, "Expired token"}
|
||||
defp registration_process(registration_open, params, token)
|
||||
when registration_open == false or is_nil(registration_open) do
|
||||
invite =
|
||||
unless is_nil(token) do
|
||||
Repo.get_by(UserInviteToken, %{token: token})
|
||||
end
|
||||
|
||||
valid_invite? = invite && UserInviteToken.valid_invite?(invite)
|
||||
|
||||
case invite do
|
||||
nil ->
|
||||
{:error, "Invalid token"}
|
||||
|
||||
invite when valid_invite? ->
|
||||
UserInviteToken.update_usage!(invite)
|
||||
create_user(params)
|
||||
|
||||
_ ->
|
||||
{:error, "Expired token"}
|
||||
end
|
||||
end
|
||||
|
||||
defp registration_process(true, params, _token) do
|
||||
create_user(params)
|
||||
end
|
||||
|
||||
defp create_user(params) do
|
||||
changeset = User.register_changeset(%User{}, params)
|
||||
|
||||
case User.register(changeset) do
|
||||
{:ok, user} ->
|
||||
{:ok, user}
|
||||
|
||||
{:error, changeset} ->
|
||||
errors =
|
||||
Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|
||||
|> Jason.encode!()
|
||||
|
||||
{:error, %{error: errors}}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue