aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMark Felder <feld@FreeBSD.org>2019-06-03 15:29:53 -0500
committerMark Felder <feld@FreeBSD.org>2019-06-03 15:29:53 -0500
commit6ef145b4fc3f2ddb5865e84039c3b56656529711 (patch)
treea10cab9e7561bdaa76777cfc6d0c3c6fe1f8189b /lib
parent5cee2fe9fea4f0c98acd49a2a288ecd44bce3d1f (diff)
parent662e95e9484a46bfc72c2edc305aefae99ff68b0 (diff)
downloadpleroma-6ef145b4fc3f2ddb5865e84039c3b56656529711.tar.gz
Merge branch 'develop' into feature/digest-email
Diffstat (limited to 'lib')
-rw-r--r--lib/healthcheck.ex4
-rw-r--r--lib/pleroma/conversation.ex2
-rw-r--r--lib/pleroma/emoji.ex4
-rw-r--r--lib/pleroma/html.ex20
-rw-r--r--lib/pleroma/http/http.ex7
-rw-r--r--lib/pleroma/notification.ex50
-rw-r--r--lib/pleroma/object.ex33
-rw-r--r--lib/pleroma/plugs/federating_plug.ex2
-rw-r--r--lib/pleroma/reverse_proxy.ex6
-rw-r--r--lib/pleroma/user.ex6
-rw-r--r--lib/pleroma/user/info.ex22
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex121
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf.ex4
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex41
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex19
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex3
-rw-r--r--lib/pleroma/web/common_api/common_api.ex53
-rw-r--r--lib/pleroma/web/common_api/utils.ex95
-rw-r--r--lib/pleroma/web/endpoint.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex139
-rw-r--r--lib/pleroma/web/mastodon_api/views/conversation_view.ex7
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex63
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy.ex30
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex66
-rw-r--r--lib/pleroma/web/rich_media/parser.ex5
-rw-r--r--lib/pleroma/web/router.ex28
-rw-r--r--lib/pleroma/web/templates/layout/app.html.eex4
-rw-r--r--lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex10
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex2
-rw-r--r--lib/pleroma/web/twitter_api/views/user_view.ex7
31 files changed, 589 insertions, 268 deletions
diff --git a/lib/healthcheck.ex b/lib/healthcheck.ex
index 646fb3b9d..32aafc210 100644
--- a/lib/healthcheck.ex
+++ b/lib/healthcheck.ex
@@ -29,13 +29,13 @@ defmodule Pleroma.Healthcheck do
end
defp assign_db_info(healthcheck) do
- database = Application.get_env(:pleroma, Repo)[:database]
+ database = Pleroma.Config.get([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]
+ pool_size = Pleroma.Config.get([Repo, :pool_size])
db_info =
Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states ->
diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex
index 238c1acf2..bc97b39ca 100644
--- a/lib/pleroma/conversation.ex
+++ b/lib/pleroma/conversation.ex
@@ -49,7 +49,7 @@ defmodule Pleroma.Conversation do
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
"Create" <- activity.data["type"],
object <- Pleroma.Object.normalize(activity),
- "Note" <- object.data["type"],
+ true <- object.data["type"] in ["Note", "Question"],
ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do
{:ok, conversation} = create_for_ap_id(ap_id)
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index 6390cce4c..7d12eff7f 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -22,7 +22,7 @@ defmodule Pleroma.Emoji do
@ets __MODULE__.Ets
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
- @groups Application.get_env(:pleroma, :emoji)[:groups]
+ @groups Pleroma.Config.get([:emoji, :groups])
@doc false
def start_link do
@@ -112,7 +112,7 @@ defmodule Pleroma.Emoji do
# 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] || []
+ shortcode_globs = Pleroma.Config.get([:emoji, :shortcode_globs], [])
emojis =
(load_from_file("config/emoji.txt") ++
diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex
index d1da746de..e5e78ee4f 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -104,7 +104,6 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
paragraphs, breaks and links are allowed through the filter.
"""
- @markup Application.get_env(:pleroma, :markup)
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
require HtmlSanitizeEx.Scrubber.Meta
@@ -142,9 +141,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
Meta.allow_tag_with_these_attributes("span", [])
# allow inline images for custom emoji
- @allow_inline_images Keyword.get(@markup, :allow_inline_images)
-
- if @allow_inline_images do
+ if Pleroma.Config.get([:markup, :allow_inline_images]) do
# restrict img tags to http/https only, because of MediaProxy.
Meta.allow_tag_with_uri_attributes("img", ["src"], ["http", "https"])
@@ -168,7 +165,6 @@ defmodule Pleroma.HTML.Scrubber.Default do
# credo:disable-for-previous-line
# No idea how to fix this one…
- @markup Application.get_env(:pleroma, :markup)
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
Meta.remove_cdata_sections_before_scrub()
@@ -213,7 +209,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_this_attribute_values("span", "class", ["h-card"])
Meta.allow_tag_with_these_attributes("span", [])
- @allow_inline_images Keyword.get(@markup, :allow_inline_images)
+ @allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images])
if @allow_inline_images do
# restrict img tags to http/https only, because of MediaProxy.
@@ -228,9 +224,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
])
end
- @allow_tables Keyword.get(@markup, :allow_tables)
-
- if @allow_tables do
+ if Pleroma.Config.get([:markup, :allow_tables]) do
Meta.allow_tag_with_these_attributes("table", [])
Meta.allow_tag_with_these_attributes("tbody", [])
Meta.allow_tag_with_these_attributes("td", [])
@@ -239,9 +233,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("tr", [])
end
- @allow_headings Keyword.get(@markup, :allow_headings)
-
- if @allow_headings do
+ if Pleroma.Config.get([:markup, :allow_headings]) do
Meta.allow_tag_with_these_attributes("h1", [])
Meta.allow_tag_with_these_attributes("h2", [])
Meta.allow_tag_with_these_attributes("h3", [])
@@ -249,9 +241,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("h5", [])
end
- @allow_fonts Keyword.get(@markup, :allow_fonts)
-
- if @allow_fonts do
+ if Pleroma.Config.get([:markup, :allow_fonts]) do
Meta.allow_tag_with_these_attributes("font", ["face"])
end
diff --git a/lib/pleroma/http/http.ex b/lib/pleroma/http/http.ex
index c5f720bc9..c96ee7353 100644
--- a/lib/pleroma/http/http.ex
+++ b/lib/pleroma/http/http.ex
@@ -65,12 +65,9 @@ defmodule Pleroma.HTTP do
end
def process_request_options(options) do
- config = Application.get_env(:pleroma, :http, [])
- proxy = Keyword.get(config, :proxy_url, nil)
-
- case proxy do
+ case Pleroma.Config.get([:http, :proxy_url]) do
nil -> options
- _ -> options ++ [proxy: proxy]
+ proxy -> options ++ [proxy: proxy]
end
end
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 6b6607f0f..736ebc3af 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -151,10 +151,15 @@ defmodule Pleroma.Notification do
def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
when type in ["Create", "Like", "Announce", "Follow"] do
- users = get_notified_from_activity(activity)
-
- notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
- {:ok, notifications}
+ object = Object.normalize(activity)
+
+ unless object && object.data["type"] == "Answer" do
+ users = get_notified_from_activity(activity)
+ notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
+ {:ok, notifications}
+ else
+ {:ok, []}
+ end
end
def create_notifications(_), do: {:ok, []}
@@ -190,7 +195,16 @@ defmodule Pleroma.Notification do
def get_notified_from_activity(_, _local_only), do: []
def skip?(activity, user) do
- [:self, :blocked, :local, :muted, :followers, :follows, :recently_followed]
+ [
+ :self,
+ :blocked,
+ :muted,
+ :followers,
+ :follows,
+ :non_followers,
+ :non_follows,
+ :recently_followed
+ ]
|> Enum.any?(&skip?(&1, activity, user))
end
@@ -203,12 +217,6 @@ defmodule Pleroma.Notification do
User.blocks?(user, %{ap_id: actor})
end
- def skip?(:local, %{local: true}, %{info: %{notification_settings: %{"local" => false}}}),
- do: true
-
- def skip?(:local, %{local: false}, %{info: %{notification_settings: %{"remote" => false}}}),
- do: true
-
def skip?(:muted, activity, user) do
actor = activity.data["actor"]
@@ -225,12 +233,32 @@ defmodule Pleroma.Notification do
User.following?(follower, user)
end
+ def skip?(
+ :non_followers,
+ activity,
+ %{info: %{notification_settings: %{"non_followers" => false}}} = user
+ ) do
+ actor = activity.data["actor"]
+ follower = User.get_cached_by_ap_id(actor)
+ !User.following?(follower, user)
+ end
+
def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor)
User.following?(user, followed)
end
+ def skip?(
+ :non_follows,
+ activity,
+ %{info: %{notification_settings: %{"non_follows" => false}}} = user
+ ) do
+ actor = activity.data["actor"]
+ followed = User.get_cached_by_ap_id(actor)
+ !User.following?(user, followed)
+ end
+
def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do
actor = activity.data["actor"]
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index cc6fc9c5d..4b181ec59 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -35,6 +35,9 @@ defmodule Pleroma.Object do
|> unique_constraint(:ap_id, name: :objects_unique_apid_index)
end
+ def get_by_id(nil), do: nil
+ def get_by_id(id), do: Repo.get(Object, id)
+
def get_by_ap_id(nil), do: nil
def get_by_ap_id(ap_id) do
@@ -195,4 +198,34 @@ defmodule Pleroma.Object do
_ -> {:error, "Not found"}
end
end
+
+ def increase_vote_count(ap_id, name) do
+ with %Object{} = object <- Object.normalize(ap_id),
+ "Question" <- object.data["type"] do
+ multiple = Map.has_key?(object.data, "anyOf")
+
+ options =
+ (object.data["anyOf"] || object.data["oneOf"] || [])
+ |> Enum.map(fn
+ %{"name" => ^name} = option ->
+ Kernel.update_in(option["replies"]["totalItems"], &(&1 + 1))
+
+ option ->
+ option
+ end)
+
+ data =
+ if multiple do
+ Map.put(object.data, "anyOf", options)
+ else
+ Map.put(object.data, "oneOf", options)
+ end
+
+ object
+ |> Object.change(%{data: data})
+ |> update_and_set_cache()
+ else
+ _ -> :noop
+ end
+ end
end
diff --git a/lib/pleroma/plugs/federating_plug.ex b/lib/pleroma/plugs/federating_plug.ex
index effc154bf..4dc4e9279 100644
--- a/lib/pleroma/plugs/federating_plug.ex
+++ b/lib/pleroma/plugs/federating_plug.ex
@@ -10,7 +10,7 @@ defmodule Pleroma.Web.FederatingPlug do
end
def call(conn, _opts) do
- if Keyword.get(Application.get_env(:pleroma, :instance), :federating) do
+ if Pleroma.Config.get([:instance, :federating]) do
conn
else
conn
diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex
index 6e5feb4c3..285d57309 100644
--- a/lib/pleroma/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy.ex
@@ -61,8 +61,6 @@ defmodule Pleroma.ReverseProxy do
* `http`: options for [hackney](https://github.com/benoitc/hackney).
"""
- @hackney Application.get_env(:pleroma, :hackney, :hackney)
-
@default_hackney_options []
@inline_content_types [
@@ -148,7 +146,7 @@ defmodule Pleroma.ReverseProxy do
Logger.debug("#{__MODULE__} #{method} #{url} #{inspect(headers)}")
method = method |> String.downcase() |> String.to_existing_atom()
- case @hackney.request(method, url, headers, "", hackney_opts) do
+ case :hackney.request(method, url, headers, "", hackney_opts) do
{:ok, code, headers, client} when code in @valid_resp_codes ->
{:ok, code, downcase_headers(headers), client}
@@ -198,7 +196,7 @@ defmodule Pleroma.ReverseProxy do
duration,
Keyword.get(opts, :max_read_duration, @max_read_duration)
),
- {:ok, data} <- @hackney.stream_body(client),
+ {:ok, data} <- :hackney.stream_body(client),
{:ok, duration} <- increase_read_duration(duration),
sent_so_far = sent_so_far + byte_size(data),
:ok <- body_size_constraint(sent_so_far, Keyword.get(opts, :max_body_size)),
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index e5b1219b2..fc7fcf7f5 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -367,9 +367,7 @@ defmodule Pleroma.User do
end
def follow(%User{} = follower, %User{info: info} = followed) do
- user_config = Application.get_env(:pleroma, :user)
- deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked)
-
+ deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
ap_followers = followed.follower_address
cond do
@@ -761,7 +759,7 @@ defmodule Pleroma.User do
from(s in subquery(boost_search_rank_query(distinct_query, for_user)),
order_by: [desc: s.search_rank],
- limit: 20
+ limit: 40
)
end
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index e88ee4164..36bae61a4 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -48,7 +48,12 @@ defmodule Pleroma.User.Info do
field(:emoji, {:array, :map}, default: [])
field(:notification_settings, :map,
- default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
+ default: %{
+ "followers" => true,
+ "follows" => true,
+ "non_follows" => true,
+ "non_followers" => true
+ }
)
# Found in the wild
@@ -69,10 +74,15 @@ defmodule Pleroma.User.Info do
end
def update_notification_settings(info, settings) do
+ settings =
+ settings
+ |> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
+ |> Map.new()
+
notification_settings =
info.notification_settings
|> Map.merge(settings)
- |> Map.take(["remote", "local", "followers", "follows"])
+ |> Map.take(["followers", "follows", "non_follows", "non_followers"])
params = %{notification_settings: notification_settings}
@@ -266,14 +276,6 @@ defmodule Pleroma.User.Info do
|> validate_required([:settings])
end
- def mastodon_flavour_update(info, flavour) do
- params = %{flavour: flavour}
-
- info
- |> cast(params, [:flavour])
- |> validate_required([:flavour])
- end
-
def mascot_update(info, url) do
params = %{mascot: url}
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index aa0229db7..45feae25a 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -108,6 +108,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def decrease_replies_count_if_reply(_object), do: :noop
+ def increase_poll_votes_if_vote(%{
+ "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
+ "type" => "Create"
+ }) do
+ Object.increase_vote_count(reply_ap_id, name)
+ end
+
+ def increase_poll_votes_if_vote(_create_data), do: :noop
+
def insert(map, local \\ true, fake \\ false) when is_map(map) do
with nil <- Activity.normalize(map),
map <- lazy_put_activity_defaults(map, fake),
@@ -183,40 +192,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
public = "https://www.w3.org/ns/activitystreams#Public"
if activity.data["type"] in ["Create", "Announce", "Delete"] do
- Pleroma.Web.Streamer.stream("user", activity)
- Pleroma.Web.Streamer.stream("list", activity)
+ object = Object.normalize(activity)
+ # Do not stream out poll replies
+ unless object.data["type"] == "Answer" do
+ Pleroma.Web.Streamer.stream("user", activity)
+ Pleroma.Web.Streamer.stream("list", activity)
- if Enum.member?(activity.data["to"], public) do
- Pleroma.Web.Streamer.stream("public", activity)
+ if Enum.member?(activity.data["to"], public) do
+ Pleroma.Web.Streamer.stream("public", activity)
- if activity.local do
- Pleroma.Web.Streamer.stream("public:local", activity)
- end
-
- if activity.data["type"] in ["Create"] do
- object = Object.normalize(activity)
+ if activity.local do
+ Pleroma.Web.Streamer.stream("public:local", activity)
+ end
- 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["type"] in ["Create"] do
+ 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 object.data["attachment"] != [] do
- Pleroma.Web.Streamer.stream("public:media", activity)
+ if object.data["attachment"] != [] do
+ Pleroma.Web.Streamer.stream("public:media", activity)
- if activity.local do
- Pleroma.Web.Streamer.stream("public:local:media", activity)
+ if activity.local do
+ Pleroma.Web.Streamer.stream("public:local:media", activity)
+ end
end
end
+ else
+ # TODO: Write test, replace with visibility test
+ if !Enum.member?(activity.data["cc"] || [], public) &&
+ !Enum.member?(
+ activity.data["to"],
+ User.get_cached_by_ap_id(activity.data["actor"]).follower_address
+ ),
+ do: Pleroma.Web.Streamer.stream("direct", activity)
end
- else
- # TODO: Write test, replace with visibility test
- if !Enum.member?(activity.data["cc"] || [], public) &&
- !Enum.member?(
- activity.data["to"],
- User.get_cached_by_ap_id(activity.data["actor"]).follower_address
- ),
- do: Pleroma.Web.Streamer.stream("direct", activity)
end
end
end
@@ -235,6 +246,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, activity} <- insert(create_data, local, fake),
{:fake, false, activity} <- {:fake, fake, activity},
_ <- increase_replies_count_if_reply(create_data),
+ _ <- increase_poll_votes_if_vote(create_data),
# Changing note count prior to enqueuing federation task in order to avoid
# race conditions on updating user.info
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
@@ -399,16 +411,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
- ap_config = Application.get_env(:pleroma, :activitypub)
- unfollow_blocked = Keyword.get(ap_config, :unfollow_blocked)
- outgoing_blocks = Keyword.get(ap_config, :outgoing_blocks)
+ outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
+ unfollow_blocked = Pleroma.Config.get([:activitypub, :unfollow_blocked])
- with true <- unfollow_blocked do
+ if unfollow_blocked do
follow_activity = fetch_latest_follow(blocker, blocked)
-
- if follow_activity do
- unfollow(blocker, blocked, nil, local)
- end
+ if follow_activity, do: unfollow(blocker, blocked, nil, local)
end
with true <- outgoing_blocks,
@@ -480,6 +488,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
from(activity in Activity)
+ |> maybe_preload_objects(opts)
|> restrict_blocked(opts)
|> restrict_recipients(recipients, opts["user"])
|> where(
@@ -492,6 +501,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
^context
)
)
+ |> exclude_poll_votes(opts)
|> order_by([activity], desc: activity.id)
end
@@ -499,7 +509,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_activities_for_context(context, opts \\ %{}) do
context
|> fetch_activities_for_context_query(opts)
- |> Activity.with_preloaded_object()
|> Repo.all()
end
@@ -507,7 +516,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Pleroma.FlakeId.t() | nil
def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
context
- |> fetch_activities_for_context_query(opts)
+ |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
|> limit(1)
|> select([a], a.id)
|> Repo.one()
@@ -653,20 +662,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_tag(query, _), do: query
- defp restrict_to_cc(query, recipients_to, recipients_cc) do
- from(
- activity in query,
- where:
- fragment(
- "(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
- activity.data,
- ^recipients_to,
- activity.data,
- ^recipients_cc
- )
- )
- end
-
defp restrict_recipients(query, [], _user), do: query
defp restrict_recipients(query, recipients, nil) do
@@ -820,6 +815,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_muted_reblogs(query, _), do: query
+ defp exclude_poll_votes(query, %{"include_poll_votes" => "true"}), do: query
+
+ defp exclude_poll_votes(query, _) do
+ if has_named_binding?(query, :object) do
+ from([activity, object: o] in query,
+ where: fragment("not(?->>'type' = ?)", o.data, "Answer")
+ )
+ else
+ query
+ end
+ end
+
defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
defp maybe_preload_objects(query, _) do
@@ -881,6 +888,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_pinned(opts)
|> restrict_muted_reblogs(opts)
|> Activity.restrict_deactivated_users()
+ |> exclude_poll_votes(opts)
end
def fetch_activities(recipients, opts \\ %{}) do
@@ -889,9 +897,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Enum.reverse()
end
- def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
+ def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
+ from(activity in query,
+ where:
+ fragment("? && ?", activity.recipients, ^recipients) or
+ (fragment("? && ?", activity.recipients, ^recipients_with_public) and
+ "https://www.w3.org/ns/activitystreams#Public" in activity.recipients)
+ )
+ end
+
+ def fetch_activities_bounded(recipients, recipients_with_public, opts \\ %{}) do
fetch_activities_query([], opts)
- |> restrict_to_cc(recipients_to, recipients_cc)
+ |> fetch_activities_bounded_query(recipients, recipients_with_public)
|> Pagination.fetch_paginated(opts)
|> Enum.reverse()
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index ad2ca1e54..0182bda46 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -27,7 +27,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
plug(:relay_active? when action in [:relay])
def relay_active?(conn, _) do
- if Keyword.get(Application.get_env(:pleroma, :instance), :allow_relay) do
+ if Pleroma.Config.get([:instance, :allow_relay]) do
conn
else
conn
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
index 1aaa20050..3bf7955f3 100644
--- a/lib/pleroma/web/activity_pub/mrf.ex
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -17,9 +17,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
end
def get_policies do
- Application.get_env(:pleroma, :instance, [])
- |> Keyword.get(:rewrite_policy, [])
- |> get_policies()
+ Pleroma.Config.get([:instance, :rewrite_policy], []) |> get_policies()
end
defp get_policies(policy) when is_atom(policy), do: [policy]
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 5edd8ccc7..66fa7c0b3 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -35,6 +35,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_likes
|> fix_addressing
|> fix_summary
+ |> fix_type
end
def fix_summary(%{"summary" => nil} = object) do
@@ -65,7 +66,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
- def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, explicit_mentions) do
+ def fix_explicit_addressing(
+ %{"to" => to, "cc" => cc} = object,
+ explicit_mentions,
+ follower_collection
+ ) do
explicit_to =
to
|> Enum.filter(fn x -> x in explicit_mentions end)
@@ -76,6 +81,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
final_cc =
(cc ++ explicit_cc)
+ |> Enum.reject(fn x -> String.ends_with?(x, "/followers") and x != follower_collection end)
|> Enum.uniq()
object
@@ -83,7 +89,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("cc", final_cc)
end
- def fix_explicit_addressing(object, _explicit_mentions), do: object
+ def fix_explicit_addressing(object, _explicit_mentions, _followers_collection), do: object
# if directMessage flag is set to true, leave the addressing alone
def fix_explicit_addressing(%{"directMessage" => true} = object), do: object
@@ -93,10 +99,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
object
|> Utils.determine_explicit_mentions()
- explicit_mentions = explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public"]
+ follower_collection = User.get_cached_by_ap_id(Containment.get_actor(object)).follower_address
- object
- |> fix_explicit_addressing(explicit_mentions)
+ explicit_mentions =
+ explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public", follower_collection]
+
+ fix_explicit_addressing(object, explicit_mentions, follower_collection)
end
# if as:Public is addressed, then make sure the followers collection is also addressed
@@ -133,7 +141,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_addressing_list("cc")
|> fix_addressing_list("bto")
|> fix_addressing_list("bcc")
- |> fix_explicit_addressing
+ |> fix_explicit_addressing()
|> fix_implicit_addressing(followers_collection)
end
@@ -328,6 +336,18 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_content_map(object), do: object
+ def fix_type(%{"inReplyTo" => reply_id} = object) when is_binary(reply_id) do
+ reply = Object.normalize(reply_id)
+
+ if reply.data["type"] == "Question" and object["name"] do
+ Map.put(object, "type", "Answer")
+ else
+ object
+ end
+ end
+
+ def fix_type(object), do: object
+
defp mastodon_follow_hack(%{"id" => id, "actor" => follower_id}, followed) do
with true <- id =~ "follows",
%User{local: true} = follower <- User.get_cached_by_ap_id(follower_id),
@@ -398,7 +418,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# - tags
# - emoji
def handle_incoming(%{"type" => "Create", "object" => %{"type" => objtype} = object} = data)
- when objtype in ["Article", "Note", "Video", "Page"] do
+ when objtype in ["Article", "Note", "Video", "Page", "Question", "Answer"] do
actor = Containment.get_actor(data)
data =
@@ -731,6 +751,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> set_reply_to_uri
|> strip_internal_fields
|> strip_internal_tags
+ |> set_type
end
# @doc
@@ -895,6 +916,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
Map.put(object, "sensitive", "nsfw" in tags)
end
+ def set_type(%{"type" => "Answer"} = object) do
+ Map.put(object, "type", "Note")
+ end
+
+ def set_type(object), do: object
+
def add_attributed_to(object) do
attributed_to = object["attributedTo"] || object["actor"]
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index ca8a0844b..b292d7d8d 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
require Logger
- @supported_object_types ["Article", "Note", "Video", "Page"]
+ @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer"]
@supported_report_states ~w(open closed resolved)
@valid_visibilities ~w(public unlisted private direct)
@@ -789,4 +789,21 @@ defmodule Pleroma.Web.ActivityPub.Utils do
[to, cc, recipients]
end
end
+
+ def get_existing_votes(actor, %{data: %{"id" => id}}) do
+ query =
+ from(
+ [activity, object: object] in Activity.with_preloaded_object(Activity),
+ where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
+ where:
+ fragment(
+ "(?)->'inReplyTo' = ?",
+ object.data,
+ ^to_string(id)
+ ),
+ where: fragment("(?)->>'type' = 'Answer'", object.data)
+ )
+
+ Repo.all(query)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 93b50ee47..8965e3253 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -66,6 +66,9 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
Enum.any?(to, &String.contains?(&1, "/followers")) ->
"private"
+ object.data["directMessage"] == true ->
+ "direct"
+
length(cc) > 0 ->
"private"
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 5a312d673..5212d5ce5 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -119,6 +119,53 @@ defmodule Pleroma.Web.CommonAPI do
end
end
+ def vote(user, object, choices) do
+ with "Question" <- object.data["type"],
+ {:author, false} <- {:author, object.data["actor"] == user.ap_id},
+ {:existing_votes, []} <- {:existing_votes, Utils.get_existing_votes(user.ap_id, object)},
+ {options, max_count} <- get_options_and_max_count(object),
+ option_count <- Enum.count(options),
+ {:choice_check, {choices, true}} <-
+ {:choice_check, normalize_and_validate_choice_indices(choices, option_count)},
+ {:count_check, true} <- {:count_check, Enum.count(choices) <= max_count} do
+ answer_activities =
+ Enum.map(choices, fn index ->
+ answer_data = make_answer_data(user, object, Enum.at(options, index)["name"])
+
+ ActivityPub.create(%{
+ to: answer_data["to"],
+ actor: user,
+ context: object.data["context"],
+ object: answer_data,
+ additional: %{"cc" => answer_data["cc"]}
+ })
+ end)
+
+ object = Object.get_cached_by_ap_id(object.data["id"])
+ {:ok, answer_activities, object}
+ else
+ {:author, _} -> {:error, "Poll's author can't vote"}
+ {:existing_votes, _} -> {:error, "Already voted"}
+ {:choice_check, {_, false}} -> {:error, "Invalid indices"}
+ {:count_check, false} -> {:error, "Too many choices"}
+ end
+ end
+
+ defp get_options_and_max_count(object) do
+ if Map.has_key?(object.data, "anyOf") do
+ {object.data["anyOf"], Enum.count(object.data["anyOf"])}
+ else
+ {object.data["oneOf"], 1}
+ end
+ end
+
+ defp normalize_and_validate_choice_indices(choices, count) do
+ Enum.map_reduce(choices, true, fn index, valid ->
+ index = if is_binary(index), do: String.to_integer(index), else: index
+ {index, if(valid, do: index < count, else: valid)}
+ end)
+ end
+
def get_visibility(%{"visibility" => visibility}, in_reply_to)
when visibility in ~w{public unlisted private direct},
do: {visibility, get_replied_to_visibility(in_reply_to)}
@@ -154,6 +201,7 @@ defmodule Pleroma.Web.CommonAPI do
data,
visibility
),
+ {poll, poll_emoji} <- make_poll_data(data),
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
context <- make_context(in_reply_to),
cw <- data["spoiler_text"] || "",
@@ -171,13 +219,14 @@ defmodule Pleroma.Web.CommonAPI do
tags,
cw,
cc,
- sensitive
+ sensitive,
+ poll
),
object <-
Map.put(
object,
"emoji",
- Formatter.get_emoji_map(full_payload)
+ Map.merge(Formatter.get_emoji_map(full_payload), poll_emoji)
) do
res =
ActivityPub.create(
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index d93c0d46e..f35ed36ab 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -102,6 +102,72 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end
end
+ def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data)
+ when is_list(options) do
+ %{max_expiration: max_expiration, min_expiration: min_expiration} =
+ limits = Pleroma.Config.get([:instance, :poll_limits])
+
+ # XXX: There is probably a cleaner way of doing this
+ try do
+ # In some cases mastofe sends out strings instead of integers
+ expires_in = if is_binary(expires_in), do: String.to_integer(expires_in), else: expires_in
+
+ if Enum.count(options) > limits.max_options do
+ raise ArgumentError, message: "Poll can't contain more than #{limits.max_options} options"
+ end
+
+ {poll, emoji} =
+ Enum.map_reduce(options, %{}, fn option, emoji ->
+ if String.length(option) > limits.max_option_chars do
+ raise ArgumentError,
+ message:
+ "Poll options cannot be longer than #{limits.max_option_chars} characters each"
+ end
+
+ {%{
+ "name" => option,
+ "type" => "Note",
+ "replies" => %{"type" => "Collection", "totalItems" => 0}
+ }, Map.merge(emoji, Formatter.get_emoji_map(option))}
+ end)
+
+ case expires_in do
+ expires_in when expires_in > max_expiration ->
+ raise ArgumentError, message: "Expiration date is too far in the future"
+
+ expires_in when expires_in < min_expiration ->
+ raise ArgumentError, message: "Expiration date is too soon"
+
+ _ ->
+ :noop
+ end
+
+ end_time =
+ NaiveDateTime.utc_now()
+ |> NaiveDateTime.add(expires_in)
+ |> NaiveDateTime.to_iso8601()
+
+ poll =
+ if Pleroma.Web.ControllerHelper.truthy_param?(data["poll"]["multiple"]) do
+ %{"type" => "Question", "anyOf" => poll, "closed" => end_time}
+ else
+ %{"type" => "Question", "oneOf" => poll, "closed" => end_time}
+ end
+
+ {poll, emoji}
+ rescue
+ e in ArgumentError -> e.message
+ end
+ end
+
+ def make_poll_data(%{"poll" => poll}) when is_map(poll) do
+ "Invalid poll"
+ end
+
+ def make_poll_data(_data) do
+ {%{}, %{}}
+ end
+
def make_content_html(
status,
attachments,
@@ -224,7 +290,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
tags,
cw \\ nil,
cc \\ [],
- sensitive \\ false
+ sensitive \\ false,
+ merge \\ %{}
) do
object = %{
"type" => "Note",
@@ -239,12 +306,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
}
- with false <- is_nil(in_reply_to),
- %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
- Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
- else
- _ -> object
- end
+ object =
+ with false <- is_nil(in_reply_to),
+ %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
+ Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
+ else
+ _ -> object
+ end
+
+ Map.merge(object, merge)
end
def format_naive_asctime(date) do
@@ -421,4 +491,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do
{:error, "No such conversation"}
end
end
+
+ def make_answer_data(%User{ap_id: ap_id}, object, name) do
+ %{
+ "type" => "Answer",
+ "actor" => ap_id,
+ "cc" => [object.data["actor"]],
+ "to" => [],
+ "name" => name,
+ "inReplyTo" => object.data["id"]
+ }
+ end
end
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 8cd7a2270..bd76e4295 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -66,7 +66,7 @@ defmodule Pleroma.Web.Endpoint do
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason,
- length: Application.get_env(:pleroma, :instance) |> Keyword.get(:upload_limit),
+ length: Pleroma.Config.get([:instance, :upload_limit]),
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
)
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 0fe09c285..dfd05271a 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -197,7 +197,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
languages: ["en"],
registrations: Pleroma.Config.get([:instance, :registrations_open]),
# Extra (not present in Mastodon):
- max_toot_chars: Keyword.get(instance, :limit)
+ max_toot_chars: Keyword.get(instance, :limit),
+ poll_limits: Keyword.get(instance, :poll_limits)
}
json(conn, response)
@@ -409,6 +410,53 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Object{} = object <- Object.get_by_id(id),
+ %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
+ true <- Visibility.visible_for_user?(activity, user) do
+ conn
+ |> put_view(StatusView)
+ |> try_render("poll.json", %{object: object, for: user})
+ else
+ nil ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Record not found"})
+
+ false ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Record not found"})
+ end
+ end
+
+ def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
+ with %Object{} = object <- Object.get_by_id(id),
+ true <- object.data["type"] == "Question",
+ %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
+ true <- Visibility.visible_for_user?(activity, user),
+ {:ok, _activities, object} <- CommonAPI.vote(user, object, choices) do
+ conn
+ |> put_view(StatusView)
+ |> try_render("poll.json", %{object: object, for: user})
+ else
+ nil ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Record not found"})
+
+ false ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Record not found"})
+
+ {:error, message} ->
+ conn
+ |> put_status(422)
+ |> json(%{error: message})
+ end
+ end
+
def scheduled_statuses(%{assigns: %{user: user}} = conn, params) do
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
conn
@@ -472,12 +520,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
params
|> Map.put("in_reply_to_status_id", params["in_reply_to_id"])
- idempotency_key =
- case get_req_header(conn, "idempotency-key") do
- [key] -> key
- _ -> Ecto.UUID.generate()
- end
-
scheduled_at = params["scheduled_at"]
if scheduled_at && ScheduledActivity.far_enough?(scheduled_at) do
@@ -490,17 +532,40 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
else
params = Map.drop(params, ["scheduled_at"])
- {:ok, activity} =
- Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ ->
- CommonAPI.post(user, params)
- end)
-
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
+ case get_cached_status_or_post(conn, params) do
+ {:ignore, message} ->
+ conn
+ |> put_status(422)
+ |> json(%{error: message})
+
+ {:error, message} ->
+ conn
+ |> put_status(422)
+ |> json(%{error: message})
+
+ {_, activity} ->
+ conn
+ |> put_view(StatusView)
+ |> try_render("status.json", %{activity: activity, for: user, as: :activity})
+ end
end
end
+ defp get_cached_status_or_post(%{assigns: %{user: user}} = conn, params) do
+ idempotency_key =
+ case get_req_header(conn, "idempotency-key") do
+ [key] -> key
+ _ -> Ecto.UUID.generate()
+ end
+
+ Cachex.fetch(:idempotency_cache, idempotency_key, fn _ ->
+ case CommonAPI.post(user, params) do
+ {:ok, activity} -> activity
+ {:error, message} -> {:ignore, message}
+ end
+ end)
+ end
+
def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
json(conn, %{})
@@ -1084,7 +1149,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
from([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,
- limit: 20
+ limit: 40
)
q =
@@ -1346,8 +1411,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
accounts =
Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user}))
- flavour = get_user_flavour(user)
-
initial_state =
%{
meta: %{
@@ -1366,6 +1429,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
max_toot_chars: limit,
mascot: User.get_mascot(user)["url"]
},
+ poll_limits: Config.get([:instance, :poll_limits]),
rights: %{
delete_others_notice: present?(user.info.is_moderator),
admin: present?(user.info.is_admin)
@@ -1433,7 +1497,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conn
|> put_layout(false)
|> put_view(MastodonView)
- |> render("index.html", %{initial_state: initial_state, flavour: flavour})
+ |> render("index.html", %{initial_state: initial_state})
else
conn
|> put_session(:return_to, conn.request_path)
@@ -1456,43 +1520,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- @supported_flavours ["glitch", "vanilla"]
-
- def set_flavour(%{assigns: %{user: user}} = conn, %{"flavour" => flavour} = _params)
- when flavour in @supported_flavours do
- flavour_cng = User.Info.mastodon_flavour_update(user.info, flavour)
-
- with changeset <- Ecto.Changeset.change(user),
- changeset <- Ecto.Changeset.put_embed(changeset, :info, flavour_cng),
- {:ok, user} <- User.update_and_set_cache(changeset),
- flavour <- user.info.flavour do
- json(conn, flavour)
- else
- e ->
- conn
- |> put_resp_content_type("application/json")
- |> send_resp(500, Jason.encode!(%{"error" => inspect(e)}))
- end
- end
-
- def set_flavour(conn, _params) do
- conn
- |> put_status(400)
- |> json(%{error: "Unsupported flavour"})
- end
-
- def get_flavour(%{assigns: %{user: user}} = conn, _params) do
- json(conn, get_user_flavour(user))
- end
-
- defp get_user_flavour(%User{info: %{flavour: flavour}}) when flavour in @supported_flavours do
- flavour
- end
-
- defp get_user_flavour(_) do
- "glitch"
- end
-
def login(%{assigns: %{user: %User{}}} = conn, _params) do
redirect(conn, to: local_mastodon_root_path(conn))
end
diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
index 8e8f7cf31..af1dcf66d 100644
--- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
@@ -22,9 +22,14 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
last_status = StatusView.render("status.json", %{activity: activity, for: user})
+ # Conversations return all users except the current user.
+ users =
+ participation.conversation.users
+ |> Enum.reject(&(&1.id == user.id))
+
accounts =
AccountView.render("accounts.json", %{
- users: participation.conversation.users,
+ users: users,
as: :user
})
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index e55f9b96e..6836d331a 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -240,6 +240,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
spoiler_text: summary_html,
visibility: get_visibility(object),
media_attachments: attachments,
+ poll: render("poll.json", %{object: object, for: opts[:for]}),
mentions: mentions,
tags: build_tags(tags),
application: %{
@@ -290,8 +291,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
url: page_url,
image: image_url |> MediaProxy.url(),
- title: rich_media[:title],
- description: rich_media[:description],
+ title: rich_media[:title] || "",
+ description: rich_media[:description] || "",
pleroma: %{
opengraph: rich_media
}
@@ -329,6 +330,64 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
+ def render("poll.json", %{object: object} = opts) do
+ {multiple, options} =
+ case object.data do
+ %{"anyOf" => options} when is_list(options) -> {true, options}
+ %{"oneOf" => options} when is_list(options) -> {false, options}
+ _ -> {nil, nil}
+ end
+
+ if options do
+ end_time =
+ (object.data["closed"] || object.data["endTime"])
+ |> NaiveDateTime.from_iso8601!()
+
+ expired =
+ end_time
+ |> NaiveDateTime.compare(NaiveDateTime.utc_now())
+ |> case do
+ :lt -> true
+ _ -> false
+ end
+
+ voted =
+ if opts[:for] do
+ existing_votes =
+ Pleroma.Web.ActivityPub.Utils.get_existing_votes(opts[:for].ap_id, object)
+
+ existing_votes != [] or opts[:for].ap_id == object.data["actor"]
+ else
+ false
+ end
+
+ {options, votes_count} =
+ Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
+ current_count = option["replies"]["totalItems"] || 0
+
+ {%{
+ title: HTML.strip_tags(name),
+ votes_count: current_count
+ }, current_count + count}
+ end)
+
+ %{
+ # Mastodon uses separate ids for polls, but an object can't have
+ # more than one poll embedded so object id is fine
+ id: object.id,
+ expires_at: Utils.to_masto_date(end_time),
+ expired: expired,
+ multiple: multiple,
+ votes_count: votes_count,
+ options: options,
+ voted: voted,
+ emojis: build_emojis(object.data["emoji"])
+ }
+ else
+ nil
+ end
+ end
+
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
object = Object.normalize(activity)
diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex
index 5762e767b..cee6d8481 100644
--- a/lib/pleroma/web/media_proxy/media_proxy.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy.ex
@@ -12,25 +12,27 @@ defmodule Pleroma.Web.MediaProxy do
def url("/" <> _ = url), do: url
def url(url) do
- config = Application.get_env(:pleroma, :media_proxy, [])
- domain = URI.parse(url).host
+ if !enabled?() or local?(url) or whitelisted?(url) do
+ url
+ else
+ encode_url(url)
+ end
+ end
- cond do
- !Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) ->
- url
+ defp enabled?, do: Pleroma.Config.get([:media_proxy, :enabled], false)
- Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
- String.equivalent?(domain, pattern)
- end) ->
- url
+ defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
- true ->
- encode_url(url)
- end
+ defp whitelisted?(url) do
+ %{host: domain} = URI.parse(url)
+
+ Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
+ String.equivalent?(domain, pattern)
+ end)
end
def encode_url(url) do
- secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
+ secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
# Must preserve `%2F` for compatibility with S3
# https://git.pleroma.social/pleroma/pleroma/issues/580
@@ -52,7 +54,7 @@ defmodule Pleroma.Web.MediaProxy do
end
def decode_url(sig, url) do
- secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
+ secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
sig = Base.url_decode64!(sig, @base64_opts)
local_sig = :crypto.hmac(:sha, secret, url)
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index 45f90c579..59f3d4e11 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -32,20 +32,15 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
# under software.
def raw_nodeinfo do
- instance = Application.get_env(:pleroma, :instance)
- media_proxy = Application.get_env(:pleroma, :media_proxy)
- suggestions = Application.get_env(:pleroma, :suggestions)
- chat = Application.get_env(:pleroma, :chat)
- gopher = Application.get_env(:pleroma, :gopher)
stats = Stats.get_stats()
mrf_simple =
- Application.get_env(:pleroma, :mrf_simple)
+ Config.get(:mrf_simple)
|> Enum.into(%{})
# This horror is needed to convert regex sigils to strings
mrf_keyword =
- Application.get_env(:pleroma, :mrf_keyword, [])
+ Config.get(:mrf_keyword, [])
|> Enum.map(fn {key, value} ->
{key,
Enum.map(value, fn
@@ -74,14 +69,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
MRF.get_policies()
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)
- quarantined = Keyword.get(instance, :quarantined_instances)
-
- quarantined =
- if is_list(quarantined) do
- quarantined
- else
- []
- end
+ quarantined = Config.get([:instance, :quarantined_instances], [])
staff_accounts =
User.all_superusers()
@@ -92,7 +80,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)
federation_response =
- if Keyword.get(instance, :mrf_transparency) do
+ if Config.get([:instance, :mrf_transparency]) do
%{
mrf_policies: mrf_policies,
mrf_simple: mrf_simple,
@@ -109,22 +97,22 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
"pleroma_api",
"mastodon_api",
"mastodon_api_streaming",
- if Keyword.get(media_proxy, :enabled) do
+ if Config.get([:media_proxy, :enabled]) do
"media_proxy"
end,
- if Keyword.get(gopher, :enabled) do
+ if Config.get([:gopher, :enabled]) do
"gopher"
end,
- if Keyword.get(chat, :enabled) do
+ if Config.get([:chat, :enabled]) do
"chat"
end,
- if Keyword.get(suggestions, :enabled) do
+ if Config.get([:suggestions, :enabled]) do
"suggestions"
end,
- if Keyword.get(instance, :allow_relay) do
+ if Config.get([:instance, :allow_relay]) do
"relay"
end,
- if Keyword.get(instance, :safe_dm_mentions) do
+ if Config.get([:instance, :safe_dm_mentions]) do
"safe_dm_mentions"
end
]
@@ -141,7 +129,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
inbound: [],
outbound: []
},
- openRegistrations: Keyword.get(instance, :registrations_open),
+ openRegistrations: Config.get([:instance, :registrations_open]),
usage: %{
users: %{
total: stats.user_count || 0
@@ -149,29 +137,29 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
localPosts: stats.status_count || 0
},
metadata: %{
- nodeName: Keyword.get(instance, :name),
- nodeDescription: Keyword.get(instance, :description),
- private: !Keyword.get(instance, :public, true),
+ nodeName: Config.get([:instance, :name]),
+ nodeDescription: Config.get([:instance, :description]),
+ private: !Config.get([:instance, :public], true),
suggestions: %{
- enabled: Keyword.get(suggestions, :enabled, false),
- thirdPartyEngine: Keyword.get(suggestions, :third_party_engine, ""),
- timeout: Keyword.get(suggestions, :timeout, 5000),
- limit: Keyword.get(suggestions, :limit, 23),
- web: Keyword.get(suggestions, :web, "")
+ enabled: Config.get([:suggestions, :enabled], false),
+ thirdPartyEngine: Config.get([:suggestions, :third_party_engine], ""),
+ timeout: Config.get([:suggestions, :timeout], 5000),
+ limit: Config.get([:suggestions, :limit], 23),
+ web: Config.get([:suggestions, :web], "")
},
staffAccounts: staff_accounts,
federation: federation_response,
- postFormats: Keyword.get(instance, :allowed_post_formats),
+ postFormats: Config.get([:instance, :allowed_post_formats]),
uploadLimits: %{
- general: Keyword.get(instance, :upload_limit),
- avatar: Keyword.get(instance, :avatar_upload_limit),
- banner: Keyword.get(instance, :banner_upload_limit),
- background: Keyword.get(instance, :background_upload_limit)
+ general: Config.get([:instance, :upload_limit]),
+ avatar: Config.get([:instance, :avatar_upload_limit]),
+ banner: Config.get([:instance, :banner_upload_limit]),
+ background: Config.get([:instance, :background_upload_limit])
},
- accountActivationRequired: Keyword.get(instance, :account_activation_required, false),
- invitesEnabled: Keyword.get(instance, :invites_enabled, false),
+ accountActivationRequired: Config.get([:instance, :account_activation_required], false),
+ invitesEnabled: Config.get([:instance, :invites_enabled], false),
features: features,
- restrictedNicknames: Pleroma.Config.get([Pleroma.User, :restricted_nicknames])
+ restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames])
}
}
end
diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex
index 62e8fa610..e4595800c 100644
--- a/lib/pleroma/web/rich_media/parser.ex
+++ b/lib/pleroma/web/rich_media/parser.ex
@@ -37,7 +37,10 @@ defmodule Pleroma.Web.RichMedia.Parser do
try do
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
- html |> maybe_parse() |> clean_parsed_data() |> check_parsed_data()
+ html
+ |> maybe_parse()
+ |> clean_parsed_data()
+ |> check_parsed_data()
rescue
e ->
{:error, "Parsing error: #{inspect(e)}"}
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 8b7dc9af6..ddab99254 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -309,8 +309,6 @@ defmodule Pleroma.Web.Router do
post("/conversations/:id/read", MastodonAPIController, :conversation_read)
get("/endorsements", MastodonAPIController, :empty_array)
-
- get("/pleroma/flavour", MastodonAPIController, :get_flavour)
end
scope [] do
@@ -335,6 +333,8 @@ defmodule Pleroma.Web.Router do
put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status)
delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status)
+ post("/polls/:id/votes", MastodonAPIController, :poll_vote)
+
post("/media", MastodonAPIController, :upload)
put("/media/:id", MastodonAPIController, :update_media)
@@ -350,8 +350,6 @@ defmodule Pleroma.Web.Router do
put("/filters/:id", MastodonAPIController, :update_filter)
delete("/filters/:id", MastodonAPIController, :delete_filter)
- post("/pleroma/flavour/:flavour", MastodonAPIController, :set_flavour)
-
get("/pleroma/mascot", MastodonAPIController, :get_mascot)
put("/pleroma/mascot", MastodonAPIController, :set_mascot)
@@ -414,12 +412,7 @@ defmodule Pleroma.Web.Router do
get("/trends", MastodonAPIController, :empty_array)
- scope [] do
- pipe_through(:oauth_read)
-
- get("/search", MastodonAPIController, :search)
- get("/accounts/search", MastodonAPIController, :account_search)
- end
+ get("/accounts/search", MastodonAPIController, :account_search)
scope [] do
pipe_through(:oauth_read_or_public)
@@ -431,17 +424,21 @@ defmodule Pleroma.Web.Router do
get("/statuses/:id", MastodonAPIController, :get_status)
get("/statuses/:id/context", MastodonAPIController, :get_context)
+ get("/polls/:id", MastodonAPIController, :get_poll)
+
get("/accounts/:id/statuses", MastodonAPIController, :user_statuses)
get("/accounts/:id/followers", MastodonAPIController, :followers)
get("/accounts/:id/following", MastodonAPIController, :following)
get("/accounts/:id", MastodonAPIController, :user)
+ get("/search", MastodonAPIController, :search)
+
get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
end
end
scope "/api/v2", Pleroma.Web.MastodonAPI do
- pipe_through([:api, :oauth_read])
+ pipe_through([:api, :oauth_read_or_public])
get("/search", MastodonAPIController, :search2)
end
@@ -483,13 +480,8 @@ defmodule Pleroma.Web.Router do
get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status)
get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation)
- get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline)
- end
-
- scope [] do
- pipe_through(:oauth_read)
-
get("/search", TwitterAPI.Controller, :search)
+ get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline)
end
end
@@ -508,7 +500,7 @@ defmodule Pleroma.Web.Router do
end
scope "/api", Pleroma.Web, as: :twitter_api_search do
- pipe_through([:api, :oauth_read])
+ pipe_through([:api, :oauth_read_or_public])
get("/pleroma/search_user", TwitterAPI.Controller, :search_user)
end
diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex
index 3389c91cc..85ec4d76c 100644
--- a/lib/pleroma/web/templates/layout/app.html.eex
+++ b/lib/pleroma/web/templates/layout/app.html.eex
@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" />
<title>
- <%= Application.get_env(:pleroma, :instance)[:name] %>
+ <%= Pleroma.Config.get([:instance, :name]) %>
</title>
<style>
body {
@@ -194,7 +194,7 @@
</head>
<body>
<div class="container">
- <h1><%= Application.get_env(:pleroma, :instance)[:name] %></h1>
+ <h1><%= Pleroma.Config.get([:instance, :name]) %></h1>
<%= render @view_module, @view_template, assigns %>
</div>
</body>
diff --git a/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex b/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex
index 5659c7828..3325beca1 100644
--- a/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex
+++ b/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex
@@ -4,11 +4,11 @@
<meta charset='utf-8'>
<meta content='width=device-width, initial-scale=1' name='viewport'>
<title>
-<%= Application.get_env(:pleroma, :instance)[:name] %>
+<%= Pleroma.Config.get([:instance, :name]) %>
</title>
<link rel="icon" type="image/png" href="/favicon.png"/>
<script crossorigin='anonymous' src="/packs/locales.js"></script>
-<script crossorigin='anonymous' src="/packs/locales/<%= @flavour %>/en.js"></script>
+<script crossorigin='anonymous' src="/packs/locales/glitch/en.js"></script>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/getting_started.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/compose.js'>
@@ -19,10 +19,10 @@
<script src="/packs/core/common.js"></script>
<link rel="stylesheet" media="all" href="/packs/core/common.css" />
-<script src="/packs/flavours/<%= @flavour %>/common.js"></script>
-<link rel="stylesheet" media="all" href="/packs/flavours/<%= @flavour %>/common.css" />
+<script src="/packs/flavours/glitch/common.js"></script>
+<link rel="stylesheet" media="all" href="/packs/flavours/glitch/common.css" />
-<script src="/packs/flavours/<%= @flavour %>/home.js"></script>
+<script src="/packs/flavours/glitch/home.js"></script>
</head>
<body class='app-body no-reduce-motion system-font'>
<div class='app-holder' data-props='{&quot;locale&quot;:&quot;en&quot;}' id='mastodon'>
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 31e86685a..1b6b33e69 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -728,7 +728,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
def only_if_public_instance(conn, _) do
- if Keyword.get(Application.get_env(:pleroma, :instance), :public) do
+ if Pleroma.Config.get([:instance, :public]) do
conn
else
conn
diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex
index f0a4ddbd3..550f35f5f 100644
--- a/lib/pleroma/web/twitter_api/views/user_view.ex
+++ b/lib/pleroma/web/twitter_api/views/user_view.ex
@@ -121,6 +121,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
"tags" => user.tags
}
|> maybe_with_activation_status(user, for_user)
+ |> with_notification_settings(user, for_user)
}
|> maybe_with_user_settings(user, for_user)
|> maybe_with_role(user, for_user)
@@ -132,6 +133,12 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
end
end
+ defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
+ Map.put(data, "notification_settings", user.info.notification_settings)
+ end
+
+ defp with_notification_settings(data, _, _), do: data
+
defp maybe_with_activation_status(data, user, %User{info: %{is_admin: true}}) do
Map.put(data, "deactivated", user.info.deactivated)
end