aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/healthcheck.ex60
-rw-r--r--lib/mix/tasks/pleroma/database.ex51
-rw-r--r--lib/mix/tasks/pleroma/emoji.ex293
-rw-r--r--lib/mix/tasks/pleroma/user.ex20
-rw-r--r--lib/pleroma/PasswordResetToken.ex2
-rw-r--r--lib/pleroma/activity.ex79
-rw-r--r--lib/pleroma/emoji.ex165
-rw-r--r--lib/pleroma/gopher/server.ex5
-rw-r--r--lib/pleroma/html.ex3
-rw-r--r--lib/pleroma/list.ex2
-rw-r--r--lib/pleroma/notification.ex2
-rw-r--r--lib/pleroma/object.ex22
-rw-r--r--lib/pleroma/object/containment.ex61
-rw-r--r--lib/pleroma/object/fetcher.ex75
-rw-r--r--lib/pleroma/user.ex41
-rw-r--r--lib/pleroma/user/info.ex2
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex107
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex3
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex97
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex10
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex23
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex24
-rw-r--r--lib/pleroma/web/channels/user_socket.ex2
-rw-r--r--lib/pleroma/web/common_api/common_api.ex13
-rw-r--r--lib/pleroma/web/common_api/utils.ex10
-rw-r--r--lib/pleroma/web/federator/federator.ex5
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api.ex28
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex118
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex90
-rw-r--r--lib/pleroma/web/mastodon_api/websocket_handler.ex2
-rw-r--r--lib/pleroma/web/oauth/oauth_controller.ex27
-rw-r--r--lib/pleroma/web/oauth/token.ex2
-rw-r--r--lib/pleroma/web/ostatus/activity_representer.ex42
-rw-r--r--lib/pleroma/web/ostatus/handlers/note_handler.ex5
-rw-r--r--lib/pleroma/web/ostatus/ostatus.ex2
-rw-r--r--lib/pleroma/web/router.ex3
-rw-r--r--lib/pleroma/web/streamer.ex2
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex32
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex9
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex8
-rw-r--r--lib/pleroma/web/twitter_api/views/activity_view.ex30
-rw-r--r--lib/pleroma/web/web_finger/web_finger.ex2
43 files changed, 1060 insertions, 521 deletions
diff --git a/lib/healthcheck.ex b/lib/healthcheck.ex
new file mode 100644
index 000000000..646fb3b9d
--- /dev/null
+++ b/lib/healthcheck.ex
@@ -0,0 +1,60 @@
+defmodule Pleroma.Healthcheck do
+ @moduledoc """
+ Module collects metrics about app and assign healthy status.
+ """
+ alias Pleroma.Healthcheck
+ alias Pleroma.Repo
+
+ defstruct pool_size: 0,
+ active: 0,
+ idle: 0,
+ memory_used: 0,
+ healthy: true
+
+ @type t :: %__MODULE__{
+ pool_size: non_neg_integer(),
+ active: non_neg_integer(),
+ idle: non_neg_integer(),
+ memory_used: number(),
+ healthy: boolean()
+ }
+
+ @spec system_info() :: t()
+ def system_info do
+ %Healthcheck{
+ memory_used: Float.round(:erlang.memory(:total) / 1024 / 1024, 2)
+ }
+ |> assign_db_info()
+ |> check_health()
+ end
+
+ defp assign_db_info(healthcheck) do
+ database = Application.get_env(:pleroma, Repo)[:database]
+
+ query =
+ "select state, count(pid) from pg_stat_activity where datname = '#{database}' group by state;"
+
+ result = Repo.query!(query)
+ pool_size = Application.get_env(:pleroma, Repo)[:pool_size]
+
+ db_info =
+ Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states ->
+ if state == "active" do
+ Map.put(states, :active, states.active + cnt)
+ else
+ Map.put(states, :idle, states.idle + cnt)
+ end
+ end)
+ |> Map.put(:pool_size, pool_size)
+
+ Map.merge(healthcheck, db_info)
+ end
+
+ @spec check_health(Healthcheck.t()) :: Healthcheck.t()
+ def check_health(%{pool_size: pool_size, active: active} = check)
+ when active >= pool_size do
+ %{check | healthy: false}
+ end
+
+ def check_health(check), do: check
+end
diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex
new file mode 100644
index 000000000..ab9a3a7ff
--- /dev/null
+++ b/lib/mix/tasks/pleroma/database.ex
@@ -0,0 +1,51 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Mix.Tasks.Pleroma.Database do
+ alias Mix.Tasks.Pleroma.Common
+ require Logger
+ use Mix.Task
+
+ @shortdoc "A collection of database related tasks"
+ @moduledoc """
+ A collection of database related tasks
+
+ ## Replace embedded objects with their references
+
+ Replaces embedded objects with references to them in the `objects` table. Only needs to be ran once. The reason why this is not a migration is because it could significantly increase the database size after being ran, however after this `VACUUM FULL` will be able to reclaim about 20% (really depends on what is in the database, your mileage may vary) of the db size before the migration.
+
+ mix pleroma.database remove_embedded_objects
+
+ Options:
+ - `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
+ """
+ def run(["remove_embedded_objects" | args]) do
+ {options, [], []} =
+ OptionParser.parse(
+ args,
+ strict: [
+ vacuum: :boolean
+ ]
+ )
+
+ Common.start_pleroma()
+ Logger.info("Removing embedded objects")
+
+ Pleroma.Repo.query!(
+ "update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
+ [],
+ timeout: :infinity
+ )
+
+ if Keyword.get(options, :vacuum) do
+ Logger.info("Runnning VACUUM FULL")
+
+ Pleroma.Repo.query!(
+ "vacuum full;",
+ [],
+ timeout: :infinity
+ )
+ end
+ end
+end
diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex
new file mode 100644
index 000000000..cced73226
--- /dev/null
+++ b/lib/mix/tasks/pleroma/emoji.ex
@@ -0,0 +1,293 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Mix.Tasks.Pleroma.Emoji do
+ use Mix.Task
+
+ @shortdoc "Manages emoji packs"
+ @moduledoc """
+ Manages emoji packs
+
+ ## ls-packs
+
+ mix pleroma.emoji ls-packs [OPTION...]
+
+ Lists the emoji packs and metadata specified in the manifest.
+
+ ### Options
+
+ - `-m, --manifest PATH/URL` - path to a custom manifest, it can
+ either be an URL starting with `http`, in that case the
+ manifest will be fetched from that address, or a local path
+
+ ## get-packs
+
+ mix pleroma.emoji get-packs [OPTION...] PACKS
+
+ Fetches, verifies and installs the specified PACKS from the
+ manifest into the `STATIC-DIR/emoji/PACK-NAME`
+
+ ### Options
+
+ - `-m, --manifest PATH/URL` - same as ls-packs
+
+ ## gen-pack
+
+ mix pleroma.emoji gen-pack PACK-URL
+
+ Creates a new manifest entry and a file list from the specified
+ remote pack file. Currently, only .zip archives are recognized
+ as remote pack files and packs are therefore assumed to be zip
+ archives. This command is intended to run interactively and will
+ first ask you some basic questions about the pack, then download
+ the remote file and generate an SHA256 checksum for it, then
+ generate an emoji file list for you.
+
+ The manifest entry will either be written to a newly created
+ `index.json` file or appended to the existing one, *replacing*
+ the old pack with the same name if it was in the file previously.
+
+ The file list will be written to the file specified previously,
+ *replacing* that file. You _should_ check that the file list doesn't
+ contain anything you don't need in the pack, that is, anything that is
+ not an emoji (the whole pack is downloaded, but only emoji files
+ are extracted).
+ """
+
+ @default_manifest Pleroma.Config.get!([:emoji, :default_manifest])
+
+ def run(["ls-packs" | args]) do
+ Application.ensure_all_started(:hackney)
+
+ {options, [], []} = parse_global_opts(args)
+
+ manifest =
+ fetch_manifest(if options[:manifest], do: options[:manifest], else: @default_manifest)
+
+ Enum.each(manifest, fn {name, info} ->
+ to_print = [
+ {"Name", name},
+ {"Homepage", info["homepage"]},
+ {"Description", info["description"]},
+ {"License", info["license"]},
+ {"Source", info["src"]}
+ ]
+
+ for {param, value} <- to_print do
+ IO.puts(IO.ANSI.format([:bright, param, :normal, ": ", value]))
+ end
+
+ # A newline
+ IO.puts("")
+ end)
+ end
+
+ def run(["get-packs" | args]) do
+ Application.ensure_all_started(:hackney)
+
+ {options, pack_names, []} = parse_global_opts(args)
+
+ manifest_url = if options[:manifest], do: options[:manifest], else: @default_manifest
+
+ manifest = fetch_manifest(manifest_url)
+
+ for pack_name <- pack_names do
+ if Map.has_key?(manifest, pack_name) do
+ pack = manifest[pack_name]
+ src_url = pack["src"]
+
+ IO.puts(
+ IO.ANSI.format([
+ "Downloading ",
+ :bright,
+ pack_name,
+ :normal,
+ " from ",
+ :underline,
+ src_url
+ ])
+ )
+
+ binary_archive = Tesla.get!(src_url).body
+ archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
+
+ sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
+
+ if archive_sha == String.upcase(pack["src_sha256"]) do
+ IO.puts(IO.ANSI.format(sha_status_text ++ [:green, "OK"]))
+ else
+ IO.puts(IO.ANSI.format(sha_status_text ++ [:red, "BAD"]))
+
+ raise "Bad SHA256 for #{pack_name}"
+ end
+
+ # The url specified in files should be in the same directory
+ files_url = Path.join(Path.dirname(manifest_url), pack["files"])
+
+ IO.puts(
+ IO.ANSI.format([
+ "Fetching the file list for ",
+ :bright,
+ pack_name,
+ :normal,
+ " from ",
+ :underline,
+ files_url
+ ])
+ )
+
+ files = Tesla.get!(files_url).body |> Poison.decode!()
+
+ IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
+
+ pack_path =
+ Path.join([
+ Pleroma.Config.get!([:instance, :static_dir]),
+ "emoji",
+ pack_name
+ ])
+
+ files_to_unzip =
+ Enum.map(
+ files,
+ fn {_, f} -> to_charlist(f) end
+ )
+
+ {:ok, _} =
+ :zip.unzip(binary_archive,
+ cwd: pack_path,
+ file_list: files_to_unzip
+ )
+
+ IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
+
+ emoji_txt_str =
+ Enum.map(
+ files,
+ fn {shortcode, path} ->
+ emojo_path = Path.join("/emoji/#{pack_name}", path)
+ "#{shortcode}, #{emojo_path}"
+ end
+ )
+ |> Enum.join("\n")
+
+ File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
+ else
+ IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
+ end
+ end
+ end
+
+ def run(["gen-pack", src]) do
+ Application.ensure_all_started(:hackney)
+
+ proposed_name = Path.basename(src) |> Path.rootname()
+ name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
+ # If there's no name, use the default one
+ name = if String.length(name) > 0, do: name, else: proposed_name
+
+ license = String.trim(IO.gets("License: "))
+ homepage = String.trim(IO.gets("Homepage: "))
+ description = String.trim(IO.gets("Description: "))
+
+ proposed_files_name = "#{name}.json"
+ files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
+ files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
+
+ default_exts = [".png", ".gif"]
+ default_exts_str = Enum.join(default_exts, " ")
+
+ exts =
+ String.trim(
+ IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
+ )
+
+ exts =
+ if String.length(exts) > 0 do
+ String.split(exts, " ")
+ |> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
+ else
+ default_exts
+ end
+
+ IO.puts("Downloading the pack and generating SHA256")
+
+ binary_archive = Tesla.get!(src).body
+ archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
+
+ IO.puts("SHA256 is #{archive_sha}")
+
+ pack_json = %{
+ name => %{
+ license: license,
+ homepage: homepage,
+ description: description,
+ src: src,
+ src_sha256: archive_sha,
+ files: files_name
+ }
+ }
+
+ tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
+
+ {:ok, _} =
+ :zip.unzip(
+ binary_archive,
+ cwd: tmp_pack_dir
+ )
+
+ emoji_map = Pleroma.Emoji.make_shortcode_to_file_map(tmp_pack_dir, exts)
+
+ File.write!(files_name, Poison.encode!(emoji_map, pretty: true))
+
+ IO.puts("""
+
+ #{files_name} has been created and contains the list of all found emojis in the pack.
+ Please review the files in the remove those not needed.
+ """)
+
+ if File.exists?("index.json") do
+ existing_data = File.read!("index.json") |> Poison.decode!()
+
+ File.write!(
+ "index.json",
+ Poison.encode!(
+ Map.merge(
+ existing_data,
+ pack_json
+ ),
+ pretty: true
+ )
+ )
+
+ IO.puts("index.json file has been update with the #{name} pack")
+ else
+ File.write!("index.json", Poison.encode!(pack_json, pretty: true))
+
+ IO.puts("index.json has been created with the #{name} pack")
+ end
+ end
+
+ defp fetch_manifest(from) do
+ Poison.decode!(
+ if String.starts_with?(from, "http") do
+ Tesla.get!(from).body
+ else
+ File.read!(from)
+ end
+ )
+ end
+
+ defp parse_global_opts(args) do
+ OptionParser.parse(
+ args,
+ strict: [
+ manifest: :string
+ ],
+ aliases: [
+ m: :manifest
+ ]
+ )
+ end
+end
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index 441168df2..b396ff0de 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -162,7 +162,7 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["rm", nickname]) do
Common.start_pleroma()
- with %User{local: true} = user <- User.get_by_nickname(nickname) do
+ with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
User.delete(user)
Mix.shell().info("User #{nickname} deleted.")
else
@@ -174,7 +174,7 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["toggle_activated", nickname]) do
Common.start_pleroma()
- with %User{} = user <- User.get_by_nickname(nickname) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
{:ok, user} = User.deactivate(user, !user.info.deactivated)
Mix.shell().info(
@@ -189,7 +189,7 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["reset_password", nickname]) do
Common.start_pleroma()
- with %User{local: true} = user <- User.get_by_nickname(nickname),
+ with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
Mix.shell().info("Generated password reset token for #{user.nickname}")
@@ -211,14 +211,14 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["unsubscribe", nickname]) do
Common.start_pleroma()
- with %User{} = user <- User.get_by_nickname(nickname) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
Mix.shell().info("Deactivating #{user.nickname}")
User.deactivate(user)
{:ok, friends} = User.get_friends(user)
Enum.each(friends, fn friend ->
- user = User.get_by_id(user.id)
+ user = User.get_cached_by_id(user.id)
Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}")
User.unfollow(user, friend)
@@ -226,7 +226,7 @@ defmodule Mix.Tasks.Pleroma.User do
:timer.sleep(500)
- user = User.get_by_id(user.id)
+ user = User.get_cached_by_id(user.id)
if Enum.empty?(user.following) do
Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
@@ -250,7 +250,7 @@ defmodule Mix.Tasks.Pleroma.User do
]
)
- with %User{local: true} = user <- User.get_by_nickname(nickname) do
+ with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
user =
case Keyword.get(options, :moderator) do
nil -> user
@@ -277,7 +277,7 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["tag", nickname | tags]) do
Common.start_pleroma()
- with %User{} = user <- User.get_by_nickname(nickname) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
user = user |> User.tag(tags)
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
@@ -290,7 +290,7 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["untag", nickname | tags]) do
Common.start_pleroma()
- with %User{} = user <- User.get_by_nickname(nickname) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
user = user |> User.untag(tags)
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
@@ -379,7 +379,7 @@ defmodule Mix.Tasks.Pleroma.User do
def run(["delete_activities", nickname]) do
Common.start_pleroma()
- with %User{local: true} = user <- User.get_by_nickname(nickname) do
+ with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
User.delete_user_activities(user)
Mix.shell().info("User #{nickname} statuses deleted.")
else
diff --git a/lib/pleroma/PasswordResetToken.ex b/lib/pleroma/PasswordResetToken.ex
index 7afbc8751..f31ea5bc5 100644
--- a/lib/pleroma/PasswordResetToken.ex
+++ b/lib/pleroma/PasswordResetToken.ex
@@ -39,7 +39,7 @@ defmodule Pleroma.PasswordResetToken do
def reset_password(token, data) do
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
- %User{} = user <- User.get_by_id(token.user_id),
+ %User{} = user <- User.get_cached_by_id(token.user_id),
{:ok, _user} <- User.reset_password(user, data),
{:ok, token} <- Repo.update(used_changeset(token)) do
{:ok, token}
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index e6507e5ca..4a2ded518 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -10,6 +10,7 @@ defmodule Pleroma.Activity do
alias Pleroma.Object
alias Pleroma.Repo
+ import Ecto.Changeset
import Ecto.Query
@type t :: %__MODULE__{}
@@ -79,6 +80,13 @@ defmodule Pleroma.Activity do
)
end
+ def change(struct, params \\ %{}) do
+ struct
+ |> cast(params, [:data])
+ |> validate_required([:data])
+ |> unique_constraint(:ap_id, name: :activities_unique_apid_index)
+ end
+
def get_by_ap_id_with_object(ap_id) do
Repo.one(
from(
@@ -196,21 +204,27 @@ defmodule Pleroma.Activity do
def create_by_object_ap_id_with_object(_), do: nil
- def get_create_by_object_ap_id_with_object(ap_id) do
+ def get_create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do
ap_id
|> create_by_object_ap_id_with_object()
|> Repo.one()
end
- def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
- def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
- def normalize(_), do: nil
+ def get_create_by_object_ap_id_with_object(_), do: nil
- def get_in_reply_to_activity(%Activity{data: %{"object" => %{"inReplyTo" => ap_id}}}) do
- get_create_by_object_ap_id(ap_id)
+ defp get_in_reply_to_activity_from_object(%Object{data: %{"inReplyTo" => ap_id}}) do
+ get_create_by_object_ap_id_with_object(ap_id)
end
- def get_in_reply_to_activity(_), do: nil
+ defp get_in_reply_to_activity_from_object(_), do: nil
+
+ def get_in_reply_to_activity(%Activity{data: %{"object" => object}}) do
+ get_in_reply_to_activity_from_object(Object.normalize(object))
+ end
+
+ def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
+ def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
+ def normalize(_), do: nil
def delete_by_ap_id(id) when is_binary(id) do
by_object_ap_id(id)
@@ -218,6 +232,7 @@ defmodule Pleroma.Activity do
|> Repo.delete_all()
|> elem(1)
|> Enum.find(fn
+ %{data: %{"type" => "Create", "object" => ap_id}} when is_binary(ap_id) -> ap_id == id
%{data: %{"type" => "Create", "object" => %{"id" => ap_id}}} -> ap_id == id
_ -> nil
end)
@@ -245,54 +260,4 @@ defmodule Pleroma.Activity do
|> where([s], s.actor == ^actor)
|> Repo.all()
end
-
- def increase_replies_count(nil), do: nil
-
- def increase_replies_count(object_ap_id) do
- from(a in create_by_object_ap_id(object_ap_id),
- update: [
- set: [
- data:
- fragment(
- """
- jsonb_set(?, '{object, repliesCount}',
- (coalesce((?->'object'->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true)
- """,
- a.data,
- a.data
- )
- ]
- ]
- )
- |> Repo.update_all([])
- |> case do
- {1, [activity]} -> activity
- _ -> {:error, "Not found"}
- end
- end
-
- def decrease_replies_count(nil), do: nil
-
- def decrease_replies_count(object_ap_id) do
- from(a in create_by_object_ap_id(object_ap_id),
- update: [
- set: [
- data:
- fragment(
- """
- jsonb_set(?, '{object, repliesCount}',
- (greatest(0, (?->'object'->>'repliesCount')::int - 1))::varchar::jsonb, true)
- """,
- a.data,
- a.data
- )
- ]
- ]
- )
- |> Repo.update_all([])
- |> case do
- {1, [activity]} -> activity
- _ -> {:error, "Not found"}
- end
- end
end
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index 87c7f2cec..6390cce4c 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -6,7 +6,7 @@ defmodule Pleroma.Emoji do
@moduledoc """
The emojis are loaded from:
- * the built-in Finmojis (if enabled in configuration),
+ * emoji packs in INSTANCE-DIR/emoji
* the files: `config/emoji.txt` and `config/custom_emoji.txt`
* glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder
@@ -14,6 +14,8 @@ defmodule Pleroma.Emoji do
"""
use GenServer
+ require Logger
+
@type pattern :: Regex.t() | module() | String.t()
@type patterns :: pattern() | [pattern()]
@type group_patterns :: keyword(patterns())
@@ -79,95 +81,94 @@ defmodule Pleroma.Emoji do
end
defp load do
- finmoji_enabled = Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)
+ emoji_dir_path =
+ Path.join(
+ Pleroma.Config.get!([:instance, :static_dir]),
+ "emoji"
+ )
+
+ case File.ls(emoji_dir_path) do
+ {:error, :enoent} ->
+ # The custom emoji directory doesn't exist,
+ # don't do anything
+ nil
+
+ {:error, e} ->
+ # There was some other error
+ Logger.error("Could not access the custom emoji directory #{emoji_dir_path}: #{e}")
+
+ {:ok, packs} ->
+ # Print the packs we've found
+ Logger.info("Found emoji packs: #{Enum.join(packs, ", ")}")
+
+ emojis =
+ Enum.flat_map(
+ packs,
+ fn pack -> load_pack(Path.join(emoji_dir_path, pack)) end
+ )
+
+ true = :ets.insert(@ets, emojis)
+ end
+
+ # Compat thing for old custom emoji handling & default emoji,
+ # it should run even if there are no emoji packs
shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || []
emojis =
- (load_finmoji(finmoji_enabled) ++
- load_from_file("config/emoji.txt") ++
+ (load_from_file("config/emoji.txt") ++
load_from_file("config/custom_emoji.txt") ++
load_from_globs(shortcode_globs))
|> Enum.reject(fn value -> value == nil end)
true = :ets.insert(@ets, emojis)
+
:ok
end
- @finmoji [
- "a_trusted_friend",
- "alandislands",
- "association",
- "auroraborealis",
- "baby_in_a_box",
- "bear",
- "black_gold",
- "christmasparty",
- "crosscountryskiing",
- "cupofcoffee",
- "education",
- "fashionista_finns",
- "finnishlove",
- "flag",
- "forest",
- "four_seasons_of_bbq",
- "girlpower",
- "handshake",
- "happiness",
- "headbanger",
- "icebreaker",
- "iceman",
- "joulutorttu",
- "kaamos",
- "kalsarikannit_f",
- "kalsarikannit_m",
- "karjalanpiirakka",
- "kicksled",
- "kokko",
- "lavatanssit",
- "losthopes_f",
- "losthopes_m",
- "mattinykanen",
- "meanwhileinfinland",
- "moominmamma",
- "nordicfamily",
- "out_of_office",
- "peacemaker",
- "perkele",
- "pesapallo",
- "polarbear",
- "pusa_hispida_saimensis",
- "reindeer",
- "sami",
- "sauna_f",
- "sauna_m",
- "sauna_whisk",
- "sisu",
- "stuck",
- "suomimainittu",
- "superfood",
- "swan",
- "the_cap",
- "the_conductor",
- "the_king",
- "the_voice",
- "theoriginalsanta",
- "tomoffinland",
- "torillatavataan",
- "unbreakable",
- "waiting",
- "white_nights",
- "woollysocks"
- ]
-
- defp load_finmoji(true) do
- Enum.map(@finmoji, fn finmoji ->
- file_name = "/finmoji/128px/#{finmoji}-128.png"
- group = match_extra(@groups, file_name)
- {finmoji, file_name, to_string(group)}
- end)
+ defp load_pack(pack_dir) do
+ pack_name = Path.basename(pack_dir)
+
+ emoji_txt = Path.join(pack_dir, "emoji.txt")
+
+ if File.exists?(emoji_txt) do
+ load_from_file(emoji_txt)
+ else
+ Logger.info(
+ "No emoji.txt found for pack \"#{pack_name}\", assuming all .png files are emoji"
+ )
+
+ make_shortcode_to_file_map(pack_dir, [".png"])
+ |> Enum.map(fn {shortcode, rel_file} ->
+ filename = Path.join("/emoji/#{pack_name}", rel_file)
+
+ {shortcode, filename, [to_string(match_extra(@groups, filename))]}
+ end)
+ end
+ end
+
+ def make_shortcode_to_file_map(pack_dir, exts) do
+ find_all_emoji(pack_dir, exts)
+ |> Enum.map(&Path.relative_to(&1, pack_dir))
+ |> Enum.map(fn f -> {f |> Path.basename() |> Path.rootname(), f} end)
+ |> Enum.into(%{})
end
- defp load_finmoji(_), do: []
+ def find_all_emoji(dir, exts) do
+ Enum.reduce(
+ File.ls!(dir),
+ [],
+ fn f, acc ->
+ filepath = Path.join(dir, f)
+
+ if File.dir?(filepath) do
+ acc ++ find_all_emoji(filepath, exts)
+ else
+ acc ++ [filepath]
+ end
+ end
+ )
+ |> Enum.filter(fn f -> Path.extname(f) in exts end)
+ end
defp load_from_file(file) do
if File.exists?(file) do
@@ -182,11 +183,11 @@ defmodule Pleroma.Emoji do
|> Stream.map(&String.trim/1)
|> Stream.map(fn line ->
case String.split(line, ~r/,\s*/) do
- [name, file, tags] ->
- {name, file, tags}
-
[name, file] ->
- {name, file, to_string(match_extra(@groups, file))}
+ {name, file, [to_string(match_extra(@groups, file))]}
+
+ [name, file | tags] ->
+ {name, file, tags}
_ ->
nil
@@ -209,7 +210,7 @@ defmodule Pleroma.Emoji do
tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path)))
shortcode = Path.basename(path, Path.extname(path))
external_path = Path.join("/", Path.relative_to(path, static_path))
- {shortcode, external_path, to_string(tag)}
+ {shortcode, external_path, [to_string(tag)]}
end)
end
diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex
index 6a56a6f67..1d2e0785c 100644
--- a/lib/pleroma/gopher/server.ex
+++ b/lib/pleroma/gopher/server.ex
@@ -38,6 +38,7 @@ end
defmodule Pleroma.Gopher.Server.ProtocolHandler do
alias Pleroma.Activity
alias Pleroma.HTML
+ alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
@@ -75,14 +76,14 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
|> Enum.map(fn activity ->
user = User.get_cached_by_ap_id(activity.data["actor"])
- object = activity.data["object"]
+ object = Object.normalize(activity)
like_count = object["like_count"] || 0
announcement_count = object["announcement_count"] || 0
link("Post ##{activity.id} by #{user.nickname}", "/notices/#{activity.id}") <>
info("#{like_count} likes, #{announcement_count} repeats") <>
"i\tfake\t(NULL)\t0\r\n" <>
- info(HTML.strip_tags(String.replace(activity.data["object"]["content"], "<br>", "\r")))
+ info(HTML.strip_tags(String.replace(object["content"], "<br>", "\r")))
end)
|> Enum.join("i\tfake\t(NULL)\t0\r\n")
end
diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex
index 2c701adb5..cf6c0ee0a 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -32,7 +32,8 @@ defmodule Pleroma.HTML do
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
Cachex.fetch!(:scrubber_cache, key, fn _key ->
- ensure_scrubbed_html(content, scrubbers, activity.data["object"]["fake"] || false)
+ object = Pleroma.Object.normalize(activity)
+ ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false)
end)
end
diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex
index 110be8355..a5b1cad68 100644
--- a/lib/pleroma/list.ex
+++ b/lib/pleroma/list.ex
@@ -80,7 +80,7 @@ defmodule Pleroma.List do
# Get lists to which the account belongs.
def get_lists_account_belongs(%User{} = owner, account_id) do
- user = User.get_by_id(account_id)
+ user = User.get_cached_by_id(account_id)
query =
from(
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index b357d5399..dd274cf6b 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -196,7 +196,7 @@ defmodule Pleroma.Notification do
def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
actor = activity.data["actor"]
- followed = User.get_by_ap_id(actor)
+ followed = User.get_cached_by_ap_id(actor)
User.following?(user, followed)
end
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 013d62157..740d687a3 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Object do
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.Object.Fetcher
alias Pleroma.ObjectTombstone
alias Pleroma.Repo
alias Pleroma.User
@@ -40,41 +41,44 @@ defmodule Pleroma.Object do
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
end
+ def normalize(_, fetch_remote \\ true)
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
# Use this whenever possible, especially when walking graphs in an O(N) loop!
- def normalize(%Activity{object: %Object{} = object}), do: object
+ def normalize(%Object{} = object, _), do: object
+ def normalize(%Activity{object: %Object{} = object}, _), do: object
# A hack for fake activities
- def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}) do
+ def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _) do
%Object{id: "pleroma:fake_object_id", data: data}
end
# Catch and log Object.normalize() calls where the Activity's child object is not
# preloaded.
- def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do
+ def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote) do
Logger.debug(
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
)
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
- normalize(ap_id)
+ normalize(ap_id, fetch_remote)
end
- def normalize(%Activity{data: %{"object" => ap_id}}) do
+ def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote) do
Logger.debug(
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
)
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
- normalize(ap_id)
+ normalize(ap_id, fetch_remote)
end
# Old way, try fetching the object through cache.
- def normalize(%{"id" => ap_id}), do: normalize(ap_id)
- def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id)
- def normalize(_), do: nil
+ def normalize(%{"id" => ap_id}, fetch_remote), do: normalize(ap_id, fetch_remote)
+ def normalize(ap_id, false) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id)
+ def normalize(ap_id, true) when is_binary(ap_id), do: Fetcher.fetch_object_from_id!(ap_id)
+ def normalize(_, _), do: nil
# Owned objects can only be mutated by their owner
def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}),
diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex
new file mode 100644
index 000000000..25bd911fb
--- /dev/null
+++ b/lib/pleroma/object/containment.ex
@@ -0,0 +1,61 @@
+defmodule Pleroma.Object.Containment do
+ @moduledoc """
+ # Object Containment
+
+ This module contains some useful functions for containing objects to specific
+ origins and determining those origins. They previously lived in the
+ ActivityPub `Transmogrifier` module.
+
+ Object containment is an important step in validating remote objects to prevent
+ spoofing, therefore removal of object containment functions is NOT recommended.
+ """
+ def get_actor(%{"actor" => actor}) when is_binary(actor) do
+ actor
+ end
+
+ def get_actor(%{"actor" => actor}) when is_list(actor) do
+ if is_binary(Enum.at(actor, 0)) do
+ Enum.at(actor, 0)
+ else
+ Enum.find(actor, fn %{"type" => type} -> type in ["Person", "Service", "Application"] end)
+ |> Map.get("id")
+ end
+ end
+
+ def get_actor(%{"actor" => %{"id" => id}}) when is_bitstring(id) do
+ id
+ end
+
+ def get_actor(%{"actor" => nil, "attributedTo" => actor}) when not is_nil(actor) do
+ get_actor(%{"actor" => actor})
+ end
+
+ @doc """
+ Checks that an imported AP object's actor matches the domain it came from.
+ """
+ def contain_origin(_id, %{"actor" => nil}), do: :error
+
+ def contain_origin(id, %{"actor" => _actor} = params) do
+ id_uri = URI.parse(id)
+ actor_uri = URI.parse(get_actor(params))
+
+ if id_uri.host == actor_uri.host do
+ :ok
+ else
+ :error
+ end
+ end
+
+ def contain_origin_from_id(_id, %{"id" => nil}), do: :error
+
+ def contain_origin_from_id(id, %{"id" => other_id} = _params) do
+ id_uri = URI.parse(id)
+ other_uri = URI.parse(other_id)
+
+ if id_uri.host == other_uri.host do
+ :ok
+ else
+ :error
+ end
+ end
+end
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
new file mode 100644
index 000000000..8d4bcc95e
--- /dev/null
+++ b/lib/pleroma/object/fetcher.ex
@@ -0,0 +1,75 @@
+defmodule Pleroma.Object.Fetcher do
+ alias Pleroma.Object
+ alias Pleroma.Object.Containment
+ alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.OStatus
+
+ require Logger
+
+ @httpoison Application.get_env(:pleroma, :httpoison)
+
+ # TODO:
+ # This will create a Create activity, which we need internally at the moment.
+ def fetch_object_from_id(id) do
+ if object = Object.get_cached_by_ap_id(id) do
+ {:ok, object}
+ else
+ Logger.info("Fetching #{id} via AP")
+
+ with {:ok, data} <- fetch_and_contain_remote_object_from_id(id),
+ nil <- Object.normalize(data, false),
+ params <- %{
+ "type" => "Create",
+ "to" => data["to"],
+ "cc" => data["cc"],
+ "actor" => data["actor"] || data["attributedTo"],
+ "object" => data
+ },
+ :ok <- Containment.contain_origin(id, params),
+ {:ok, activity} <- Transmogrifier.handle_incoming(params) do
+ {:ok, Object.normalize(activity, false)}
+ else
+ {:error, {:reject, nil}} ->
+ {:reject, nil}
+
+ object = %Object{} ->
+ {:ok, object}
+
+ _e ->
+ Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
+
+ case OStatus.fetch_activity_from_url(id) do
+ {:ok, [activity | _]} -> {:ok, Object.normalize(activity, false)}
+ e -> e
+ end
+ end
+ end
+ end
+
+ def fetch_object_from_id!(id) do
+ with {:ok, object} <- fetch_object_from_id(id) do
+ object
+ else
+ _e ->
+ nil
+ end
+ end
+
+ def fetch_and_contain_remote_object_from_id(id) do
+ Logger.info("Fetching object #{id} via AP")
+
+ with true <- String.starts_with?(id, "http"),
+ {:ok, %{body: body, status: code}} when code in 200..299 <-
+ @httpoison.get(
+ id,
+ [{:Accept, "application/activity+json"}]
+ ),
+ {:ok, data} <- Jason.decode(body),
+ :ok <- Containment.contain_origin_from_id(id, data) do
+ {:ok, data}
+ else
+ e ->
+ {:error, e}
+ end
+ end
+end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 78eb29ddd..f1feab279 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -269,6 +269,7 @@ defmodule Pleroma.User do
def register(%Ecto.Changeset{} = changeset) do
with {:ok, user} <- Repo.insert(changeset),
{:ok, user} <- autofollow_users(user),
+ {:ok, user} <- set_cache(user),
{:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
{:ok, _} <- try_send_confirmation_email(user) do
{:ok, user}
@@ -453,10 +454,13 @@ defmodule Pleroma.User do
name = List.last(String.split(ap_id, "/"))
nickname = "#{name}@#{domain}"
- get_by_nickname(nickname)
+ get_cached_by_nickname(nickname)
end
- def set_cache(user) do
+ def set_cache({:ok, user}), do: set_cache(user)
+ def set_cache({:error, err}), do: {:error, err}
+
+ def set_cache(%User{} = user) do
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
@@ -544,6 +548,7 @@ defmodule Pleroma.User do
with [_nick, _domain] <- String.split(nickname, "@"),
{:ok, user} <- fetch_by_nickname(nickname) do
if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
+ # TODO turn into job
{:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
end
@@ -1002,7 +1007,7 @@ defmodule Pleroma.User do
# helper to handle the block given only an actor's AP id
def block(blocker, %{ap_id: ap_id}) do
- block(blocker, User.get_by_ap_id(ap_id))
+ block(blocker, get_cached_by_ap_id(ap_id))
end
def unblock(blocker, %{ap_id: ap_id}) do
@@ -1032,7 +1037,7 @@ defmodule Pleroma.User do
end
def subscribed_to?(user, %{ap_id: ap_id}) do
- with %User{} = target <- User.get_by_ap_id(ap_id) do
+ with %User{} = target <- get_cached_by_ap_id(ap_id) do
Enum.member?(target.info.subscribers, user.ap_id)
end
end
@@ -1207,7 +1212,7 @@ defmodule Pleroma.User do
end
def get_or_fetch_by_ap_id(ap_id) do
- user = get_by_ap_id(ap_id)
+ user = get_cached_by_ap_id(ap_id)
if !is_nil(user) and !User.needs_update?(user) do
user
@@ -1230,7 +1235,7 @@ defmodule Pleroma.User do
def get_or_create_instance_user do
relay_uri = "#{Pleroma.Web.Endpoint.url()}/relay"
- if user = get_by_ap_id(relay_uri) do
+ if user = get_cached_by_ap_id(relay_uri) do
user
else
changes =
@@ -1277,13 +1282,11 @@ defmodule Pleroma.User do
defp blank?(n), do: n
def insert_or_update_user(data) do
- data =
- data
- |> Map.put(:name, blank?(data[:name]) || data[:nickname])
-
- cs = User.remote_user_creation(data)
-
- Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname)
+ data
+ |> Map.put(:name, blank?(data[:name]) || data[:nickname])
+ |> remote_user_creation()
+ |> Repo.insert(on_conflict: :replace_all, conflict_target: :nickname)
+ |> set_cache()
end
def ap_enabled?(%User{local: true}), do: true
@@ -1299,8 +1302,8 @@ defmodule Pleroma.User do
# this is because we have synchronous follow APIs and need to simulate them
# with an async handshake
def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
- with %User{} = a <- User.get_by_id(a.id),
- %User{} = b <- User.get_by_id(b.id) do
+ with %User{} = a <- User.get_cached_by_id(a.id),
+ %User{} = b <- User.get_cached_by_id(b.id) do
{:ok, a, b}
else
_e ->
@@ -1310,8 +1313,8 @@ defmodule Pleroma.User do
def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
with :ok <- :timer.sleep(timeout),
- %User{} = a <- User.get_by_id(a.id),
- %User{} = b <- User.get_by_id(b.id) do
+ %User{} = a <- User.get_cached_by_id(a.id),
+ %User{} = b <- User.get_cached_by_id(b.id) do
{:ok, a, b}
else
_e ->
@@ -1350,7 +1353,7 @@ defmodule Pleroma.User do
end
def tag(nickname, tags) when is_binary(nickname),
- do: tag(User.get_by_nickname(nickname), tags)
+ do: tag(get_by_nickname(nickname), tags)
def tag(%User{} = user, tags),
do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
@@ -1362,7 +1365,7 @@ defmodule Pleroma.User do
end
def untag(nickname, tags) when is_binary(nickname),
- do: untag(User.get_by_nickname(nickname), tags)
+ do: untag(get_by_nickname(nickname), tags)
def untag(%User{} = user, tags),
do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 5afa7988c..7f22a45b5 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -38,6 +38,7 @@ defmodule Pleroma.User.Info do
field(:salmon, :string, default: nil)
field(:hide_followers, :boolean, default: false)
field(:hide_follows, :boolean, default: false)
+ field(:hide_favorites, :boolean, default: true)
field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil)
@@ -202,6 +203,7 @@ defmodule Pleroma.User.Info do
:banner,
:hide_follows,
:hide_followers,
+ :hide_favorites,
:background,
:show_role
])
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index cb88ba308..604ffae7b 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Instances
alias Pleroma.Notification
alias Pleroma.Object
+ alias Pleroma.Object.Fetcher
alias Pleroma.Pagination
alias Pleroma.Repo
alias Pleroma.Upload
@@ -14,7 +15,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.Federator
- alias Pleroma.Web.OStatus
alias Pleroma.Web.WebFinger
import Ecto.Query
@@ -95,7 +95,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"type" => "Create"
}) do
if is_public?(object) do
- Activity.increase_replies_count(reply_ap_id)
Object.increase_replies_count(reply_ap_id)
end
end
@@ -106,7 +105,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
data: %{"inReplyTo" => reply_ap_id} = object
}) do
if is_public?(object) do
- Activity.decrease_replies_count(reply_ap_id)
Object.decrease_replies_count(reply_ap_id)
end
end
@@ -121,7 +119,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, map} <- MRF.filter(map),
{recipients, _, _} = get_recipients(map),
{:fake, false, map, recipients} <- {:fake, fake, map, recipients},
- {:ok, object} <- insert_full_object(map) do
+ {:ok, map, object} <- insert_full_object(map) do
{:ok, activity} =
Repo.insert(%Activity{
data: map,
@@ -170,6 +168,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
public = "https://www.w3.org/ns/activitystreams#Public"
if activity.data["type"] in ["Create", "Announce", "Delete"] do
+ object = Object.normalize(activity)
Pleroma.Web.Streamer.stream("user", activity)
Pleroma.Web.Streamer.stream("list", activity)
@@ -181,12 +180,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
if activity.data["type"] in ["Create"] do
- activity.data["object"]
+ object.data
|> Map.get("tag", [])
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
- if activity.data["object"]["attachment"] != [] do
+ if object.data["attachment"] != [] do
Pleroma.Web.Streamer.stream("public:media", activity)
if activity.local do
@@ -198,7 +197,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
if !Enum.member?(activity.data["cc"] || [], public) &&
!Enum.member?(
activity.data["to"],
- User.get_by_ap_id(activity.data["actor"]).follower_address
+ User.get_cached_by_ap_id(activity.data["actor"]).follower_address
),
do: Pleroma.Web.Streamer.stream("direct", activity)
end
@@ -572,37 +571,49 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_since(query, _), do: query
+ defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
+ raise "Can't use the child object without preloading!"
+ end
+
defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
when is_list(tag_reject) and tag_reject != [] do
from(
- activity in query,
- where: fragment(~s(\(not \(? #> '{"object","tag"}'\) \\?| ?\)), activity.data, ^tag_reject)
+ [_activity, object] in query,
+ where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
)
end
defp restrict_tag_reject(query, _), do: query
+ defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
+ raise "Can't use the child object without preloading!"
+ end
+
defp restrict_tag_all(query, %{"tag_all" => tag_all})
when is_list(tag_all) and tag_all != [] do
from(
- activity in query,
- where: fragment(~s(\(? #> '{"object","tag"}'\) \\?& ?), activity.data, ^tag_all)
+ [_activity, object] in query,
+ where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
)
end
defp restrict_tag_all(query, _), do: query
+ defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
+ raise "Can't use the child object without preloading!"
+ end
+
defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
from(
- activity in query,
- where: fragment(~s(\(? #> '{"object","tag"}'\) \\?| ?), activity.data, ^tag)
+ [_activity, object] in query,
+ where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
)
end
defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
from(
- activity in query,
- where: fragment(~s(? <@ (? #> '{"object","tag"}'\)), ^tag, activity.data)
+ [_activity, object] in query,
+ where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
)
end
@@ -667,10 +678,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_favorited_by(query, _), do: query
+ defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
+ raise "Can't use the child object without preloading!"
+ end
+
defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
from(
- activity in query,
- where: fragment(~s(not (? #> '{"object","attachment"}' = ?\)), activity.data, ^[])
+ [_activity, object] in query,
+ where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
)
end
@@ -866,7 +881,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
def fetch_and_prepare_user_from_ap_id(ap_id) do
- with {:ok, data} <- fetch_and_contain_remote_object_from_id(ap_id) do
+ with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
user_data_from_user_object(data)
else
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
@@ -874,7 +889,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
def make_user_from_ap_id(ap_id) do
- if _user = User.get_by_ap_id(ap_id) do
+ if _user = User.get_cached_by_ap_id(ap_id) do
Transmogrifier.upgrade_user_from_ap_id(ap_id)
else
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
@@ -976,60 +991,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- # TODO:
- # This will create a Create activity, which we need internally at the moment.
- def fetch_object_from_id(id) do
- if object = Object.get_cached_by_ap_id(id) do
- {:ok, object}
- else
- with {:ok, data} <- fetch_and_contain_remote_object_from_id(id),
- nil <- Object.normalize(data),
- params <- %{
- "type" => "Create",
- "to" => data["to"],
- "cc" => data["cc"],
- "actor" => data["actor"] || data["attributedTo"],
- "object" => data
- },
- :ok <- Transmogrifier.contain_origin(id, params),
- {:ok, activity} <- Transmogrifier.handle_incoming(params) do
- {:ok, Object.normalize(activity)}
- else
- {:error, {:reject, nil}} ->
- {:reject, nil}
-
- object = %Object{} ->
- {:ok, object}
-
- _e ->
- Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
-
- case OStatus.fetch_activity_from_url(id) do
- {:ok, [activity | _]} -> {:ok, Object.normalize(activity)}
- e -> e
- end
- end
- end
- end
-
- def fetch_and_contain_remote_object_from_id(id) do
- Logger.info("Fetching object #{id} via AP")
-
- with true <- String.starts_with?(id, "http"),
- {:ok, %{body: body, status: code}} when code in 200..299 <-
- @httpoison.get(
- id,
- [{:Accept, "application/activity+json"}]
- ),
- {:ok, data} <- Jason.decode(body),
- :ok <- Transmogrifier.contain_origin_from_id(id, data) do
- {:ok, data}
- else
- e ->
- {:error, e}
- end
- end
-
# filter out broken threads
def contain_broken_threads(%Activity{} = activity, %User{} = user) do
entire_thread_visible_for_user?(activity, user)
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 3331ebebd..0b80566bf 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.Object.Fetcher
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectView
@@ -173,7 +174,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
"Signature missing or not from author, relayed Create message, fetching object from source"
)
- ActivityPub.fetch_object_from_id(params["object"]["id"])
+ Fetcher.fetch_object_from_id(params["object"]["id"])
json(conn, "ok")
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 39cd31921..52666a409 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -8,8 +8,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"""
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.Object.Containment
alias Pleroma.Repo
alias Pleroma.User
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
@@ -18,56 +20,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
require Logger
- def get_actor(%{"actor" => actor}) when is_binary(actor) do
- actor
- end
-
- def get_actor(%{"actor" => actor}) when is_list(actor) do
- if is_binary(Enum.at(actor, 0)) do
- Enum.at(actor, 0)
- else
- Enum.find(actor, fn %{"type" => type} -> type in ["Person", "Service", "Application"] end)
- |> Map.get("id")
- end
- end
-
- def get_actor(%{"actor" => %{"id" => id}}) when is_bitstring(id) do
- id
- end
-
- def get_actor(%{"actor" => nil, "attributedTo" => actor}) when not is_nil(actor) do
- get_actor(%{"actor" => actor})
- end
-
- @doc """
- Checks that an imported AP object's actor matches the domain it came from.
- """
- def contain_origin(_id, %{"actor" => nil}), do: :error
-
- def contain_origin(id, %{"actor" => _actor} = params) do
- id_uri = URI.parse(id)
- actor_uri = URI.parse(get_actor(params))
-
- if id_uri.host == actor_uri.host do
- :ok
- else
- :error
- end
- end
-
- def contain_origin_from_id(_id, %{"id" => nil}), do: :error
-
- def contain_origin_from_id(id, %{"id" => other_id} = _params) do
- id_uri = URI.parse(id)
- other_uri = URI.parse(other_id)
-
- if id_uri.host == other_uri.host do
- :ok
- else
- :error
- end
- end
-
@doc """
Modifies an incoming AP object (mastodon format) to our internal format.
"""
@@ -188,7 +140,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_actor(%{"attributedTo" => actor} = object) do
object
- |> Map.put("actor", get_actor(%{"actor" => actor}))
+ |> Map.put("actor", Containment.get_actor(%{"actor" => actor}))
end
# Check for standardisation
@@ -223,7 +175,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
""
end
- case fetch_obj_helper(in_reply_to_id) do
+ case get_obj_helper(in_reply_to_id) do
{:ok, replied_object} ->
with %Activity{} = _activity <-
Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
@@ -448,7 +400,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# - emoji
def handle_incoming(%{"type" => "Create", "object" => %{"type" => objtype} = object} = data)
when objtype in ["Article", "Note", "Video", "Page"] do
- actor = get_actor(data)
+ actor = Containment.get_actor(data)
data =
Map.put(data, "actor", actor)
@@ -506,7 +458,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data
) do
- with actor <- get_actor(data),
+ with actor <- Containment.get_actor(data),
%User{} = followed <- User.get_or_fetch_by_ap_id(actor),
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
@@ -532,7 +484,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data
) do
- with actor <- get_actor(data),
+ with actor <- Containment.get_actor(data),
%User{} = followed <- User.get_or_fetch_by_ap_id(actor),
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
@@ -556,9 +508,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data
) do
- with actor <- get_actor(data),
+ with actor <- Containment.get_actor(data),
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
+ {:ok, object} <- get_obj_helper(object_id),
{:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do
{:ok, activity}
else
@@ -569,9 +521,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data
) do
- with actor <- get_actor(data),
+ with actor <- Containment.get_actor(data),
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
+ {:ok, object} <- get_obj_helper(object_id),
public <- Visibility.is_public?(data),
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
{:ok, activity}
@@ -585,7 +537,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
data
)
when object_type in ["Person", "Application", "Service", "Organization"] do
- with %User{ap_id: ^actor_id} = actor <- User.get_by_ap_id(object["id"]) do
+ with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
banner = new_user_data[:info]["banner"]
@@ -624,10 +576,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
) do
object_id = Utils.get_ap_id(object_id)
- with actor <- get_actor(data),
+ with actor <- Containment.get_actor(data),
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
- :ok <- contain_origin(actor.ap_id, object.data),
+ {:ok, object} <- get_obj_helper(object_id),
+ :ok <- Containment.contain_origin(actor.ap_id, object.data),
{:ok, activity} <- ActivityPub.delete(object, false) do
{:ok, activity}
else
@@ -643,9 +595,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"id" => id
} = data
) do
- with actor <- get_actor(data),
+ with actor <- Containment.get_actor(data),
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
+ {:ok, object} <- get_obj_helper(object_id),
{:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do
{:ok, activity}
else
@@ -713,9 +665,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"id" => id
} = data
) do
- with actor <- get_actor(data),
+ with actor <- Containment.get_actor(data),
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
+ {:ok, object} <- get_obj_helper(object_id),
{:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do
{:ok, activity}
else
@@ -725,9 +677,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(_), do: :error
- def fetch_obj_helper(id) when is_bitstring(id), do: ActivityPub.fetch_object_from_id(id)
- def fetch_obj_helper(obj) when is_map(obj), do: ActivityPub.fetch_object_from_id(obj["id"])
-
def get_obj_helper(id) do
if object = Object.normalize(id), do: {:ok, object}, else: nil
end
@@ -764,9 +713,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# internal -> Mastodon
# """
- def prepare_outgoing(%{"type" => "Create", "object" => object} = data) do
+ def prepare_outgoing(%{"type" => "Create", "object" => object_id} = data) do
object =
- object
+ Object.normalize(object_id).data
|> prepare_object
data =
@@ -827,7 +776,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def maybe_fix_object_url(data) do
if is_binary(data["object"]) and not String.starts_with?(data["object"], "http") do
- case fetch_obj_helper(data["object"]) do
+ case get_obj_helper(data["object"]) do
{:ok, relative_object} ->
if relative_object.data["external_url"] do
_data =
@@ -1015,7 +964,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def upgrade_user_from_ap_id(ap_id) do
- with %User{local: false} = user <- User.get_by_ap_id(ap_id),
+ with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
already_ap <- User.ap_enabled?(user),
{:ok, user} <- user |> User.upgrade_changeset(data) |> User.update_and_set_cache() do
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index ccc9da7c6..581b9d1ab 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -234,14 +234,18 @@ defmodule Pleroma.Web.ActivityPub.Utils do
@doc """
Inserts a full object if it is contained in an activity.
"""
- def insert_full_object(%{"object" => %{"type" => type} = object_data})
+ def insert_full_object(%{"object" => %{"type" => type} = object_data} = map)
when is_map(object_data) and type in @supported_object_types do
with {:ok, object} <- Object.create(object_data) do
- {:ok, object}
+ map =
+ map
+ |> Map.put("object", object.data["id"])
+
+ {:ok, map, object}
end
end
- def insert_full_object(_), do: {:ok, nil}
+ def insert_full_object(map), do: {:ok, map, nil}
def update_object_in_activities(%{data: %{"id" => id}} = object) do
# TODO
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index db52fe933..6dee61dd6 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -41,16 +41,21 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
# guard
def entire_thread_visible_for_user?(nil, _user), do: false
- # child
+ # XXX: Probably even more inefficient than the previous implementation intended to be a placeholder untill https://git.pleroma.social/pleroma/pleroma/merge_requests/971 is in develop
+ # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
+
def entire_thread_visible_for_user?(
- %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
+ %Activity{} = tail,
+ # %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
user
- )
- when is_binary(parent_id) do
- parent = Activity.get_in_reply_to_activity(tail)
- visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
+ ) do
+ case Object.normalize(tail) do
+ %{data: %{"inReplyTo" => parent_id}} when is_binary(parent_id) ->
+ parent = Activity.get_in_reply_to_activity(tail)
+ visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
+
+ _ ->
+ visible_for_user?(tail, user)
+ end
end
-
- # root
- def entire_thread_visible_for_user?(tail, user), do: visible_for_user?(tail, user)
end
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index c436715d5..711f233a6 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -19,7 +19,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
action_fallback(:errors)
def user_delete(conn, %{"nickname" => nickname}) do
- User.get_by_nickname(nickname)
+ User.get_cached_by_nickname(nickname)
|> User.delete()
conn
@@ -27,8 +27,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
- with %User{} = follower <- User.get_by_nickname(follower_nick),
- %User{} = followed <- User.get_by_nickname(followed_nick) do
+ with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
+ %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
User.follow(follower, followed)
end
@@ -37,8 +37,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
- with %User{} = follower <- User.get_by_nickname(follower_nick),
- %User{} = followed <- User.get_by_nickname(followed_nick) do
+ with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
+ %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
User.unfollow(follower, followed)
end
@@ -67,7 +67,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def user_show(conn, %{"nickname" => nickname}) do
- with %User{} = user <- User.get_by_nickname(nickname) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
conn
|> json(AccountView.render("show.json", %{user: user}))
else
@@ -76,7 +76,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def user_toggle_activation(conn, %{"nickname" => nickname}) do
- user = User.get_by_nickname(nickname)
+ user = User.get_cached_by_nickname(nickname)
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
@@ -131,7 +131,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
when permission_group in ["moderator", "admin"] do
- user = User.get_by_nickname(nickname)
+ user = User.get_cached_by_nickname(nickname)
info =
%{}
@@ -156,7 +156,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def right_get(conn, %{"nickname" => nickname}) do
- user = User.get_by_nickname(nickname)
+ user = User.get_cached_by_nickname(nickname)
conn
|> json(%{
@@ -178,7 +178,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> put_status(403)
|> json(%{error: "You can't revoke your own admin status."})
else
- user = User.get_by_nickname(nickname)
+ user = User.get_cached_by_nickname(nickname)
info =
%{}
@@ -204,7 +204,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
with {:ok, status} <- Ecto.Type.cast(:boolean, status),
- %User{} = user <- User.get_by_nickname(nickname),
+ %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, _} <- User.deactivate(user, !status),
do: json_response(conn, :no_content, "")
end
@@ -277,7 +277,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
@doc "Get a password reset token (base64 string) for given nickname"
def get_password_reset(conn, %{"nickname" => nickname}) do
- (%User{local: true} = user) = User.get_by_nickname(nickname)
+ (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
{:ok, token} = Pleroma.PasswordResetToken.create_token(user)
conn
diff --git a/lib/pleroma/web/channels/user_socket.ex b/lib/pleroma/web/channels/user_socket.ex
index 6503979a1..8e2759e3b 100644
--- a/lib/pleroma/web/channels/user_socket.ex
+++ b/lib/pleroma/web/channels/user_socket.ex
@@ -24,7 +24,7 @@ defmodule Pleroma.Web.UserSocket do
def connect(%{"token" => token}, socket) do
with true <- Pleroma.Config.get([:chat, :enabled]),
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
- %User{} = user <- Pleroma.User.get_by_id(user_id) do
+ %User{} = user <- Pleroma.User.get_cached_by_id(user_id) do
{:ok, assign(socket, :user_name, user.nickname)}
else
_e -> :error
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 74babdf14..cfbc5dc10 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -125,7 +125,10 @@ defmodule Pleroma.Web.CommonAPI do
"public"
in_reply_to ->
- Pleroma.Web.MastodonAPI.StatusView.get_visibility(in_reply_to.data["object"])
+ # XXX: these heuristics should be moved out of MastodonAPI.
+ with %Object{} = object <- Object.normalize(in_reply_to) do
+ Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
+ end
end
end
@@ -214,8 +217,10 @@ defmodule Pleroma.Web.CommonAPI do
with %Activity{
actor: ^user_ap_id,
data: %{
- "type" => "Create",
- "object" => %{
+ "type" => "Create"
+ },
+ object: %Object{
+ data: %{
"to" => object_to,
"type" => "Note"
}
@@ -279,7 +284,7 @@ defmodule Pleroma.Web.CommonAPI do
def report(user, data) do
with {:account_id, %{"account_id" => account_id}} <- {:account_id, data},
- {:account, %User{} = account} <- {:account, User.get_by_id(account_id)},
+ {:account, %User{} = account} <- {:account, User.get_cached_by_id(account_id)},
{:ok, {content_html, _, _}} <- make_report_content_html(data["comment"]),
{:ok, statuses} <- get_report_statuses(account, data),
{:ok, activity} <-
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 185292878..887f878c4 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -208,7 +208,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
context,
content_html,
attachments,
- inReplyTo,
+ in_reply_to,
tags,
cw \\ nil,
cc \\ []
@@ -225,9 +225,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
}
- if inReplyTo do
+ if in_reply_to do
+ in_reply_to_object = Object.normalize(in_reply_to)
+
object
- |> Map.put("inReplyTo", inReplyTo.data["object"]["id"])
+ |> Map.put("inReplyTo", in_reply_to_object.data["id"])
else
object
end
@@ -282,7 +284,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end
def confirm_current_password(user, password) do
- with %User{local: true} = db_user <- User.get_by_id(user.id),
+ with %User{local: true} = db_user <- User.get_cached_by_id(user.id),
true <- Pbkdf2.checkpw(password, db_user.password_hash) do
{:ok, db_user}
else
diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex
index c47328e13..29e178ba9 100644
--- a/lib/pleroma/web/federator/federator.ex
+++ b/lib/pleroma/web/federator/federator.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.Federator do
alias Pleroma.Activity
+ alias Pleroma.Object.Containment
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
@@ -136,7 +137,7 @@ defmodule Pleroma.Web.Federator do
# actor shouldn't be acting on objects outside their own AP server.
with {:ok, _user} <- ap_enabled_actor(params["actor"]),
nil <- Activity.normalize(params["id"]),
- :ok <- Transmogrifier.contain_origin_from_id(params["actor"], params),
+ :ok <- Containment.contain_origin_from_id(params["actor"], params),
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
{:ok, activity}
else
@@ -185,7 +186,7 @@ defmodule Pleroma.Web.Federator do
end
def ap_enabled_actor(id) do
- user = User.get_by_ap_id(id)
+ user = User.get_cached_by_ap_id(id)
if User.ap_enabled?(user) do
{:ok, user}
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex
index 382f07e6b..3a3ec7c2a 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex
@@ -7,6 +7,31 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
alias Pleroma.Pagination
alias Pleroma.ScheduledActivity
alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+
+ def follow(follower, followed, params \\ %{}) do
+ options = cast_params(params)
+ reblogs = options[:reblogs]
+
+ result =
+ if not User.following?(follower, followed) do
+ CommonAPI.follow(follower, followed)
+ else
+ {:ok, follower, followed, nil}
+ end
+
+ with {:ok, follower, followed, _} <- result do
+ reblogs
+ |> case do
+ false -> CommonAPI.hide_reblogs(follower, followed)
+ _ -> CommonAPI.show_reblogs(follower, followed)
+ end
+ |> case do
+ {:ok, follower} -> {:ok, follower}
+ _ -> {:ok, follower}
+ end
+ end
+ end
def get_followers(user, params \\ %{}) do
user
@@ -37,7 +62,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
defp cast_params(params) do
param_types = %{
- exclude_types: {:array, :string}
+ exclude_types: {:array, :string},
+ reblogs: :boolean
}
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 63fadce38..0ba8d9eea 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -4,13 +4,13 @@
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
use Pleroma.Web, :controller
-
alias Ecto.Changeset
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Filter
alias Pleroma.Notification
alias Pleroma.Object
+ alias Pleroma.Object.Fetcher
alias Pleroma.Pagination
alias Pleroma.Repo
alias Pleroma.ScheduledActivity
@@ -190,7 +190,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
"static_url" => url,
"visible_in_picker" => true,
"url" => url,
- "tags" => String.split(tags, ",")
+ "tags" => tags
}
end)
end
@@ -304,7 +304,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
- with %User{} = user <- User.get_by_id(params["id"]) do
+ with %User{} = user <- User.get_cached_by_id(params["id"]) do
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
conn
@@ -338,7 +338,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id(id),
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
true <- Visibility.visible_for_user?(activity, user) do
conn
|> put_view(StatusView)
@@ -487,7 +487,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user) do
+ with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
+ %Activity{} = announce <- Activity.normalize(announce.data) do
conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: announce, for: user, as: :activity})
@@ -496,7 +497,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
- %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
+ %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
@@ -543,10 +544,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id(id),
- %User{} = user <- User.get_by_nickname(user.nickname),
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ %Object{} = object <- Object.normalize(activity),
+ %User{} = user <- User.get_cached_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
- {:ok, user} <- User.bookmark(user, activity.data["object"]["id"]) do
+ {:ok, user} <- User.bookmark(user, object.data["id"]) do
conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
@@ -554,10 +556,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id(id),
- %User{} = user <- User.get_by_nickname(user.nickname),
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ %Object{} = object <- Object.normalize(activity),
+ %User{} = user <- User.get_cached_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
- {:ok, user} <- User.unbookmark(user, activity.data["object"]["id"]) do
+ {:ok, user} <- User.unbookmark(user, object.data["id"]) do
conn
|> put_view(StatusView)
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
@@ -681,7 +684,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def favourited_by(conn, %{"id" => id}) do
- with %Activity{data: %{"object" => %{"likes" => likes}}} <- Activity.get_by_id(id) do
+ with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id),
+ %Object{data: %{"likes" => likes}} <- Object.normalize(object) do
q = from(u in User, where: u.ap_id in ^likes)
users = Repo.all(q)
@@ -694,7 +698,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def reblogged_by(conn, %{"id" => id}) do
- with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Activity.get_by_id(id) do
+ with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id),
+ %Object{data: %{"announcements" => announces}} <- Object.normalize(object) do
q = from(u in User, where: u.ap_id in ^announces)
users = Repo.all(q)
@@ -745,7 +750,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
- with %User{} = user <- User.get_by_id(id),
+ with %User{} = user <- User.get_cached_by_id(id),
followers <- MastodonAPI.get_followers(user, params) do
followers =
cond do
@@ -762,7 +767,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
- with %User{} = user <- User.get_by_id(id),
+ with %User{} = user <- User.get_cached_by_id(id),
followers <- MastodonAPI.get_friends(user, params) do
followers =
cond do
@@ -787,7 +792,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
- with %User{} = follower <- User.get_by_id(id),
+ with %User{} = follower <- User.get_cached_by_id(id),
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
conn
|> put_view(AccountView)
@@ -801,7 +806,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
- with %User{} = follower <- User.get_by_id(id),
+ with %User{} = follower <- User.get_cached_by_id(id),
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
conn
|> put_view(AccountView)
@@ -817,8 +822,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
with {_, %User{} = followed} <- {:followed, User.get_cached_by_id(id)},
{_, true} <- {:followed, follower.id != followed.id},
- false <- User.following?(follower, followed),
- {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
+ {:ok, follower} <- MastodonAPI.follow(follower, followed, conn.params) do
conn
|> put_view(AccountView)
|> render("relationship.json", %{user: follower, target: followed})
@@ -826,19 +830,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
{:followed, _} ->
{:error, :not_found}
- true ->
- followed = User.get_cached_by_id(id)
-
- {:ok, follower} =
- case conn.params["reblogs"] do
- true -> CommonAPI.show_reblogs(follower, followed)
- false -> CommonAPI.hide_reblogs(follower, followed)
- end
-
- conn
- |> put_view(AccountView)
- |> render("relationship.json", %{user: follower, target: followed})
-
{:error, message} ->
conn
|> put_resp_content_type("application/json")
@@ -881,7 +872,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
- with %User{} = muted <- User.get_by_id(id),
+ with %User{} = muted <- User.get_cached_by_id(id),
{:ok, muter} <- User.mute(muter, muted) do
conn
|> put_view(AccountView)
@@ -895,7 +886,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do
- with %User{} = muted <- User.get_by_id(id),
+ with %User{} = muted <- User.get_cached_by_id(id),
{:ok, muter} <- User.unmute(muter, muted) do
conn
|> put_view(AccountView)
@@ -916,7 +907,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
- with %User{} = blocked <- User.get_by_id(id),
+ with %User{} = blocked <- User.get_cached_by_id(id),
{:ok, blocker} <- User.block(blocker, blocked),
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do
conn
@@ -931,7 +922,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
- with %User{} = blocked <- User.get_by_id(id),
+ with %User{} = blocked <- User.get_cached_by_id(id),
{:ok, blocker} <- User.unblock(blocker, blocked),
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
conn
@@ -997,7 +988,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def status_search(user, query) do
fetched =
if Regex.match?(~r/https?:/, query) do
- with {:ok, object} <- ActivityPub.fetch_object_from_id(query),
+ with {:ok, object} <- Fetcher.fetch_object_from_id(query),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user) do
[activity]
@@ -1008,13 +999,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
q =
from(
- a in Activity,
+ [a, o] in Activity.with_preloaded_object(Activity),
where: fragment("?->>'type' = 'Create'", a.data),
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
where:
fragment(
- "to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)",
- a.data,
+ "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
+ o.data,
^query
),
limit: 20,
@@ -1096,8 +1087,45 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> render("index.json", %{activities: activities, for: user, as: :activity})
end
+ def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
+ with %User{} = user <- User.get_by_id(id),
+ false <- user.info.hide_favorites do
+ params =
+ params
+ |> Map.put("type", "Create")
+ |> Map.put("favorited_by", user.ap_id)
+ |> Map.put("blocking_user", for_user)
+
+ recipients =
+ if for_user do
+ ["https://www.w3.org/ns/activitystreams#Public"] ++
+ [for_user.ap_id | for_user.following]
+ else
+ ["https://www.w3.org/ns/activitystreams#Public"]
+ end
+
+ activities =
+ recipients
+ |> ActivityPub.fetch_activities(params)
+ |> Enum.reverse()
+
+ conn
+ |> add_link_headers(:favourites, activities)
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: for_user, as: :activity})
+ else
+ nil ->
+ {:error, :not_found}
+
+ true ->
+ conn
+ |> put_status(403)
+ |> json(%{error: "Can't get favorites"})
+ end
+ end
+
def bookmarks(%{assigns: %{user: user}} = conn, _) do
- user = User.get_by_id(user.id)
+ user = User.get_cached_by_id(user.id)
activities =
user.bookmarks
@@ -1154,7 +1182,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
accounts
|> Enum.each(fn account_id ->
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
- %User{} = followed <- User.get_by_id(account_id) do
+ %User{} = followed <- User.get_cached_by_id(account_id) do
Pleroma.List.follow(list, followed)
end
end)
@@ -1166,7 +1194,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
accounts
|> Enum.each(fn account_id ->
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
- %User{} = followed <- Pleroma.User.get_by_id(account_id) do
+ %User{} = followed <- Pleroma.User.get_cached_by_id(account_id) do
Pleroma.List.unfollow(list, followed)
end
end)
@@ -1459,7 +1487,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def relationship_noop(%{assigns: %{user: user}} = conn, %{"id" => id}) do
Logger.debug("Unimplemented, returning unmodified relationship")
- with %User{} = target <- User.get_by_id(id) do
+ with %User{} = target <- User.get_cached_by_id(id) do
conn
|> put_view(AccountView)
|> render("relationship.json", %{user: user, target: target})
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index af56c4149..d87fdb15d 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -68,7 +68,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp do_render("account.json", %{user: user} = opts) do
image = User.avatar_url(user) |> MediaProxy.url()
header = User.banner_url(user) |> MediaProxy.url()
- user_info = User.user_info(user)
+ user_info = User.get_cached_user_info(user)
bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"]
emojis =
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index a9f607aa5..7dd80d708 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
alias Pleroma.Activity
alias Pleroma.HTML
+ alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI
@@ -19,8 +20,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp get_replied_to_activities(activities) do
activities
|> Enum.map(fn
- %{data: %{"type" => "Create", "object" => %{"inReplyTo" => in_reply_to}}} ->
- in_reply_to != "" && in_reply_to
+ %{data: %{"type" => "Create", "object" => object}} ->
+ object = Object.normalize(object)
+ object.data["inReplyTo"] != "" && object.data["inReplyTo"]
_ ->
nil
@@ -29,7 +31,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|> Activity.create_by_object_ap_id()
|> Repo.all()
|> Enum.reduce(%{}, fn activity, acc ->
- Map.put(acc, activity.data["object"]["id"], activity)
+ object = Object.normalize(activity)
+ Map.put(acc, object.data["id"], activity)
end)
end
@@ -55,8 +58,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp get_context_id(_), do: nil
defp reblogged?(activity, user) do
- object = activity.data["object"] || %{}
- present?(user && user.ap_id in (object["announcements"] || []))
+ object = Object.normalize(activity) || %{}
+ present?(user && user.ap_id in (object.data["announcements"] || []))
end
def render("index.json", opts) do
@@ -80,6 +83,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
reblogged_activity = Activity.get_create_by_object_ap_id(object)
reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity))
+ activity_object = Object.normalize(activity)
+ favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
+ bookmarked = opts[:for] && activity_object.data["id"] in opts[:for].bookmarks
+
mentions =
activity.recipients
|> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
@@ -100,8 +107,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
replies_count: 0,
favourites_count: 0,
reblogged: reblogged?(reblogged_activity, opts[:for]),
- favourited: false,
- bookmarked: false,
+ favourited: present?(favorited),
+ bookmarked: present?(bookmarked),
muted: false,
pinned: pinned?(activity, user),
sensitive: false,
@@ -122,14 +129,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
- def render("status.json", %{activity: %{data: %{"object" => object}} = activity} = opts) do
+ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
+ object = Object.normalize(activity)
+
user = get_user(activity.data["actor"])
- like_count = object["like_count"] || 0
- announcement_count = object["announcement_count"] || 0
+ like_count = object.data["like_count"] || 0
+ announcement_count = object.data["announcement_count"] || 0
- tags = object["tag"] || []
- sensitive = object["sensitive"] || Enum.member?(tags, "nsfw")
+ tags = object.data["tag"] || []
+ sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw")
mentions =
activity.recipients
@@ -137,15 +146,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|> Enum.filter(& &1)
|> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end)
- favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
- bookmarked = opts[:for] && object["id"] in opts[:for].bookmarks
+ favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
- attachment_data = object["attachment"] || []
+ bookmarked = opts[:for] && object.data["id"] in opts[:for].bookmarks
+
+ attachment_data = object.data["attachment"] || []
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
- created_at = Utils.to_masto_date(object["published"])
+ created_at = Utils.to_masto_date(object.data["published"])
reply_to = get_reply_to(activity, opts)
+
reply_to_user = reply_to && get_user(reply_to.data["actor"])
content =
@@ -167,7 +178,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
"mastoapi:content"
)
- summary = object["summary"] || ""
+ summary = object.data["summary"] || ""
summary_html =
summary
@@ -190,12 +201,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
if user.local do
Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, activity)
else
- object["external_url"] || object["id"]
+ object.data["external_url"] || object.data["id"]
end
%{
id: to_string(activity.id),
- uri: object["id"],
+ uri: object.data["id"],
url: url,
account: AccountView.render("account.json", %{user: user}),
in_reply_to_id: reply_to && to_string(reply_to.id),
@@ -205,7 +216,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
content: content_html,
created_at: created_at,
reblogs_count: announcement_count,
- replies_count: object["repliesCount"] || 0,
+ replies_count: object.data["repliesCount"] || 0,
favourites_count: like_count,
reblogged: reblogged?(activity, opts[:for]),
favourited: present?(favorited),
@@ -223,10 +234,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
website: nil
},
language: nil,
- emojis: build_emojis(activity.data["object"]["emoji"]),
+ emojis: build_emojis(object.data["emoji"]),
pleroma: %{
local: activity.local,
conversation_id: get_context_id(activity),
+ in_reply_to_account_acct: reply_to_user && reply_to_user.nickname,
content: %{"text/plain" => content_plaintext},
spoiler_text: %{"text/plain" => summary_plaintext}
}
@@ -305,15 +317,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
- with nil <- replied_to_activities[activity.data["object"]["inReplyTo"]] do
+ object = Object.normalize(activity)
+
+ with nil <- replied_to_activities[object.data["inReplyTo"]] do
# If user didn't participate in the thread
Activity.get_in_reply_to_activity(activity)
end
end
- def get_reply_to(%{data: %{"object" => object}}, _) do
- if object["inReplyTo"] && object["inReplyTo"] != "" do
- Activity.get_create_by_object_ap_id(object["inReplyTo"])
+ def get_reply_to(%{data: %{"object" => _object}} = activity, _) do
+ object = Object.normalize(activity)
+
+ if object.data["inReplyTo"] && object.data["inReplyTo"] != "" do
+ Activity.get_create_by_object_ap_id(object.data["inReplyTo"])
else
nil
end
@@ -321,8 +337,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
def get_visibility(object) do
public = "https://www.w3.org/ns/activitystreams#Public"
- to = object["to"] || []
- cc = object["cc"] || []
+ to = object.data["to"] || []
+ cc = object.data["cc"] || []
cond do
public in to ->
@@ -343,25 +359,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
end
- def render_content(%{"type" => "Video"} = object) do
- with name when not is_nil(name) and name != "" <- object["name"] do
- "<p><a href=\"#{object["id"]}\">#{name}</a></p>#{object["content"]}"
+ def render_content(%{data: %{"type" => "Video"}} = object) do
+ with name when not is_nil(name) and name != "" <- object.data["name"] do
+ "<p><a href=\"#{object.data["id"]}\">#{name}</a></p>#{object.data["content"]}"
else
- _ -> object["content"] || ""
+ _ -> object.data["content"] || ""
end
end
- def render_content(%{"type" => object_type} = object)
+ def render_content(%{data: %{"type" => object_type}} = object)
when object_type in ["Article", "Page"] do
- with summary when not is_nil(summary) and summary != "" <- object["name"],
- url when is_bitstring(url) <- object["url"] do
- "<p><a href=\"#{url}\">#{summary}</a></p>#{object["content"]}"
+ with summary when not is_nil(summary) and summary != "" <- object.data["name"],
+ url when is_bitstring(url) <- object.data["url"] do
+ "<p><a href=\"#{url}\">#{summary}</a></p>#{object.data["content"]}"
else
- _ -> object["content"] || ""
+ _ -> object.data["content"] || ""
end
end
- def render_content(object), do: object["content"] || ""
+ def render_content(object), do: object.data["content"] || ""
@doc """
Builds a dictionary tags.
diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex
index 1b3721e2b..abfa26754 100644
--- a/lib/pleroma/web/mastodon_api/websocket_handler.ex
+++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex
@@ -90,7 +90,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
# Authenticated streams.
defp allow_request(stream, {"access_token", access_token}) when stream in @streams do
with %Token{user_id: user_id} <- Repo.get_by(Token, token: access_token),
- user = %User{} <- User.get_by_id(user_id) do
+ user = %User{} <- User.get_cached_by_id(user_id) do
{:ok, user}
else
_ -> {:error, 403}
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index 9874bac23..688eaca11 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -23,6 +23,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
action_fallback(Pleroma.Web.OAuth.FallbackController)
+ # Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg
+ def authorize(conn, %{"authorization" => _} = params) do
+ {auth_attrs, params} = Map.pop(params, "authorization")
+ authorize(conn, Map.merge(params, auth_attrs))
+ end
+
def authorize(%{assigns: %{token: %Token{} = token}} = conn, params) do
if ControllerHelper.truthy_param?(params["force_login"]) do
do_authorize(conn, params)
@@ -44,21 +50,20 @@ defmodule Pleroma.Web.OAuth.OAuthController do
def authorize(conn, params), do: do_authorize(conn, params)
- defp do_authorize(conn, %{"authorization" => auth_attrs}), do: do_authorize(conn, auth_attrs)
-
- defp do_authorize(conn, auth_attrs) do
- app = Repo.get_by(App, client_id: auth_attrs["client_id"])
+ defp do_authorize(conn, params) do
+ app = Repo.get_by(App, client_id: params["client_id"])
available_scopes = (app && app.scopes) || []
- scopes = oauth_scopes(auth_attrs, nil) || available_scopes
+ scopes = oauth_scopes(params, nil) || available_scopes
+ # Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template
render(conn, Authenticator.auth_template(), %{
- response_type: auth_attrs["response_type"],
- client_id: auth_attrs["client_id"],
+ response_type: params["response_type"],
+ client_id: params["client_id"],
available_scopes: available_scopes,
scopes: scopes,
- redirect_uri: auth_attrs["redirect_uri"],
- state: auth_attrs["state"],
- params: auth_attrs
+ redirect_uri: params["redirect_uri"],
+ state: params["state"],
+ params: params
})
end
@@ -138,7 +143,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
fixed_token = fix_padding(params["code"]),
%Authorization{} = auth <-
Repo.get_by(Authorization, token: fixed_token, app_id: app.id),
- %User{} = user <- User.get_by_id(auth.user_id),
+ %User{} = user <- User.get_cached_by_id(auth.user_id),
{:ok, token} <- Token.exchange_token(app, auth),
{:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do
response = %{
diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex
index 2b5ad9b94..399140003 100644
--- a/lib/pleroma/web/oauth/token.ex
+++ b/lib/pleroma/web/oauth/token.ex
@@ -27,7 +27,7 @@ defmodule Pleroma.Web.OAuth.Token do
def exchange_token(app, auth) do
with {:ok, auth} <- Authorization.use_token(auth),
true <- auth.app_id == app.id do
- create_token(app, User.get_by_id(auth.user_id), auth.scopes)
+ create_token(app, User.get_cached_by_id(auth.user_id), auth.scopes)
end
end
diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex
index 1a1b74bb0..166691a09 100644
--- a/lib/pleroma/web/ostatus/activity_representer.ex
+++ b/lib/pleroma/web/ostatus/activity_representer.ex
@@ -54,23 +54,16 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
end)
end
- defp get_links(%{local: true, data: data}) do
+ defp get_links(%{local: true}, %{"id" => object_id}) do
h = fn str -> [to_charlist(str)] end
[
- {:link, [type: ['application/atom+xml'], href: h.(data["object"]["id"]), rel: 'self'], []},
- {:link, [type: ['text/html'], href: h.(data["object"]["id"]), rel: 'alternate'], []}
+ {:link, [type: ['application/atom+xml'], href: h.(object_id), rel: 'self'], []},
+ {:link, [type: ['text/html'], href: h.(object_id), rel: 'alternate'], []}
]
end
- defp get_links(%{
- local: false,
- data: %{
- "object" => %{
- "external_url" => external_url
- }
- }
- }) do
+ defp get_links(%{local: false}, %{"external_url" => external_url}) do
h = fn str -> [to_charlist(str)] end
[
@@ -78,7 +71,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
]
end
- defp get_links(_activity), do: []
+ defp get_links(_activity, _object_data), do: []
defp get_emoji_links(emojis) do
Enum.map(emojis, fn {emoji, file} ->
@@ -88,14 +81,16 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
def to_simple_form(activity, user, with_author \\ false)
- def to_simple_form(%{data: %{"object" => %{"type" => "Note"}}} = activity, user, with_author) do
+ def to_simple_form(%{data: %{"type" => "Create"}} = activity, user, with_author) do
h = fn str -> [to_charlist(str)] end
- updated_at = activity.data["object"]["published"]
- inserted_at = activity.data["object"]["published"]
+ object = Object.normalize(activity)
+
+ updated_at = object.data["published"]
+ inserted_at = object.data["published"]
attachments =
- Enum.map(activity.data["object"]["attachment"] || [], fn attachment ->
+ Enum.map(object.data["attachment"] || [], fn attachment ->
url = hd(attachment["url"])
{:link,
@@ -108,7 +103,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
mentions = activity.recipients |> get_mentions
categories =
- (activity.data["object"]["tag"] || [])
+ (object.data["tag"] || [])
|> Enum.map(fn tag ->
if is_binary(tag) do
{:category, [term: to_charlist(tag)], []}
@@ -118,11 +113,11 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
end)
|> Enum.filter(& &1)
- emoji_links = get_emoji_links(activity.data["object"]["emoji"] || %{})
+ emoji_links = get_emoji_links(object.data["emoji"] || %{})
summary =
- if activity.data["object"]["summary"] do
- [{:summary, [], h.(activity.data["object"]["summary"])}]
+ if object.data["summary"] do
+ [{:summary, [], h.(object.data["summary"])}]
else
[]
end
@@ -131,10 +126,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']},
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/post']},
# For notes, federate the object id.
- {:id, h.(activity.data["object"]["id"])},
+ {:id, h.(object.data["id"])},
{:title, ['New note by #{user.nickname}']},
- {:content, [type: 'html'],
- h.(activity.data["object"]["content"] |> String.replace(~r/[\n\r]/, ""))},
+ {:content, [type: 'html'], h.(object.data["content"] |> String.replace(~r/[\n\r]/, ""))},
{:published, h.(inserted_at)},
{:updated, h.(updated_at)},
{:"ostatus:conversation", [ref: h.(activity.data["context"])],
@@ -142,7 +136,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
{:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}
] ++
summary ++
- get_links(activity) ++
+ get_links(activity, object.data) ++
categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links
end
diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex
index db995ec77..ec6e5cfaf 100644
--- a/lib/pleroma/web/ostatus/handlers/note_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex
@@ -113,8 +113,9 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
cw <- OStatus.get_cw(entry),
in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to),
- in_reply_to <-
- (in_reply_to_activity && in_reply_to_activity.data["object"]["id"]) || in_reply_to,
+ in_reply_to_object <-
+ (in_reply_to_activity && Object.normalize(in_reply_to_activity)) || nil,
+ in_reply_to <- (in_reply_to_object && in_reply_to_object.data["id"]) || in_reply_to,
attachments <- OStatus.get_attachments(entry),
context <- get_context(entry, in_reply_to),
tags <- OStatus.get_tags(entry),
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
index 9a34d7ad5..4744c6d83 100644
--- a/lib/pleroma/web/ostatus/ostatus.ex
+++ b/lib/pleroma/web/ostatus/ostatus.ex
@@ -294,7 +294,7 @@ defmodule Pleroma.Web.OStatus do
}
with false <- update,
- %User{} = user <- User.get_by_ap_id(data.ap_id) do
+ %User{} = user <- User.get_cached_by_ap_id(data.ap_id) do
{:ok, user}
else
_e -> User.insert_or_update_user(data)
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 8b665d61b..ff4f08af5 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -135,6 +135,7 @@ defmodule Pleroma.Web.Router do
post("/password_reset", UtilController, :password_reset)
get("/emoji", UtilController, :emoji)
get("/captcha", UtilController, :captcha)
+ get("/healthcheck", UtilController, :healthcheck)
end
scope "/api/pleroma", Pleroma.Web do
@@ -394,6 +395,8 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id", MastodonAPIController, :user)
get("/search", MastodonAPIController, :search)
+
+ get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
end
end
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index a82109f92..72eaf2084 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -81,7 +81,7 @@ defmodule Pleroma.Web.Streamer do
_ ->
Pleroma.List.get_lists_from_activity(item)
|> Enum.filter(fn list ->
- owner = User.get_by_id(list.user_id)
+ owner = User.get_cached_by_id(list.user_id)
Visibility.visible_for_user?(item, owner)
end)
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index ed45ca735..1122e6c5d 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def show_password_reset(conn, %{"token" => token}) do
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
- %User{} = user <- User.get_by_id(token.user_id) do
+ %User{} = user <- User.get_cached_by_id(token.user_id) do
render(conn, "password_reset.html", %{
token: token,
user: user
@@ -75,7 +75,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
if is_status?(acct) do
- {:ok, object} = ActivityPub.fetch_object_from_id(acct)
+ {:ok, object} = Pleroma.Object.Fetcher.fetch_object_from_id(acct)
%Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
redirect(conn, to: "/notice/#{activity_id}")
else
@@ -101,7 +101,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
defp is_status?(acct) do
- case ActivityPub.fetch_and_contain_remote_object_from_id(acct) do
+ case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
{:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
true
@@ -113,13 +113,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def do_remote_follow(conn, %{
"authorization" => %{"name" => username, "password" => password, "id" => id}
}) do
- followee = User.get_by_id(id)
+ followee = User.get_cached_by_id(id)
avatar = User.avatar_url(followee)
name = followee.nickname
with %User{} = user <- User.get_cached_by_nickname(username),
true <- Pbkdf2.checkpw(password, user.password_hash),
- %User{} = _followed <- User.get_by_id(id),
+ %User{} = _followed <- User.get_cached_by_id(id),
{:ok, follower} <- User.follow(user, followee),
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
conn
@@ -141,7 +141,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
- with %User{} = followee <- User.get_by_id(id),
+ with %User{} = followee <- User.get_cached_by_id(id),
{:ok, follower} <- User.follow(user, followee),
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
conn
@@ -286,7 +286,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
emoji =
Emoji.get_all()
|> Enum.map(fn {short_code, path, tags} ->
- {short_code, %{image_url: path, tags: String.split(tags, ",")}}
+ {short_code, %{image_url: path, tags: tags}}
end)
|> Enum.into(%{})
@@ -363,4 +363,22 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def captcha(conn, _params) do
json(conn, Pleroma.Captcha.new())
end
+
+ def healthcheck(conn, _params) do
+ info =
+ if Pleroma.Config.get([:instance, :healthcheck]) do
+ Pleroma.Healthcheck.system_info()
+ else
+ %{}
+ end
+
+ conn =
+ if info[:healthy] do
+ conn
+ else
+ Plug.Conn.put_status(conn, :service_unavailable)
+ end
+
+ json(conn, info)
+ end
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index d6ce0a7c6..adeac6f3c 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -240,7 +240,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
%{"screen_name" => nickname} ->
- case User.get_by_nickname(nickname) do
+ case User.get_cached_by_nickname(nickname) do
nil -> {:error, "No user with such screen_name"}
target -> {:ok, target}
end
@@ -266,6 +266,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
defp parse_int(_, default), do: default
+ # TODO: unify the search query with MastoAPI one and do only pagination here
def search(_user, %{"q" => query} = params) do
limit = parse_int(params["rpp"], 20)
page = parse_int(params["page"], 1)
@@ -273,13 +274,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
q =
from(
- a in Activity,
+ [a, o] in Activity.with_preloaded_object(Activity),
where: fragment("?->>'type' = 'Create'", a.data),
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
where:
fragment(
- "to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)",
- a.data,
+ "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
+ o.data,
^query
),
limit: ^limit,
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index a7ec9949c..79ed9dad2 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -434,7 +434,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
- with %User{} = user <- User.get_by_id(uid),
+ with %User{} = user <- User.get_cached_by_id(uid),
true <- user.local,
true <- user.info.confirmation_pending,
true <- user.info.confirmation_token == token,
@@ -587,7 +587,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def approve_friend_request(conn, %{"user_id" => uid} = _params) do
with followed <- conn.assigns[:user],
- %User{} = follower <- User.get_by_id(uid),
+ %User{} = follower <- User.get_cached_by_id(uid),
{:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
conn
|> put_view(UserView)
@@ -599,7 +599,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def deny_friend_request(conn, %{"user_id" => uid} = _params) do
with followed <- conn.assigns[:user],
- %User{} = follower <- User.get_by_id(uid),
+ %User{} = follower <- User.get_cached_by_id(uid),
{:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
conn
|> put_view(UserView)
@@ -632,7 +632,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
defp build_info_cng(user, params) do
info_params =
- ["no_rich_text", "locked", "hide_followers", "hide_follows", "show_role"]
+ ["no_rich_text", "locked", "hide_followers", "hide_follows", "hide_favorites", "show_role"]
|> Enum.reduce(%{}, fn key, res ->
if value = params[key] do
Map.put(res, key, value == "true")
diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex
index ecb2b437b..c64152da8 100644
--- a/lib/pleroma/web/twitter_api/views/activity_view.ex
+++ b/lib/pleroma/web/twitter_api/views/activity_view.ex
@@ -224,15 +224,17 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
def render(
"activity.json",
- %{activity: %{data: %{"type" => "Create", "object" => object}} = activity} = opts
+ %{activity: %{data: %{"type" => "Create", "object" => object_id}} = activity} = opts
) do
user = get_user(activity.data["actor"], opts)
- created_at = object["published"] |> Utils.date_to_asctime()
- like_count = object["like_count"] || 0
- announcement_count = object["announcement_count"] || 0
- favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
- repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
+ object = Object.normalize(object_id)
+
+ created_at = object.data["published"] |> Utils.date_to_asctime()
+ like_count = object.data["like_count"] || 0
+ announcement_count = object.data["announcement_count"] || 0
+ favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || [])
+ repeated = opts[:for] && opts[:for].ap_id in (object.data["announcements"] || [])
pinned = activity.id in user.info.pinned_activities
attentions =
@@ -245,12 +247,12 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
conversation_id = get_context_id(activity, opts)
- tags = activity.data["object"]["tag"] || []
- possibly_sensitive = activity.data["object"]["sensitive"] || Enum.member?(tags, "nsfw")
+ tags = object.data["tag"] || []
+ possibly_sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw")
tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags
- {summary, content} = render_content(object)
+ {summary, content} = render_content(object.data)
html =
content
@@ -259,7 +261,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
activity,
"twitterapi:content"
)
- |> Formatter.emojify(object["emoji"])
+ |> Formatter.emojify(object.data["emoji"])
text =
if content do
@@ -284,7 +286,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
%{
"id" => activity.id,
- "uri" => activity.data["object"]["id"],
+ "uri" => object.data["id"],
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
"statusnet_html" => html,
"text" => text,
@@ -297,20 +299,20 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
"in_reply_to_ostatus_uri" => reply_user && reply_user.ap_id,
"in_reply_to_user_id" => reply_user && reply_user.id,
"statusnet_conversation_id" => conversation_id,
- "attachments" => (object["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts),
+ "attachments" => (object.data["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts),
"attentions" => attentions,
"fave_num" => like_count,
"repeat_num" => announcement_count,
"favorited" => !!favorited,
"repeated" => !!repeated,
"pinned" => pinned,
- "external_url" => object["external_url"] || object["id"],
+ "external_url" => object.data["external_url"] || object.data["id"],
"tags" => tags,
"activity_type" => "post",
"possibly_sensitive" => possibly_sensitive,
"visibility" => StatusView.get_visibility(object),
"summary" => summary,
- "summary_html" => summary |> Formatter.emojify(object["emoji"]),
+ "summary_html" => summary |> Formatter.emojify(object.data["emoji"]),
"card" => card,
"muted" => CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user)
}
diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex
index 32c3455f5..a3b0bf999 100644
--- a/lib/pleroma/web/web_finger/web_finger.ex
+++ b/lib/pleroma/web/web_finger/web_finger.ex
@@ -37,7 +37,7 @@ defmodule Pleroma.Web.WebFinger do
regex = ~r/(acct:)?(?<username>\w+)@#{host}/
with %{"username" => username} <- Regex.named_captures(regex, resource),
- %User{} = user <- User.get_by_nickname(username) do
+ %User{} = user <- User.get_cached_by_nickname(username) do
{:ok, represent_user(user, fmt)}
else
_e ->