From d6d5f46baea90e9b8a305a010457ac6d404d8829 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 11 Apr 2019 16:02:38 +0000 Subject: Update OAuth web template --- lib/pleroma/web/templates/layout/app.html.eex | 45 ++++++++++++++--- .../web/templates/o_auth/o_auth/show.html.eex | 57 ++++++++++++++++------ 2 files changed, 79 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex index 8333bc921..7d2d609d1 100644 --- a/lib/pleroma/web/templates/layout/app.html.eex +++ b/lib/pleroma/web/templates/layout/app.html.eex @@ -63,13 +63,14 @@ .scopes-input { display: flex; + flex-direction: column; margin-top: 1em; text-align: left; color: #89898a; } .scopes-input label:first-child { - flex-basis: 40%; + height: 2em; } .scopes { @@ -80,13 +81,22 @@ } .scope { - flex-basis: 100%; display: flex; + flex-basis: 100%; height: 2em; align-items: center; } + .scope:before { + color: #b9b9ba; + content: "✔"; + margin-left: 1em; + margin-right: 1em; + } + [type="checkbox"] + label { + display: none; + cursor: pointer; margin: 0.5em; } @@ -95,10 +105,12 @@ } [type="checkbox"] + label:before { + cursor: pointer; display: inline-block; color: white; background-color: #121a24; border: 4px solid #121a24; + box-shadow: 0px 0px 1px 0 #d8a070; box-sizing: border-box; width: 1.2em; height: 1.2em; @@ -128,7 +140,8 @@ border-radius: 4px; border: none; padding: 10px; - margin-top: 30px; + margin-top: 20px; + margin-bottom: 20px; text-transform: uppercase; font-size: 16px; box-shadow: 0px 0px 2px 0px black, @@ -147,8 +160,9 @@ box-sizing: border-box; width: 100%; background-color: #931014; + border: 1px solid #a06060; + color: #902020; border-radius: 4px; - border: none; padding: 10px; margin-top: 20px; font-weight: 500; @@ -171,12 +185,27 @@ margin-top: 0 } - .scopes-input { - flex-direction: column; + .scope { + flex-basis: 0%; } - .scope { - flex-basis: 50%; + .scope:before { + content: ""; + margin-left: 0em; + margin-right: 1em; + } + + .scope:first-child:before { + margin-left: 1em; + content: "✔"; + } + + .scope:after { + content: ","; + } + + .scope:last-child:after { + content: ""; } } diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index 87278e636..c7b4ef792 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -6,26 +6,53 @@ <% end %>

OAuth Authorization

- <%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %> -
- <%= label f, :name, "Name or email" %> - <%= text_input f, :name %> -
-
- <%= label f, :password, "Password" %> - <%= password_input f, :password %> -
-<%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f, scope_param: "authorization[scope][]"}) %> +<%= if @params["registration"] in ["true", true] do %> +

This is the first time you visit! Please enter your Pleroma handle.

+

Choose carefully! You won't be able to change this later. You will be able to change your display name, though.

+

Please only use lowercase letters and no special characters

+
+ <%= label f, :nickname, "Pleroma Handle" %> + <%= text_input f, :nickname, placeholder: "lain" %> +
+ <%= hidden_input f, :name, value: @params["name"] %> + <%= hidden_input f, :password, value: @params["password"] %> +
+ +<% else %> +
+ <%= label f, :name, "Username" %> + <%= text_input f, :name %> +
+
+ <%= label f, :password, "Password" %> + <%= password_input f, :password %> +
+ <%= submit "Log In" %> +
+ <%= label f, :scope, "The following permissions will be granted" %> +
+ <%= for scope <- @available_scopes do %> + <%# Note: using hidden input with `unchecked_value` in order to distinguish user's empty selection from `scope` param being omitted %> + <%= if scope in @scopes do %> +
+ <%= checkbox f, :"scope_#{scope}", value: scope in @scopes && scope, checked_value: scope, unchecked_value: "", name: "authorization[scope][]" %> + <%= label f, :"scope_#{scope}", String.capitalize(scope) %> + <%= if scope in @scopes && scope do %> + <%= String.capitalize(scope) %> + <% end %> +
+ <% else %> + <%= checkbox f, :"scope_#{scope}", value: scope in @scopes && scope, checked_value: scope, unchecked_value: "", name: "authorization[scope][]" %> + <% end %> + <% end %> +
+
+<% end %> <%= hidden_input f, :client_id, value: @client_id %> <%= hidden_input f, :response_type, value: @response_type %> <%= hidden_input f, :redirect_uri, value: @redirect_uri %> <%= hidden_input f, :state, value: @state %> -<%= submit "Authorize" %> -<% end %> - -<%= if Pleroma.Config.oauth_consumer_enabled?() do %> - <%= render @view_module, Pleroma.Web.Auth.Authenticator.oauth_consumer_template(), assigns %> <% end %> -- cgit v1.2.3 From a3dc02d282f886d3b4842ec70976cfa84f2e4099 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 1 May 2019 16:11:17 +0700 Subject: Add addressable lists --- lib/pleroma/web/activity_pub/activity_pub.ex | 72 +++++++++++++++++++------- lib/pleroma/web/activity_pub/transmogrifier.ex | 5 +- lib/pleroma/web/common_api/common_api.ex | 28 +++++----- lib/pleroma/web/common_api/utils.ex | 15 ++++++ lib/pleroma/web/salmon/salmon.ex | 8 +-- 5 files changed, 91 insertions(+), 37 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 604ffae7b..84d7f47b1 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -28,19 +28,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do # For Announce activities, we filter the recipients based on following status for any actors # that match actual users. See issue #164 for more information about why this is necessary. defp get_recipients(%{"type" => "Announce"} = data) do - to = data["to"] || [] - cc = data["cc"] || [] + to = Map.get(data, "to", []) + cc = Map.get(data, "cc", []) + bcc = Map.get(data, "bcc", []) actor = User.get_cached_by_ap_id(data["actor"]) recipients = - (to ++ cc) - |> Enum.filter(fn recipient -> + Enum.filter(Enum.concat([to, cc, bcc]), fn recipient -> case User.get_cached_by_ap_id(recipient) do - nil -> - true - - user -> - User.following?(user, actor) + nil -> true + user -> User.following?(user, actor) end end) @@ -48,17 +45,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp get_recipients(%{"type" => "Create"} = data) do - to = data["to"] || [] - cc = data["cc"] || [] - actor = data["actor"] || [] - recipients = (to ++ cc ++ [actor]) |> Enum.uniq() + to = Map.get(data, "to", []) + cc = Map.get(data, "cc", []) + bcc = Map.get(data, "bcc", []) + actor = Map.get(data, "actor", []) + recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq() {recipients, to, cc} end defp get_recipients(data) do - to = data["to"] || [] - cc = data["cc"] || [] - recipients = to ++ cc + to = Map.get(data, "to", []) + cc = Map.get(data, "cc", []) + bcc = Map.get(data, "bcc", []) + recipients = Enum.concat([to, cc, bcc]) {recipients, to, cc} end @@ -917,22 +916,55 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def publish(actor, activity) do - remote_followers = + defp recipients(actor, activity) do + Pleroma.Web.Salmon.remote_users(activity) ++ if actor.follower_address in activity.recipients do {:ok, followers} = User.get_followers(actor) followers |> Enum.filter(&(!&1.local)) else [] end + end + def publish(actor, %{data: %{"bcc" => bcc}} = activity) when is_list(bcc) and bcc != [] do public = is_public?(activity) + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) + + recipients = recipients(actor, activity) + + recipients + |> Enum.filter(&User.ap_enabled?/1) + |> Enum.map(fn %{info: %{source_data: data}} -> data["inbox"] end) + |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) + |> Instances.filter_reachable() + |> Enum.each(fn {inbox, unreachable_since} -> + %User{ap_id: cc} = + Enum.find(recipients, fn %{info: %{source_data: data}} -> data["inbox"] == inbox end) + json = + data + |> Map.put("cc", [cc]) + |> Map.put("directMessage", true) + |> Jason.encode!() + + Federator.publish_single_ap(%{ + inbox: inbox, + json: json, + actor: actor, + id: activity.data["id"], + unreachable_since: unreachable_since + }) + end) + end + + def publish(actor, activity) do + public = is_public?(activity) {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) + json = Jason.encode!(data) - (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) - |> Enum.filter(fn user -> User.ap_enabled?(user) end) + recipients(actor, activity) + |> Enum.filter(&User.ap_enabled?/1) |> Enum.map(fn %{info: %{source_data: data}} -> (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] end) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index b1e859d7c..3b7193eaa 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -741,13 +741,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def prepare_outgoing(%{"type" => "Create", "object" => object_id} = data) do object = - Object.normalize(object_id).data + object_id + |> Object.normalize() + |> Map.get(:data) |> prepare_object data = data |> Map.put("object", object) |> Map.merge(Utils.make_json_ld_header()) + |> Map.delete("bcc") {:ok, data} end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index cfbc5dc10..4ca59110f 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -119,6 +119,10 @@ defmodule Pleroma.Web.CommonAPI do when visibility in ~w{public unlisted private direct}, do: visibility + def get_visibility(%{"visibility" => "list:" <> list_id}) do + {:list, String.to_integer(list_id)} + end + def get_visibility(%{"in_reply_to_status_id" => status_id}) when not is_nil(status_id) do case get_replied_to_activity(status_id) do nil -> @@ -149,6 +153,7 @@ defmodule Pleroma.Web.CommonAPI do visibility ), {to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility), + {:ok, bcc} <- bcc_for_list(user, visibility), context <- make_context(in_reply_to), cw <- data["spoiler_text"], full_payload <- String.trim(status <> (data["spoiler_text"] || "")), @@ -174,19 +179,16 @@ defmodule Pleroma.Web.CommonAPI do Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") end) ) do - res = - ActivityPub.create( - %{ - to: to, - actor: user, - context: context, - object: object, - additional: %{"cc" => cc, "directMessage" => visibility == "direct"} - }, - Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false - ) - - res + ActivityPub.create( + %{ + to: to, + actor: user, + context: context, + object: object, + additional: %{"cc" => cc, "bcc" => bcc, "directMessage" => visibility == "direct"} + }, + Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false + ) end end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 887f878c4..83a745b58 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Formatter + alias Pleroma.List alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User @@ -102,6 +103,20 @@ defmodule Pleroma.Web.CommonAPI.Utils do end end + def to_for_user_and_mentions(_user, _mentions, _inReplyTo, _), do: {[], []} + + def bcc_for_list(user, {:list, list_id}) do + with {_, %List{} = list} <- {:list, List.get(list_id, user)}, + {:ok, following} <- List.get_following(list) do + {:ok, Enum.map(following, & &1.ap_id)} + else + {:list, _} -> {:error, "List not found"} + err -> err + end + end + + def bcc_for_list(_, _), do: {:ok, []} + def make_content_html( status, attachments, diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 0a9e51656..4d89f4bdb 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -157,10 +157,12 @@ defmodule Pleroma.Web.Salmon do end def remote_users(%{data: %{"to" => to} = data}) do - to = to ++ (data["cc"] || []) + cc = Map.get(data, "cc", []) + bcc = Map.get(data, "bcc", []) - to - |> Enum.map(fn id -> User.get_cached_by_ap_id(id) end) + [to, cc, bcc] + |> Enum.concat() + |> Enum.map(&User.get_cached_by_ap_id/1) |> Enum.filter(fn user -> user && !user.local end) end -- cgit v1.2.3 From 9a98f48ec3f438e543a5a621624401bb22a0d44a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 1 May 2019 12:29:48 -0500 Subject: Remove incorrect statement about valid characters --- lib/pleroma/web/templates/o_auth/o_auth/show.html.eex | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index c7b4ef792..45f2b5cc0 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -11,7 +11,6 @@ <%= if @params["registration"] in ["true", true] do %>

This is the first time you visit! Please enter your Pleroma handle.

Choose carefully! You won't be able to change this later. You will be able to change your display name, though.

-

Please only use lowercase letters and no special characters

<%= label f, :nickname, "Pleroma Handle" %> <%= text_input f, :nickname, placeholder: "lain" %> -- cgit v1.2.3 From 23276e8d6848fa8eae390c16b6e0619c12546e4a Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 13 May 2019 16:15:14 +0700 Subject: Use pseudo ap id of a list in BCC --- lib/pleroma/list.ex | 32 +++++++++++++++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 46 +++++++++++++++++++++++----- lib/pleroma/web/common_api/common_api.ex | 8 ++--- lib/pleroma/web/common_api/utils.ex | 11 ++----- lib/pleroma/web/salmon/salmon.ex | 19 ++++++++++-- 5 files changed, 92 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex index a5b1cad68..81b842e9c 100644 --- a/lib/pleroma/list.ex +++ b/lib/pleroma/list.ex @@ -12,6 +12,8 @@ defmodule Pleroma.List do alias Pleroma.Repo alias Pleroma.User + @ap_id_regex ~r/^\/users\/(?\w+)\/lists\/(?\d+)/ + schema "lists" do belongs_to(:user, User, type: Pleroma.FlakeId) field(:title, :string) @@ -32,6 +34,12 @@ defmodule Pleroma.List do |> validate_required([:following]) end + def ap_id(%User{nickname: nickname}, list_id) do + Pleroma.Web.Endpoint.url() <> "/users/#{nickname}/lists/#{list_id}" + end + + def ap_id({nickname, list_id}), do: ap_id(%User{nickname: nickname}, list_id) + def for_user(user, _opts) do query = from( @@ -55,6 +63,19 @@ defmodule Pleroma.List do Repo.one(query) end + def get_by_ap_id(ap_id) do + host = Pleroma.Web.Endpoint.host() + + with %{host: ^host, path: path} <- URI.parse(ap_id), + %{"list_id" => list_id, "nickname" => nickname} <- + Regex.named_captures(@ap_id_regex, path), + %User{} = user <- User.get_cached_by_nickname(nickname) do + get(list_id, user) + else + _ -> nil + end + end + def get_following(%Pleroma.List{following: following} = _list) do q = from( @@ -125,4 +146,15 @@ defmodule Pleroma.List do |> follow_changeset(attrs) |> Repo.update() end + + def memberships(%User{follower_address: follower_address}) do + Pleroma.List + |> where([l], ^follower_address in l.following) + |> join(:inner, [l], u in User, on: l.user_id == u.id) + |> select([l, u], {u.nickname, l.id}) + |> Repo.all() + |> Enum.map(&ap_id/1) + end + + def memberships(_), do: [] end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 84d7f47b1..3b71e0369 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -783,9 +783,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end def fetch_activities_query(recipients, opts \\ %{}) do - base_query = from(activity in Activity) - - base_query + Activity |> maybe_preload_objects(opts) |> restrict_recipients(recipients, opts["user"]) |> restrict_tag(opts) @@ -807,9 +805,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end def fetch_activities(recipients, opts \\ %{}) do - fetch_activities_query(recipients, opts) + list_memberships = Pleroma.List.memberships(opts["user"]) + + fetch_activities_query(recipients ++ list_memberships, opts) |> Pagination.fetch_paginated(opts) |> Enum.reverse() + |> maybe_update_cc(list_memberships, opts["user"]) + end + + defp maybe_update_cc(activities, [], _), do: activities + defp maybe_update_cc(activities, _, nil), do: activities + + defp maybe_update_cc(activities, list_memberships, user) do + Enum.map(activities, fn + %{data: %{"bcc" => bcc}} = activity when is_list(bcc) -> + if Enum.any?(bcc, &(&1 in list_memberships)) do + update_in(activity.data["cc"], &[user.ap_id | &1]) + else + activity + end + + activity -> + activity + end) end def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do @@ -917,13 +935,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp recipients(actor, activity) do - Pleroma.Web.Salmon.remote_users(activity) ++ + followers = if actor.follower_address in activity.recipients do {:ok, followers} = User.get_followers(actor) - followers |> Enum.filter(&(!&1.local)) + Enum.filter(followers, &(!&1.local)) else [] end + + Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers + end + + defp get_cc_ap_ids(ap_id, recipients) do + host = Map.get(URI.parse(ap_id), :host) + + recipients + |> Enum.filter(fn %User{ap_id: ap_id} -> Map.get(URI.parse(ap_id), :host) == host end) + |> Enum.map(& &1.ap_id) end def publish(actor, %{data: %{"bcc" => bcc}} = activity) when is_list(bcc) and bcc != [] do @@ -938,12 +966,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) |> Instances.filter_reachable() |> Enum.each(fn {inbox, unreachable_since} -> - %User{ap_id: cc} = + %User{ap_id: ap_id} = Enum.find(recipients, fn %{info: %{source_data: data}} -> data["inbox"] == inbox end) + cc = get_cc_ap_ids(ap_id, recipients) + json = data - |> Map.put("cc", [cc]) + |> Map.put("cc", cc) |> Map.put("directMessage", true) |> Jason.encode!() diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 4ca59110f..d47d5788c 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -153,7 +153,7 @@ defmodule Pleroma.Web.CommonAPI do visibility ), {to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility), - {:ok, bcc} <- bcc_for_list(user, visibility), + bcc <- bcc_for_list(user, visibility), context <- make_context(in_reply_to), cw <- data["spoiler_text"], full_payload <- String.trim(status <> (data["spoiler_text"] || "")), @@ -197,7 +197,7 @@ defmodule Pleroma.Web.CommonAPI do user = with emoji <- emoji_from_profile(user), source_data <- (user.info.source_data || %{}) |> Map.put("tag", emoji), - info_cng <- Pleroma.User.Info.set_source_data(user.info, source_data), + info_cng <- User.Info.set_source_data(user.info, source_data), change <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), {:ok, user} <- User.update_and_set_cache(change) do user @@ -230,7 +230,7 @@ defmodule Pleroma.Web.CommonAPI do } = activity <- get_by_id_or_ap_id(id_or_ap_id), true <- Enum.member?(object_to, "https://www.w3.org/ns/activitystreams#Public"), %{valid?: true} = info_changeset <- - Pleroma.User.Info.add_pinnned_activity(user.info, activity), + User.Info.add_pinnned_activity(user.info, activity), changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset), {:ok, _user} <- User.update_and_set_cache(changeset) do @@ -247,7 +247,7 @@ defmodule Pleroma.Web.CommonAPI do def unpin(id_or_ap_id, user) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), %{valid?: true} = info_changeset <- - Pleroma.User.Info.remove_pinnned_activity(user.info, activity), + User.Info.remove_pinnned_activity(user.info, activity), changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset), {:ok, _user} <- User.update_and_set_cache(changeset) do diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 83a745b58..32c3b4b98 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Formatter - alias Pleroma.List alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User @@ -106,16 +105,10 @@ defmodule Pleroma.Web.CommonAPI.Utils do def to_for_user_and_mentions(_user, _mentions, _inReplyTo, _), do: {[], []} def bcc_for_list(user, {:list, list_id}) do - with {_, %List{} = list} <- {:list, List.get(list_id, user)}, - {:ok, following} <- List.get_following(list) do - {:ok, Enum.map(following, & &1.ap_id)} - else - {:list, _} -> {:error, "List not found"} - err -> err - end + [Pleroma.List.ap_id(user, list_id)] end - def bcc_for_list(_, _), do: {:ok, []} + def bcc_for_list(_, _), do: [] def make_content_html( status, diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 4d89f4bdb..ca51255f3 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -156,9 +156,22 @@ defmodule Pleroma.Web.Salmon do {:ok, salmon} end - def remote_users(%{data: %{"to" => to} = data}) do + def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do cc = Map.get(data, "cc", []) - bcc = Map.get(data, "bcc", []) + + bcc = + data + |> Map.get("bcc", []) + |> Enum.reduce([], fn ap_id, bcc -> + case Pleroma.List.get_by_ap_id(ap_id) do + %Pleroma.List{user_id: ^user_id} = list -> + {:ok, following} = Pleroma.List.get_following(list) + bcc ++ Enum.map(following, & &1.ap_id) + + _ -> + bcc + end + end) [to, cc, bcc] |> Enum.concat() @@ -220,7 +233,7 @@ defmodule Pleroma.Web.Salmon do {:ok, private, _} = keys_from_pem(keys) {:ok, feed} = encode(private, feed) - remote_users = remote_users(activity) + remote_users = remote_users(user, activity) salmon_urls = Enum.map(remote_users, & &1.info.salmon) reachable_urls_metadata = Instances.filter_reachable(salmon_urls) -- cgit v1.2.3 From 62e42b03abd2cede85e85f62c35f62a8c42e8ea1 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 15 May 2019 20:10:16 +0300 Subject: Handle incoming Question objects --- lib/pleroma/web/activity_pub/transmogrifier.ex | 2 +- lib/pleroma/web/activity_pub/utils.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 508f3532f..c2596cfec 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -399,7 +399,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"] do actor = Containment.get_actor(data) data = diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 581b9d1ab..de91fa03f 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"] # Some implementations send the actor URI as the actor field, others send the entire actor object, # so figure out what the actor's URI is based on what we have. -- cgit v1.2.3 From 642a67dd4492f31b5b9fe457e34c1589c9d70c3f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 17 May 2019 11:44:47 +0300 Subject: Render polls in statuses --- lib/pleroma/web/mastodon_api/views/status_view.ex | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index bd2372944..6337c50e8 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -232,6 +232,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: %{ @@ -321,6 +322,57 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do } end + # TODO: Add tests for this view + 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!() + + votes_count = object.data["votes_count"] || 0 + + expired = + end_time + |> NaiveDateTime.compare(NaiveDateTime.utc_now()) + |> case do + :lt -> true + _ -> false + end + + options = + Enum.map(options, fn %{"name" => name} = option -> + name = + HTML.filter_tags( + name, + User.html_filter_policy(opts[:for]) + ) + + %{title: name, votes_count: option["replies"]["votes_count"] || 0} + 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: false, + 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) -- cgit v1.2.3 From f2936e0a0723956c167a06dc51518da172a508b2 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 17 May 2019 19:56:37 +0700 Subject: Add `ap_id` to List --- lib/pleroma/list.ex | 33 +++++++++++---------------------- lib/pleroma/web/common_api/utils.ex | 3 ++- 2 files changed, 13 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex index 81b842e9c..16955b3b5 100644 --- a/lib/pleroma/list.ex +++ b/lib/pleroma/list.ex @@ -12,12 +12,11 @@ defmodule Pleroma.List do alias Pleroma.Repo alias Pleroma.User - @ap_id_regex ~r/^\/users\/(?\w+)\/lists\/(?\d+)/ - schema "lists" do belongs_to(:user, User, type: Pleroma.FlakeId) field(:title, :string) field(:following, {:array, :string}, default: []) + field(:ap_id, :string) timestamps() end @@ -34,12 +33,6 @@ defmodule Pleroma.List do |> validate_required([:following]) end - def ap_id(%User{nickname: nickname}, list_id) do - Pleroma.Web.Endpoint.url() <> "/users/#{nickname}/lists/#{list_id}" - end - - def ap_id({nickname, list_id}), do: ap_id(%User{nickname: nickname}, list_id) - def for_user(user, _opts) do query = from( @@ -64,16 +57,7 @@ defmodule Pleroma.List do end def get_by_ap_id(ap_id) do - host = Pleroma.Web.Endpoint.host() - - with %{host: ^host, path: path} <- URI.parse(ap_id), - %{"list_id" => list_id, "nickname" => nickname} <- - Regex.named_captures(@ap_id_regex, path), - %User{} = user <- User.get_cached_by_nickname(nickname) do - get(list_id, user) - else - _ -> nil - end + Repo.get_by(__MODULE__, ap_id: ap_id) end def get_following(%Pleroma.List{following: following} = _list) do @@ -126,7 +110,14 @@ defmodule Pleroma.List do def create(title, %User{} = creator) do list = %Pleroma.List{user_id: creator.id, title: title} - Repo.insert(list) + + Repo.transaction(fn -> + list = Repo.insert!(list) + + list + |> change(ap_id: "#{creator.ap_id}/lists/#{list.id}") + |> Repo.update!() + end) end def follow(%Pleroma.List{following: following} = list, %User{} = followed) do @@ -150,10 +141,8 @@ defmodule Pleroma.List do def memberships(%User{follower_address: follower_address}) do Pleroma.List |> where([l], ^follower_address in l.following) - |> join(:inner, [l], u in User, on: l.user_id == u.id) - |> select([l, u], {u.nickname, l.id}) + |> select([l], l.ap_id) |> Repo.all() - |> Enum.map(&ap_id/1) end def memberships(_), do: [] diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index f082b77d8..ba6ed67ef 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -105,7 +105,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do def to_for_user_and_mentions(_user, _mentions, _inReplyTo, _), do: {[], []} def bcc_for_list(user, {:list, list_id}) do - [Pleroma.List.ap_id(user, list_id)] + list = Pleroma.List.get(list_id, user) + [list.ap_id] end def bcc_for_list(_, _), do: [] -- cgit v1.2.3 From 3b71612d3d8b5e2ad53bda4399eb7f687cd6c30e Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Sat, 18 May 2019 01:17:14 +0700 Subject: Improve Pleroma.Web.ActivityPub.ActivityPub.maybe_update_cc/3 --- lib/pleroma/web/activity_pub/activity_pub.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 048b9202b..8a5b3b8b4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -881,14 +881,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> maybe_update_cc(list_memberships, opts["user"]) end - defp maybe_update_cc(activities, [], _), do: activities - defp maybe_update_cc(activities, _, nil), do: activities - - defp maybe_update_cc(activities, list_memberships, user) do + defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id}) + when is_list(list_memberships) and length(list_memberships) > 0 do Enum.map(activities, fn - %{data: %{"bcc" => bcc}} = activity when is_list(bcc) -> + %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 -> if Enum.any?(bcc, &(&1 in list_memberships)) do - update_in(activity.data["cc"], &[user.ap_id | &1]) + update_in(activity.data["cc"], &[user_ap_id | &1]) else activity end @@ -898,6 +896,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end) end + defp maybe_update_cc(activities, _, _), do: activities + def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do fetch_activities_query([], opts) |> restrict_to_cc(recipients_to, recipients_cc) -- cgit v1.2.3 From fd920c897339b9cedea042dd6698d14380cedae7 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 18 May 2019 13:29:28 +0300 Subject: Mastodon API: Add support for posting polls --- lib/pleroma/web/common_api/common_api.ex | 4 +- lib/pleroma/web/common_api/utils.ex | 64 ++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index b53869c75..335ae70b0 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -149,6 +149,7 @@ defmodule Pleroma.Web.CommonAPI do data, visibility ), + {poll, mentions, tags} <- make_poll_data(data, mentions, tags), {to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility), context <- make_context(in_reply_to), cw <- data["spoiler_text"] || "", @@ -164,7 +165,8 @@ defmodule Pleroma.Web.CommonAPI do in_reply_to, tags, cw, - cc + cc, + poll ), object <- Map.put( diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 1dfe50b40..13cdffbbd 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -102,6 +102,48 @@ defmodule Pleroma.Web.CommonAPI.Utils do end end + def make_poll_data( + %{"poll" => %{"options" => options, "expires_in" => expires_in}} = data, + mentions, + tags + ) + when is_list(options) and is_integer(expires_in) do + content_type = get_content_type(data["content_type"]) + # XXX: There is probably a more performant/cleaner way to do this + {poll, {mentions, tags}} = + Enum.map_reduce(options, {mentions, tags}, fn option, {mentions, tags} -> + # TODO: Custom emoji + {option, mentions_merge, tags_merge} = format_input(option, content_type) + mentions = mentions ++ mentions_merge + tags = tags ++ tags_merge + + {%{ + "name" => option, + "type" => "Note", + "replies" => %{"type" => "Collection", "totalItems" => 0} + }, {mentions, tags}} + 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, mentions, tags} + end + + def make_poll_data(data, mentions, tags) do + IO.inspect(data, label: "data") + {%{}, mentions, tags} + end + def make_content_html( status, attachments, @@ -223,8 +265,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do in_reply_to, tags, cw \\ nil, - cc \\ [] + cc \\ [], + merge \\ %{} ) do + IO.inspect(merge, label: "merge") + object = %{ "type" => "Note", "to" => to, @@ -237,14 +282,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do "tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq() } - if in_reply_to do - in_reply_to_object = Object.normalize(in_reply_to) + object = + if in_reply_to do + in_reply_to_object = Object.normalize(in_reply_to) - object - |> Map.put("inReplyTo", in_reply_to_object.data["id"]) - else - object - end + object + |> Map.put("inReplyTo", in_reply_to_object.data["id"]) + else + object + end + + Map.merge(object, merge) end def format_naive_asctime(date) do -- cgit v1.2.3 From 1d90f9b96999f8bad3fa3e3ec58bf50c8666be4f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 19 May 2019 17:06:44 +0300 Subject: Remove tags/mentions/rich text from poll options because Mastodon and add custom emoji --- lib/pleroma/web/common_api/common_api.ex | 4 ++-- lib/pleroma/web/common_api/utils.ex | 25 +++++++---------------- lib/pleroma/web/mastodon_api/views/status_view.ex | 14 ++++++------- 3 files changed, 15 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index bc8f80389..374967a1b 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -154,7 +154,7 @@ defmodule Pleroma.Web.CommonAPI do data, visibility ), - {poll, mentions, tags} <- make_poll_data(data, mentions, tags), + {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"] || "", @@ -179,7 +179,7 @@ defmodule Pleroma.Web.CommonAPI do 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 2ea997789..cd8483c11 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -102,26 +102,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do end end - def make_poll_data( - %{"poll" => %{"options" => options, "expires_in" => expires_in}} = data, - mentions, - tags - ) + def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data) when is_list(options) and is_integer(expires_in) do - content_type = get_content_type(data["content_type"]) - # XXX: There is probably a more performant/cleaner way to do this - {poll, {mentions, tags}} = - Enum.map_reduce(options, {mentions, tags}, fn option, {mentions, tags} -> - # TODO: Custom emoji - {option, mentions_merge, tags_merge} = format_input(option, content_type) - mentions = mentions ++ mentions_merge - tags = tags ++ tags_merge - + {poll, emoji} = + Enum.map_reduce(options, %{}, fn option, emoji -> {%{ "name" => option, "type" => "Note", "replies" => %{"type" => "Collection", "totalItems" => 0} - }, {mentions, tags}} + }, Map.merge(emoji, Formatter.get_emoji_map(option))} end) end_time = @@ -136,11 +125,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do %{"type" => "Question", "oneOf" => poll, "closed" => end_time} end - {poll, mentions, tags} + {poll, emoji} end - def make_poll_data(_data, mentions, tags) do - {%{}, mentions, tags} + def make_poll_data(_data) do + {%{}, %{}} end def make_content_html( diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 2a5691e1f..0df8bb5c2 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -325,7 +325,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end # TODO: Add tests for this view - def render("poll.json", %{object: object} = opts) do + def render("poll.json", %{object: object} = _opts) do {multiple, options} = case object.data do %{"anyOf" => options} when is_list(options) -> {true, options} @@ -350,13 +350,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do options = Enum.map(options, fn %{"name" => name} = option -> - name = - HTML.filter_tags( - name, - User.html_filter_policy(opts[:for]) - ) - - %{title: name, votes_count: option["replies"]["votes_count"] || 0} + %{ + title: HTML.strip_tags(name), + votes_count: option["replies"]["votes_count"] || 0 + } end) %{ @@ -367,6 +364,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do multiple: multiple, votes_count: votes_count, options: options, + # TODO: Actually check for a vote voted: false, emojis: build_emojis(object.data["emoji"]) } -- cgit v1.2.3 From 6430cb1bf78e7949cc023a30df7a8d1547c36524 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 19 May 2019 17:44:18 +0300 Subject: Restrict poll replies from fetch queries by default --- lib/pleroma/web/activity_pub/activity_pub.ex | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5c3156978..035fb75d5 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -820,6 +820,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_muted_reblogs(query, _), do: query + defp restrict_poll_replies(query, %{"include_poll_replies" => "true"}), do: query + + defp restrict_poll_replies(query, _) do + if has_named_binding?(query, :object) do + from([activity, object: o] in query, where: fragment("?->'name' is null", o.data)) + else + query + end + end + defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query defp maybe_preload_objects(query, _) do @@ -873,6 +883,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_pinned(opts) |> restrict_muted_reblogs(opts) |> Activity.restrict_deactivated_users() + |> restrict_poll_replies(opts) end def fetch_activities(recipients, opts \\ %{}) do -- cgit v1.2.3 From 76a7429befb2e9a819b653ff8328cc42a565c29d Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 09:13:10 +0300 Subject: Add poll limits to /api/v1/instance and initial state --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 1051861ff..81cc5a972 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) @@ -1331,6 +1332,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do max_toot_chars: limit, mascot: "/images/pleroma-fox-tan-smol.png" }, + poll_limits: Config.get([:instance, :poll_limits]), rights: %{ delete_others_notice: present?(user.info.is_moderator), admin: present?(user.info.is_admin) -- cgit v1.2.3 From 3f96b3e4b8114ec1cf924d452907b17c2aea2003 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 10:54:20 +0300 Subject: Enforce poll limits and add error handling for MastodonAPI's post endpoint --- lib/pleroma/web/common_api/utils.ex | 69 ++++++++++++++++------ .../web/mastodon_api/mastodon_api_controller.ex | 45 +++++++++----- 2 files changed, 82 insertions(+), 32 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index cd8483c11..97172fd94 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -104,28 +104,61 @@ defmodule Pleroma.Web.CommonAPI.Utils do def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data) when is_list(options) and is_integer(expires_in) do - {poll, emoji} = - Enum.map_reduce(options, %{}, fn option, emoji -> - {%{ - "name" => option, - "type" => "Note", - "replies" => %{"type" => "Collection", "totalItems" => 0} - }, Map.merge(emoji, Formatter.get_emoji_map(option))} - end) + %{max_expiration: max_expiration, min_expiration: min_expiration} = + limits = Pleroma.Config.get([:instance, :poll_limits]) - end_time = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(expires_in) - |> NaiveDateTime.to_iso8601() + # XXX: There is probably a cleaner way of doing this + try do + if Enum.count(options) > limits.max_options do + raise ArgumentError, message: "Poll can't contain more than #{limits.max_options} options" + end - 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} + {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 - {poll, emoji} + 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" => _}) do + "Invalid poll" end def make_poll_data(_data) do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 81cc5a972..e2cab86f1 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -473,12 +473,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 @@ -491,17 +485,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(401) + |> json(%{error: message}) + + {:error, message} -> + conn + |> put_status(401) + |> 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, %{}) -- cgit v1.2.3 From aafe30d94e68ccf251c56139d07bda154dde3af9 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 14:12:10 +0300 Subject: Handle poll votes --- lib/pleroma/object.ex | 30 ++++++++++++++++++++++++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 10 ++++++++++ 2 files changed, 40 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 740d687a3..a0f7659eb 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -188,4 +188,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/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 035fb75d5..ce78d895b 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), @@ -235,6 +244,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), -- cgit v1.2.3 From a53d06273080525fdda332291838b0c95ed69690 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 14:19:03 +0300 Subject: Fix posting non-polls from mastofe --- lib/pleroma/web/common_api/utils.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 97172fd94..66153a105 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -157,7 +157,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do end end - def make_poll_data(%{"poll" => _}) do + def make_poll_data(%{"poll" => poll}) when is_map(poll) do "Invalid poll" end -- cgit v1.2.3 From f28747858bf3265e8b82eb587919f5a89386bed7 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 14:27:09 +0300 Subject: Actual vote count in poll view --- lib/pleroma/web/mastodon_api/views/status_view.ex | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 0df8bb5c2..c501c213c 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -338,8 +338,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do (object.data["closed"] || object.data["endTime"]) |> NaiveDateTime.from_iso8601!() - votes_count = object.data["votes_count"] || 0 - expired = end_time |> NaiveDateTime.compare(NaiveDateTime.utc_now()) @@ -348,12 +346,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do _ -> false end - options = - Enum.map(options, fn %{"name" => name} = option -> - %{ - title: HTML.strip_tags(name), - votes_count: option["replies"]["votes_count"] || 0 - } + {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) %{ -- cgit v1.2.3 From d7c4d029c82c833278b3640d585235dc704f30d1 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 14:35:20 +0300 Subject: Restrict poll replies when fetching activiites for context --- lib/pleroma/web/activity_pub/activity_pub.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index ce78d895b..c04e6c2b8 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -491,6 +491,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do from(activity in Activity) |> restrict_blocked(opts) + |> restrict_poll_replies(opts) |> restrict_recipients(recipients, opts["user"]) |> where( [activity], -- cgit v1.2.3 From ee682441415d7abe4acb2643e1d76fe8d78e80c1 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 16:58:15 +0300 Subject: Do not stream out poll replies --- lib/pleroma/web/activity_pub/activity_pub.ex | 54 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index c04e6c2b8..fdebf1f6b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -192,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["name"] 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 -- cgit v1.2.3 From 0407ffe75f7e91db240d491492eadf1385b1726b Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 17:12:38 +0300 Subject: Change validation error status codes to be more appropriate --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index e2cab86f1..aef2abf0b 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -488,12 +488,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do case get_cached_status_or_post(conn, params) do {:ignore, message} -> conn - |> put_status(401) + |> put_status(422) |> json(%{error: message}) {:error, message} -> conn - |> put_status(401) + |> put_status(422) |> json(%{error: message}) {_, activity} -> -- cgit v1.2.3 From 5f67c26baf2bdc98480fa4cda5895f33351b13ab Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 17:30:51 +0300 Subject: Accept strings in expires_in because sasuga javascript --- lib/pleroma/web/common_api/utils.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 66153a105..1a239de97 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -103,12 +103,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do end def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data) - when is_list(options) and is_integer(expires_in) do + 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 -- cgit v1.2.3 From ff61d345020b9ee79b243fb8d23a37a06cc41b8e Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 17:33:54 +0300 Subject: Accept question objects for conversations --- lib/pleroma/conversation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') 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) -- cgit v1.2.3 From 63b0b7190cb652cd27d236e3f9daaaf5e50701a6 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 21 May 2019 20:40:35 +0300 Subject: MastoAPI: Add GET /api/v1/polls/:id --- lib/pleroma/object.ex | 3 +++ .../web/mastodon_api/mastodon_api_controller.ex | 20 ++++++++++++++++++++ lib/pleroma/web/router.ex | 2 ++ 3 files changed, 25 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index a0f7659eb..3ffa290eb 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 diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index aef2abf0b..ecb7df459 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -410,6 +410,26 @@ 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 scheduled_statuses(%{assigns: %{user: user}} = conn, params) do with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do conn diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6a4e4a1d4..e0611e3fc 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -423,6 +423,8 @@ 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) -- cgit v1.2.3 From 17bfd000d7d44ff13cf7becbfd9ce08b896d66eb Mon Sep 17 00:00:00 2001 From: Sachin Joshi Date: Wed, 22 May 2019 06:39:19 +0200 Subject: Ability to reset avatar, profile banner and backgroud --- .../web/twitter_api/twitter_api_controller.ex | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 31e86685a..a796e38fd 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -456,6 +456,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end end + def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do + change = Changeset.change(user, %{avatar: nil}) + {:ok, user} = User.update_and_set_cache(change) + CommonAPI.update(user) + + conn + |> put_view(UserView) + |> render("show.json", %{user: user, for: user}) + end + def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, object} = ActivityPub.upload(params, type: :avatar) change = Changeset.change(user, %{avatar: object.data}) @@ -467,6 +477,19 @@ defmodule Pleroma.Web.TwitterAPI.Controller do |> render("show.json", %{user: user, for: user}) end + def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do + with new_info <- %{"banner" => %{}}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, user} <- User.update_and_set_cache(changeset) do + CommonAPI.update(user) + response = %{url: nil} |> Jason.encode!() + + conn + |> json_reply(200, response) + end + end + def update_banner(%{assigns: %{user: user}} = conn, params) do with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), new_info <- %{"banner" => object.data}, @@ -482,6 +505,18 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end end + def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do + with new_info <- %{"background" => %{}}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, _user} <- User.update_and_set_cache(changeset) do + response = %{url: nil} |> Jason.encode!() + + conn + |> json_reply(200, response) + end + end + def update_background(%{assigns: %{user: user}} = conn, params) do with {:ok, object} <- ActivityPub.upload(params, type: :background), new_info <- %{"background" => object.data}, -- cgit v1.2.3 From 19c90d47c4f649d0962098050f6cbc65eeb889d0 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 22 May 2019 21:17:57 +0300 Subject: Normalize poll votes to Answer objects --- lib/pleroma/web/activity_pub/activity_pub.ex | 14 +------------- lib/pleroma/web/activity_pub/transmogrifier.ex | 22 +++++++++++++++++++++- lib/pleroma/web/activity_pub/utils.ex | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index fdebf1f6b..5b3fb7e84 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -194,7 +194,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if activity.data["type"] in ["Create", "Announce", "Delete"] do object = Object.normalize(activity) # Do not stream out poll replies - unless object.data["name"] do + unless object.data["type"] == "Answer" do Pleroma.Web.Streamer.stream("user", activity) Pleroma.Web.Streamer.stream("list", activity) @@ -493,7 +493,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do from(activity in Activity) |> restrict_blocked(opts) - |> restrict_poll_replies(opts) |> restrict_recipients(recipients, opts["user"]) |> where( [activity], @@ -833,16 +832,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_muted_reblogs(query, _), do: query - defp restrict_poll_replies(query, %{"include_poll_replies" => "true"}), do: query - - defp restrict_poll_replies(query, _) do - if has_named_binding?(query, :object) do - from([activity, object: o] in query, where: fragment("?->'name' is null", o.data)) - else - query - end - end - defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query defp maybe_preload_objects(query, _) do @@ -896,7 +885,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_pinned(opts) |> restrict_muted_reblogs(opts) |> Activity.restrict_deactivated_users() - |> restrict_poll_replies(opts) end def fetch_activities(recipients, opts \\ %{}) do diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 8b2427258..70b467b3f 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 @@ -328,6 +329,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 +411,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", "Question"] do + when objtype in ["Article", "Note", "Video", "Page", "Question", "Answer"] do actor = Containment.get_actor(data) data = @@ -731,6 +744,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> set_reply_to_uri |> strip_internal_fields |> strip_internal_tags + |> set_type end # @doc @@ -895,6 +909,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 63454e3f7..9646bbee9 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", "Question"] + @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer"] @supported_report_states ~w(open closed resolved) @valid_visibilities ~w(public unlisted private direct) -- cgit v1.2.3 From ac7702f8009bf244a9e77cba39114dbb6584739f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 22 May 2019 21:49:19 +0300 Subject: Exclude Answers from fetching by default --- lib/pleroma/web/activity_pub/activity_pub.ex | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5b3fb7e84..db5a4a7ee 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -492,6 +492,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public from(activity in Activity) + |> Activity.with_preloaded_object() + |> exclude_poll_votes(opts) |> restrict_blocked(opts) |> restrict_recipients(recipients, opts["user"]) |> where( @@ -832,6 +834,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 @@ -865,6 +879,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) |> maybe_order(opts) + |> exclude_poll_votes(opts) |> restrict_recipients(recipients, opts["user"]) |> restrict_tag(opts) |> restrict_tag_reject(opts) -- cgit v1.2.3 From e6b175ed6cafcd6a05120e003cfe40a04b38849f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 22 May 2019 21:57:46 +0300 Subject: Fix credo issues --- lib/pleroma/web/mastodon_api/views/status_view.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index c501c213c..7a393a817 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -357,7 +357,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end) %{ - # Mastodon uses separate ids for polls, but an object can't have more than one poll embedded so object id is fine + # 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, -- cgit v1.2.3 From 8b2d39c1ec8f47df8a2159c23eabdc61b983ddf0 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 23 May 2019 14:03:16 +0300 Subject: Change the order of preloading when fetching activities for context --- lib/pleroma/web/activity_pub/activity_pub.ex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index db5a4a7ee..b06200cb5 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -492,7 +492,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) - |> Activity.with_preloaded_object() + |> maybe_preload_objects(opts) |> exclude_poll_votes(opts) |> restrict_blocked(opts) |> restrict_recipients(recipients, opts["user"]) @@ -513,7 +513,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 @@ -521,7 +520,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() -- cgit v1.2.3 From 4030837d91b7ff8525513589d5d810e9a1a6b959 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sat, 25 May 2019 05:19:47 +0000 Subject: notification: add non_follows/non_followers notification control settings --- lib/pleroma/notification.ex | 32 +++++++++++++++++++++++++++++++- lib/pleroma/user/info.ex | 11 +++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 844264307..4095e0474 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -166,7 +166,17 @@ 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, + :local, + :muted, + :followers, + :follows, + :non_followers, + :non_follows, + :recently_followed + ] |> Enum.any?(&skip?(&1, activity, user)) end @@ -201,12 +211,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/user/info.ex b/lib/pleroma/user/info.ex index 6397e2737..fb4cf3cc3 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -47,7 +47,14 @@ defmodule Pleroma.User.Info do field(:emoji, {:array, :map}, default: []) field(:notification_settings, :map, - default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true} + default: %{ + "remote" => true, + "local" => true, + "followers" => true, + "follows" => true, + "non_follows" => true, + "non_followers" => true + } ) # Found in the wild @@ -71,7 +78,7 @@ defmodule Pleroma.User.Info do notification_settings = info.notification_settings |> Map.merge(settings) - |> Map.take(["remote", "local", "followers", "follows"]) + |> Map.take(["remote", "local", "followers", "follows", "non_follows", "non_followers"]) params = %{notification_settings: notification_settings} -- cgit v1.2.3 From 59a703fcbe6436c92d0e276caaf55f599c3165f4 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sat, 25 May 2019 05:31:13 +0000 Subject: twitter api: user view: expose user notification settings under pleroma object --- lib/pleroma/web/twitter_api/views/user_view.ex | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') 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 -- cgit v1.2.3 From e7e2e7a1a633e2f7e493d040e290c931320d8cc8 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sat, 25 May 2019 05:54:02 +0000 Subject: user info: allow formdata for notification settings like every other API --- lib/pleroma/user/info.ex | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index fb4cf3cc3..b0bfdf4f4 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -75,6 +75,11 @@ 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) -- cgit v1.2.3 From 750ede5764d30063587182696f7ebc50c05f8278 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 26 May 2019 00:05:47 +0000 Subject: notification: remove local/remote match rules (too complicated) --- lib/pleroma/notification.ex | 7 ------- lib/pleroma/user/info.ex | 4 +--- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 4095e0474..35beffb87 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -169,7 +169,6 @@ defmodule Pleroma.Notification do [ :self, :blocked, - :local, :muted, :followers, :follows, @@ -189,12 +188,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"] diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index b0bfdf4f4..74573ba83 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -48,8 +48,6 @@ defmodule Pleroma.User.Info do field(:notification_settings, :map, default: %{ - "remote" => true, - "local" => true, "followers" => true, "follows" => true, "non_follows" => true, @@ -83,7 +81,7 @@ defmodule Pleroma.User.Info do notification_settings = info.notification_settings |> Map.merge(settings) - |> Map.take(["remote", "local", "followers", "follows", "non_follows", "non_followers"]) + |> Map.take(["followers", "follows", "non_follows", "non_followers"]) params = %{notification_settings: notification_settings} -- cgit v1.2.3 From 1452a96ad6cfd7d250e3f7c10805994cc92016a7 Mon Sep 17 00:00:00 2001 From: Sachin Joshi Date: Mon, 27 May 2019 15:31:01 +0545 Subject: ability to set and reset avatar, profile banner and backgroud in Mastodon API --- .../web/mastodon_api/mastodon_api_controller.ex | 63 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 4 ++ 2 files changed, 67 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 1ec0f30a1..1ff839e9e 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -152,6 +152,69 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do + change = Changeset.change(user, %{avatar: nil}) + {:ok, user} = User.update_and_set_cache(change) + CommonAPI.update(user) + + json(conn, %{url: nil}) + end + + def update_avatar(%{assigns: %{user: user}} = conn, params) do + {:ok, object} = ActivityPub.upload(params, type: :avatar) + change = Changeset.change(user, %{avatar: object.data}) + {:ok, user} = User.update_and_set_cache(change) + CommonAPI.update(user) + %{"url" => [%{"href" => href} | _]} = object.data + + json(conn, %{url: href}) + end + + def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do + with new_info <- %{"banner" => %{}}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, user} <- User.update_and_set_cache(changeset) do + CommonAPI.update(user) + + json(conn, %{url: nil}) + end + end + + def update_banner(%{assigns: %{user: user}} = conn, params) do + with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), + new_info <- %{"banner" => object.data}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, user} <- User.update_and_set_cache(changeset) do + CommonAPI.update(user) + %{"url" => [%{"href" => href} | _]} = object.data + + json(conn, %{url: href}) + end + end + + def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do + with new_info <- %{"background" => %{}}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, _user} <- User.update_and_set_cache(changeset) do + json(conn, %{url: nil}) + end + end + + def update_background(%{assigns: %{user: user}} = conn, params) do + with {:ok, object} <- ActivityPub.upload(params, type: :background), + new_info <- %{"background" => object.data}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, _user} <- User.update_and_set_cache(changeset) do + %{"url" => [%{"href" => href} | _]} = object.data + + json(conn, %{url: href}) + end + end + def verify_credentials(%{assigns: %{user: user}} = conn, _) do account = AccountView.render("account.json", %{user: user, for: user}) json(conn, account) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 352268b96..42ef64c4f 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -318,6 +318,10 @@ defmodule Pleroma.Web.Router do patch("/accounts/update_credentials", MastodonAPIController, :update_credentials) + patch("/accounts/update_avatar", MastodonAPIController, :update_avatar) + patch("/accounts/update_banner", MastodonAPIController, :update_banner) + patch("/accounts/update_background", MastodonAPIController, :update_background) + post("/statuses", MastodonAPIController, :post_status) delete("/statuses/:id", MastodonAPIController, :delete_status) -- cgit v1.2.3 From 6aec0d1b58c12db2fa669fcff043338174290a35 Mon Sep 17 00:00:00 2001 From: kaniini Date: Wed, 29 May 2019 22:10:16 +0000 Subject: Revert "Merge branch 'feature/search-authenticated-only' into 'develop'" This reverts merge request !1209 --- lib/pleroma/web/router.ex | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 08c74a742..352268b96 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -414,12 +414,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) @@ -436,12 +431,14 @@ defmodule Pleroma.Web.Router do 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 -- cgit v1.2.3 From 99f70c7e2011ab29746a8a61d3a4b354f4513045 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 30 May 2019 15:33:58 +0700 Subject: Use Pleroma.Config everywhere --- lib/healthcheck.ex | 4 +- lib/pleroma/emoji.ex | 4 +- lib/pleroma/html.ex | 20 ++----- lib/pleroma/http/http.ex | 7 +-- lib/pleroma/plugs/federating_plug.ex | 2 +- lib/pleroma/reverse_proxy.ex | 2 +- lib/pleroma/user.ex | 4 +- lib/pleroma/web/activity_pub/activity_pub.ex | 12 ++-- .../web/activity_pub/activity_pub_controller.ex | 2 +- lib/pleroma/web/activity_pub/mrf.ex | 4 +- lib/pleroma/web/endpoint.ex | 2 +- lib/pleroma/web/media_proxy/media_proxy.ex | 30 +++++----- lib/pleroma/web/nodeinfo/nodeinfo_controller.ex | 66 +++++++++------------- lib/pleroma/web/templates/layout/app.html.eex | 4 +- .../templates/mastodon_api/mastodon/index.html.eex | 2 +- .../web/twitter_api/twitter_api_controller.ex | 2 +- 16 files changed, 68 insertions(+), 99 deletions(-) (limited to 'lib') 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/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/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..983e156f5 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -61,7 +61,7 @@ defmodule Pleroma.ReverseProxy do * `http`: options for [hackney](https://github.com/benoitc/hackney). """ - @hackney Application.get_env(:pleroma, :hackney, :hackney) + @hackney Pleroma.Config.get(:hackney, :hackney) @default_hackney_options [] diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 653dec95f..5c91dc7d4 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -366,9 +366,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 diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index aa0229db7..bdd7e78d2 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -399,16 +399,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, 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/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/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/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 @@ - <%= Application.get_env(:pleroma, :instance)[:name] %> + <%= Pleroma.Config.get([:instance, :name]) %>