Merge branch 'develop' into feature/tag_feed
This commit is contained in:
commit
c9f45edeac
206 changed files with 2338 additions and 3626 deletions
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Application do
|
||||
import Cachex.Spec
|
||||
use Application
|
||||
require Logger
|
||||
|
||||
@name Mix.Project.config()[:name]
|
||||
@version Mix.Project.config()[:version]
|
||||
|
|
@ -33,6 +34,7 @@ defmodule Pleroma.Application do
|
|||
Pleroma.HTML.compile_scrubbers()
|
||||
Pleroma.Config.DeprecationWarnings.warn()
|
||||
setup_instrumenters()
|
||||
load_custom_modules()
|
||||
|
||||
# Define workers and child supervisors to be supervised
|
||||
children =
|
||||
|
|
@ -68,6 +70,28 @@ defmodule Pleroma.Application do
|
|||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
|
||||
def load_custom_modules do
|
||||
dir = Pleroma.Config.get([:modules, :runtime_dir])
|
||||
|
||||
if dir && File.exists?(dir) do
|
||||
dir
|
||||
|> Pleroma.Utils.compile_dir()
|
||||
|> case do
|
||||
{:error, _errors, _warnings} ->
|
||||
raise "Invalid custom modules"
|
||||
|
||||
{:ok, modules, _warnings} ->
|
||||
if @env != :test do
|
||||
Enum.each(modules, fn mod ->
|
||||
Logger.info("Custom module loaded: #{inspect(mod)}")
|
||||
end)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp setup_instrumenters do
|
||||
require Prometheus.Registry
|
||||
|
||||
|
|
|
|||
|
|
@ -64,11 +64,13 @@ defmodule Pleroma.Conversation.Participation do
|
|||
end
|
||||
|
||||
def mark_as_read(participation) do
|
||||
participation
|
||||
|> read_cng(%{read: true})
|
||||
|> Repo.update()
|
||||
__MODULE__
|
||||
|> where(id: ^participation.id)
|
||||
|> update(set: [read: true])
|
||||
|> select([p], p)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{:ok, participation} ->
|
||||
{1, [participation]} ->
|
||||
participation = Repo.preload(participation, :user)
|
||||
User.set_unread_conversation_count(participation.user)
|
||||
{:ok, participation}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ defmodule Pleroma.HTML do
|
|||
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
|
||||
|
||||
dir
|
||||
|> File.ls!()
|
||||
|> Enum.map(&Path.join(dir, &1))
|
||||
|> Kernel.ParallelCompiler.compile()
|
||||
|> Pleroma.Utils.compile_dir()
|
||||
|> case do
|
||||
{:error, _errors, _warnings} ->
|
||||
raise "Compiling scrubbers failed"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.Object do
|
|||
|
||||
require Logger
|
||||
|
||||
@type t() :: %__MODULE__{}
|
||||
|
||||
schema "objects" do
|
||||
field(:data, :map)
|
||||
|
||||
|
|
@ -79,6 +81,20 @@ defmodule Pleroma.Object do
|
|||
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get a single attachment by it's name and href
|
||||
"""
|
||||
@spec get_attachment_by_name_and_href(String.t(), String.t()) :: Object.t() | nil
|
||||
def get_attachment_by_name_and_href(name, href) do
|
||||
query =
|
||||
from(o in Object,
|
||||
where: fragment("(?)->>'name' = ?", o.data, ^name),
|
||||
where: fragment("(?)->>'href' = ?", o.data, ^href)
|
||||
)
|
||||
|
||||
Repo.one(query)
|
||||
end
|
||||
|
||||
defp warn_on_no_object_preloaded(ap_id) do
|
||||
"Object.normalize() called without preloaded object (#{inspect(ap_id)}). Consider preloading the object"
|
||||
|> Logger.debug()
|
||||
|
|
@ -164,6 +180,7 @@ defmodule Pleroma.Object do
|
|||
|
||||
def delete(%Object{data: %{"id" => id}} = object) do
|
||||
with {:ok, _obj} = swap_object_with_tombstone(object),
|
||||
:ok <- delete_attachments(object),
|
||||
deleted_activity = Activity.delete_all_by_object_ap_id(id),
|
||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
|
||||
{:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do
|
||||
|
|
@ -171,6 +188,77 @@ defmodule Pleroma.Object do
|
|||
end
|
||||
end
|
||||
|
||||
defp delete_attachments(%{data: %{"attachment" => [_ | _] = attachments, "actor" => actor}}) do
|
||||
hrefs =
|
||||
Enum.flat_map(attachments, fn attachment ->
|
||||
Enum.map(attachment["url"], & &1["href"])
|
||||
end)
|
||||
|
||||
names = Enum.map(attachments, & &1["name"])
|
||||
|
||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||
|
||||
# find all objects for copies of the attachments, name and actor doesn't matter here
|
||||
delete_ids =
|
||||
from(o in Object,
|
||||
where:
|
||||
fragment(
|
||||
"to_jsonb(array(select jsonb_array_elements((?)#>'{url}') ->> 'href'))::jsonb \\?| (?)",
|
||||
o.data,
|
||||
^hrefs
|
||||
)
|
||||
)
|
||||
|> Repo.all()
|
||||
# we should delete 1 object for any given attachment, but don't delete files if
|
||||
# there are more than 1 object for it
|
||||
|> Enum.reduce(%{}, fn %{
|
||||
id: id,
|
||||
data: %{
|
||||
"url" => [%{"href" => href}],
|
||||
"actor" => obj_actor,
|
||||
"name" => name
|
||||
}
|
||||
},
|
||||
acc ->
|
||||
Map.update(acc, href, %{id: id, count: 1}, fn val ->
|
||||
case obj_actor == actor and name in names do
|
||||
true ->
|
||||
# set id of the actor's object that will be deleted
|
||||
%{val | id: id, count: val.count + 1}
|
||||
|
||||
false ->
|
||||
# another actor's object, just increase count to not delete file
|
||||
%{val | count: val.count + 1}
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|> Enum.map(fn {href, %{id: id, count: count}} ->
|
||||
# only delete files that have single instance
|
||||
with 1 <- count do
|
||||
prefix =
|
||||
case Pleroma.Config.get([Pleroma.Upload, :base_url]) do
|
||||
nil -> "media"
|
||||
_ -> ""
|
||||
end
|
||||
|
||||
base_url = Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url())
|
||||
|
||||
file_path = String.trim_leading(href, "#{base_url}/#{prefix}")
|
||||
|
||||
uploader.delete_file(file_path)
|
||||
end
|
||||
|
||||
id
|
||||
end)
|
||||
|
||||
from(o in Object, where: o.id in ^delete_ids)
|
||||
|> Repo.delete_all()
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp delete_attachments(%{data: _data}), do: :ok
|
||||
|
||||
def prune(%Object{data: %{"id" => id}} = object) do
|
||||
with {:ok, object} <- Repo.delete(object),
|
||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ defmodule Pleroma.Object.Fetcher do
|
|||
end
|
||||
|
||||
def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
|
||||
Logger.info("Fetching object #{id} via AP")
|
||||
Logger.debug("Fetching object #{id} via AP")
|
||||
|
||||
date = Pleroma.Signature.signed_date()
|
||||
|
||||
|
|
|
|||
|
|
@ -18,16 +18,13 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
|||
token = assigns[:token]
|
||||
|
||||
scopes = transform_scopes(scopes, options)
|
||||
matched_scopes = token && filter_descendants(scopes, token.scopes)
|
||||
matched_scopes = (token && filter_descendants(scopes, token.scopes)) || []
|
||||
|
||||
cond do
|
||||
is_nil(token) ->
|
||||
maybe_perform_instance_privacy_check(conn, options)
|
||||
|
||||
op == :| && Enum.any?(matched_scopes) ->
|
||||
token && op == :| && Enum.any?(matched_scopes) ->
|
||||
conn
|
||||
|
||||
op == :& && matched_scopes == scopes ->
|
||||
token && op == :& && matched_scopes == scopes ->
|
||||
conn
|
||||
|
||||
options[:fallback] == :proceed_unauthenticated ->
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@
|
|||
defmodule Pleroma.Uploaders.Local do
|
||||
@behaviour Pleroma.Uploaders.Uploader
|
||||
|
||||
@impl true
|
||||
def get_file(_) do
|
||||
{:ok, {:static_dir, upload_path()}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def put_file(upload) do
|
||||
{local_path, file} =
|
||||
case Enum.reverse(Path.split(upload.path)) do
|
||||
|
|
@ -33,4 +35,15 @@ defmodule Pleroma.Uploaders.Local do
|
|||
def upload_path do
|
||||
Pleroma.Config.get!([__MODULE__, :uploads])
|
||||
end
|
||||
|
||||
@impl true
|
||||
def delete_file(path) do
|
||||
upload_path()
|
||||
|> Path.join(path)
|
||||
|> File.rm()
|
||||
|> case do
|
||||
:ok -> :ok
|
||||
{:error, posix_error} -> {:error, to_string(posix_error)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,37 +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.Uploaders.MDII do
|
||||
@moduledoc "Represents uploader for https://github.com/hakaba-hitoyo/minimal-digital-image-infrastructure"
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP
|
||||
|
||||
@behaviour Pleroma.Uploaders.Uploader
|
||||
|
||||
# MDII-hosted images are never passed through the MediaPlug; only local media.
|
||||
# Delegate to Pleroma.Uploaders.Local
|
||||
def get_file(file) do
|
||||
Pleroma.Uploaders.Local.get_file(file)
|
||||
end
|
||||
|
||||
def put_file(upload) do
|
||||
cgi = Config.get([Pleroma.Uploaders.MDII, :cgi])
|
||||
files = Config.get([Pleroma.Uploaders.MDII, :files])
|
||||
|
||||
{:ok, file_data} = File.read(upload.tempfile)
|
||||
|
||||
extension = String.split(upload.name, ".") |> List.last()
|
||||
query = "#{cgi}?#{extension}"
|
||||
|
||||
with {:ok, %{status: 200, body: body}} <-
|
||||
HTTP.post(query, file_data, [], adapter: [pool: :default]) do
|
||||
remote_file_name = String.split(body) |> List.first()
|
||||
public_url = "#{files}/#{remote_file_name}.#{extension}"
|
||||
{:ok, {:url, public_url}}
|
||||
else
|
||||
_ -> Pleroma.Uploaders.Local.put_file(upload)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
|
||||
# The file name is re-encoded with S3's constraints here to comply with previous
|
||||
# links with less strict filenames
|
||||
@impl true
|
||||
def get_file(file) do
|
||||
config = Config.get([__MODULE__])
|
||||
bucket = Keyword.fetch!(config, :bucket)
|
||||
|
|
@ -35,6 +36,7 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
])}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def put_file(%Pleroma.Upload{} = upload) do
|
||||
config = Config.get([__MODULE__])
|
||||
bucket = Keyword.get(config, :bucket)
|
||||
|
|
@ -69,6 +71,18 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def delete_file(file) do
|
||||
[__MODULE__, :bucket]
|
||||
|> Config.get()
|
||||
|> ExAws.S3.delete_object(file)
|
||||
|> ExAws.request()
|
||||
|> case do
|
||||
{:ok, %{status_code: 204}} -> :ok
|
||||
error -> {:error, inspect(error)}
|
||||
end
|
||||
end
|
||||
|
||||
@regex Regex.compile!("[^0-9a-zA-Z!.*/'()_-]")
|
||||
def strict_encode(name) do
|
||||
String.replace(name, @regex, "-")
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ defmodule Pleroma.Uploaders.Uploader do
|
|||
@callback put_file(Pleroma.Upload.t()) ::
|
||||
:ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
|
||||
|
||||
@callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
|
||||
|
||||
@callback http_callback(Plug.Conn.t(), Map.t()) ::
|
||||
{:ok, Plug.Conn.t()}
|
||||
| {:ok, Plug.Conn.t(), file_spec()}
|
||||
|
|
@ -43,7 +45,6 @@ defmodule Pleroma.Uploaders.Uploader do
|
|||
@optional_callbacks http_callback: 2
|
||||
|
||||
@spec put_file(module(), Pleroma.Upload.t()) :: {:ok, file_spec()} | {:error, String.t()}
|
||||
|
||||
def put_file(uploader, upload) do
|
||||
case uploader.put_file(upload) do
|
||||
:ok -> {:ok, {:file, upload.path}}
|
||||
|
|
|
|||
|
|
@ -1430,20 +1430,47 @@ defmodule Pleroma.User do
|
|||
Creates an internal service actor by URI if missing.
|
||||
Optionally takes nickname for addressing.
|
||||
"""
|
||||
def get_or_create_service_actor_by_ap_id(uri, nickname \\ nil) do
|
||||
with user when is_nil(user) <- get_cached_by_ap_id(uri) do
|
||||
{:ok, user} =
|
||||
%User{
|
||||
invisible: true,
|
||||
local: true,
|
||||
ap_id: uri,
|
||||
nickname: nickname,
|
||||
follower_address: uri <> "/followers"
|
||||
}
|
||||
|> Repo.insert()
|
||||
@spec get_or_create_service_actor_by_ap_id(String.t(), String.t()) :: User.t() | nil
|
||||
def get_or_create_service_actor_by_ap_id(uri, nickname) do
|
||||
{_, user} =
|
||||
case get_cached_by_ap_id(uri) do
|
||||
nil ->
|
||||
with {:error, %{errors: errors}} <- create_service_actor(uri, nickname) do
|
||||
Logger.error("Cannot create service actor: #{uri}/.\n#{inspect(errors)}")
|
||||
{:error, nil}
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
%User{invisible: false} = user ->
|
||||
set_invisible(user)
|
||||
|
||||
user ->
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
|
||||
@spec set_invisible(User.t()) :: {:ok, User.t()}
|
||||
defp set_invisible(user) do
|
||||
user
|
||||
|> change(%{invisible: true})
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@spec create_service_actor(String.t(), String.t()) ::
|
||||
{:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
defp create_service_actor(uri, nickname) do
|
||||
%User{
|
||||
invisible: true,
|
||||
local: true,
|
||||
ap_id: uri,
|
||||
nickname: nickname,
|
||||
follower_address: uri <> "/followers"
|
||||
}
|
||||
|> change
|
||||
|> unique_constraint(:nickname)
|
||||
|> Repo.insert()
|
||||
|> set_cache()
|
||||
end
|
||||
|
||||
# AP style
|
||||
|
|
@ -1855,9 +1882,9 @@ defmodule Pleroma.User do
|
|||
])
|
||||
|
||||
with {:ok, updated_user} <- update_and_set_cache(changeset) do
|
||||
if user.is_admin && !updated_user.is_admin do
|
||||
# Tokens & authorizations containing any admin scopes must be revoked (revoking all).
|
||||
# This is an extra safety measure (tokens' admin scopes won't be accepted for non-admins).
|
||||
if user.is_admin != updated_user.is_admin do
|
||||
# Admin status change results in change of accessible OAuth scopes, and instead of changing
|
||||
# already issued tokens we revoke them, requiring user to sign in again
|
||||
global_sign_out(user)
|
||||
end
|
||||
|
||||
|
|
|
|||
12
lib/pleroma/utils.ex
Normal file
12
lib/pleroma/utils.ex
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Utils do
|
||||
def compile_dir(dir) when is_binary(dir) do
|
||||
dir
|
||||
|> File.ls!()
|
||||
|> Enum.map(&Path.join(dir, &1))
|
||||
|> Kernel.ParallelCompiler.compile()
|
||||
end
|
||||
end
|
||||
|
|
@ -1298,28 +1298,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
def fetch_follow_information_for_user(user) do
|
||||
with {:ok, following_data} <-
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
|
||||
following_count when is_integer(following_count) <- following_data["totalItems"],
|
||||
{:ok, hide_follows} <- collection_private(following_data),
|
||||
{:ok, followers_data} <-
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
|
||||
followers_count when is_integer(followers_count) <- followers_data["totalItems"],
|
||||
{:ok, hide_followers} <- collection_private(followers_data) do
|
||||
{:ok,
|
||||
%{
|
||||
hide_follows: hide_follows,
|
||||
follower_count: followers_count,
|
||||
following_count: following_count,
|
||||
follower_count: normalize_counter(followers_data["totalItems"]),
|
||||
following_count: normalize_counter(following_data["totalItems"]),
|
||||
hide_followers: hide_followers
|
||||
}}
|
||||
else
|
||||
{:error, _} = e ->
|
||||
e
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
{:error, _} = e -> e
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_counter(counter) when is_integer(counter), do: counter
|
||||
defp normalize_counter(_), do: 0
|
||||
|
||||
defp maybe_update_follow_information(data) do
|
||||
with {:enabled, true} <-
|
||||
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
|
||||
|
|
@ -1339,24 +1337,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
defp collection_private(%{"first" => %{"type" => type}})
|
||||
when type in ["CollectionPage", "OrderedCollectionPage"],
|
||||
do: {:ok, false}
|
||||
|
||||
defp collection_private(%{"first" => first}) do
|
||||
if is_map(first) and
|
||||
first["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||
{:ok, false}
|
||||
else
|
||||
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||
{:ok, false}
|
||||
else
|
||||
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
||||
{:ok, true}
|
||||
|
||||
{:error, _} = e ->
|
||||
e
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
{:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
|
||||
{:error, _} = e -> e
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
# only accept relayed Creates
|
||||
def inbox(conn, %{"type" => "Create"} = params) do
|
||||
Logger.info(
|
||||
Logger.debug(
|
||||
"Signature missing or not from author, relayed Create message, fetching object from source"
|
||||
)
|
||||
|
||||
|
|
@ -270,11 +270,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
headers = Enum.into(conn.req_headers, %{})
|
||||
|
||||
if String.contains?(headers["signature"], params["actor"]) do
|
||||
Logger.info(
|
||||
Logger.debug(
|
||||
"Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"
|
||||
)
|
||||
|
||||
Logger.info(inspect(conn.req_headers))
|
||||
Logger.debug(inspect(conn.req_headers))
|
||||
end
|
||||
|
||||
json(conn, dgettext("errors", "error"))
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
|||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
Logger.info("REJECTING #{inspect(object)}")
|
||||
Logger.debug("REJECTING #{inspect(object)}")
|
||||
{:reject, object}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
|
|||
]
|
||||
|
||||
def perform(:prefetch, url) do
|
||||
Logger.info("Prefetching #{inspect(url)}")
|
||||
Logger.debug("Prefetching #{inspect(url)}")
|
||||
|
||||
url
|
||||
|> MediaProxy.url()
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
* `id`: the ActivityStreams URI of the message
|
||||
"""
|
||||
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
|
||||
Logger.info("Federating #{id} to #{inbox}")
|
||||
Logger.debug("Federating #{id} to #{inbox}")
|
||||
%{host: host, path: path} = URI.parse(inbox)
|
||||
|
||||
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
|
||||
|
|
@ -228,7 +228,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
public = is_public?(activity)
|
||||
|
||||
if public && Config.get([:instance, :allow_relay]) do
|
||||
Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
|
||||
Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end)
|
||||
Relay.publish(activity)
|
||||
end
|
||||
|
||||
|
|
@ -264,6 +264,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
"rel" => "self",
|
||||
"type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
|
||||
"href" => user.ap_id
|
||||
},
|
||||
%{
|
||||
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
||||
"template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
|||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
require Logger
|
||||
|
||||
@relay_nickname "relay"
|
||||
|
||||
def get_actor do
|
||||
actor =
|
||||
relay_ap_id()
|
||||
|> User.get_or_create_service_actor_by_ap_id()
|
||||
|> User.get_or_create_service_actor_by_ap_id(@relay_nickname)
|
||||
|
||||
actor
|
||||
end
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
|
||||
options
|
||||
)
|
||||
when objtype in ["Article", "Note", "Video", "Page", "Question", "Answer"] do
|
||||
when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer"] do
|
||||
actor = Containment.get_actor(data)
|
||||
|
||||
data =
|
||||
|
|
|
|||
|
|
@ -22,7 +22,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
||||
@supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer", "Audio"]
|
||||
@supported_object_types [
|
||||
"Article",
|
||||
"Note",
|
||||
"Event",
|
||||
"Video",
|
||||
"Page",
|
||||
"Question",
|
||||
"Answer",
|
||||
"Audio"
|
||||
]
|
||||
@strip_status_report_states ~w(closed resolved)
|
||||
@supported_report_states ~w(open closed resolved)
|
||||
@valid_visibilities ~w(public unlisted private direct)
|
||||
|
|
|
|||
|
|
@ -201,7 +201,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
%{
|
||||
"id" => "#{user.ap_id}/followers",
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => total,
|
||||
"first" =>
|
||||
if showing_items do
|
||||
collection(followers, "#{user.ap_id}/followers", 1, showing_items, total)
|
||||
|
|
@ -209,6 +208,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"#{user.ap_id}/followers?page=1"
|
||||
end
|
||||
}
|
||||
|> maybe_put_total_items(showing_count, total)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
|
|
@ -251,6 +251,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
defp maybe_put_total_items(map, false, _total), do: map
|
||||
|
||||
defp maybe_put_total_items(map, true, total) do
|
||||
Map.put(map, "totalItems", total)
|
||||
end
|
||||
|
||||
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
||||
offset = (page - 1) * 10
|
||||
items = Enum.slice(collection, offset, 10)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
plug(Pleroma.Plugs.TrailingFormatPlug)
|
||||
plug(Plug.RequestId)
|
||||
plug(Plug.Logger)
|
||||
plug(Plug.Logger, log: :debug)
|
||||
|
||||
plug(Pleroma.Plugs.Parsers)
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ defmodule Pleroma.Web.Federator do
|
|||
end
|
||||
|
||||
def perform(:incoming_ap_doc, params) do
|
||||
Logger.info("Handling incoming AP activity")
|
||||
Logger.debug("Handling incoming AP activity")
|
||||
|
||||
params = Utils.normalize_params(params)
|
||||
|
||||
|
|
@ -71,13 +71,13 @@ defmodule Pleroma.Web.Federator do
|
|||
{:ok, activity}
|
||||
else
|
||||
%Activity{} ->
|
||||
Logger.info("Already had #{params["id"]}")
|
||||
Logger.debug("Already had #{params["id"]}")
|
||||
:error
|
||||
|
||||
_e ->
|
||||
# Just drop those for now
|
||||
Logger.info("Unhandled activity")
|
||||
Logger.info(Jason.encode!(params, pretty: true))
|
||||
Logger.debug("Unhandled activity")
|
||||
Logger.debug(Jason.encode!(params, pretty: true))
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ defmodule Pleroma.Web.Federator.Publisher do
|
|||
Config.get([:instance, :federation_publisher_modules])
|
||||
|> Enum.each(fn module ->
|
||||
if module.is_representable?(activity) do
|
||||
Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}")
|
||||
Logger.debug("Publishing #{activity.data["id"]} using #{inspect(module)}")
|
||||
module.publish(user, activity)
|
||||
end
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -20,18 +20,21 @@ defmodule Pleroma.Web.MastoFEController do
|
|||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index)
|
||||
|
||||
@doc "GET /web/*path"
|
||||
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||
token = get_session(conn, :oauth_token)
|
||||
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
||||
when not is_nil(user) and not is_nil(token) do
|
||||
conn
|
||||
|> put_layout(false)
|
||||
|> render("index.html",
|
||||
token: token.token,
|
||||
user: user,
|
||||
custom_emojis: Pleroma.Emoji.get_all()
|
||||
)
|
||||
end
|
||||
|
||||
if user && token do
|
||||
conn
|
||||
|> put_layout(false)
|
||||
|> render("index.html", token: token, user: user, custom_emojis: Pleroma.Emoji.get_all())
|
||||
else
|
||||
conn
|
||||
|> put_session(:return_to, conn.request_path)
|
||||
|> redirect(to: "/web/login")
|
||||
end
|
||||
def index(conn, _params) do
|
||||
conn
|
||||
|> put_session(:return_to, conn.request_path)
|
||||
|> redirect(to: "/web/login")
|
||||
end
|
||||
|
||||
@doc "GET /web/manifest.json"
|
||||
|
|
|
|||
|
|
@ -421,7 +421,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end
|
||||
end
|
||||
|
||||
def render_content(%{data: %{"type" => "Video"}} = object) do
|
||||
def render_content(%{data: %{"type" => object_type}} = object)
|
||||
when object_type in ["Video", "Event"] do
|
||||
with name when not is_nil(name) and name != "" <- object.data["name"] do
|
||||
"<p><a href=\"#{object.data["id"]}\">#{name}</a></p>#{object.data["content"]}"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
if attachments == [] or Metadata.activity_nsfw?(object) do
|
||||
[
|
||||
image_tag(user),
|
||||
{:meta, [property: "twitter:card", content: "summary_large_image"], []}
|
||||
{:meta, [property: "twitter:card", content: "summary"], []}
|
||||
]
|
||||
else
|
||||
attachments
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.Metadata.Utils do
|
|||
|> String.replace(~r/<br\s?\/?>/, " ")
|
||||
|> HTML.get_cached_stripped_html_for_activity(object, "metadata")
|
||||
|> Emoji.Formatter.demojify()
|
||||
|> HtmlEntities.decode()
|
||||
|> Formatter.truncate()
|
||||
end
|
||||
|
||||
|
|
@ -22,6 +23,7 @@ defmodule Pleroma.Web.Metadata.Utils do
|
|||
content
|
||||
|> scrub_html
|
||||
|> Emoji.Formatter.demojify()
|
||||
|> HtmlEntities.decode()
|
||||
|> Formatter.truncate(max_length)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||
@doc """
|
||||
Lists the packs available on the instance as JSON.
|
||||
|
||||
The information is public and does not require authentification. The format is
|
||||
The information is public and does not require authentication. The format is
|
||||
a map of "pack directory name" to pack.json contents.
|
||||
"""
|
||||
def list_packs(conn, _params) do
|
||||
|
|
|
|||
|
|
@ -22,7 +22,14 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"]} when action in [:conversation, :conversation_statuses]
|
||||
%{scopes: ["read:statuses"]}
|
||||
when action in [:conversation, :conversation_statuses, :emoji_reactions_by]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:statuses"]}
|
||||
when action in [:react_with_emoji, :unreact_with_emoji]
|
||||
)
|
||||
|
||||
plug(
|
||||
|
|
|
|||
|
|
@ -229,9 +229,9 @@ defmodule Pleroma.Web.Router do
|
|||
pipe_through(:pleroma_html)
|
||||
|
||||
post("/main/ostatus", UtilController, :remote_subscribe)
|
||||
get("/ostatus_subscribe", UtilController, :remote_follow)
|
||||
get("/ostatus_subscribe", RemoteFollowController, :follow)
|
||||
|
||||
post("/ostatus_subscribe", UtilController, :do_remote_follow)
|
||||
post("/ostatus_subscribe", RemoteFollowController, :do_follow)
|
||||
end
|
||||
|
||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<%= if @error == :error do %>
|
||||
<h2>Error fetching user</h2>
|
||||
<% else %>
|
||||
<h2>Remote follow</h2>
|
||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||
<p><%= @followee.nickname %></p>
|
||||
<%= form_for @conn, remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %>
|
||||
<%= hidden_input f, :id, value: @followee.id %>
|
||||
<%= submit "Authorize" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<%= if @error do %>
|
||||
<h2><%= @error %></h2>
|
||||
<% end %>
|
||||
<h2>Log in to follow</h2>
|
||||
<p><%= @followee.nickname %></p>
|
||||
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
|
||||
<%= form_for @conn, remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %>
|
||||
<%= text_input f, :name, placeholder: "Username", required: true %>
|
||||
<br>
|
||||
<%= password_input f, :password, placeholder: "Password", required: true %>
|
||||
<br>
|
||||
<%= hidden_input f, :id, value: @followee.id %>
|
||||
<%= submit "Authorize" %>
|
||||
<% end %>
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<%= if @error == :error do %>
|
||||
<h2>Error fetching user</h2>
|
||||
<% else %>
|
||||
<h2>Remote follow</h2>
|
||||
<img width="128" height="128" src="<%= @avatar %>">
|
||||
<p><%= @name %></p>
|
||||
<%= form_for @conn, util_path(@conn, :do_remote_follow), [as: "user"], fn f -> %>
|
||||
<%= hidden_input f, :id, value: @id %>
|
||||
<%= submit "Authorize" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<%= if @error do %>
|
||||
<h2><%= @error %></h2>
|
||||
<% end %>
|
||||
<h2>Log in to follow</h2>
|
||||
<p><%= @name %></p>
|
||||
<img height="128" width="128" src="<%= @avatar %>">
|
||||
<%= form_for @conn, util_path(@conn, :do_remote_follow), [as: "authorization"], fn f -> %>
|
||||
<%= text_input f, :name, placeholder: "Username" %>
|
||||
<br>
|
||||
<%= password_input f, :password, placeholder: "Password" %>
|
||||
<br>
|
||||
<%= hidden_input f, :id, value: @id %>
|
||||
<%= submit "Authorize" %>
|
||||
<% end %>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
require Logger
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Auth.Authenticator
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
|
||||
|
||||
# Note: follower can submit the form (with password auth) not being signed in (having no token)
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
|
||||
when action in [:do_follow]
|
||||
)
|
||||
|
||||
# GET /ostatus_subscribe
|
||||
#
|
||||
def follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
|
||||
case is_status?(acct) do
|
||||
true -> follow_status(conn, user, acct)
|
||||
_ -> follow_account(conn, user, acct)
|
||||
end
|
||||
end
|
||||
|
||||
defp follow_status(conn, _user, acct) do
|
||||
with {:ok, object} <- Fetcher.fetch_object_from_id(acct),
|
||||
%Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object.data["id"]) do
|
||||
redirect(conn, to: o_status_path(conn, :notice, activity_id))
|
||||
else
|
||||
error ->
|
||||
handle_follow_error(conn, error)
|
||||
end
|
||||
end
|
||||
|
||||
defp follow_account(conn, user, acct) do
|
||||
with {:ok, followee} <- User.get_or_fetch(acct) do
|
||||
render(conn, follow_template(user), %{error: false, followee: followee, acct: acct})
|
||||
else
|
||||
{:error, _reason} ->
|
||||
render(conn, follow_template(user), %{error: :error})
|
||||
end
|
||||
end
|
||||
|
||||
defp follow_template(%User{} = _user), do: "follow.html"
|
||||
defp follow_template(_), do: "follow_login.html"
|
||||
|
||||
defp is_status?(acct) do
|
||||
case Fetcher.fetch_and_contain_remote_object_from_id(acct) do
|
||||
{:ok, %{"type" => type}} when type in @status_types ->
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# POST /ostatus_subscribe
|
||||
#
|
||||
def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do
|
||||
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
||||
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
|
||||
render(conn, "followed.html", %{error: false})
|
||||
else
|
||||
error ->
|
||||
handle_follow_error(conn, error)
|
||||
end
|
||||
end
|
||||
|
||||
def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" => id}}) do
|
||||
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
||||
{_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
|
||||
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
|
||||
render(conn, "followed.html", %{error: false})
|
||||
else
|
||||
error ->
|
||||
handle_follow_error(conn, error)
|
||||
end
|
||||
end
|
||||
|
||||
def do_follow(%{assigns: %{user: nil}} = conn, _) do
|
||||
Logger.debug("Insufficient permissions: follow | write:follows.")
|
||||
render(conn, "followed.html", %{error: "Insufficient permissions: follow | write:follows."})
|
||||
end
|
||||
|
||||
defp handle_follow_error(conn, {:auth, _, followee} = _) do
|
||||
render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee})
|
||||
end
|
||||
|
||||
defp handle_follow_error(conn, {:fetch_user, error} = _) do
|
||||
Logger.debug("Remote follow failed with error #{inspect(error)}")
|
||||
render(conn, "followed.html", %{error: "Could not find user"})
|
||||
end
|
||||
|
||||
defp handle_follow_error(conn, {:error, "Could not follow user:" <> _} = _) do
|
||||
render(conn, "followed.html", %{error: "Error following account"})
|
||||
end
|
||||
|
||||
defp handle_follow_error(conn, error) do
|
||||
Logger.debug("Remote follow failed with error #{inspect(error)}")
|
||||
render(conn, "followed.html", %{error: "Something went wrong."})
|
||||
end
|
||||
end
|
||||
|
|
@ -7,12 +7,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
|
||||
require Logger
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Healthcheck
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Plugs.AuthenticationPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
|
|
@ -22,7 +20,14 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["follow", "write:follows"]}
|
||||
when action in [:do_remote_follow, :follow_import]
|
||||
when action == :follow_import
|
||||
)
|
||||
|
||||
# Note: follower can submit the form (with password auth) not being signed in (having no token)
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
|
||||
when action == :do_remote_follow
|
||||
)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import)
|
||||
|
|
@ -77,94 +82,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
end
|
||||
|
||||
def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
|
||||
if is_status?(acct) do
|
||||
{:ok, object} = Pleroma.Object.Fetcher.fetch_object_from_id(acct)
|
||||
%Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
|
||||
redirect(conn, to: "/notice/#{activity_id}")
|
||||
else
|
||||
with {:ok, followee} <- User.get_or_fetch(acct) do
|
||||
conn
|
||||
|> render(follow_template(user), %{
|
||||
error: false,
|
||||
acct: acct,
|
||||
avatar: User.avatar_url(followee),
|
||||
name: followee.nickname,
|
||||
id: followee.id
|
||||
})
|
||||
else
|
||||
{:error, _reason} ->
|
||||
render(conn, follow_template(user), %{error: :error})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp follow_template(%User{} = _user), do: "follow.html"
|
||||
defp follow_template(_), do: "follow_login.html"
|
||||
|
||||
defp is_status?(acct) do
|
||||
case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
|
||||
{:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def do_remote_follow(conn, %{
|
||||
"authorization" => %{"name" => username, "password" => password, "id" => id}
|
||||
}) do
|
||||
with %User{} = followee <- User.get_cached_by_id(id),
|
||||
{_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee},
|
||||
{_, true, _} <- {
|
||||
:auth,
|
||||
AuthenticationPlug.checkpw(password, user.password_hash),
|
||||
followee
|
||||
},
|
||||
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
|
||||
conn
|
||||
|> render("followed.html", %{error: false})
|
||||
else
|
||||
# Was already following user
|
||||
{:error, "Could not follow user:" <> _rest} ->
|
||||
render(conn, "followed.html", %{error: "Error following account"})
|
||||
|
||||
{:auth, _, followee} ->
|
||||
conn
|
||||
|> render("follow_login.html", %{
|
||||
error: "Wrong username or password",
|
||||
id: id,
|
||||
name: followee.nickname,
|
||||
avatar: User.avatar_url(followee)
|
||||
})
|
||||
|
||||
e ->
|
||||
Logger.debug("Remote follow failed with error #{inspect(e)}")
|
||||
render(conn, "followed.html", %{error: "Something went wrong."})
|
||||
end
|
||||
end
|
||||
|
||||
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
|
||||
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
||||
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
|
||||
conn
|
||||
|> render("followed.html", %{error: false})
|
||||
else
|
||||
# Was already following user
|
||||
{:error, "Could not follow user:" <> _rest} ->
|
||||
render(conn, "followed.html", %{error: "Error following account"})
|
||||
|
||||
{:fetch_user, error} ->
|
||||
Logger.debug("Remote follow failed with error #{inspect(error)}")
|
||||
render(conn, "followed.html", %{error: "Could not find user"})
|
||||
|
||||
e ->
|
||||
Logger.debug("Remote follow failed with error #{inspect(e)}")
|
||||
render(conn, "followed.html", %{error: "Something went wrong."})
|
||||
end
|
||||
end
|
||||
|
||||
def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
|
||||
with {:ok, _} <- Notification.read_one(user, notification_id) do
|
||||
json(conn, %{status: "success"})
|
||||
|
|
@ -345,7 +262,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
|
||||
def delete_account(%{assigns: %{user: user}} = conn, params) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
|
||||
password = params["password"] || ""
|
||||
|
||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||
{:ok, user} ->
|
||||
User.delete(user)
|
||||
json(conn, %{status: "success"})
|
||||
|
|
|
|||
10
lib/pleroma/web/twitter_api/views/remote_follow_view.ex
Normal file
10
lib/pleroma/web/twitter_api/views/remote_follow_view.ex
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do
|
||||
use Pleroma.Web, :view
|
||||
import Phoenix.HTML.Form
|
||||
|
||||
defdelegate avatar_url(user), to: Pleroma.User
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue