Merge branch 'develop' into 'oembed_provider'
# Conflicts: # lib/pleroma/activity.ex
This commit is contained in:
commit
c9b418e547
@ -105,6 +105,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||||||
)
|
)
|
||||||
|
|
||||||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
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)
|
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
||||||
|
|
||||||
result_config =
|
result_config =
|
||||||
@ -120,6 +121,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||||||
dbpass: dbpass,
|
dbpass: dbpass,
|
||||||
version: Pleroma.Mixfile.project() |> Keyword.get(:version),
|
version: Pleroma.Mixfile.project() |> Keyword.get(:version),
|
||||||
secret: secret,
|
secret: secret,
|
||||||
|
signing_salt: signing_salt,
|
||||||
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
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)
|
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,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
|
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
|
||||||
secret_key_base: "<%= secret %>"
|
secret_key_base: "<%= secret %>",
|
||||||
|
signing_salt: "<%= signing_salt %>"
|
||||||
|
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
name: "<%= name %>",
|
name: "<%= name %>",
|
||||||
|
@ -10,7 +10,7 @@ defmodule Pleroma.PasswordResetToken do
|
|||||||
alias Pleroma.{User, PasswordResetToken, Repo}
|
alias Pleroma.{User, PasswordResetToken, Repo}
|
||||||
|
|
||||||
schema "password_reset_tokens" do
|
schema "password_reset_tokens" do
|
||||||
belongs_to(:user, User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
field(:used, :boolean, default: false)
|
field(:used, :boolean, default: false)
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ defmodule Pleroma.Activity do
|
|||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
@primary_key {:id, Pleroma.FlakeId, autogenerate: true}
|
||||||
|
|
||||||
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
||||||
@mastodon_notification_types %{
|
@mastodon_notification_types %{
|
||||||
@ -40,25 +41,7 @@ defmodule Pleroma.Activity do
|
|||||||
Repo.get(Activity, id)
|
Repo.get(Activity, id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO:
|
def by_object_ap_id(ap_id) do
|
||||||
# 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
|
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
where:
|
where:
|
||||||
@ -71,12 +54,7 @@ defmodule Pleroma.Activity do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Wrong name plz fix thx
|
def create_by_object_ap_id(ap_ids) when is_list(ap_ids) do
|
||||||
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
|
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
where:
|
where:
|
||||||
@ -90,19 +68,37 @@ defmodule Pleroma.Activity do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_create_activity_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
def create_by_object_ap_id(ap_id) do
|
||||||
create_activity_by_object_id_query([ap_id])
|
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()
|
|> Repo.one()
|
||||||
end
|
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(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(ap_id) when is_binary(ap_id), do: Activity.get_by_ap_id(ap_id)
|
||||||
def normalize(_), do: nil
|
def normalize(_), do: nil
|
||||||
|
|
||||||
def get_in_reply_to_activity(%Activity{data: %{"object" => %{"inReplyTo" => ap_id}}}) do
|
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
|
end
|
||||||
|
|
||||||
def get_in_reply_to_activity(_), do: nil
|
def get_in_reply_to_activity(_), do: nil
|
||||||
|
@ -99,6 +99,7 @@ defmodule Pleroma.Application do
|
|||||||
],
|
],
|
||||||
id: :cachex_idem
|
id: :cachex_idem
|
||||||
),
|
),
|
||||||
|
worker(Pleroma.FlakeId, []),
|
||||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||||
worker(Pleroma.Web.Federator, []),
|
worker(Pleroma.Web.Federator, []),
|
||||||
worker(Pleroma.Stats, []),
|
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}
|
alias Pleroma.{User, Repo}
|
||||||
|
|
||||||
schema "filters" do
|
schema "filters" do
|
||||||
belongs_to(:user, User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
field(:filter_id, :integer)
|
field(:filter_id, :integer)
|
||||||
field(:hide, :boolean, default: false)
|
field(:hide, :boolean, default: false)
|
||||||
field(:whole_word, :boolean, default: true)
|
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
|
end
|
||||||
|
|
||||||
@doc "Adds the links to mentioned users"
|
@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 =
|
||||||
mentions
|
mentions
|
||||||
|> Enum.sort_by(fn {name, _} -> -String.length(name) end)
|
|> Enum.sort_by(fn {name, _} -> -String.length(name) end)
|
||||||
@ -152,12 +152,16 @@ defmodule Pleroma.Formatter do
|
|||||||
ap_id
|
ap_id
|
||||||
end
|
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,
|
{uuid,
|
||||||
"<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>@<span>#{
|
"<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>" <>
|
||||||
short_match
|
"@<span>#{nickname}</span></a></span>"}
|
||||||
}</span></a></span>"}
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{subs, uuid_text}
|
{subs, uuid_text}
|
||||||
|
@ -8,7 +8,7 @@ defmodule Pleroma.List do
|
|||||||
alias Pleroma.{User, Repo, Activity}
|
alias Pleroma.{User, Repo, Activity}
|
||||||
|
|
||||||
schema "lists" do
|
schema "lists" do
|
||||||
belongs_to(:user, Pleroma.User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
field(:title, :string)
|
field(:title, :string)
|
||||||
field(:following, {:array, :string}, default: [])
|
field(:following, {:array, :string}, default: [])
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ defmodule Pleroma.Notification do
|
|||||||
|
|
||||||
schema "notifications" do
|
schema "notifications" do
|
||||||
field(:seen, :boolean, default: false)
|
field(:seen, :boolean, default: false)
|
||||||
belongs_to(:user, Pleroma.User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
belongs_to(:activity, Pleroma.Activity)
|
belongs_to(:activity, Activity, type: Pleroma.FlakeId)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
@ -96,7 +96,7 @@ defmodule Pleroma.Notification do
|
|||||||
end
|
end
|
||||||
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
|
when type in ["Create", "Like", "Announce", "Follow"] do
|
||||||
users = get_notified_from_activity(activity)
|
users = get_notified_from_activity(activity)
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ defmodule Pleroma.Object do
|
|||||||
|
|
||||||
def delete(%Object{data: %{"id" => id}} = object) do
|
def delete(%Object{data: %{"id" => id}} = object) do
|
||||||
with {:ok, _obj} = swap_object_with_tombstone(object),
|
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, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
end
|
end
|
||||||
|
@ -275,11 +275,24 @@ defmodule Pleroma.ReverseProxy do
|
|||||||
|
|
||||||
defp build_resp_cache_headers(headers, _opts) do
|
defp build_resp_cache_headers(headers, _opts) do
|
||||||
has_cache? = Enum.any?(headers, fn {k, _} -> k in @resp_cache_headers end)
|
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
|
cond do
|
||||||
headers
|
has_cache? && has_cache_control? ->
|
||||||
else
|
headers
|
||||||
List.keystore(headers, "cache-control", 0, {"cache-control", @default_cache_control_header})
|
|
||||||
|
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
|
||||||
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
|
# 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
|
def get_file(file) do
|
||||||
config = Pleroma.Config.get([__MODULE__])
|
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,
|
{:ok,
|
||||||
{:url,
|
{:url,
|
||||||
Path.join([
|
Path.join([
|
||||||
Keyword.fetch!(config, :public_endpoint),
|
Keyword.fetch!(config, :public_endpoint),
|
||||||
Keyword.fetch!(config, :bucket),
|
bucket_with_namespace,
|
||||||
strict_encode(URI.decode(file))
|
strict_encode(URI.decode(file))
|
||||||
])}}
|
])}}
|
||||||
end
|
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.
|
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.
|
* `{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.
|
* `{: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()) ::
|
@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
|
def put_file(uploader, upload) do
|
||||||
case uploader.put_file(upload) do
|
case uploader.put_file(upload) do
|
||||||
:ok -> {:ok, {:file, upload.path}}
|
: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
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,6 +17,8 @@ defmodule Pleroma.User do
|
|||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@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])?)*$/
|
@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]+$/
|
@strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
|
||||||
@ -35,7 +37,7 @@ defmodule Pleroma.User do
|
|||||||
field(:avatar, :map)
|
field(:avatar, :map)
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
field(:follower_address, :string)
|
field(:follower_address, :string)
|
||||||
field(:search_distance, :float, virtual: true)
|
field(:search_rank, :float, virtual: true)
|
||||||
field(:tags, {:array, :string}, default: [])
|
field(:tags, {:array, :string}, default: [])
|
||||||
field(:last_refreshed_at, :naive_datetime)
|
field(:last_refreshed_at, :naive_datetime)
|
||||||
has_many(:notifications, Notification)
|
has_many(:notifications, Notification)
|
||||||
@ -473,8 +475,7 @@ defmodule Pleroma.User do
|
|||||||
def get_by_nickname(nickname) do
|
def get_by_nickname(nickname) do
|
||||||
Repo.get_by(User, nickname: nickname) ||
|
Repo.get_by(User, nickname: nickname) ||
|
||||||
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
|
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
|
||||||
[local_nickname, _] = String.split(nickname, "@")
|
Repo.get_by(User, nickname: local_nickname(nickname))
|
||||||
Repo.get_by(User, nickname: local_nickname)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -537,6 +538,12 @@ defmodule Pleroma.User do
|
|||||||
{:ok, Repo.all(q)}
|
{:ok, Repo.all(q)}
|
||||||
end
|
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
|
def get_friends_query(%User{id: id, following: following}, nil) do
|
||||||
from(
|
from(
|
||||||
u in User,
|
u in User,
|
||||||
@ -561,6 +568,12 @@ defmodule Pleroma.User do
|
|||||||
{:ok, Repo.all(q)}
|
{:ok, Repo.all(q)}
|
||||||
end
|
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
|
def get_follow_requests_query(%User{} = user) do
|
||||||
from(
|
from(
|
||||||
a in Activity,
|
a in Activity,
|
||||||
@ -692,37 +705,120 @@ defmodule Pleroma.User do
|
|||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search(query, resolve \\ false) do
|
def search(query, resolve \\ false, for_user \\ nil) do
|
||||||
# strip the beginning @ off if there is a query
|
# Strip the beginning @ off if there is a query
|
||||||
query = String.trim_leading(query, "@")
|
query = String.trim_leading(query, "@")
|
||||||
|
|
||||||
if resolve do
|
if resolve, do: User.get_or_fetch_by_nickname(query)
|
||||||
User.get_or_fetch_by_nickname(query)
|
|
||||||
end
|
|
||||||
|
|
||||||
inner =
|
fts_results = do_search(fts_search_subquery(query), for_user)
|
||||||
from(
|
|
||||||
u in User,
|
|
||||||
select_merge: %{
|
|
||||||
search_distance:
|
|
||||||
fragment(
|
|
||||||
"? <-> (? || coalesce(?, ''))",
|
|
||||||
^query,
|
|
||||||
u.nickname,
|
|
||||||
u.name
|
|
||||||
)
|
|
||||||
},
|
|
||||||
where: not is_nil(u.nickname)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
{: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 =
|
q =
|
||||||
from(
|
from(
|
||||||
s in subquery(inner),
|
s in subquery(subquery),
|
||||||
order_by: s.search_distance,
|
order_by: [desc: s.search_rank],
|
||||||
limit: 20
|
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
|
end
|
||||||
|
|
||||||
def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
|
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
|
def active_local_user_query do
|
||||||
from(
|
from(
|
||||||
u in local_user_query(),
|
u in local_user_query(),
|
||||||
where: fragment("?->'deactivated' @> 'false'", u.info)
|
where: fragment("not (?->'deactivated' @> 'true')", u.info)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1023,7 +1119,7 @@ defmodule Pleroma.User do
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
bio
|
bio
|
||||||
|> CommonUtils.format_input(mentions, tags, "text/plain")
|
|> CommonUtils.format_input(mentions, tags, "text/plain", user_links: [format: :full])
|
||||||
|> Formatter.emojify(emoji)
|
|> Formatter.emojify(emoji)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1074,6 +1170,16 @@ defmodule Pleroma.User do
|
|||||||
end
|
end
|
||||||
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
|
def error_user(ap_id) do
|
||||||
%User{
|
%User{
|
||||||
name: ap_id,
|
name: ap_id,
|
||||||
|
@ -31,7 +31,7 @@ defmodule Pleroma.User.Info do
|
|||||||
field(:hub, :string, default: nil)
|
field(:hub, :string, default: nil)
|
||||||
field(:salmon, :string, default: nil)
|
field(:salmon, :string, default: nil)
|
||||||
field(:hide_network, :boolean, default: false)
|
field(:hide_network, :boolean, default: false)
|
||||||
field(:pinned_activities, {:array, :integer}, default: [])
|
field(:pinned_activities, {:array, :string}, default: [])
|
||||||
|
|
||||||
# Found in the wild
|
# Found in the wild
|
||||||
# ap_id -> Where is this used?
|
# ap_id -> Where is this used?
|
||||||
|
@ -92,7 +92,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
def stream_out(activity) do
|
def stream_out(activity) do
|
||||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
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("user", activity)
|
||||||
Pleroma.Web.Streamer.stream("list", activity)
|
Pleroma.Web.Streamer.stream("list", activity)
|
||||||
|
|
||||||
@ -103,16 +103,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
Pleroma.Web.Streamer.stream("public:local", activity)
|
Pleroma.Web.Streamer.stream("public:local", activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
activity.data["object"]
|
if activity.data["type"] in ["Create"] do
|
||||||
|> Map.get("tag", [])
|
activity.data["object"]
|
||||||
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|
|> Map.get("tag", [])
|
||||||
|> Enum.map(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
|
|> 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
|
if activity.data["object"]["attachment"] != [] do
|
||||||
Pleroma.Web.Streamer.stream("public:media", activity)
|
Pleroma.Web.Streamer.stream("public:media", activity)
|
||||||
|
|
||||||
if activity.local do
|
if activity.local do
|
||||||
Pleroma.Web.Streamer.stream("public:local:media", activity)
|
Pleroma.Web.Streamer.stream("public:local:media", activity)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -138,8 +140,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
additional
|
additional
|
||||||
),
|
),
|
||||||
{:ok, activity} <- insert(create_data, local),
|
{:ok, activity} <- insert(create_data, local),
|
||||||
:ok <- maybe_federate(activity),
|
# 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) do
|
{:ok, _actor} <- User.increase_note_count(actor),
|
||||||
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -224,10 +227,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
%User{ap_id: _} = user,
|
%User{ap_id: _} = user,
|
||||||
%Object{data: %{"id" => _}} = object,
|
%Object{data: %{"id" => _}} = object,
|
||||||
activity_id \\ nil,
|
activity_id \\ nil,
|
||||||
local \\ true
|
local \\ true,
|
||||||
|
public \\ true
|
||||||
) do
|
) do
|
||||||
with true <- is_public?(object),
|
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, activity} <- insert(announce_data, local),
|
||||||
{:ok, object} <- add_announce_to_object(activity, object),
|
{:ok, object} <- add_announce_to_object(activity, object),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
@ -285,8 +289,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
|
|
||||||
with {:ok, _} <- Object.delete(object),
|
with {:ok, _} <- Object.delete(object),
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
:ok <- maybe_federate(activity),
|
# 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) do
|
{:ok, _actor} <- User.decrease_note_count(user),
|
||||||
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -405,6 +410,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp restrict_since(query, %{"since_id" => ""}), do: query
|
||||||
|
|
||||||
defp restrict_since(query, %{"since_id" => since_id}) do
|
defp restrict_since(query, %{"since_id" => since_id}) do
|
||||||
from(activity in query, where: activity.id > ^since_id)
|
from(activity in query, where: activity.id > ^since_id)
|
||||||
end
|
end
|
||||||
@ -460,6 +467,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
|
|
||||||
defp restrict_local(query, _), do: query
|
defp restrict_local(query, _), do: query
|
||||||
|
|
||||||
|
defp restrict_max(query, %{"max_id" => ""}), do: query
|
||||||
|
|
||||||
defp restrict_max(query, %{"max_id" => max_id}) do
|
defp restrict_max(query, %{"max_id" => max_id}) do
|
||||||
from(activity in query, where: activity.id < ^max_id)
|
from(activity in query, where: activity.id < ^max_id)
|
||||||
end
|
end
|
||||||
@ -796,13 +805,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}) do
|
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||||
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
|
end
|
||||||
|
|
||||||
def is_public?(activity) do
|
def is_private?(activity) do
|
||||||
"https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++
|
!is_public?(activity) && Enum.any?(activity.data["to"], &String.contains?(&1, "/followers"))
|
||||||
(activity.data["cc"] || []))
|
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
|
end
|
||||||
|
|
||||||
def visible_for_user?(activity, nil) do
|
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
|
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
with %User{} = user <- get_actor(),
|
with %User{} = user <- get_actor(),
|
||||||
%Object{} = object <- Object.normalize(activity.data["object"]["id"]) do
|
%Object{} = object <- Object.normalize(activity.data["object"]["id"]) do
|
||||||
ActivityPub.announce(user, object)
|
ActivityPub.announce(user, object, nil, true, false)
|
||||||
else
|
else
|
||||||
e -> Logger.error("error: #{inspect(e)}")
|
e -> Logger.error("error: #{inspect(e)}")
|
||||||
end
|
end
|
||||||
|
@ -93,12 +93,47 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_addressing(map) do
|
def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, explicit_mentions) do
|
||||||
map
|
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("to")
|
||||||
|> fix_addressing_list("cc")
|
|> fix_addressing_list("cc")
|
||||||
|> fix_addressing_list("bto")
|
|> fix_addressing_list("bto")
|
||||||
|> fix_addressing_list("bcc")
|
|> fix_addressing_list("bcc")
|
||||||
|
|> fix_explicit_addressing
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_actor(%{"attributedTo" => actor} = object) do
|
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
|
case fetch_obj_helper(in_reply_to_id) do
|
||||||
{:ok, replied_object} ->
|
{:ok, replied_object} ->
|
||||||
with %Activity{} = activity <-
|
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
|
object
|
||||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_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)
|
Map.put(data, "actor", actor)
|
||||||
|> fix_addressing
|
|> 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
|
%User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do
|
||||||
object = fix_object(data["object"])
|
object = fix_object(data["object"])
|
||||||
|
|
||||||
@ -348,6 +383,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||||||
additional:
|
additional:
|
||||||
Map.take(data, [
|
Map.take(data, [
|
||||||
"cc",
|
"cc",
|
||||||
|
"directMessage",
|
||||||
"id"
|
"id"
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@ -451,7 +487,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||||||
with actor <- get_actor(data),
|
with actor <- get_actor(data),
|
||||||
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
||||||
{:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
|
{: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}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
_e -> :error
|
_e -> :error
|
||||||
@ -863,15 +900,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||||||
|
|
||||||
maybe_retire_websub(user.ap_id)
|
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 =
|
q =
|
||||||
from(
|
from(
|
||||||
a in Activity,
|
a in Activity,
|
||||||
where: ^old_follower_address in a.recipients,
|
where: ^old_follower_address in a.recipients,
|
||||||
where: a.id > ^since,
|
|
||||||
update: [
|
update: [
|
||||||
set: [
|
set: [
|
||||||
recipients:
|
recipients:
|
||||||
|
@ -25,6 +25,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||||||
Map.put(params, "actor", get_ap_id(params["actor"]))
|
Map.put(params, "actor", get_ap_id(params["actor"]))
|
||||||
end
|
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_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(ap_id, coll) when is_list(coll), do: ap_id in coll
|
||||||
defp recipient_in_collection(_, _), do: false
|
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.
|
# 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
|
# Alternatively, just don't do this and fetch the current object each time. Most
|
||||||
# could probably be taken from cache.
|
# 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 ->
|
Enum.map(relevant_activities, fn activity ->
|
||||||
new_activity_data = activity.data |> Map.put("object", object.data)
|
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
|
# for relayed messages, we only want to send to subscribers
|
||||||
def make_announce_data(
|
def make_announce_data(
|
||||||
%User{ap_id: ap_id, nickname: nil} = user,
|
%User{ap_id: ap_id} = user,
|
||||||
%Object{data: %{"id" => id}} = object,
|
%Object{data: %{"id" => id}} = object,
|
||||||
activity_id
|
activity_id,
|
||||||
|
false
|
||||||
) do
|
) do
|
||||||
data = %{
|
data = %{
|
||||||
"type" => "Announce",
|
"type" => "Announce",
|
||||||
@ -405,7 +420,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||||||
def make_announce_data(
|
def make_announce_data(
|
||||||
%User{ap_id: ap_id} = user,
|
%User{ap_id: ap_id} = user,
|
||||||
%Object{data: %{"id" => id}} = object,
|
%Object{data: %{"id" => id}} = object,
|
||||||
activity_id
|
activity_id,
|
||||||
|
true
|
||||||
) do
|
) do
|
||||||
data = %{
|
data = %{
|
||||||
"type" => "Announce",
|
"type" => "Announce",
|
||||||
|
@ -160,7 +160,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||||||
"partOf" => iri,
|
"partOf" => iri,
|
||||||
"totalItems" => info.note_count,
|
"totalItems" => info.note_count,
|
||||||
"orderedItems" => collection,
|
"orderedItems" => collection,
|
||||||
"next" => "#{iri}?max_id=#{min_id - 1}"
|
"next" => "#{iri}?max_id=#{min_id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
if max_qid == nil do
|
if max_qid == nil do
|
||||||
@ -207,7 +207,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||||||
"partOf" => iri,
|
"partOf" => iri,
|
||||||
"totalItems" => -1,
|
"totalItems" => -1,
|
||||||
"orderedItems" => collection,
|
"orderedItems" => collection,
|
||||||
"next" => "#{iri}?max_id=#{min_id - 1}"
|
"next" => "#{iri}?max_id=#{min_id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
if max_qid == nil do
|
if max_qid == nil do
|
||||||
|
@ -143,7 +143,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||||||
actor: user,
|
actor: user,
|
||||||
context: context,
|
context: context,
|
||||||
object: object,
|
object: object,
|
||||||
additional: %{"cc" => cc}
|
additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
|
||||||
})
|
})
|
||||||
|
|
||||||
res
|
res
|
||||||
|
@ -14,13 +14,13 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||||||
|
|
||||||
# This is a hack for twidere.
|
# This is a hack for twidere.
|
||||||
def get_by_id_or_ap_id(id) do
|
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 &&
|
activity &&
|
||||||
if activity.data["type"] == "Create" do
|
if activity.data["type"] == "Create" do
|
||||||
activity
|
activity
|
||||||
else
|
else
|
||||||
Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -116,16 +116,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||||||
Enum.join([text | attachment_text], "<br>")
|
Enum.join([text | attachment_text], "<br>")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_input(text, mentions, tags, format, options \\ [])
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Formatting text to plain text.
|
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
|
text
|
||||||
|> Formatter.html_escape("text/plain")
|
|> Formatter.html_escape("text/plain")
|
||||||
|> String.replace(~r/\r?\n/, "<br>")
|
|> String.replace(~r/\r?\n/, "<br>")
|
||||||
|> (&{[], &1}).()
|
|> (&{[], &1}).()
|
||||||
|> Formatter.add_links()
|
|> Formatter.add_links()
|
||||||
|> Formatter.add_user_links(mentions)
|
|> Formatter.add_user_links(mentions, options[:user_links] || [])
|
||||||
|> Formatter.add_hashtag_links(tags)
|
|> Formatter.add_hashtag_links(tags)
|
||||||
|> Formatter.finalize()
|
|> Formatter.finalize()
|
||||||
end
|
end
|
||||||
@ -133,24 +135,24 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||||||
@doc """
|
@doc """
|
||||||
Formatting text to html.
|
Formatting text to html.
|
||||||
"""
|
"""
|
||||||
def format_input(text, mentions, _tags, "text/html") do
|
def format_input(text, mentions, _tags, "text/html", options) do
|
||||||
text
|
text
|
||||||
|> Formatter.html_escape("text/html")
|
|> Formatter.html_escape("text/html")
|
||||||
|> (&{[], &1}).()
|
|> (&{[], &1}).()
|
||||||
|> Formatter.add_user_links(mentions)
|
|> Formatter.add_user_links(mentions, options[:user_links] || [])
|
||||||
|> Formatter.finalize()
|
|> Formatter.finalize()
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Formatting text to markdown.
|
Formatting text to markdown.
|
||||||
"""
|
"""
|
||||||
def format_input(text, mentions, tags, "text/markdown") do
|
def format_input(text, mentions, tags, "text/markdown", options) do
|
||||||
text
|
text
|
||||||
|> Formatter.mentions_escape(mentions)
|
|> Formatter.mentions_escape(mentions)
|
||||||
|> Earmark.as_html!()
|
|> Earmark.as_html!()
|
||||||
|> Formatter.html_escape("text/html")
|
|> Formatter.html_escape("text/html")
|
||||||
|> (&{[], &1}).()
|
|> (&{[], &1}).()
|
||||||
|> Formatter.add_user_links(mentions)
|
|> Formatter.add_user_links(mentions, options[:user_links] || [])
|
||||||
|> Formatter.add_hashtag_links(tags)
|
|> Formatter.add_hashtag_links(tags)
|
||||||
|> Formatter.finalize()
|
|> Formatter.finalize()
|
||||||
end
|
end
|
||||||
|
@ -377,7 +377,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||||||
|
|
||||||
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) 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),
|
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
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> 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
|
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),
|
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
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> 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
|
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),
|
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
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||||
@ -743,8 +743,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||||||
fetched =
|
fetched =
|
||||||
if Regex.match?(~r/https?:/, query) do
|
if Regex.match?(~r/https?:/, query) do
|
||||||
with {:ok, object} <- ActivityPub.fetch_object_from_id(query),
|
with {:ok, object} <- ActivityPub.fetch_object_from_id(query),
|
||||||
%Activity{} = activity <-
|
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||||
Activity.get_create_activity_by_object_ap_id(object.data["id"]),
|
|
||||||
true <- ActivityPub.visible_for_user?(activity, user) do
|
true <- ActivityPub.visible_for_user?(activity, user) do
|
||||||
[activity]
|
[activity]
|
||||||
else
|
else
|
||||||
@ -771,7 +770,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
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)
|
statuses = status_search(user, query)
|
||||||
|
|
||||||
@ -795,7 +794,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
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)
|
statuses = status_search(user, query)
|
||||||
|
|
||||||
@ -816,7 +815,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
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)
|
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
|
def render_notification(user, %{id: id, activity: activity, inserted_at: created_at} = _params) do
|
||||||
actor = User.get_cached_by_ap_id(activity.data["actor"])
|
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)
|
mastodon_type = Activity.mastodon_notification_type(activity)
|
||||||
|
|
||||||
response = %{
|
response = %{
|
||||||
|
@ -25,7 +25,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
nil
|
nil
|
||||||
end)
|
end)
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
|> Activity.create_activity_by_object_id_query()
|
|> Activity.create_by_object_ap_id()
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Enum.reduce(%{}, fn activity, acc ->
|
|> Enum.reduce(%{}, fn activity, acc ->
|
||||||
Map.put(acc, activity.data["object"]["id"], activity)
|
Map.put(acc, activity.data["object"]["id"], activity)
|
||||||
@ -64,7 +64,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
user = get_user(activity.data["actor"])
|
user = get_user(activity.data["actor"])
|
||||||
created_at = Utils.to_masto_date(activity.data["published"])
|
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))
|
reblogged = render("status.json", Map.put(opts, :activity, reblogged))
|
||||||
|
|
||||||
mentions =
|
mentions =
|
||||||
@ -209,7 +209,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
|
|
||||||
def get_reply_to(%{data: %{"object" => object}}, _) do
|
def get_reply_to(%{data: %{"object" => object}}, _) do
|
||||||
if object["inReplyTo"] && object["inReplyTo"] != "" 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
|
else
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
@ -231,6 +231,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
Enum.any?(to, &String.contains?(&1, "/followers")) ->
|
Enum.any?(to, &String.contains?(&1, "/followers")) ->
|
||||||
"private"
|
"private"
|
||||||
|
|
||||||
|
length(cc) > 0 ->
|
||||||
|
"private"
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
"direct"
|
"direct"
|
||||||
end
|
end
|
||||||
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
|
|||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
field(:valid_until, :naive_datetime)
|
field(:valid_until, :naive_datetime)
|
||||||
field(:used, :boolean, default: false)
|
field(:used, :boolean, default: false)
|
||||||
belongs_to(:user, Pleroma.User)
|
belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
|
||||||
belongs_to(:app, App)
|
belongs_to(:app, App)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
field(:refresh_token, :string)
|
field(:refresh_token, :string)
|
||||||
field(:valid_until, :naive_datetime)
|
field(:valid_until, :naive_datetime)
|
||||||
belongs_to(:user, Pleroma.User)
|
belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
|
||||||
belongs_to(:app, App)
|
belongs_to(:app, App)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
|
@ -183,7 +183,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
|||||||
_in_reply_to = get_in_reply_to(activity.data)
|
_in_reply_to = get_in_reply_to(activity.data)
|
||||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
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_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"])
|
||||||
|
|
||||||
retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
|
retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
|
||||||
|
@ -86,7 +86,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fetch_replied_to_activity(entry, inReplyTo) do
|
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
|
activity
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
@ -103,7 +103,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
|
|||||||
# TODO: Clean this up a bit.
|
# TODO: Clean this up a bit.
|
||||||
def handle_note(entry, doc \\ nil) do
|
def handle_note(entry, doc \\ nil) do
|
||||||
with id <- XML.string_from_xpath("//id", entry),
|
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),
|
[author] <- :xmerl_xpath.string('//author[1]', doc),
|
||||||
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
{:ok, actor} <- OStatus.find_make_or_update_user(author),
|
||||||
content_html <- OStatus.get_content(entry),
|
content_html <- OStatus.get_content(entry),
|
||||||
|
@ -148,7 +148,7 @@ defmodule Pleroma.Web.OStatus do
|
|||||||
Logger.debug("Trying to get entry from db")
|
Logger.debug("Trying to get entry from db")
|
||||||
|
|
||||||
with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry),
|
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}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -93,8 +93,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||||||
ActivityPubController.call(conn, :object)
|
ActivityPubController.call(conn, :object)
|
||||||
else
|
else
|
||||||
with id <- o_status_url(conn, :object, uuid),
|
with id <- o_status_url(conn, :object, uuid),
|
||||||
{_, %Activity{} = activity} <-
|
{_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id(id)},
|
||||||
{:activity, Activity.get_create_activity_by_object_ap_id(id)},
|
|
||||||
{_, true} <- {:public?, ActivityPub.is_public?(activity)},
|
{_, true} <- {:public?, ActivityPub.is_public?(activity)},
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||||
case get_format(conn) do
|
case get_format(conn) do
|
||||||
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.Push.Subscription do
|
|||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
schema "push_subscriptions" do
|
schema "push_subscriptions" do
|
||||||
belongs_to(:user, User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
belongs_to(:token, Token)
|
belongs_to(:token, Token)
|
||||||
field(:endpoint, :string)
|
field(:endpoint, :string)
|
||||||
field(:key_p256dh, :string)
|
field(:key_p256dh, :string)
|
||||||
|
@ -107,6 +107,11 @@ defmodule Pleroma.Web.Router do
|
|||||||
get("/captcha", UtilController, :captcha)
|
get("/captcha", UtilController, :captcha)
|
||||||
end
|
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
|
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
pipe_through(:admin_api)
|
pipe_through(:admin_api)
|
||||||
delete("/user", AdminAPIController, :user_delete)
|
delete("/user", AdminAPIController, :user_delete)
|
||||||
|
@ -205,6 +205,15 @@ defmodule Pleroma.Web.Streamer do
|
|||||||
end)
|
end)
|
||||||
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
|
def push_to_socket(topics, topic, item) do
|
||||||
Enum.each(topics[topic] || [], fn socket ->
|
Enum.each(topics[topic] || [], fn socket ->
|
||||||
# Get the current user so we have up-to-date blocks etc.
|
# 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
|
def repeat(%User{} = user, ap_id_or_id) do
|
||||||
with {:ok, _announce, %{data: %{"id" => id}}} <- CommonAPI.repeat(ap_id_or_id, user),
|
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}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unrepeat(%User{} = user, ap_id_or_id) do
|
def unrepeat(%User{} = user, ap_id_or_id) do
|
||||||
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
|
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}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -92,14 +92,14 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||||||
|
|
||||||
def fav(%User{} = user, ap_id_or_id) do
|
def fav(%User{} = user, ap_id_or_id) do
|
||||||
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
|
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}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unfav(%User{} = user, ap_id_or_id) do
|
def unfav(%User{} = user, ap_id_or_id) do
|
||||||
with {:ok, _unfav, _fav, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
|
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}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -265,8 +265,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
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),
|
with context when is_binary(context) <- TwitterAPI.conversation_id_to_context(id),
|
||||||
activities <-
|
activities <-
|
||||||
ActivityPub.fetch_activities_for_context(context, %{
|
ActivityPub.fetch_activities_for_context(context, %{
|
||||||
@ -330,54 +328,57 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def get_by_id_or_ap_id(id) do
|
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
|
if activity.data["type"] == "Create" do
|
||||||
activity
|
activity
|
||||||
else
|
else
|
||||||
Activity.get_create_activity_by_object_ap_id(activity.data["object"])
|
Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
with {:ok, activity} <- TwitterAPI.fav(user, id) do
|
||||||
{:ok, activity} <- TwitterAPI.fav(user, id) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ActivityView)
|
|> put_view(ActivityView)
|
||||||
|> render("activity.json", %{activity: activity, for: user})
|
|> render("activity.json", %{activity: activity, for: user})
|
||||||
|
else
|
||||||
|
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
with {:ok, activity} <- TwitterAPI.unfav(user, id) do
|
||||||
{:ok, activity} <- TwitterAPI.unfav(user, id) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ActivityView)
|
|> put_view(ActivityView)
|
||||||
|> render("activity.json", %{activity: activity, for: user})
|
|> render("activity.json", %{activity: activity, for: user})
|
||||||
|
else
|
||||||
|
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
with {:ok, activity} <- TwitterAPI.repeat(user, id) do
|
||||||
{:ok, activity} <- TwitterAPI.repeat(user, id) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ActivityView)
|
|> put_view(ActivityView)
|
||||||
|> render("activity.json", %{activity: activity, for: user})
|
|> render("activity.json", %{activity: activity, for: user})
|
||||||
|
else
|
||||||
|
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
with {:ok, activity} <- TwitterAPI.unrepeat(user, id) do
|
||||||
{:ok, activity} <- TwitterAPI.unrepeat(user, id) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ActivityView)
|
|> put_view(ActivityView)
|
||||||
|> render("activity.json", %{activity: activity, for: user})
|
|> render("activity.json", %{activity: activity, for: user})
|
||||||
|
else
|
||||||
|
_ -> json_reply(conn, 400, Jason.encode!(%{}))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
with {:ok, activity} <- TwitterAPI.pin(user, id) do
|
||||||
{:ok, activity} <- TwitterAPI.pin(user, id) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ActivityView)
|
|> put_view(ActivityView)
|
||||||
|> render("activity.json", %{activity: activity, for: user})
|
|> render("activity.json", %{activity: activity, for: user})
|
||||||
@ -388,8 +389,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)},
|
with {:ok, activity} <- TwitterAPI.unpin(user, id) do
|
||||||
{:ok, activity} <- TwitterAPI.unpin(user, id) do
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ActivityView)
|
|> put_view(ActivityView)
|
||||||
|> render("activity.json", %{activity: activity, for: user})
|
|> 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
|
def approve_friend_request(conn, %{"user_id" => uid} = _params) do
|
||||||
with followed <- conn.assigns[:user],
|
with followed <- conn.assigns[:user],
|
||||||
uid when is_number(uid) <- String.to_integer(uid),
|
|
||||||
%User{} = follower <- Repo.get(User, uid),
|
%User{} = follower <- Repo.get(User, uid),
|
||||||
{:ok, follower} <- User.maybe_follow(follower, followed),
|
{:ok, follower} <- User.maybe_follow(follower, followed),
|
||||||
%Activity{} = follow_activity <- Utils.fetch_latest_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
|
def deny_friend_request(conn, %{"user_id" => uid} = _params) do
|
||||||
with followed <- conn.assigns[:user],
|
with followed <- conn.assigns[:user],
|
||||||
uid when is_number(uid) <- String.to_integer(uid),
|
|
||||||
%User{} = follower <- Repo.get(User, uid),
|
%User{} = follower <- Repo.get(User, uid),
|
||||||
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
|
||||||
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||||
@ -675,7 +673,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
|
def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do
|
||||||
users = User.search(query, true)
|
users = User.search(query, true, user)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|
@ -168,7 +168,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||||||
def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do
|
def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do
|
||||||
user = get_user(activity.data["actor"], opts)
|
user = get_user(activity.data["actor"], opts)
|
||||||
created_at = activity.data["published"] |> Utils.date_to_asctime()
|
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."
|
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
|
def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do
|
||||||
user = get_user(activity.data["actor"], opts)
|
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
|
liked_activity_id = if liked_activity, do: liked_activity.id, else: nil
|
||||||
|
|
||||||
created_at =
|
created_at =
|
||||||
|
@ -108,6 +108,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
|
|||||||
"locked" => user.info.locked,
|
"locked" => user.info.locked,
|
||||||
"default_scope" => user.info.default_scope,
|
"default_scope" => user.info.default_scope,
|
||||||
"no_rich_text" => user.info.no_rich_text,
|
"no_rich_text" => user.info.no_rich_text,
|
||||||
|
"hide_network" => user.info.hide_network,
|
||||||
"fields" => fields,
|
"fields" => fields,
|
||||||
|
|
||||||
# Pleroma extension
|
# 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(:state, :string)
|
||||||
field(:subscribers, {:array, :string}, default: [])
|
field(:subscribers, {:array, :string}, default: [])
|
||||||
field(:hub, :string)
|
field(:hub, :string)
|
||||||
belongs_to(:user, User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.UsersAndActivitiesFlakeId do
|
||||||
|
use Ecto.Migration
|
||||||
|
alias Pleroma.Clippy
|
||||||
|
require Integer
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
# This migrates from int serial IDs to custom Flake:
|
||||||
|
# 1- create a temporary uuid column
|
||||||
|
# 2- fill this column with compatibility ids (see below)
|
||||||
|
# 3- remove pkeys constraints
|
||||||
|
# 4- update relation pkeys with the new ids
|
||||||
|
# 5- rename the temporary column to id
|
||||||
|
# 6- re-create the constraints
|
||||||
|
def change do
|
||||||
|
# Old serial int ids are transformed to 128bits with extra padding.
|
||||||
|
# The application (in `Pleroma.FlakeId`) handles theses IDs properly as integers; to keep compatibility
|
||||||
|
# with previously issued ids.
|
||||||
|
#execute "update activities set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);"
|
||||||
|
#execute "update users set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);"
|
||||||
|
|
||||||
|
clippy = start_clippy_heartbeats()
|
||||||
|
|
||||||
|
# Lock both tables to avoid a running server to meddling with our transaction
|
||||||
|
execute "LOCK TABLE activities;"
|
||||||
|
execute "LOCK TABLE users;"
|
||||||
|
|
||||||
|
execute """
|
||||||
|
ALTER TABLE activities
|
||||||
|
DROP CONSTRAINT activities_pkey CASCADE,
|
||||||
|
ALTER COLUMN id DROP default,
|
||||||
|
ALTER COLUMN id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid),
|
||||||
|
ADD PRIMARY KEY (id);
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute """
|
||||||
|
ALTER TABLE users
|
||||||
|
DROP CONSTRAINT users_pkey CASCADE,
|
||||||
|
ALTER COLUMN id DROP default,
|
||||||
|
ALTER COLUMN id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid),
|
||||||
|
ADD PRIMARY KEY (id);
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute "UPDATE users SET info = jsonb_set(info, '{pinned_activities}', array_to_json(ARRAY(select jsonb_array_elements_text(info->'pinned_activities')))::jsonb);"
|
||||||
|
|
||||||
|
# Fkeys:
|
||||||
|
# Activities - Referenced by:
|
||||||
|
# TABLE "notifications" CONSTRAINT "notifications_activity_id_fkey" FOREIGN KEY (activity_id) REFERENCES activities(id) ON DELETE CASCADE
|
||||||
|
# Users - Referenced by:
|
||||||
|
# TABLE "filters" CONSTRAINT "filters_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
# TABLE "lists" CONSTRAINT "lists_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
# TABLE "notifications" CONSTRAINT "notifications_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
# TABLE "oauth_authorizations" CONSTRAINT "oauth_authorizations_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
# TABLE "oauth_tokens" CONSTRAINT "oauth_tokens_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
# TABLE "password_reset_tokens" CONSTRAINT "password_reset_tokens_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
# TABLE "push_subscriptions" CONSTRAINT "push_subscriptions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
# TABLE "websub_client_subscriptions" CONSTRAINT "websub_client_subscriptions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
|
||||||
|
execute """
|
||||||
|
ALTER TABLE notifications
|
||||||
|
ALTER COLUMN activity_id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(activity_id), 32, '0' ) AS uuid),
|
||||||
|
ADD CONSTRAINT notifications_activity_id_fkey FOREIGN KEY (activity_id) REFERENCES activities(id) ON DELETE CASCADE;
|
||||||
|
"""
|
||||||
|
|
||||||
|
for table <- ~w(notifications filters lists oauth_authorizations oauth_tokens password_reset_tokens push_subscriptions websub_client_subscriptions) do
|
||||||
|
execute """
|
||||||
|
ALTER TABLE #{table}
|
||||||
|
ALTER COLUMN user_id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(user_id), 32, '0' ) AS uuid),
|
||||||
|
ADD CONSTRAINT #{table}_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
flush()
|
||||||
|
|
||||||
|
stop_clippy_heartbeats(clippy)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp start_clippy_heartbeats() do
|
||||||
|
count = from(a in "activities", select: count(a.id)) |> Repo.one!
|
||||||
|
|
||||||
|
if count > 5000 do
|
||||||
|
heartbeat_interval = :timer.minutes(2) + :timer.seconds(30)
|
||||||
|
all_tips = Clippy.tips() ++ [
|
||||||
|
"The migration is still running, maybe it's time for another “tea”?",
|
||||||
|
"Happy rabbits practice a cute behavior known as a\n“binky:” they jump up in the air\nand twist\nand spin around!",
|
||||||
|
"Nothing and everything.\n\nI still work.",
|
||||||
|
"Pleroma runs on a Raspberry Pi!\n\n … but this migration will take forever if you\nactually run on a raspberry pi",
|
||||||
|
"Status? Stati? Post? Note? Toot?\nRepeat? Reboost? Boost? Retweet? Retoot??\n\nI-I'm confused.",
|
||||||
|
]
|
||||||
|
|
||||||
|
heartbeat = fn(heartbeat, runs, all_tips, tips) ->
|
||||||
|
tips = if Integer.is_even(runs) do
|
||||||
|
tips = if tips == [], do: all_tips, else: tips
|
||||||
|
[tip | tips] = Enum.shuffle(tips)
|
||||||
|
Clippy.puts(tip)
|
||||||
|
tips
|
||||||
|
else
|
||||||
|
IO.puts "\n -- #{DateTime.to_string(DateTime.utc_now())} Migration still running, please wait…\n"
|
||||||
|
tips
|
||||||
|
end
|
||||||
|
:timer.sleep(heartbeat_interval)
|
||||||
|
heartbeat.(heartbeat, runs + 1, all_tips, tips)
|
||||||
|
end
|
||||||
|
|
||||||
|
Clippy.puts [
|
||||||
|
[:red, :bright, "It looks like you are running an older instance!"],
|
||||||
|
[""],
|
||||||
|
[:bright, "This migration may take a long time", :reset, " -- so you probably should"],
|
||||||
|
["go drink a cofe, or a tea, or a beer, a whiskey, a vodka,"],
|
||||||
|
["while it runs to deal with your temporary fediverse pause!"]
|
||||||
|
]
|
||||||
|
:timer.sleep(heartbeat_interval)
|
||||||
|
spawn_link(fn() -> heartbeat.(heartbeat, 1, all_tips, []) end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp stop_clippy_heartbeats(pid) do
|
||||||
|
if pid do
|
||||||
|
Process.unlink(pid)
|
||||||
|
Process.exit(pid, :kill)
|
||||||
|
Clippy.puts [[:green, :bright, "Hurray!!", "", "", "Migration completed!"]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -0,0 +1,17 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.CreateUserFtsIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create index(
|
||||||
|
:users,
|
||||||
|
[
|
||||||
|
"""
|
||||||
|
(setweight(to_tsvector('simple', regexp_replace(nickname, '\\W', ' ', 'g')), 'A') ||
|
||||||
|
setweight(to_tsvector('simple', regexp_replace(coalesce(name, ''), '\\W', ' ', 'g')), 'B'))
|
||||||
|
"""
|
||||||
|
],
|
||||||
|
name: :users_fts_index,
|
||||||
|
using: :gin
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,22 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.FixUserTrigramIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
drop_if_exists(index(:users, [], name: :users_trigram_index))
|
||||||
|
|
||||||
|
create(
|
||||||
|
index(:users, ["(trim(nickname || ' ' || coalesce(name, ''))) gist_trgm_ops"],
|
||||||
|
name: :users_trigram_index,
|
||||||
|
using: :gist
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
drop_if_exists(index(:users, [], name: :users_trigram_index))
|
||||||
|
|
||||||
|
create(
|
||||||
|
index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,36 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.UpdateActivityVisibility do
|
||||||
|
use Ecto.Migration
|
||||||
|
@disable_ddl_transaction true
|
||||||
|
|
||||||
|
def up do
|
||||||
|
definition = """
|
||||||
|
create or replace function activity_visibility(actor varchar, recipients varchar[], data jsonb) returns varchar as $$
|
||||||
|
DECLARE
|
||||||
|
fa varchar;
|
||||||
|
public varchar := 'https://www.w3.org/ns/activitystreams#Public';
|
||||||
|
BEGIN
|
||||||
|
SELECT COALESCE(users.follower_address, '') into fa from users where users.ap_id = actor;
|
||||||
|
|
||||||
|
IF data->'to' ? public THEN
|
||||||
|
RETURN 'public';
|
||||||
|
ELSIF data->'cc' ? public THEN
|
||||||
|
RETURN 'unlisted';
|
||||||
|
ELSIF ARRAY[fa] && recipients THEN
|
||||||
|
RETURN 'private';
|
||||||
|
ELSIF not(ARRAY[fa, public] && recipients) THEN
|
||||||
|
RETURN 'direct';
|
||||||
|
ELSE
|
||||||
|
RETURN 'unknown';
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE SECURITY DEFINER;
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute(definition)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
9
priv/repo/migrations/20190123125839_fix_info_ids.exs
Normal file
9
priv/repo/migrations/20190123125839_fix_info_ids.exs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.FixInfoIds do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
execute(
|
||||||
|
"update users set info = jsonb_set(info, '{id}', to_jsonb(uuid_generate_v4())) where info->'id' is null;"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,37 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.UpdateActivityVisibilityAgain do
|
||||||
|
use Ecto.Migration
|
||||||
|
@disable_ddl_transaction true
|
||||||
|
|
||||||
|
def up do
|
||||||
|
definition = """
|
||||||
|
create or replace function activity_visibility(actor varchar, recipients varchar[], data jsonb) returns varchar as $$
|
||||||
|
DECLARE
|
||||||
|
fa varchar;
|
||||||
|
public varchar := 'https://www.w3.org/ns/activitystreams#Public';
|
||||||
|
BEGIN
|
||||||
|
SELECT COALESCE(users.follower_address, '') into fa from public.users where users.ap_id = actor;
|
||||||
|
|
||||||
|
IF data->'to' ? public THEN
|
||||||
|
RETURN 'public';
|
||||||
|
ELSIF data->'cc' ? public THEN
|
||||||
|
RETURN 'unlisted';
|
||||||
|
ELSIF ARRAY[fa] && recipients THEN
|
||||||
|
RETURN 'private';
|
||||||
|
ELSIF not(ARRAY[fa, public] && recipients) THEN
|
||||||
|
RETURN 'direct';
|
||||||
|
ELSE
|
||||||
|
RETURN 'unknown';
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE SECURITY DEFINER;
|
||||||
|
"""
|
||||||
|
|
||||||
|
execute(definition)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1 +1 @@
|
|||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.c52cbb57296d5c682ff405d562e83a9b.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.e833e1c75fbc9f2b69b4.js></script><script type=text/javascript src=/static/js/vendor.b6e63c523d95d763c254.js></script><script type=text/javascript src=/static/js/app.1c83eacd8eddeef56c69.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.3d3e30a9afb8c41739656f496e8c79e6.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.6802874440f1854a6844.js></script><script type=text/javascript src=/static/js/vendor.61fd03d8471aaadcf63c.js></script><script type=text/javascript src=/static/js/app.5ed04a69c4a6e8b1f455.js></script></body></html>
|
@ -17,7 +17,9 @@
|
|||||||
"toot": "http://joinmastodon.org/ns#",
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
"totalItems": "as:totalItems",
|
"totalItems": "as:totalItems",
|
||||||
"value": "schema:value",
|
"value": "schema:value",
|
||||||
"sensitive": "as:sensitive"
|
"sensitive": "as:sensitive",
|
||||||
|
"litepub": "http://litepub.social/ns#",
|
||||||
|
"directMessage": "litepub:directMessage"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -18,5 +18,6 @@
|
|||||||
"hideUserStats": false,
|
"hideUserStats": false,
|
||||||
"loginMethod": "password",
|
"loginMethod": "password",
|
||||||
"webPushNotifications": false,
|
"webPushNotifications": false,
|
||||||
"noAttachmentLinks": false
|
"noAttachmentLinks": false,
|
||||||
|
"nsfwCensorImage": ""
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
14
priv/static/static/js/app.5ed04a69c4a6e8b1f455.js
Normal file
14
priv/static/static/js/app.5ed04a69c4a6e8b1f455.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/app.5ed04a69c4a6e8b1f455.js.map
Normal file
1
priv/static/static/js/app.5ed04a69c4a6e8b1f455.js.map
Normal file
File diff suppressed because one or more lines are too long
2
priv/static/static/js/manifest.6802874440f1854a6844.js
Normal file
2
priv/static/static/js/manifest.6802874440f1854a6844.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,i=[];s<c.length;s++)l=c[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(a&&a(c,o);i.length;)i.shift().call(null,t);if(o[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"61fd03d8471aaadcf63c",2:"5ed04a69c4a6e8b1f455"}[e]+".js",r.appendChild(c)}},t.m=e,t.c=r,t.p="/"}([]);
|
||||||
|
//# sourceMappingURL=manifest.6802874440f1854a6844.js.map
|
File diff suppressed because one or more lines are too long
@ -1,2 +0,0 @@
|
|||||||
!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var r=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,d=[];s<c.length;s++)l=c[s],a[l]&&d.push.apply(d,a[l]),a[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(r&&r(c,o);d.length;)d.shift().call(null,t);if(o[0])return n[0]=0,t(0)};var n={},a={0:0};t.e=function(e,r){if(0===a[e])return r.call(null,t);if(void 0!==a[e])a[e].push(r);else{a[e]=[r];var n=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"b6e63c523d95d763c254",2:"1c83eacd8eddeef56c69"}[e]+".js",n.appendChild(c)}},t.m=e,t.c=n,t.p="/"}([]);
|
|
||||||
//# sourceMappingURL=manifest.e833e1c75fbc9f2b69b4.js.map
|
|
35
priv/static/static/js/vendor.61fd03d8471aaadcf63c.js
Normal file
35
priv/static/static/js/vendor.61fd03d8471aaadcf63c.js
Normal file
File diff suppressed because one or more lines are too long
1
priv/static/static/js/vendor.61fd03d8471aaadcf63c.js.map
Normal file
1
priv/static/static/js/vendor.61fd03d8471aaadcf63c.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.3 KiB |
@ -1,4 +1,4 @@
|
|||||||
var serviceWorkerOption = {"assets":["/static/img/nsfw.50fd83c.png","/static/js/manifest.e833e1c75fbc9f2b69b4.js","/static/js/vendor.b6e63c523d95d763c254.js","/static/js/app.1c83eacd8eddeef56c69.js","/static/css/app.c52cbb57296d5c682ff405d562e83a9b.css"]};
|
var serviceWorkerOption = {"assets":["/static/img/nsfw.50fd83c.png","/static/js/manifest.6802874440f1854a6844.js","/static/js/vendor.61fd03d8471aaadcf63c.js","/static/js/app.5ed04a69c4a6e8b1f455.js","/static/css/app.3d3e30a9afb8c41739656f496e8c79e6.css"]};
|
||||||
|
|
||||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var t={};return n.m=e,n.c=t,n.p="/",n(0)}([function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(){return u.default.getItem("vuex-lz").then(function(e){return e.config.webPushNotifications})}function i(){return clients.matchAll({includeUncontrolled:!0}).then(function(e){return e.filter(function(e){var n=e.type;return"window"===n})})}var a=t(1),u=r(a);self.addEventListener("push",function(e){e.data&&e.waitUntil(o().then(function(n){return n&&i().then(function(n){var t=e.data.json();if(0===n.length)return self.registration.showNotification(t.title,t)})}))}),self.addEventListener("notificationclick",function(e){e.notification.close(),e.waitUntil(i().then(function(e){for(var n=0;n<e.length;n++){var t=e[n];if("/"===t.url&&"focus"in t)return t.focus()}if(clients.openWindow)return clients.openWindow("/")}))})},function(e,n){/*!
|
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var t={};return n.m=e,n.c=t,n.p="/",n(0)}([function(e,n,t){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(){return u.default.getItem("vuex-lz").then(function(e){return e.config.webPushNotifications})}function i(){return clients.matchAll({includeUncontrolled:!0}).then(function(e){return e.filter(function(e){var n=e.type;return"window"===n})})}var a=t(1),u=r(a);self.addEventListener("push",function(e){e.data&&e.waitUntil(o().then(function(n){return n&&i().then(function(n){var t=e.data.json();if(0===n.length)return self.registration.showNotification(t.title,t)})}))}),self.addEventListener("notificationclick",function(e){e.notification.close(),e.waitUntil(i().then(function(e){for(var n=0;n<e.length;n++){var t=e[n];if("/"===t.url&&"focus"in t)return t.focus()}if(clients.openWindow)return clients.openWindow("/")}))})},function(e,n){/*!
|
||||||
localForage -- Offline Storage, Improved
|
localForage -- Offline Storage, Improved
|
||||||
|
@ -16,7 +16,7 @@ defmodule Pleroma.ActivityTest do
|
|||||||
|
|
||||||
test "returns activities by it's objects AP ids" do
|
test "returns activities by it's objects AP ids" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
[found_activity] = Activity.all_by_object_ap_id(activity.data["object"]["id"])
|
[found_activity] = Activity.get_all_create_by_object_ap_id(activity.data["object"]["id"])
|
||||||
|
|
||||||
assert activity == found_activity
|
assert activity == found_activity
|
||||||
end
|
end
|
||||||
@ -24,7 +24,7 @@ defmodule Pleroma.ActivityTest do
|
|||||||
test "returns the activity that created an object" do
|
test "returns the activity that created an object" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
found_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]["id"])
|
found_activity = Activity.get_create_by_object_ap_id(activity.data["object"]["id"])
|
||||||
|
|
||||||
assert activity == found_activity
|
assert activity == found_activity
|
||||||
end
|
end
|
||||||
|
41
test/flake_id_test.exs
Normal file
41
test/flake_id_test.exs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.FlakeIdTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
import Kernel, except: [to_string: 1]
|
||||||
|
import Pleroma.FlakeId
|
||||||
|
|
||||||
|
describe "fake flakes (compatibility with older serial integers)" do
|
||||||
|
test "from_string/1" do
|
||||||
|
fake_flake = <<0::integer-size(64), 42::integer-size(64)>>
|
||||||
|
assert from_string("42") == fake_flake
|
||||||
|
end
|
||||||
|
|
||||||
|
test "zero or -1 is a null flake" do
|
||||||
|
fake_flake = <<0::integer-size(128)>>
|
||||||
|
assert from_string("0") == fake_flake
|
||||||
|
assert from_string("-1") == fake_flake
|
||||||
|
end
|
||||||
|
|
||||||
|
test "to_string/1" do
|
||||||
|
fake_flake = <<0::integer-size(64), 42::integer-size(64)>>
|
||||||
|
assert to_string(fake_flake) == "42"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ecto type behaviour" do
|
||||||
|
flake = <<0, 0, 1, 104, 80, 229, 2, 235, 140, 22, 69, 201, 53, 210, 0, 0>>
|
||||||
|
flake_s = "9eoozpwTul5mjSEDRI"
|
||||||
|
|
||||||
|
assert cast(flake) == {:ok, flake_s}
|
||||||
|
assert cast(flake_s) == {:ok, flake_s}
|
||||||
|
|
||||||
|
assert load(flake) == {:ok, flake_s}
|
||||||
|
assert load(flake_s) == {:ok, flake_s}
|
||||||
|
|
||||||
|
assert dump(flake_s) == {:ok, flake}
|
||||||
|
assert dump(flake) == {:ok, flake}
|
||||||
|
end
|
||||||
|
end
|
@ -124,7 +124,7 @@ defmodule Pleroma.FormatterTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "add_user_links" do
|
describe "add_user_links" do
|
||||||
test "gives a replacement for user links" do
|
test "gives a replacement for user links, using local nicknames in user links text" do
|
||||||
text = "@gsimg According to @archa_eme_, that is @daggsy. Also hello @archaeme@archae.me"
|
text = "@gsimg According to @archa_eme_, that is @daggsy. Also hello @archaeme@archae.me"
|
||||||
gsimg = insert(:user, %{nickname: "gsimg"})
|
gsimg = insert(:user, %{nickname: "gsimg"})
|
||||||
|
|
||||||
|
@ -66,13 +66,10 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||||||
assert json["payload"]
|
assert json["payload"]
|
||||||
assert {:ok, json} = Jason.decode(json["payload"])
|
assert {:ok, json} = Jason.decode(json["payload"])
|
||||||
|
|
||||||
# Note: we remove the "statuses_count" from this result as it changes in the meantime
|
|
||||||
|
|
||||||
view_json =
|
view_json =
|
||||||
Pleroma.Web.MastodonAPI.StatusView.render("status.json", activity: activity, for: nil)
|
Pleroma.Web.MastodonAPI.StatusView.render("status.json", activity: activity, for: nil)
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> put_in(["account", "statuses_count"], 0)
|
|
||||||
|
|
||||||
assert json == view_json
|
assert json == view_json
|
||||||
end
|
end
|
||||||
|
@ -775,14 +775,61 @@ defmodule Pleroma.UserTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "User.search" do
|
describe "User.search" do
|
||||||
test "finds a user, ranking by similarity" do
|
test "finds a user by full or partial nickname" do
|
||||||
_user = insert(:user, %{name: "lain"})
|
user = insert(:user, %{nickname: "john"})
|
||||||
_user_two = insert(:user, %{name: "ean"})
|
|
||||||
_user_three = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
|
|
||||||
user_four = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
|
|
||||||
|
|
||||||
assert user_four ==
|
Enum.each(["john", "jo", "j"], fn query ->
|
||||||
User.search("lain@ple") |> List.first() |> Map.put(:search_distance, nil)
|
assert user == User.search(query) |> List.first() |> Map.put(:search_rank, nil)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "finds a user by full or partial name" do
|
||||||
|
user = insert(:user, %{name: "John Doe"})
|
||||||
|
|
||||||
|
Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
|
||||||
|
assert user == User.search(query) |> List.first() |> Map.put(:search_rank, nil)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "finds users, preferring nickname matches over name matches" do
|
||||||
|
u1 = insert(:user, %{name: "lain", nickname: "nick1"})
|
||||||
|
u2 = insert(:user, %{nickname: "lain", name: "nick1"})
|
||||||
|
|
||||||
|
assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "finds users, considering density of matched tokens" do
|
||||||
|
u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
|
||||||
|
u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
|
||||||
|
|
||||||
|
assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "finds users, ranking by similarity" do
|
||||||
|
u1 = insert(:user, %{name: "lain"})
|
||||||
|
_u2 = insert(:user, %{name: "ean"})
|
||||||
|
u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
|
||||||
|
u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
|
||||||
|
|
||||||
|
assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple"), & &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "finds users, handling misspelled requests" do
|
||||||
|
u1 = insert(:user, %{name: "lain"})
|
||||||
|
|
||||||
|
assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "finds users, boosting ranks of friends and followers" do
|
||||||
|
u1 = insert(:user)
|
||||||
|
u2 = insert(:user, %{name: "Doe"})
|
||||||
|
follower = insert(:user, %{name: "Doe"})
|
||||||
|
friend = insert(:user, %{name: "Doe"})
|
||||||
|
|
||||||
|
{:ok, follower} = User.follow(follower, u1)
|
||||||
|
{:ok, u1} = User.follow(u1, friend)
|
||||||
|
|
||||||
|
assert [friend.id, follower.id, u2.id] == Enum.map(User.search("doe", false, u1), & &1.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "finds a user whose name is nil" do
|
test "finds a user whose name is nil" do
|
||||||
@ -792,7 +839,15 @@ defmodule Pleroma.UserTest do
|
|||||||
assert user_two ==
|
assert user_two ==
|
||||||
User.search("lain@pleroma.soykaf.com")
|
User.search("lain@pleroma.soykaf.com")
|
||||||
|> List.first()
|
|> List.first()
|
||||||
|> Map.put(:search_distance, nil)
|
|> Map.put(:search_rank, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not yield false-positive matches" do
|
||||||
|
insert(:user, %{name: "John Doe"})
|
||||||
|
|
||||||
|
Enum.each(["mary", "a", ""], fn query ->
|
||||||
|
assert [] == User.search(query)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -874,4 +929,19 @@ defmodule Pleroma.UserTest do
|
|||||||
Pleroma.Config.put([:instance, :account_activation_required], false)
|
Pleroma.Config.put([:instance, :account_activation_required], false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "parse_bio/2" do
|
||||||
|
test "preserves hosts in user links text" do
|
||||||
|
remote_user = insert(:user, local: false, nickname: "nick@domain.com")
|
||||||
|
user = insert(:user)
|
||||||
|
bio = "A.k.a. @nick@domain.com"
|
||||||
|
|
||||||
|
expected_text =
|
||||||
|
"A.k.a. <span class='h-card'><a data-user='#{remote_user.id}' class='u-url mention' href='#{
|
||||||
|
remote_user.ap_id
|
||||||
|
}'>" <> "@<span>nick@domain.com</span></a></span>"
|
||||||
|
|
||||||
|
assert expected_text == User.parse_bio(bio, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -216,7 +216,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||||||
|
|
||||||
{:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
|
{:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
|
||||||
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
||||||
%Activity{} = boost_activity = Activity.get_create_activity_by_object_ap_id(id)
|
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||||
activity_three = Repo.get(Activity, activity_three.id)
|
activity_three = Repo.get(Activity, activity_three.id)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
|
activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
|
||||||
@ -330,7 +330,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||||||
assert like_activity == same_like_activity
|
assert like_activity == same_like_activity
|
||||||
assert object.data["likes"] == [user.ap_id]
|
assert object.data["likes"] == [user.ap_id]
|
||||||
|
|
||||||
[note_activity] = Activity.all_by_object_ap_id(object.data["id"])
|
[note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
|
||||||
assert note_activity.data["object"]["like_count"] == 1
|
assert note_activity.data["object"]["like_count"] == 1
|
||||||
|
|
||||||
{:ok, _like_activity, object} = ActivityPub.like(user_two, object)
|
{:ok, _like_activity, object} = ActivityPub.like(user_two, object)
|
||||||
@ -445,7 +445,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||||||
{:ok, object} =
|
{:ok, object} =
|
||||||
ActivityPub.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
ActivityPub.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
||||||
|
|
||||||
assert activity = Activity.get_create_activity_by_object_ap_id(object.data["id"])
|
assert activity = Activity.get_create_by_object_ap_id(object.data["id"])
|
||||||
assert activity.data["id"]
|
assert activity.data["id"]
|
||||||
|
|
||||||
{:ok, object_again} =
|
{:ok, object_again} =
|
||||||
@ -459,7 +459,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||||||
|
|
||||||
test "it works with objects only available via Ostatus" do
|
test "it works with objects only available via Ostatus" do
|
||||||
{:ok, object} = ActivityPub.fetch_object_from_id("https://shitposter.club/notice/2827873")
|
{:ok, object} = ActivityPub.fetch_object_from_id("https://shitposter.club/notice/2827873")
|
||||||
assert activity = Activity.get_create_activity_by_object_ap_id(object.data["id"])
|
assert activity = Activity.get_create_by_object_ap_id(object.data["id"])
|
||||||
assert activity.data["id"]
|
assert activity.data["id"]
|
||||||
|
|
||||||
{:ok, object_again} =
|
{:ok, object_again} =
|
||||||
|
57
test/web/activity_pub/mrf/anti_followbot_policy_test.exs
Normal file
57
test/web/activity_pub/mrf/anti_followbot_policy_test.exs
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.AntiFollowbotPolicyTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy
|
||||||
|
|
||||||
|
describe "blocking based on attributes" do
|
||||||
|
test "matches followbots by nickname" do
|
||||||
|
actor = insert(:user, %{nickname: "followbot@example.com"})
|
||||||
|
target = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type" => "Follow",
|
||||||
|
"actor" => actor.ap_id,
|
||||||
|
"object" => target.ap_id,
|
||||||
|
"id" => "https://example.com/activities/1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
{:reject, nil} = AntiFollowbotPolicy.filter(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "matches followbots by display name" do
|
||||||
|
actor = insert(:user, %{name: "Federation Bot"})
|
||||||
|
target = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type" => "Follow",
|
||||||
|
"actor" => actor.ap_id,
|
||||||
|
"object" => target.ap_id,
|
||||||
|
"id" => "https://example.com/activities/1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
{:reject, nil} = AntiFollowbotPolicy.filter(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it allows non-followbots" do
|
||||||
|
actor = insert(:user)
|
||||||
|
target = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type" => "Follow",
|
||||||
|
"actor" => actor.ap_id,
|
||||||
|
"object" => target.ap_id,
|
||||||
|
"id" => "https://example.com/activities/1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, _} = AntiFollowbotPolicy.filter(message)
|
||||||
|
end
|
||||||
|
end
|
@ -51,7 +51,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||||||
{:ok, returned_activity} = Transmogrifier.handle_incoming(data)
|
{:ok, returned_activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
assert activity =
|
assert activity =
|
||||||
Activity.get_create_activity_by_object_ap_id(
|
Activity.get_create_by_object_ap_id(
|
||||||
"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
|
"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -162,6 +162,36 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||||||
assert data["object"]["url"] == "https://prismo.news/posts/83"
|
assert data["object"]["url"] == "https://prismo.news/posts/83"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it cleans up incoming notices which are not really DMs" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
to = [user.ap_id, other_user.ap_id]
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("to", to)
|
||||||
|
|> Map.put("cc", [])
|
||||||
|
|
||||||
|
object =
|
||||||
|
data["object"]
|
||||||
|
|> Map.put("to", to)
|
||||||
|
|> Map.put("cc", [])
|
||||||
|
|
||||||
|
data = Map.put(data, "object", object)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["to"] == []
|
||||||
|
assert data["cc"] == to
|
||||||
|
|
||||||
|
object = data["object"]
|
||||||
|
|
||||||
|
assert object["to"] == []
|
||||||
|
assert object["cc"] == to
|
||||||
|
end
|
||||||
|
|
||||||
test "it works for incoming follow requests" do
|
test "it works for incoming follow requests" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
@ -263,7 +293,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||||||
assert data["object"] ==
|
assert data["object"] ==
|
||||||
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
|
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
|
||||||
|
|
||||||
assert Activity.get_create_activity_by_object_ap_id(data["object"])
|
assert Activity.get_create_by_object_ap_id(data["object"])
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming announces with an existing activity" do
|
test "it works for incoming announces with an existing activity" do
|
||||||
@ -285,7 +315,23 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||||||
|
|
||||||
assert data["object"] == activity.data["object"]["id"]
|
assert data["object"] == activity.data["object"]["id"]
|
||||||
|
|
||||||
assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id
|
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it does not clobber the addressing on announce activities" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"]["id"])
|
||||||
|
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|
||||||
|
|> Map.put("cc", [])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming update activities" do
|
test "it works for incoming update activities" do
|
||||||
@ -856,6 +902,34 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||||||
assert modified["object"]["likes"]["type"] == "OrderedCollection"
|
assert modified["object"]["likes"]["type"] == "OrderedCollection"
|
||||||
assert modified["object"]["likes"]["totalItems"] == 0
|
assert modified["object"]["likes"]["totalItems"] == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "the directMessage flag is present" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "2hu :moominmamma:"})
|
||||||
|
|
||||||
|
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
|
|
||||||
|
assert modified["directMessage"] == false
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => "@#{other_user.nickname} :moominmamma:"})
|
||||||
|
|
||||||
|
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
|
|
||||||
|
assert modified["directMessage"] == false
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" => "@#{other_user.nickname} :moominmamma:",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
|
|
||||||
|
assert modified["directMessage"] == true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "user upgrade" do
|
describe "user upgrade" do
|
||||||
|
57
test/web/activity_pub/utils_test.exs
Normal file
57
test/web/activity_pub/utils_test.exs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
|
||||||
|
describe "determine_explicit_mentions()" do
|
||||||
|
test "works with an object that has mentions" do
|
||||||
|
object = %{
|
||||||
|
"tag" => [
|
||||||
|
%{
|
||||||
|
"type" => "Mention",
|
||||||
|
"href" => "https://example.com/~alyssa",
|
||||||
|
"name" => "Alyssa P. Hacker"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "works with an object that does not have mentions" do
|
||||||
|
object = %{
|
||||||
|
"tag" => [
|
||||||
|
%{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Utils.determine_explicit_mentions(object) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "works with an object that has mentions and other tags" do
|
||||||
|
object = %{
|
||||||
|
"tag" => [
|
||||||
|
%{
|
||||||
|
"type" => "Mention",
|
||||||
|
"href" => "https://example.com/~alyssa",
|
||||||
|
"name" => "Alyssa P. Hacker"
|
||||||
|
},
|
||||||
|
%{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "works with an object that has no tags" do
|
||||||
|
object = %{}
|
||||||
|
|
||||||
|
assert Utils.determine_explicit_mentions(object) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "works with an object that has only IR tags" do
|
||||||
|
object = %{"tag" => ["2hu"]}
|
||||||
|
|
||||||
|
assert Utils.determine_explicit_mentions(object) == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -17,6 +17,13 @@ defmodule Pleroma.Web.CommonAPI.Test do
|
|||||||
assert activity.data["object"]["tag"] == ["2hu"]
|
assert activity.data["object"]["tag"] == ["2hu"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it adds emoji in the object" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => ":moominmamma:"})
|
||||||
|
|
||||||
|
assert activity.data["object"]["emoji"]["moominmamma"]
|
||||||
|
end
|
||||||
|
|
||||||
test "it adds emoji when updating profiles" do
|
test "it adds emoji when updating profiles" do
|
||||||
user = insert(:user, %{name: ":karjalanpiirakka:"})
|
user = insert(:user, %{name: ":karjalanpiirakka:"})
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||||||
alias Pleroma.Web.{OStatus, CommonAPI}
|
alias Pleroma.Web.{OStatus, CommonAPI}
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.MastodonAPI.FilterView
|
alias Pleroma.Web.MastodonAPI.FilterView
|
||||||
|
alias Ecto.Changeset
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
import Tesla.Mock
|
import Tesla.Mock
|
||||||
@ -1483,6 +1484,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||||||
|
|
||||||
{:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
|
{:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
|
||||||
|
|
||||||
|
# Stats should count users with missing or nil `info.deactivated` value
|
||||||
|
user = Repo.get(User, user.id)
|
||||||
|
info_change = Changeset.change(user.info, %{deactivated: nil})
|
||||||
|
|
||||||
|
{:ok, _user} =
|
||||||
|
user
|
||||||
|
|> Changeset.change()
|
||||||
|
|> Changeset.put_embed(:info, info_change)
|
||||||
|
|> User.update_and_set_cache()
|
||||||
|
|
||||||
Pleroma.Stats.update_stats()
|
Pleroma.Stats.update_stats()
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/instance")
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
@ -202,7 +202,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||||||
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
|
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
|
||||||
)
|
)
|
||||||
|
|
||||||
%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"])
|
||||||
|
|
||||||
represented = StatusView.render("status.json", %{for: user, activity: activity})
|
represented = StatusView.render("status.json", %{for: user, activity: activity})
|
||||||
|
|
||||||
|
@ -6,7 +6,8 @@ defmodule Pleroma.Web.StreamerTest do
|
|||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
alias Pleroma.{List, User}
|
alias Pleroma.List
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
@ -35,6 +36,28 @@ defmodule Pleroma.Web.StreamerTest do
|
|||||||
Streamer.push_to_socket(topics, "public", activity)
|
Streamer.push_to_socket(topics, "public", activity)
|
||||||
|
|
||||||
Task.await(task)
|
Task.await(task)
|
||||||
|
|
||||||
|
task =
|
||||||
|
Task.async(fn ->
|
||||||
|
assert_receive {:text, _}, 4_000
|
||||||
|
end)
|
||||||
|
|
||||||
|
fake_socket = %{
|
||||||
|
transport_pid: task.pid,
|
||||||
|
assigns: %{
|
||||||
|
user: user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.delete(activity.id, other_user)
|
||||||
|
|
||||||
|
topics = %{
|
||||||
|
"public" => [fake_socket]
|
||||||
|
}
|
||||||
|
|
||||||
|
Streamer.push_to_socket(topics, "public", activity)
|
||||||
|
|
||||||
|
Task.await(task)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't send to blocked users" do
|
test "it doesn't send to blocked users" do
|
||||||
|
@ -797,7 +797,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||||||
|> with_credentials(current_user.nickname, "test")
|
|> with_credentials(current_user.nickname, "test")
|
||||||
|> post("/api/favorites/create/1.json")
|
|> post("/api/favorites/create/1.json")
|
||||||
|
|
||||||
assert json_response(conn, 500)
|
assert json_response(conn, 400)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1621,7 +1621,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> post("/api/pleroma/friendships/approve", %{"user_id" => to_string(other_user.id)})
|
|> post("/api/pleroma/friendships/approve", %{"user_id" => other_user.id})
|
||||||
|
|
||||||
assert relationship = json_response(conn, 200)
|
assert relationship = json_response(conn, 200)
|
||||||
assert other_user.id == relationship["id"]
|
assert other_user.id == relationship["id"]
|
||||||
@ -1644,7 +1644,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> post("/api/pleroma/friendships/deny", %{"user_id" => to_string(other_user.id)})
|
|> post("/api/pleroma/friendships/deny", %{"user_id" => other_user.id})
|
||||||
|
|
||||||
assert relationship = json_response(conn, 200)
|
assert relationship = json_response(conn, 200)
|
||||||
assert other_user.id == relationship["id"]
|
assert other_user.id == relationship["id"]
|
||||||
@ -1655,16 +1655,16 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
|||||||
describe "GET /api/pleroma/search_user" do
|
describe "GET /api/pleroma/search_user" do
|
||||||
test "it returns users, ordered by similarity", %{conn: conn} do
|
test "it returns users, ordered by similarity", %{conn: conn} do
|
||||||
user = insert(:user, %{name: "eal"})
|
user = insert(:user, %{name: "eal"})
|
||||||
user_two = insert(:user, %{name: "ean"})
|
user_two = insert(:user, %{name: "eal me"})
|
||||||
user_three = insert(:user, %{name: "ebn"})
|
_user_three = insert(:user, %{name: "zzz"})
|
||||||
|
|
||||||
resp =
|
resp =
|
||||||
conn
|
conn
|
||||||
|> get(twitter_api_search__path(conn, :search_user), query: "eal")
|
|> get(twitter_api_search__path(conn, :search_user), query: "eal me")
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert length(resp) == 3
|
assert length(resp) == 2
|
||||||
assert [user.id, user_two.id, user_three.id] == Enum.map(resp, fn %{"id" => id} -> id end)
|
assert [user_two.id, user.id] == Enum.map(resp, fn %{"id" => id} -> id end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -451,7 +451,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
assert represented["id"] == UserView.render("show.json", %{user: remote, for: user})["id"]
|
assert represented["id"] == UserView.render("show.json", %{user: remote, for: user})["id"]
|
||||||
|
|
||||||
# Also fetches the feed.
|
# Also fetches the feed.
|
||||||
# assert Activity.get_create_activity_by_object_ap_id("tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status")
|
# assert Activity.get_create_by_object_ap_id("tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -344,7 +344,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
|
|||||||
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
|
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
|
||||||
)
|
)
|
||||||
|
|
||||||
%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"])
|
||||||
|
|
||||||
result = ActivityView.render("activity.json", activity: activity)
|
result = ActivityView.render("activity.json", activity: activity)
|
||||||
|
|
||||||
|
@ -100,6 +100,7 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||||||
"locked" => false,
|
"locked" => false,
|
||||||
"default_scope" => "public",
|
"default_scope" => "public",
|
||||||
"no_rich_text" => false,
|
"no_rich_text" => false,
|
||||||
|
"hide_network" => false,
|
||||||
"fields" => [],
|
"fields" => [],
|
||||||
"pleroma" => %{
|
"pleroma" => %{
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
@ -146,6 +147,7 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||||||
"locked" => false,
|
"locked" => false,
|
||||||
"default_scope" => "public",
|
"default_scope" => "public",
|
||||||
"no_rich_text" => false,
|
"no_rich_text" => false,
|
||||||
|
"hide_network" => false,
|
||||||
"fields" => [],
|
"fields" => [],
|
||||||
"pleroma" => %{
|
"pleroma" => %{
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
@ -193,6 +195,7 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||||||
"locked" => false,
|
"locked" => false,
|
||||||
"default_scope" => "public",
|
"default_scope" => "public",
|
||||||
"no_rich_text" => false,
|
"no_rich_text" => false,
|
||||||
|
"hide_network" => false,
|
||||||
"fields" => [],
|
"fields" => [],
|
||||||
"pleroma" => %{
|
"pleroma" => %{
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
@ -254,6 +257,7 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
|
|||||||
"locked" => false,
|
"locked" => false,
|
||||||
"default_scope" => "public",
|
"default_scope" => "public",
|
||||||
"no_rich_text" => false,
|
"no_rich_text" => false,
|
||||||
|
"hide_network" => false,
|
||||||
"fields" => [],
|
"fields" => [],
|
||||||
"pleroma" => %{
|
"pleroma" => %{
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
|
Loading…
Reference in New Issue
Block a user