Merge branch 'develop' into 'oembed_provider'
# Conflicts: # lib/pleroma/activity.ex
This commit is contained in:
commit
c9b418e547
85 changed files with 1521 additions and 267 deletions
|
|
@ -105,6 +105,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
)
|
||||
|
||||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
||||
|
||||
result_config =
|
||||
|
|
@ -120,6 +121,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
dbpass: dbpass,
|
||||
version: Pleroma.Mixfile.project() |> Keyword.get(:version),
|
||||
secret: secret,
|
||||
signing_salt: signing_salt,
|
||||
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
||||
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ use Mix.Config
|
|||
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
|
||||
secret_key_base: "<%= secret %>"
|
||||
secret_key_base: "<%= secret %>",
|
||||
signing_salt: "<%= signing_salt %>"
|
||||
|
||||
config :pleroma, :instance,
|
||||
name: "<%= name %>",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.PasswordResetToken do
|
|||
alias Pleroma.{User, PasswordResetToken, Repo}
|
||||
|
||||
schema "password_reset_tokens" do
|
||||
belongs_to(:user, User)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
field(:token, :string)
|
||||
field(:used, :boolean, default: false)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Activity do
|
|||
import Ecto.Query
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
@primary_key {:id, Pleroma.FlakeId, autogenerate: true}
|
||||
|
||||
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
||||
@mastodon_notification_types %{
|
||||
|
|
@ -40,25 +41,7 @@ defmodule Pleroma.Activity do
|
|||
Repo.get(Activity, id)
|
||||
end
|
||||
|
||||
# TODO:
|
||||
# Go through these and fix them everywhere.
|
||||
# Wrong name, only returns create activities
|
||||
def all_by_object_ap_id_q(ap_id) do
|
||||
from(
|
||||
activity in Activity,
|
||||
where:
|
||||
fragment(
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^to_string(ap_id)
|
||||
),
|
||||
where: fragment("(?)->>'type' = 'Create'", activity.data)
|
||||
)
|
||||
end
|
||||
|
||||
# Wrong name, returns all.
|
||||
def all_non_create_by_object_ap_id_q(ap_id) do
|
||||
def by_object_ap_id(ap_id) do
|
||||
from(
|
||||
activity in Activity,
|
||||
where:
|
||||
|
|
@ -71,12 +54,7 @@ defmodule Pleroma.Activity do
|
|||
)
|
||||
end
|
||||
|
||||
# Wrong name plz fix thx
|
||||
def all_by_object_ap_id(ap_id) do
|
||||
Repo.all(all_by_object_ap_id_q(ap_id))
|
||||
end
|
||||
|
||||
def create_activity_by_object_id_query(ap_ids) do
|
||||
def create_by_object_ap_id(ap_ids) when is_list(ap_ids) do
|
||||
from(
|
||||
activity in Activity,
|
||||
where:
|
||||
|
|
@ -90,19 +68,37 @@ defmodule Pleroma.Activity do
|
|||
)
|
||||
end
|
||||
|
||||
def get_create_activity_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||
create_activity_by_object_id_query([ap_id])
|
||||
def create_by_object_ap_id(ap_id) do
|
||||
from(
|
||||
activity in Activity,
|
||||
where:
|
||||
fragment(
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^to_string(ap_id)
|
||||
),
|
||||
where: fragment("(?)->>'type' = 'Create'", activity.data)
|
||||
)
|
||||
end
|
||||
|
||||
def get_all_create_by_object_ap_id(ap_id) do
|
||||
Repo.all(create_by_object_ap_id(ap_id))
|
||||
end
|
||||
|
||||
def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||
create_by_object_ap_id(ap_id)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def get_create_activity_by_object_ap_id(_), do: nil
|
||||
def get_create_by_object_ap_id(_), do: nil
|
||||
|
||||
def normalize(obj) when is_map(obj), do: Activity.get_by_ap_id(obj["id"])
|
||||
def normalize(ap_id) when is_binary(ap_id), do: Activity.get_by_ap_id(ap_id)
|
||||
def normalize(_), do: nil
|
||||
|
||||
def get_in_reply_to_activity(%Activity{data: %{"object" => %{"inReplyTo" => ap_id}}}) do
|
||||
get_create_activity_by_object_ap_id(ap_id)
|
||||
get_create_by_object_ap_id(ap_id)
|
||||
end
|
||||
|
||||
def get_in_reply_to_activity(_), do: nil
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ defmodule Pleroma.Application do
|
|||
],
|
||||
id: :cachex_idem
|
||||
),
|
||||
worker(Pleroma.FlakeId, []),
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Web.Federator, []),
|
||||
worker(Pleroma.Stats, []),
|
||||
|
|
|
|||
155
lib/pleroma/clippy.ex
Normal file
155
lib/pleroma/clippy.ex
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Clippy do
|
||||
@moduledoc false
|
||||
# No software is complete until they have a Clippy implementation.
|
||||
# A ballmer peak _may_ be required to change this module.
|
||||
|
||||
def tip() do
|
||||
tips()
|
||||
|> Enum.random()
|
||||
|> puts()
|
||||
end
|
||||
|
||||
def tips() do
|
||||
host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
|
||||
|
||||
[
|
||||
"“πλήρωμα” is “pleroma” in greek",
|
||||
"For an extended Pleroma Clippy Experience, use the “Redmond” themes in Pleroma FE settings",
|
||||
"Staff accounts and MRF policies of Pleroma instances are disclosed on the NodeInfo endpoints for easy transparency!\n
|
||||
- https://catgirl.science/misc/nodeinfo.lua?#{host}
|
||||
- https://fediverse.network/#{host}/federation",
|
||||
"Pleroma can federate to the Dark Web!\n
|
||||
- Tor: https://git.pleroma.social/pleroma/pleroma/wikis/Easy%20Onion%20Federation%20(Tor)
|
||||
- i2p: https://git.pleroma.social/pleroma/pleroma/wikis/I2p%20federation",
|
||||
"Lists of Pleroma instances:\n\n- http://distsn.org/pleroma-instances.html\n- https://fediverse.network/pleroma\n- https://the-federation.info/pleroma",
|
||||
"Pleroma uses the LitePub protocol - https://litepub.social",
|
||||
"To receive more federated posts, subscribe to relays!\n
|
||||
- How-to: https://git.pleroma.social/pleroma/pleroma/wikis/Admin%20tasks#relay-managment
|
||||
- Relays: https://fediverse.network/activityrelay"
|
||||
]
|
||||
end
|
||||
|
||||
@spec puts(String.t() | [[IO.ANSI.ansicode() | String.t(), ...], ...]) :: nil
|
||||
def puts(text_or_lines) do
|
||||
import IO.ANSI
|
||||
|
||||
lines =
|
||||
if is_binary(text_or_lines) do
|
||||
String.split(text_or_lines, ~r/\n/)
|
||||
else
|
||||
text_or_lines
|
||||
end
|
||||
|
||||
longest_line_size =
|
||||
lines
|
||||
|> Enum.map(&charlist_count_text/1)
|
||||
|> Enum.sort(&>=/2)
|
||||
|> List.first()
|
||||
|
||||
pad_text = longest_line_size
|
||||
|
||||
pad =
|
||||
for(_ <- 1..pad_text, do: "_")
|
||||
|> Enum.join("")
|
||||
|
||||
pad_spaces =
|
||||
for(_ <- 1..pad_text, do: " ")
|
||||
|> Enum.join("")
|
||||
|
||||
spaces = " "
|
||||
|
||||
pre_lines = [
|
||||
" / \\#{spaces} _#{pad}___",
|
||||
" | |#{spaces} / #{pad_spaces} \\"
|
||||
]
|
||||
|
||||
for l <- pre_lines do
|
||||
IO.puts(l)
|
||||
end
|
||||
|
||||
clippy_lines = [
|
||||
" #{bright()}@ @#{reset()}#{spaces} ",
|
||||
" || ||#{spaces}",
|
||||
" || || <--",
|
||||
" |\\_/| ",
|
||||
" \\___/ "
|
||||
]
|
||||
|
||||
noclippy_line = " "
|
||||
|
||||
env = %{
|
||||
max_size: pad_text,
|
||||
pad: pad,
|
||||
pad_spaces: pad_spaces,
|
||||
spaces: spaces,
|
||||
pre_lines: pre_lines,
|
||||
noclippy_line: noclippy_line
|
||||
}
|
||||
|
||||
# surrond one/five line clippy with blank lines around to not fuck up the layout
|
||||
#
|
||||
# yes this fix sucks but it's good enough, have you ever seen a release of windows wihtout some butched
|
||||
# features anyway?
|
||||
lines =
|
||||
if length(lines) == 1 or length(lines) == 5 do
|
||||
[""] ++ lines ++ [""]
|
||||
else
|
||||
lines
|
||||
end
|
||||
|
||||
clippy_line(lines, clippy_lines, env)
|
||||
rescue
|
||||
e ->
|
||||
IO.puts("(Clippy crashed, sorry: #{inspect(e)})")
|
||||
IO.puts(text_or_lines)
|
||||
end
|
||||
|
||||
defp clippy_line([line | lines], [prefix | clippy_lines], env) do
|
||||
IO.puts([prefix <> "| ", rpad_line(line, env.max_size)])
|
||||
clippy_line(lines, clippy_lines, env)
|
||||
end
|
||||
|
||||
# more text lines but clippy's complete
|
||||
defp clippy_line([line | lines], [], env) do
|
||||
IO.puts([env.noclippy_line, "| ", rpad_line(line, env.max_size)])
|
||||
|
||||
if lines == [] do
|
||||
IO.puts(env.noclippy_line <> "\\_#{env.pad}___/")
|
||||
end
|
||||
|
||||
clippy_line(lines, [], env)
|
||||
end
|
||||
|
||||
# no more text lines but clippy's not complete
|
||||
defp clippy_line([], [clippy | clippy_lines], env) do
|
||||
if env.pad do
|
||||
IO.puts(clippy <> "\\_#{env.pad}___/")
|
||||
clippy_line([], clippy_lines, %{env | pad: nil})
|
||||
else
|
||||
IO.puts(clippy)
|
||||
clippy_line([], clippy_lines, env)
|
||||
end
|
||||
end
|
||||
|
||||
defp clippy_line(_, _, _) do
|
||||
end
|
||||
|
||||
defp rpad_line(line, max) do
|
||||
pad = max - (charlist_count_text(line) - 2)
|
||||
pads = Enum.join(for(_ <- 1..pad, do: " "))
|
||||
[IO.ANSI.format(line), pads <> " |"]
|
||||
end
|
||||
|
||||
defp charlist_count_text(line) do
|
||||
if is_list(line) do
|
||||
text = Enum.join(Enum.filter(line, &is_binary/1))
|
||||
String.length(text)
|
||||
else
|
||||
String.length(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Filter do
|
|||
alias Pleroma.{User, Repo}
|
||||
|
||||
schema "filters" do
|
||||
belongs_to(:user, User)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
field(:filter_id, :integer)
|
||||
field(:hide, :boolean, default: false)
|
||||
field(:whole_word, :boolean, default: true)
|
||||
|
|
|
|||
183
lib/pleroma/flake_id.ex
Normal file
183
lib/pleroma/flake_id.ex
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.FlakeId do
|
||||
@moduledoc """
|
||||
Flake is a decentralized, k-ordered id generation service.
|
||||
|
||||
Adapted from:
|
||||
|
||||
* [flaky](https://github.com/nirvana/flaky), released under the terms of the Truly Free License,
|
||||
* [Flake](https://github.com/boundary/flake), Copyright 2012, Boundary, Apache License, Version 2.0
|
||||
"""
|
||||
|
||||
@type t :: binary
|
||||
|
||||
@behaviour Ecto.Type
|
||||
use GenServer
|
||||
require Logger
|
||||
alias __MODULE__
|
||||
import Kernel, except: [to_string: 1]
|
||||
|
||||
defstruct node: nil, time: 0, sq: 0
|
||||
|
||||
@doc "Converts a binary Flake to a String"
|
||||
def to_string(<<0::integer-size(64), id::integer-size(64)>>) do
|
||||
Kernel.to_string(id)
|
||||
end
|
||||
|
||||
def to_string(flake = <<_::integer-size(64), _::integer-size(48), _::integer-size(16)>>) do
|
||||
encode_base62(flake)
|
||||
end
|
||||
|
||||
def to_string(s), do: s
|
||||
|
||||
for i <- [-1, 0] do
|
||||
def from_string(unquote(i)), do: <<0::integer-size(128)>>
|
||||
def from_string(unquote(Kernel.to_string(i))), do: <<0::integer-size(128)>>
|
||||
end
|
||||
|
||||
def from_string(flake = <<_::integer-size(128)>>), do: flake
|
||||
|
||||
def from_string(string) when is_binary(string) and byte_size(string) < 18 do
|
||||
case Integer.parse(string) do
|
||||
{id, _} -> <<0::integer-size(64), id::integer-size(64)>>
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def from_string(string) do
|
||||
string |> decode_base62 |> from_integer
|
||||
end
|
||||
|
||||
def to_integer(<<integer::integer-size(128)>>), do: integer
|
||||
|
||||
def from_integer(integer) do
|
||||
<<_time::integer-size(64), _node::integer-size(48), _seq::integer-size(16)>> =
|
||||
<<integer::integer-size(128)>>
|
||||
end
|
||||
|
||||
@doc "Generates a Flake"
|
||||
@spec get :: binary
|
||||
def get, do: to_string(:gen_server.call(:flake, :get))
|
||||
|
||||
# -- Ecto.Type API
|
||||
@impl Ecto.Type
|
||||
def type, do: :uuid
|
||||
|
||||
@impl Ecto.Type
|
||||
def cast(value) do
|
||||
{:ok, FlakeId.to_string(value)}
|
||||
end
|
||||
|
||||
@impl Ecto.Type
|
||||
def load(value) do
|
||||
{:ok, FlakeId.to_string(value)}
|
||||
end
|
||||
|
||||
@impl Ecto.Type
|
||||
def dump(value) do
|
||||
{:ok, FlakeId.from_string(value)}
|
||||
end
|
||||
|
||||
def autogenerate(), do: get()
|
||||
|
||||
# -- GenServer API
|
||||
def start_link do
|
||||
:gen_server.start_link({:local, :flake}, __MODULE__, [], [])
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def init([]) do
|
||||
{:ok, %FlakeId{node: mac(), time: time()}}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call(:get, _from, state) do
|
||||
{flake, new_state} = get(time(), state)
|
||||
{:reply, flake, new_state}
|
||||
end
|
||||
|
||||
# Matches when the calling time is the same as the state time. Incr. sq
|
||||
defp get(time, %FlakeId{time: time, node: node, sq: seq}) do
|
||||
new_state = %FlakeId{time: time, node: node, sq: seq + 1}
|
||||
{gen_flake(new_state), new_state}
|
||||
end
|
||||
|
||||
# Matches when the times are different, reset sq
|
||||
defp get(newtime, %FlakeId{time: time, node: node}) when newtime > time do
|
||||
new_state = %FlakeId{time: newtime, node: node, sq: 0}
|
||||
{gen_flake(new_state), new_state}
|
||||
end
|
||||
|
||||
# Error when clock is running backwards
|
||||
defp get(newtime, %FlakeId{time: time}) when newtime < time do
|
||||
{:error, :clock_running_backwards}
|
||||
end
|
||||
|
||||
defp gen_flake(%FlakeId{time: time, node: node, sq: seq}) do
|
||||
<<time::integer-size(64), node::integer-size(48), seq::integer-size(16)>>
|
||||
end
|
||||
|
||||
defp nthchar_base62(n) when n <= 9, do: ?0 + n
|
||||
defp nthchar_base62(n) when n <= 35, do: ?A + n - 10
|
||||
defp nthchar_base62(n), do: ?a + n - 36
|
||||
|
||||
defp encode_base62(<<integer::integer-size(128)>>) do
|
||||
integer
|
||||
|> encode_base62([])
|
||||
|> List.to_string()
|
||||
end
|
||||
|
||||
defp encode_base62(int, acc) when int < 0, do: encode_base62(-int, acc)
|
||||
defp encode_base62(int, []) when int == 0, do: '0'
|
||||
defp encode_base62(int, acc) when int == 0, do: acc
|
||||
|
||||
defp encode_base62(int, acc) do
|
||||
r = rem(int, 62)
|
||||
id = div(int, 62)
|
||||
acc = [nthchar_base62(r) | acc]
|
||||
encode_base62(id, acc)
|
||||
end
|
||||
|
||||
defp decode_base62(s) do
|
||||
decode_base62(String.to_charlist(s), 0)
|
||||
end
|
||||
|
||||
defp decode_base62([c | cs], acc) when c >= ?0 and c <= ?9,
|
||||
do: decode_base62(cs, 62 * acc + (c - ?0))
|
||||
|
||||
defp decode_base62([c | cs], acc) when c >= ?A and c <= ?Z,
|
||||
do: decode_base62(cs, 62 * acc + (c - ?A + 10))
|
||||
|
||||
defp decode_base62([c | cs], acc) when c >= ?a and c <= ?z,
|
||||
do: decode_base62(cs, 62 * acc + (c - ?a + 36))
|
||||
|
||||
defp decode_base62([], acc), do: acc
|
||||
|
||||
defp time do
|
||||
{mega_seconds, seconds, micro_seconds} = :erlang.timestamp()
|
||||
1_000_000_000 * mega_seconds + seconds * 1000 + :erlang.trunc(micro_seconds / 1000)
|
||||
end
|
||||
|
||||
def mac do
|
||||
{:ok, addresses} = :inet.getifaddrs()
|
||||
|
||||
macids =
|
||||
Enum.reduce(addresses, [], fn {_iface, attrs}, acc ->
|
||||
case attrs[:hwaddr] do
|
||||
[0, 0, 0 | _] -> acc
|
||||
mac when is_list(mac) -> [mac_to_worker_id(mac) | acc]
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
List.first(macids)
|
||||
end
|
||||
|
||||
def mac_to_worker_id(mac) do
|
||||
<<worker::integer-size(48)>> = :binary.list_to_bin(mac)
|
||||
worker
|
||||
end
|
||||
end
|
||||
|
|
@ -130,7 +130,7 @@ defmodule Pleroma.Formatter do
|
|||
end
|
||||
|
||||
@doc "Adds the links to mentioned users"
|
||||
def add_user_links({subs, text}, mentions) do
|
||||
def add_user_links({subs, text}, mentions, options \\ []) do
|
||||
mentions =
|
||||
mentions
|
||||
|> Enum.sort_by(fn {name, _} -> -String.length(name) end)
|
||||
|
|
@ -152,12 +152,16 @@ defmodule Pleroma.Formatter do
|
|||
ap_id
|
||||
end
|
||||
|
||||
short_match = String.split(match, "@") |> tl() |> hd()
|
||||
nickname =
|
||||
if options[:format] == :full do
|
||||
User.full_nickname(match)
|
||||
else
|
||||
User.local_nickname(match)
|
||||
end
|
||||
|
||||
{uuid,
|
||||
"<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>@<span>#{
|
||||
short_match
|
||||
}</span></a></span>"}
|
||||
"<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>" <>
|
||||
"@<span>#{nickname}</span></a></span>"}
|
||||
end)
|
||||
|
||||
{subs, uuid_text}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.List do
|
|||
alias Pleroma.{User, Repo, Activity}
|
||||
|
||||
schema "lists" do
|
||||
belongs_to(:user, Pleroma.User)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
field(:title, :string)
|
||||
field(:following, {:array, :string}, default: [])
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ defmodule Pleroma.Notification do
|
|||
|
||||
schema "notifications" do
|
||||
field(:seen, :boolean, default: false)
|
||||
belongs_to(:user, Pleroma.User)
|
||||
belongs_to(:activity, Pleroma.Activity)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
belongs_to(:activity, Activity, type: Pleroma.FlakeId)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
|
@ -96,7 +96,7 @@ defmodule Pleroma.Notification do
|
|||
end
|
||||
end
|
||||
|
||||
def create_notifications(%Activity{id: _, data: %{"to" => _, "type" => type}} = activity)
|
||||
def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
|
||||
when type in ["Create", "Like", "Announce", "Follow"] do
|
||||
users = get_notified_from_activity(activity)
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ defmodule Pleroma.Object do
|
|||
|
||||
def delete(%Object{data: %{"id" => id}} = object) do
|
||||
with {:ok, _obj} = swap_object_with_tombstone(object),
|
||||
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
|
||||
Repo.delete_all(Activity.by_object_ap_id(id)),
|
||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||
{:ok, object}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -275,11 +275,24 @@ defmodule Pleroma.ReverseProxy do
|
|||
|
||||
defp build_resp_cache_headers(headers, _opts) do
|
||||
has_cache? = Enum.any?(headers, fn {k, _} -> k in @resp_cache_headers end)
|
||||
has_cache_control? = List.keymember?(headers, "cache-control", 0)
|
||||
|
||||
if has_cache? do
|
||||
headers
|
||||
else
|
||||
List.keystore(headers, "cache-control", 0, {"cache-control", @default_cache_control_header})
|
||||
cond do
|
||||
has_cache? && has_cache_control? ->
|
||||
headers
|
||||
|
||||
has_cache? ->
|
||||
# There's caching header present but no cache-control -- we need to explicitely override it to public
|
||||
# as Plug defaults to "max-age=0, private, must-revalidate"
|
||||
List.keystore(headers, "cache-control", 0, {"cache-control", "public"})
|
||||
|
||||
true ->
|
||||
List.keystore(
|
||||
headers,
|
||||
"cache-control",
|
||||
0,
|
||||
{"cache-control", @default_cache_control_header}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,12 +9,20 @@ 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
|
||||
def get_file(file) do
|
||||
config = Pleroma.Config.get([__MODULE__])
|
||||
bucket = Keyword.fetch!(config, :bucket)
|
||||
|
||||
bucket_with_namespace =
|
||||
if namespace = Keyword.get(config, :bucket_namespace) do
|
||||
namespace <> ":" <> bucket
|
||||
else
|
||||
bucket
|
||||
end
|
||||
|
||||
{:ok,
|
||||
{:url,
|
||||
Path.join([
|
||||
Keyword.fetch!(config, :public_endpoint),
|
||||
Keyword.fetch!(config, :bucket),
|
||||
bucket_with_namespace,
|
||||
strict_encode(URI.decode(file))
|
||||
])}}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,18 +27,47 @@ defmodule Pleroma.Uploaders.Uploader do
|
|||
This allows to correctly proxy or redirect requests to the backend, while allowing to migrate backends without breaking any URL.
|
||||
* `{url, url :: String.t}` to bypass `get_file/2` and use the `url` directly in the activity.
|
||||
* `{:error, String.t}` error information if the file failed to be saved to the backend.
|
||||
* `:wait_callback` will wait for an http post request at `/api/pleroma/upload_callback/:upload_path` and call the uploader's `http_callback/3` method.
|
||||
|
||||
|
||||
"""
|
||||
@type file_spec :: {:file | :url, String.t()}
|
||||
@callback put_file(Pleroma.Upload.t()) ::
|
||||
:ok | {:ok, {:file | :url, String.t()}} | {:error, String.t()}
|
||||
:ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
|
||||
|
||||
@callback http_callback(Plug.Conn.t(), Map.t()) ::
|
||||
{:ok, Plug.Conn.t()}
|
||||
| {:ok, Plug.Conn.t(), file_spec()}
|
||||
| {:error, Plug.Conn.t(), String.t()}
|
||||
@optional_callbacks http_callback: 2
|
||||
|
||||
@spec put_file(module(), Pleroma.Upload.t()) :: {:ok, file_spec()} | {:error, String.t()}
|
||||
|
||||
@spec put_file(module(), Pleroma.Upload.t()) ::
|
||||
{:ok, {:file | :url, String.t()}} | {:error, String.t()}
|
||||
def put_file(uploader, upload) do
|
||||
case uploader.put_file(upload) do
|
||||
:ok -> {:ok, {:file, upload.path}}
|
||||
other -> other
|
||||
:wait_callback -> handle_callback(uploader, upload)
|
||||
{:ok, _} = ok -> ok
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_callback(uploader, upload) do
|
||||
:global.register_name({__MODULE__, upload.path}, self())
|
||||
|
||||
receive do
|
||||
{__MODULE__, pid, conn, params} ->
|
||||
case uploader.http_callback(conn, params) do
|
||||
{:ok, conn, ok} ->
|
||||
send(pid, {__MODULE__, conn})
|
||||
{:ok, ok}
|
||||
|
||||
{:error, conn, error} ->
|
||||
send(pid, {__MODULE__, conn})
|
||||
{:error, error}
|
||||
end
|
||||
after
|
||||
30_000 -> {:error, "Uploader callback timeout"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.User do
|
|||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
@primary_key {:id, Pleroma.FlakeId, autogenerate: true}
|
||||
|
||||
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
||||
|
||||
@strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
|
||||
|
|
@ -35,7 +37,7 @@ defmodule Pleroma.User do
|
|||
field(:avatar, :map)
|
||||
field(:local, :boolean, default: true)
|
||||
field(:follower_address, :string)
|
||||
field(:search_distance, :float, virtual: true)
|
||||
field(:search_rank, :float, virtual: true)
|
||||
field(:tags, {:array, :string}, default: [])
|
||||
field(:last_refreshed_at, :naive_datetime)
|
||||
has_many(:notifications, Notification)
|
||||
|
|
@ -473,8 +475,7 @@ defmodule Pleroma.User do
|
|||
def get_by_nickname(nickname) do
|
||||
Repo.get_by(User, nickname: nickname) ||
|
||||
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
|
||||
[local_nickname, _] = String.split(nickname, "@")
|
||||
Repo.get_by(User, nickname: local_nickname)
|
||||
Repo.get_by(User, nickname: local_nickname(nickname))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -537,6 +538,12 @@ defmodule Pleroma.User do
|
|||
{:ok, Repo.all(q)}
|
||||
end
|
||||
|
||||
def get_followers_ids(user, page \\ nil) do
|
||||
q = get_followers_query(user, page)
|
||||
|
||||
Repo.all(from(u in q, select: u.id))
|
||||
end
|
||||
|
||||
def get_friends_query(%User{id: id, following: following}, nil) do
|
||||
from(
|
||||
u in User,
|
||||
|
|
@ -561,6 +568,12 @@ defmodule Pleroma.User do
|
|||
{:ok, Repo.all(q)}
|
||||
end
|
||||
|
||||
def get_friends_ids(user, page \\ nil) do
|
||||
q = get_friends_query(user, page)
|
||||
|
||||
Repo.all(from(u in q, select: u.id))
|
||||
end
|
||||
|
||||
def get_follow_requests_query(%User{} = user) do
|
||||
from(
|
||||
a in Activity,
|
||||
|
|
@ -692,37 +705,120 @@ defmodule Pleroma.User do
|
|||
Repo.all(query)
|
||||
end
|
||||
|
||||
def search(query, resolve \\ false) do
|
||||
# strip the beginning @ off if there is a query
|
||||
def search(query, resolve \\ false, for_user \\ nil) do
|
||||
# Strip the beginning @ off if there is a query
|
||||
query = String.trim_leading(query, "@")
|
||||
|
||||
if resolve do
|
||||
User.get_or_fetch_by_nickname(query)
|
||||
end
|
||||
if resolve, do: User.get_or_fetch_by_nickname(query)
|
||||
|
||||
inner =
|
||||
from(
|
||||
u in User,
|
||||
select_merge: %{
|
||||
search_distance:
|
||||
fragment(
|
||||
"? <-> (? || coalesce(?, ''))",
|
||||
^query,
|
||||
u.nickname,
|
||||
u.name
|
||||
)
|
||||
},
|
||||
where: not is_nil(u.nickname)
|
||||
)
|
||||
fts_results = do_search(fts_search_subquery(query), for_user)
|
||||
|
||||
{:ok, trigram_results} =
|
||||
Repo.transaction(fn ->
|
||||
Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", [])
|
||||
do_search(trigram_search_subquery(query), for_user)
|
||||
end)
|
||||
|
||||
Enum.uniq_by(fts_results ++ trigram_results, & &1.id)
|
||||
end
|
||||
|
||||
defp do_search(subquery, for_user, options \\ []) do
|
||||
q =
|
||||
from(
|
||||
s in subquery(inner),
|
||||
order_by: s.search_distance,
|
||||
limit: 20
|
||||
s in subquery(subquery),
|
||||
order_by: [desc: s.search_rank],
|
||||
limit: ^(options[:limit] || 20)
|
||||
)
|
||||
|
||||
Repo.all(q)
|
||||
results =
|
||||
q
|
||||
|> Repo.all()
|
||||
|> Enum.filter(&(&1.search_rank > 0))
|
||||
|
||||
boost_search_results(results, for_user)
|
||||
end
|
||||
|
||||
defp fts_search_subquery(query) do
|
||||
processed_query =
|
||||
query
|
||||
|> String.replace(~r/\W+/, " ")
|
||||
|> String.trim()
|
||||
|> String.split()
|
||||
|> Enum.map(&(&1 <> ":*"))
|
||||
|> Enum.join(" | ")
|
||||
|
||||
from(
|
||||
u in User,
|
||||
select_merge: %{
|
||||
search_rank:
|
||||
fragment(
|
||||
"""
|
||||
ts_rank_cd(
|
||||
setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
|
||||
setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'),
|
||||
to_tsquery('simple', ?),
|
||||
32
|
||||
)
|
||||
""",
|
||||
u.nickname,
|
||||
u.name,
|
||||
^processed_query
|
||||
)
|
||||
},
|
||||
where:
|
||||
fragment(
|
||||
"""
|
||||
(setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
|
||||
setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?)
|
||||
""",
|
||||
u.nickname,
|
||||
u.name,
|
||||
^processed_query
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
defp trigram_search_subquery(query) do
|
||||
from(
|
||||
u in User,
|
||||
select_merge: %{
|
||||
search_rank:
|
||||
fragment(
|
||||
"similarity(?, trim(? || ' ' || coalesce(?, '')))",
|
||||
^query,
|
||||
u.nickname,
|
||||
u.name
|
||||
)
|
||||
},
|
||||
where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^query)
|
||||
)
|
||||
end
|
||||
|
||||
defp boost_search_results(results, nil), do: results
|
||||
|
||||
defp boost_search_results(results, for_user) do
|
||||
friends_ids = get_friends_ids(for_user)
|
||||
followers_ids = get_followers_ids(for_user)
|
||||
|
||||
Enum.map(
|
||||
results,
|
||||
fn u ->
|
||||
search_rank_coef =
|
||||
cond do
|
||||
u.id in friends_ids ->
|
||||
1.2
|
||||
|
||||
u.id in followers_ids ->
|
||||
1.1
|
||||
|
||||
true ->
|
||||
1
|
||||
end
|
||||
|
||||
Map.put(u, :search_rank, u.search_rank * search_rank_coef)
|
||||
end
|
||||
)
|
||||
|> Enum.sort_by(&(-&1.search_rank))
|
||||
end
|
||||
|
||||
def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
|
||||
|
|
@ -833,7 +929,7 @@ defmodule Pleroma.User do
|
|||
def active_local_user_query do
|
||||
from(
|
||||
u in local_user_query(),
|
||||
where: fragment("?->'deactivated' @> 'false'", u.info)
|
||||
where: fragment("not (?->'deactivated' @> 'true')", u.info)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -1023,7 +1119,7 @@ defmodule Pleroma.User do
|
|||
end)
|
||||
|
||||
bio
|
||||
|> CommonUtils.format_input(mentions, tags, "text/plain")
|
||||
|> CommonUtils.format_input(mentions, tags, "text/plain", user_links: [format: :full])
|
||||
|> Formatter.emojify(emoji)
|
||||
end
|
||||
|
||||
|
|
@ -1074,6 +1170,16 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def local_nickname(nickname_or_mention) do
|
||||
nickname_or_mention
|
||||
|> full_nickname()
|
||||
|> String.split("@")
|
||||
|> hd()
|
||||
end
|
||||
|
||||
def full_nickname(nickname_or_mention),
|
||||
do: String.trim_leading(nickname_or_mention, "@")
|
||||
|
||||
def error_user(ap_id) do
|
||||
%User{
|
||||
name: ap_id,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ defmodule Pleroma.User.Info do
|
|||
field(:hub, :string, default: nil)
|
||||
field(:salmon, :string, default: nil)
|
||||
field(:hide_network, :boolean, default: false)
|
||||
field(:pinned_activities, {:array, :integer}, default: [])
|
||||
field(:pinned_activities, {:array, :string}, default: [])
|
||||
|
||||
# Found in the wild
|
||||
# ap_id -> Where is this used?
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
def stream_out(activity) do
|
||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
if activity.data["type"] in ["Create", "Announce"] do
|
||||
if activity.data["type"] in ["Create", "Announce", "Delete"] do
|
||||
Pleroma.Web.Streamer.stream("user", activity)
|
||||
Pleroma.Web.Streamer.stream("list", activity)
|
||||
|
||||
|
|
@ -103,16 +103,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Pleroma.Web.Streamer.stream("public:local", activity)
|
||||
end
|
||||
|
||||
activity.data["object"]
|
||||
|> Map.get("tag", [])
|
||||
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|
||||
|> Enum.map(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
|
||||
if activity.data["type"] in ["Create"] do
|
||||
activity.data["object"]
|
||||
|> Map.get("tag", [])
|
||||
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|
||||
|> Enum.map(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
|
||||
|
||||
if activity.data["object"]["attachment"] != [] do
|
||||
Pleroma.Web.Streamer.stream("public:media", activity)
|
||||
if activity.data["object"]["attachment"] != [] do
|
||||
Pleroma.Web.Streamer.stream("public:media", activity)
|
||||
|
||||
if activity.local do
|
||||
Pleroma.Web.Streamer.stream("public:local:media", activity)
|
||||
if activity.local do
|
||||
Pleroma.Web.Streamer.stream("public:local:media", activity)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
|
|
@ -138,8 +140,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
additional
|
||||
),
|
||||
{:ok, activity} <- insert(create_data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
{:ok, _actor} <- User.increase_note_count(actor) do
|
||||
# Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
|
||||
{:ok, _actor} <- User.increase_note_count(actor),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -224,10 +227,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
%User{ap_id: _} = user,
|
||||
%Object{data: %{"id" => _}} = object,
|
||||
activity_id \\ nil,
|
||||
local \\ true
|
||||
local \\ true,
|
||||
public \\ true
|
||||
) do
|
||||
with true <- is_public?(object),
|
||||
announce_data <- make_announce_data(user, object, activity_id),
|
||||
announce_data <- make_announce_data(user, object, activity_id, public),
|
||||
{:ok, activity} <- insert(announce_data, local),
|
||||
{:ok, object} <- add_announce_to_object(activity, object),
|
||||
:ok <- maybe_federate(activity) do
|
||||
|
|
@ -285,8 +289,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
with {:ok, _} <- Object.delete(object),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
{:ok, _actor} <- User.decrease_note_count(user) do
|
||||
# Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
|
||||
{:ok, _actor} <- User.decrease_note_count(user),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -405,6 +410,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
defp restrict_since(query, %{"since_id" => ""}), do: query
|
||||
|
||||
defp restrict_since(query, %{"since_id" => since_id}) do
|
||||
from(activity in query, where: activity.id > ^since_id)
|
||||
end
|
||||
|
|
@ -460,6 +467,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_local(query, _), do: query
|
||||
|
||||
defp restrict_max(query, %{"max_id" => ""}), do: query
|
||||
|
||||
defp restrict_max(query, %{"max_id" => max_id}) do
|
||||
from(activity in query, where: activity.id < ^max_id)
|
||||
end
|
||||
|
|
@ -796,13 +805,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}) do
|
||||
false
|
||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||
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
|
||||
"https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || []))
|
||||
end
|
||||
|
||||
def is_public?(activity) do
|
||||
"https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++
|
||||
(activity.data["cc"] || []))
|
||||
def is_private?(activity) do
|
||||
!is_public?(activity) && Enum.any?(activity.data["to"], &String.contains?(&1, "/followers"))
|
||||
end
|
||||
|
||||
def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true
|
||||
def is_direct?(%Object{data: %{"directMessage" => true}}), do: true
|
||||
|
||||
def is_direct?(activity) do
|
||||
!is_public?(activity) && !is_private?(activity)
|
||||
end
|
||||
|
||||
def visible_for_user?(activity, nil) do
|
||||
|
|
|
|||
57
lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
Normal file
57
lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
||||
alias Pleroma.User
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
# XXX: this should become User.normalize_by_ap_id() or similar, really.
|
||||
defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id)
|
||||
defp normalize_by_ap_id(uri) when is_binary(uri), do: User.get_cached_by_ap_id(uri)
|
||||
defp normalize_by_ap_id(_), do: nil
|
||||
|
||||
defp score_nickname("followbot@" <> _), do: 1.0
|
||||
defp score_nickname("federationbot@" <> _), do: 1.0
|
||||
defp score_nickname("federation_bot@" <> _), do: 1.0
|
||||
defp score_nickname(_), do: 0.0
|
||||
|
||||
defp score_displayname("federation bot"), do: 1.0
|
||||
defp score_displayname("federationbot"), do: 1.0
|
||||
defp score_displayname("fedibot"), do: 1.0
|
||||
defp score_displayname(_), do: 0.0
|
||||
|
||||
defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
|
||||
nick_score =
|
||||
nickname
|
||||
|> String.downcase()
|
||||
|> score_nickname()
|
||||
|
||||
name_score =
|
||||
displayname
|
||||
|> String.downcase()
|
||||
|> score_displayname()
|
||||
|
||||
nick_score + name_score
|
||||
end
|
||||
|
||||
defp determine_if_followbot(_), do: 0.0
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Follow", "actor" => actor_id} = message) do
|
||||
%User{} = actor = normalize_by_ap_id(actor_id)
|
||||
|
||||
score = determine_if_followbot(actor)
|
||||
|
||||
# TODO: scan biography data for keywords and score it somehow.
|
||||
if score < 0.8 do
|
||||
{:ok, message}
|
||||
else
|
||||
{:reject, nil}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def filter(message), do: {:ok, message}
|
||||
end
|
||||
|
|
@ -40,7 +40,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
|||
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||
with %User{} = user <- get_actor(),
|
||||
%Object{} = object <- Object.normalize(activity.data["object"]["id"]) do
|
||||
ActivityPub.announce(user, object)
|
||||
ActivityPub.announce(user, object, nil, true, false)
|
||||
else
|
||||
e -> Logger.error("error: #{inspect(e)}")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -93,12 +93,47 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end
|
||||
end
|
||||
|
||||
def fix_addressing(map) do
|
||||
map
|
||||
def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, explicit_mentions) do
|
||||
explicit_to =
|
||||
to
|
||||
|> Enum.filter(fn x -> x in explicit_mentions end)
|
||||
|
||||
explicit_cc =
|
||||
to
|
||||
|> Enum.filter(fn x -> x not in explicit_mentions end)
|
||||
|
||||
final_cc =
|
||||
(cc ++ explicit_cc)
|
||||
|> Enum.uniq()
|
||||
|
||||
object
|
||||
|> Map.put("to", explicit_to)
|
||||
|> Map.put("cc", final_cc)
|
||||
end
|
||||
|
||||
def fix_explicit_addressing(object, _explicit_mentions), do: object
|
||||
|
||||
# if directMessage flag is set to true, leave the addressing alone
|
||||
def fix_explicit_addressing(%{"directMessage" => true} = object), do: object
|
||||
|
||||
def fix_explicit_addressing(object) do
|
||||
explicit_mentions =
|
||||
object
|
||||
|> Utils.determine_explicit_mentions()
|
||||
|
||||
explicit_mentions = explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
|
||||
object
|
||||
|> fix_explicit_addressing(explicit_mentions)
|
||||
end
|
||||
|
||||
def fix_addressing(object) do
|
||||
object
|
||||
|> fix_addressing_list("to")
|
||||
|> fix_addressing_list("cc")
|
||||
|> fix_addressing_list("bto")
|
||||
|> fix_addressing_list("bcc")
|
||||
|> fix_explicit_addressing
|
||||
end
|
||||
|
||||
def fix_actor(%{"attributedTo" => actor} = object) do
|
||||
|
|
@ -141,7 +176,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
case fetch_obj_helper(in_reply_to_id) do
|
||||
{:ok, replied_object} ->
|
||||
with %Activity{} = activity <-
|
||||
Activity.get_create_activity_by_object_ap_id(replied_object.data["id"]) do
|
||||
Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
|
||||
object
|
||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
||||
|
|
@ -334,7 +369,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
Map.put(data, "actor", actor)
|
||||
|> fix_addressing
|
||||
|
||||
with nil <- Activity.get_create_activity_by_object_ap_id(object["id"]),
|
||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||
%User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do
|
||||
object = fix_object(data["object"])
|
||||
|
||||
|
|
@ -348,6 +383,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
additional:
|
||||
Map.take(data, [
|
||||
"cc",
|
||||
"directMessage",
|
||||
"id"
|
||||
])
|
||||
}
|
||||
|
|
@ -451,7 +487,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
with actor <- get_actor(data),
|
||||
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
|
||||
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do
|
||||
public <- ActivityPub.is_public?(data),
|
||||
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
|
||||
{:ok, activity}
|
||||
else
|
||||
_e -> :error
|
||||
|
|
@ -863,15 +900,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|
||||
maybe_retire_websub(user.ap_id)
|
||||
|
||||
# Only do this for recent activties, don't go through the whole db.
|
||||
# Only look at the last 1000 activities.
|
||||
since = (Repo.aggregate(Activity, :max, :id) || 0) - 1_000
|
||||
|
||||
q =
|
||||
from(
|
||||
a in Activity,
|
||||
where: ^old_follower_address in a.recipients,
|
||||
where: a.id > ^since,
|
||||
update: [
|
||||
set: [
|
||||
recipients:
|
||||
|
|
|
|||
|
|
@ -25,6 +25,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
Map.put(params, "actor", get_ap_id(params["actor"]))
|
||||
end
|
||||
|
||||
def determine_explicit_mentions(%{"tag" => tag} = _object) when is_list(tag) do
|
||||
tag
|
||||
|> Enum.filter(fn x -> is_map(x) end)
|
||||
|> Enum.filter(fn x -> x["type"] == "Mention" end)
|
||||
|> Enum.map(fn x -> x["href"] end)
|
||||
end
|
||||
|
||||
def determine_explicit_mentions(%{"tag" => tag} = object) when is_map(tag) do
|
||||
Map.put(object, "tag", [tag])
|
||||
|> determine_explicit_mentions()
|
||||
end
|
||||
|
||||
def determine_explicit_mentions(_), do: []
|
||||
|
||||
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
|
||||
|
|
@ -198,7 +212,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
# Update activities that already had this. Could be done in a seperate process.
|
||||
# Alternatively, just don't do this and fetch the current object each time. Most
|
||||
# could probably be taken from cache.
|
||||
relevant_activities = Activity.all_by_object_ap_id(id)
|
||||
relevant_activities = Activity.get_all_create_by_object_ap_id(id)
|
||||
|
||||
Enum.map(relevant_activities, fn activity ->
|
||||
new_activity_data = activity.data |> Map.put("object", object.data)
|
||||
|
|
@ -386,9 +400,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"""
|
||||
# for relayed messages, we only want to send to subscribers
|
||||
def make_announce_data(
|
||||
%User{ap_id: ap_id, nickname: nil} = user,
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Object{data: %{"id" => id}} = object,
|
||||
activity_id
|
||||
activity_id,
|
||||
false
|
||||
) do
|
||||
data = %{
|
||||
"type" => "Announce",
|
||||
|
|
@ -405,7 +420,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
def make_announce_data(
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Object{data: %{"id" => id}} = object,
|
||||
activity_id
|
||||
activity_id,
|
||||
true
|
||||
) do
|
||||
data = %{
|
||||
"type" => "Announce",
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"partOf" => iri,
|
||||
"totalItems" => info.note_count,
|
||||
"orderedItems" => collection,
|
||||
"next" => "#{iri}?max_id=#{min_id - 1}"
|
||||
"next" => "#{iri}?max_id=#{min_id}"
|
||||
}
|
||||
|
||||
if max_qid == nil do
|
||||
|
|
@ -207,7 +207,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"partOf" => iri,
|
||||
"totalItems" => -1,
|
||||
"orderedItems" => collection,
|
||||
"next" => "#{iri}?max_id=#{min_id - 1}"
|
||||
"next" => "#{iri}?max_id=#{min_id}"
|
||||
}
|
||||
|
||||
if max_qid == nil do
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc}
|
||||
additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
|
||||
})
|
||||
|
||||
res
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|
||||
# This is a hack for twidere.
|
||||
def get_by_id_or_ap_id(id) do
|
||||
activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id)
|
||||
activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
|
||||
|
||||
activity &&
|
||||
if activity.data["type"] == "Create" do
|
||||
activity
|
||||
else
|
||||
Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
||||
Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -116,16 +116,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
Enum.join([text | attachment_text], "<br>")
|
||||
end
|
||||
|
||||
def format_input(text, mentions, tags, format, options \\ [])
|
||||
|
||||
@doc """
|
||||
Formatting text to plain text.
|
||||
"""
|
||||
def format_input(text, mentions, tags, "text/plain") do
|
||||
def format_input(text, mentions, tags, "text/plain", options) do
|
||||
text
|
||||
|> Formatter.html_escape("text/plain")
|
||||
|> String.replace(~r/\r?\n/, "<br>")
|
||||
|> (&{[], &1}).()
|
||||
|> Formatter.add_links()
|
||||
|> Formatter.add_user_links(mentions)
|
||||
|> Formatter.add_user_links(mentions, options[:user_links] || [])
|
||||
|> Formatter.add_hashtag_links(tags)
|
||||
|> Formatter.finalize()
|
||||
end
|
||||
|
|
@ -133,24 +135,24 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
@doc """
|
||||
Formatting text to html.
|
||||
"""
|
||||
def format_input(text, mentions, _tags, "text/html") do
|
||||
def format_input(text, mentions, _tags, "text/html", options) do
|
||||
text
|
||||
|> Formatter.html_escape("text/html")
|
||||
|> (&{[], &1}).()
|
||||
|> Formatter.add_user_links(mentions)
|
||||
|> Formatter.add_user_links(mentions, options[:user_links] || [])
|
||||
|> Formatter.finalize()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Formatting text to markdown.
|
||||
"""
|
||||
def format_input(text, mentions, tags, "text/markdown") do
|
||||
def format_input(text, mentions, tags, "text/markdown", options) do
|
||||
text
|
||||
|> Formatter.mentions_escape(mentions)
|
||||
|> Earmark.as_html!()
|
||||
|> Formatter.html_escape("text/html")
|
||||
|> (&{[], &1}).()
|
||||
|> Formatter.add_user_links(mentions)
|
||||
|> Formatter.add_user_links(mentions, options[:user_links] || [])
|
||||
|> Formatter.add_hashtag_links(tags)
|
||||
|> Formatter.finalize()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
|
|
@ -386,7 +386,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
|
|
@ -395,7 +395,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
|
|
@ -743,8 +743,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
fetched =
|
||||
if Regex.match?(~r/https?:/, query) do
|
||||
with {:ok, object} <- ActivityPub.fetch_object_from_id(query),
|
||||
%Activity{} = activity <-
|
||||
Activity.get_create_activity_by_object_ap_id(object.data["id"]),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- ActivityPub.visible_for_user?(activity, user) do
|
||||
[activity]
|
||||
else
|
||||
|
|
@ -771,7 +770,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, params["resolve"] == "true")
|
||||
accounts = User.search(query, params["resolve"] == "true", user)
|
||||
|
||||
statuses = status_search(user, query)
|
||||
|
||||
|
|
@ -795,7 +794,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, params["resolve"] == "true")
|
||||
accounts = User.search(query, params["resolve"] == "true", user)
|
||||
|
||||
statuses = status_search(user, query)
|
||||
|
||||
|
|
@ -816,7 +815,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||
accounts = User.search(query, params["resolve"] == "true")
|
||||
accounts = User.search(query, params["resolve"] == "true", user)
|
||||
|
||||
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
||||
|
||||
|
|
@ -1138,7 +1137,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
def render_notification(user, %{id: id, activity: activity, inserted_at: created_at} = _params) do
|
||||
actor = User.get_cached_by_ap_id(activity.data["actor"])
|
||||
parent_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
||||
parent_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
mastodon_type = Activity.mastodon_notification_type(activity)
|
||||
|
||||
response = %{
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
nil
|
||||
end)
|
||||
|> Enum.filter(& &1)
|
||||
|> Activity.create_activity_by_object_id_query()
|
||||
|> Activity.create_by_object_ap_id()
|
||||
|> Repo.all()
|
||||
|> Enum.reduce(%{}, fn activity, acc ->
|
||||
Map.put(acc, activity.data["object"]["id"], activity)
|
||||
|
|
@ -64,7 +64,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
user = get_user(activity.data["actor"])
|
||||
created_at = Utils.to_masto_date(activity.data["published"])
|
||||
|
||||
reblogged = Activity.get_create_activity_by_object_ap_id(object)
|
||||
reblogged = Activity.get_create_by_object_ap_id(object)
|
||||
reblogged = render("status.json", Map.put(opts, :activity, reblogged))
|
||||
|
||||
mentions =
|
||||
|
|
@ -209,7 +209,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
def get_reply_to(%{data: %{"object" => object}}, _) do
|
||||
if object["inReplyTo"] && object["inReplyTo"] != "" do
|
||||
Activity.get_create_activity_by_object_ap_id(object["inReplyTo"])
|
||||
Activity.get_create_by_object_ap_id(object["inReplyTo"])
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
|
@ -231,6 +231,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
Enum.any?(to, &String.contains?(&1, "/followers")) ->
|
||||
"private"
|
||||
|
||||
length(cc) > 0 ->
|
||||
"private"
|
||||
|
||||
true ->
|
||||
"direct"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
|
|||
field(:token, :string)
|
||||
field(:valid_until, :naive_datetime)
|
||||
field(:used, :boolean, default: false)
|
||||
belongs_to(:user, Pleroma.User)
|
||||
belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
|
||||
belongs_to(:app, App)
|
||||
|
||||
timestamps()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
field(:token, :string)
|
||||
field(:refresh_token, :string)
|
||||
field(:valid_until, :naive_datetime)
|
||||
belongs_to(:user, Pleroma.User)
|
||||
belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
|
||||
belongs_to(:app, App)
|
||||
|
||||
timestamps()
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
|||
_in_reply_to = get_in_reply_to(activity.data)
|
||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||
|
||||
retweeted_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
||||
retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"])
|
||||
|
||||
retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
|
|||
end
|
||||
|
||||
def fetch_replied_to_activity(entry, inReplyTo) do
|
||||
with %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(inReplyTo) do
|
||||
with %Activity{} = activity <- Activity.get_create_by_object_ap_id(inReplyTo) do
|
||||
activity
|
||||
else
|
||||
_e ->
|
||||
|
|
@ -103,7 +103,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
|
|||
# TODO: Clean this up a bit.
|
||||
def handle_note(entry, doc \\ nil) do
|
||||
with id <- XML.string_from_xpath("//id", entry),
|
||||
activity when is_nil(activity) <- Activity.get_create_activity_by_object_ap_id(id),
|
||||
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id(id),
|
||||
[author] <- :xmerl_xpath.string('//author[1]', doc),
|
||||
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
||||
content_html <- OStatus.get_content(entry),
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ defmodule Pleroma.Web.OStatus do
|
|||
Logger.debug("Trying to get entry from db")
|
||||
|
||||
with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
else
|
||||
_ ->
|
||||
|
|
|
|||
|
|
@ -93,8 +93,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
ActivityPubController.call(conn, :object)
|
||||
else
|
||||
with id <- o_status_url(conn, :object, uuid),
|
||||
{_, %Activity{} = activity} <-
|
||||
{:activity, Activity.get_create_activity_by_object_ap_id(id)},
|
||||
{_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id(id)},
|
||||
{_, true} <- {:public?, ActivityPub.is_public?(activity)},
|
||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
case get_format(conn) do
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.Push.Subscription do
|
|||
alias Pleroma.Web.Push.Subscription
|
||||
|
||||
schema "push_subscriptions" do
|
||||
belongs_to(:user, User)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
belongs_to(:token, Token)
|
||||
field(:endpoint, :string)
|
||||
field(:key_p256dh, :string)
|
||||
|
|
|
|||
|
|
@ -107,6 +107,11 @@ defmodule Pleroma.Web.Router do
|
|||
get("/captcha", UtilController, :captcha)
|
||||
end
|
||||
|
||||
scope "/api/pleroma", Pleroma.Web do
|
||||
pipe_through(:pleroma_api)
|
||||
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
||||
end
|
||||
|
||||
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
pipe_through(:admin_api)
|
||||
delete("/user", AdminAPIController, :user_delete)
|
||||
|
|
|
|||
|
|
@ -205,6 +205,15 @@ defmodule Pleroma.Web.Streamer do
|
|||
end)
|
||||
end
|
||||
|
||||
def push_to_socket(topics, topic, %Activity{id: id, data: %{"type" => "Delete"}}) do
|
||||
Enum.each(topics[topic] || [], fn socket ->
|
||||
send(
|
||||
socket.transport_pid,
|
||||
{:text, %{event: "delete", payload: to_string(id)} |> Jason.encode!()}
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
def push_to_socket(topics, topic, item) do
|
||||
Enum.each(topics[topic] || [], fn socket ->
|
||||
# Get the current user so we have up-to-date blocks etc.
|
||||
|
|
|
|||
|
|
@ -70,14 +70,14 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
|
||||
def repeat(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _announce, %{data: %{"id" => id}}} <- CommonAPI.repeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def unrepeat(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
@ -92,14 +92,14 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
|
||||
def fav(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def unfav(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _unfav, _fav, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -265,8 +265,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
|
||||
def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
id = String.to_integer(id)
|
||||
|
||||
with context when is_binary(context) <- TwitterAPI.conversation_id_to_context(id),
|
||||
activities <-
|
||||
ActivityPub.fetch_activities_for_context(context, %{
|
||||
|
|
@ -330,54 +328,57 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
|
||||
def get_by_id_or_ap_id(id) do
|
||||
activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id)
|
||||
activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
|
||||
|
||||
if activity.data["type"] == "Create" do
|
||||
activity
|
||||
else
|
||||
Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
||||
Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
end
|
||||
end
|
||||
|
||||
def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||
{:ok, activity} <- TwitterAPI.fav(user, id) do
|
||||
with {:ok, activity} <- TwitterAPI.fav(user, id) do
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|> render("activity.json", %{activity: activity, for: user})
|
||||
else
|
||||
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||
end
|
||||
end
|
||||
|
||||
def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||
{:ok, activity} <- TwitterAPI.unfav(user, id) do
|
||||
with {:ok, activity} <- TwitterAPI.unfav(user, id) do
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|> render("activity.json", %{activity: activity, for: user})
|
||||
else
|
||||
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||
end
|
||||
end
|
||||
|
||||
def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||
{:ok, activity} <- TwitterAPI.repeat(user, id) do
|
||||
with {:ok, activity} <- TwitterAPI.repeat(user, id) do
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|> render("activity.json", %{activity: activity, for: user})
|
||||
else
|
||||
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||
end
|
||||
end
|
||||
|
||||
def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||
{:ok, activity} <- TwitterAPI.unrepeat(user, id) do
|
||||
with {:ok, activity} <- TwitterAPI.unrepeat(user, id) do
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|> render("activity.json", %{activity: activity, for: user})
|
||||
else
|
||||
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||
end
|
||||
end
|
||||
|
||||
def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||
{:ok, activity} <- TwitterAPI.pin(user, id) do
|
||||
with {:ok, activity} <- TwitterAPI.pin(user, id) do
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|> render("activity.json", %{activity: activity, for: user})
|
||||
|
|
@ -388,8 +389,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
|
||||
def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
||||
{:ok, activity} <- TwitterAPI.unpin(user, id) do
|
||||
with {:ok, activity} <- TwitterAPI.unpin(user, id) do
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|> render("activity.json", %{activity: activity, for: user})
|
||||
|
|
@ -556,7 +556,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
|
||||
def approve_friend_request(conn, %{"user_id" => uid} = _params) do
|
||||
with followed <- conn.assigns[:user],
|
||||
uid when is_number(uid) <- String.to_integer(uid),
|
||||
%User{} = follower <- Repo.get(User, uid),
|
||||
{:ok, follower} <- User.maybe_follow(follower, followed),
|
||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
|
|
@ -578,7 +577,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
|
||||
def deny_friend_request(conn, %{"user_id" => uid} = _params) do
|
||||
with followed <- conn.assigns[:user],
|
||||
uid when is_number(uid) <- String.to_integer(uid),
|
||||
%User{} = follower <- Repo.get(User, uid),
|
||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||
|
|
@ -675,7 +673,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
|
||||
def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
|
||||
users = User.search(query, true)
|
||||
users = User.search(query, true, user)
|
||||
|
||||
conn
|
||||
|> put_view(UserView)
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do
|
||||
user = get_user(activity.data["actor"], opts)
|
||||
created_at = activity.data["published"] |> Utils.date_to_asctime()
|
||||
announced_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
||||
announced_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
|
||||
text = "#{user.nickname} retweeted a status."
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
|
||||
def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do
|
||||
user = get_user(activity.data["actor"], opts)
|
||||
liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
||||
liked_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
liked_activity_id = if liked_activity, do: liked_activity.id, else: nil
|
||||
|
||||
created_at =
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||
"locked" => user.info.locked,
|
||||
"default_scope" => user.info.default_scope,
|
||||
"no_rich_text" => user.info.no_rich_text,
|
||||
"hide_network" => user.info.hide_network,
|
||||
"fields" => fields,
|
||||
|
||||
# Pleroma extension
|
||||
|
|
|
|||
25
lib/pleroma/web/uploader_controller.ex
Normal file
25
lib/pleroma/web/uploader_controller.ex
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
defmodule Pleroma.Web.UploaderController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Uploaders.Uploader
|
||||
|
||||
def callback(conn, params = %{"upload_path" => upload_path}) do
|
||||
process_callback(conn, :global.whereis_name({Uploader, upload_path}), params)
|
||||
end
|
||||
|
||||
def callbacks(conn, _) do
|
||||
send_resp(conn, 400, "bad request")
|
||||
end
|
||||
|
||||
defp process_callback(conn, pid, params) when is_pid(pid) do
|
||||
send(pid, {Uploader, self(), conn, params})
|
||||
|
||||
receive do
|
||||
{Uploader, conn} -> conn
|
||||
end
|
||||
end
|
||||
|
||||
defp process_callback(conn, _, _) do
|
||||
send_resp(conn, 400, "bad request")
|
||||
end
|
||||
end
|
||||
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.Websub.WebsubClientSubscription do
|
|||
field(:state, :string)
|
||||
field(:subscribers, {:array, :string}, default: [])
|
||||
field(:hub, :string)
|
||||
belongs_to(:user, User)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue