summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/activity.ex15
-rw-r--r--lib/pleroma/bbs/handler.ex2
-rw-r--r--lib/pleroma/object.ex7
-rw-r--r--lib/pleroma/plugs/remote_ip.ex54
-rw-r--r--lib/pleroma/user.ex18
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex4
-rw-r--r--lib/pleroma/web/common_api/activity_draft.ex2
-rw-r--r--lib/pleroma/web/endpoint.ex5
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex26
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/filter_controller.ex72
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex49
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex524
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex51
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/status_controller.ex274
-rw-r--r--lib/pleroma/web/mastodon_api/views/conversation_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/notification_view.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex23
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex34
-rw-r--r--lib/pleroma/web/oauth/app.ex26
-rw-r--r--lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex6
-rw-r--r--lib/pleroma/web/router.ex66
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex2
-rw-r--r--lib/pleroma/web/views/streamer_view.ex4
23 files changed, 694 insertions, 578 deletions
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 2c04a26f9..c1065611b 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -137,11 +137,18 @@ defmodule Pleroma.Activity do
|> Repo.one()
end
+ @spec get_by_id(String.t()) :: Activity.t() | nil
def get_by_id(id) do
- Activity
- |> where([a], a.id == ^id)
- |> restrict_deactivated_users()
- |> Repo.one()
+ case FlakeId.flake_id?(id) do
+ true ->
+ Activity
+ |> where([a], a.id == ^id)
+ |> restrict_deactivated_users()
+ |> Repo.one()
+
+ _ ->
+ nil
+ end
end
def get_by_id_with_object(id) do
diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex
index 0a381f592..fa838a4e4 100644
--- a/lib/pleroma/bbs/handler.ex
+++ b/lib/pleroma/bbs/handler.ex
@@ -42,7 +42,7 @@ defmodule Pleroma.BBS.Handler do
end
def puts_activity(activity) do
- status = Pleroma.Web.MastodonAPI.StatusView.render("status.json", %{activity: activity})
+ status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity})
IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})")
IO.puts(HtmlSanitizeEx.strip_tags(status.content))
IO.puts("")
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 3fa407931..cdfbacb0e 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -248,4 +248,11 @@ defmodule Pleroma.Object do
_ -> :noop
end
end
+
+ @doc "Updates data field of an object"
+ def update_data(%Object{data: data} = object, attrs \\ %{}) do
+ object
+ |> Object.change(%{data: Map.merge(data || %{}, attrs)})
+ |> Repo.update()
+ end
end
diff --git a/lib/pleroma/plugs/remote_ip.ex b/lib/pleroma/plugs/remote_ip.ex
new file mode 100644
index 000000000..fdedc27ee
--- /dev/null
+++ b/lib/pleroma/plugs/remote_ip.ex
@@ -0,0 +1,54 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.RemoteIp do
+ @moduledoc """
+ This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
+ """
+
+ @behaviour Plug
+
+ @headers ~w[
+ forwarded
+ x-forwarded-for
+ x-client-ip
+ x-real-ip
+ ]
+
+ # https://en.wikipedia.org/wiki/Localhost
+ # https://en.wikipedia.org/wiki/Private_network
+ @reserved ~w[
+ 127.0.0.0/8
+ ::1/128
+ fc00::/7
+ 10.0.0.0/8
+ 172.16.0.0/12
+ 192.168.0.0/16
+ ]
+
+ def init(_), do: nil
+
+ def call(conn, _) do
+ config = Pleroma.Config.get(__MODULE__, [])
+
+ if Keyword.get(config, :enabled, false) do
+ RemoteIp.call(conn, remote_ip_opts(config))
+ else
+ conn
+ end
+ end
+
+ defp remote_ip_opts(config) do
+ headers = config |> Keyword.get(:headers, @headers) |> MapSet.new()
+ reserved = Keyword.get(config, :reserved, @reserved)
+
+ proxies =
+ config
+ |> Keyword.get(:proxies, [])
+ |> Enum.concat(reserved)
+ |> Enum.map(&InetCidr.parse/1)
+
+ {headers, proxies}
+ end
+end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index f3dcf7ad4..4c1cdd042 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -505,6 +505,11 @@ defmodule Pleroma.User do
|> Repo.all()
end
+ def get_all_by_ids(ids) do
+ from(u in __MODULE__, where: u.id in ^ids)
+ |> Repo.all()
+ end
+
# This is mostly an SPC migration fix. This guesses the user nickname by taking the last part
# of the ap_id and the domain and tries to get that user
def get_by_guessed_nickname(ap_id) do
@@ -765,6 +770,19 @@ defmodule Pleroma.User do
update_info(user, &User.Info.set_note_count(&1, note_count))
end
+ def update_mascot(user, url) do
+ info_changeset =
+ User.Info.mascot_update(
+ user.info,
+ url
+ )
+
+ user
+ |> change()
+ |> put_embed(:info, info_changeset)
+ |> update_and_set_cache()
+ end
+
@spec maybe_fetch_follow_information(User.t()) :: User.t()
def maybe_fetch_follow_information(user) do
with {:ok, user} <- fetch_follow_information(user) do
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 90aef99f7..21da8a7ff 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -513,7 +513,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn
|> put_view(StatusView)
- |> render("status.json", %{activity: activity})
+ |> render("show.json", %{activity: activity})
else
true ->
{:param_cast, nil}
@@ -537,7 +537,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn
|> put_view(StatusView)
- |> render("status.json", %{activity: activity})
+ |> render("show.json", %{activity: activity})
end
end
diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex
index aa7c8c381..f7da81b34 100644
--- a/lib/pleroma/web/common_api/activity_draft.ex
+++ b/lib/pleroma/web/common_api/activity_draft.ex
@@ -40,11 +40,11 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> put_params(params)
|> status()
|> summary()
+ |> with_valid(&attachments/1)
|> full_payload()
|> expires_at()
|> poll()
|> with_valid(&in_reply_to/1)
- |> with_valid(&attachments/1)
|> with_valid(&in_reply_to_conversation/1)
|> with_valid(&visibility/1)
|> content()
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index eb805e853..2212e93f4 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -97,10 +97,7 @@ defmodule Pleroma.Web.Endpoint do
extra: extra
)
- # Note: the plug and its configuration is compile-time this can't be upstreamed yet
- if proxies = Pleroma.Config.get([__MODULE__, :reverse_proxies]) do
- plug(RemoteIp, proxies: proxies)
- end
+ plug(Pleroma.Plugs.RemoteIp)
defmodule Instrumenter do
use Prometheus.PhoenixInstrumenter
diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex
new file mode 100644
index 000000000..03db6c9b8
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex
@@ -0,0 +1,26 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.User
+
+ @doc "GET /api/v1/domain_blocks"
+ def index(%{assigns: %{user: %{info: info}}} = conn, _) do
+ json(conn, Map.get(info, :domain_blocks, []))
+ end
+
+ @doc "POST /api/v1/domain_blocks"
+ def create(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
+ User.block_domain(blocker, domain)
+ json(conn, %{})
+ end
+
+ @doc "DELETE /api/v1/domain_blocks"
+ def delete(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
+ User.unblock_domain(blocker, domain)
+ json(conn, %{})
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
new file mode 100644
index 000000000..19041304e
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
@@ -0,0 +1,72 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.FilterController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.Filter
+
+ @doc "GET /api/v1/filters"
+ def index(%{assigns: %{user: user}} = conn, _) do
+ filters = Filter.get_filters(user)
+
+ render(conn, "filters.json", filters: filters)
+ end
+
+ @doc "POST /api/v1/filters"
+ def create(
+ %{assigns: %{user: user}} = conn,
+ %{"phrase" => phrase, "context" => context} = params
+ ) do
+ query = %Filter{
+ user_id: user.id,
+ phrase: phrase,
+ context: context,
+ hide: Map.get(params, "irreversible", false),
+ whole_word: Map.get(params, "boolean", true)
+ # expires_at
+ }
+
+ {:ok, response} = Filter.create(query)
+
+ render(conn, "filter.json", filter: response)
+ end
+
+ @doc "GET /api/v1/filters/:id"
+ def show(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
+ filter = Filter.get(filter_id, user)
+
+ render(conn, "filter.json", filter: filter)
+ end
+
+ @doc "PUT /api/v1/filters/:id"
+ def update(
+ %{assigns: %{user: user}} = conn,
+ %{"phrase" => phrase, "context" => context, "id" => filter_id} = params
+ ) do
+ query = %Filter{
+ user_id: user.id,
+ filter_id: filter_id,
+ phrase: phrase,
+ context: context,
+ hide: Map.get(params, "irreversible", nil),
+ whole_word: Map.get(params, "boolean", true)
+ # expires_at
+ }
+
+ {:ok, response} = Filter.update(query)
+ render(conn, "filter.json", filter: response)
+ end
+
+ @doc "DELETE /api/v1/filters/:id"
+ def delete(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
+ query = %Filter{
+ user_id: user.id,
+ filter_id: filter_id
+ }
+
+ {:ok, _} = Filter.delete(query)
+ json(conn, %{})
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex
new file mode 100644
index 000000000..267014b97
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex
@@ -0,0 +1,49 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+
+ plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
+ plug(:assign_follower when action != :index)
+
+ action_fallback(:errors)
+
+ @doc "GET /api/v1/follow_requests"
+ def index(%{assigns: %{user: followed}} = conn, _params) do
+ follow_requests = User.get_follow_requests(followed)
+
+ render(conn, "accounts.json", for: followed, users: follow_requests, as: :user)
+ end
+
+ @doc "POST /api/v1/follow_requests/:id/authorize"
+ def authorize(%{assigns: %{user: followed, follower: follower}} = conn, _params) do
+ with {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
+ render(conn, "relationship.json", user: followed, target: follower)
+ end
+ end
+
+ @doc "POST /api/v1/follow_requests/:id/reject"
+ def reject(%{assigns: %{user: followed, follower: follower}} = conn, _params) do
+ with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
+ render(conn, "relationship.json", user: followed, target: follower)
+ end
+ end
+
+ defp assign_follower(%{params: %{"id" => id}} = conn, _) do
+ case User.get_cached_by_id(id) do
+ %User{} = follower -> assign(conn, :follower, follower)
+ nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
+ end
+ end
+
+ defp errors(conn, {:error, message}) do
+ conn
+ |> put_status(:forbidden)
+ |> json(%{error: message})
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
index e4ae63231..0878f7ba6 100644
--- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
@@ -14,13 +14,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.Config
alias Pleroma.Conversation.Participation
alias Pleroma.Emoji
- alias Pleroma.Filter
alias Pleroma.HTTP
alias Pleroma.Object
alias Pleroma.Pagination
alias Pleroma.Plugs.RateLimiter
alias Pleroma.Repo
- alias Pleroma.ScheduledActivity
alias Pleroma.Stats
alias Pleroma.User
alias Pleroma.Web
@@ -30,12 +28,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.AppView
alias Pleroma.Web.MastodonAPI.ConversationView
- alias Pleroma.Web.MastodonAPI.FilterView
alias Pleroma.Web.MastodonAPI.ListView
alias Pleroma.Web.MastodonAPI.MastodonAPI
alias Pleroma.Web.MastodonAPI.MastodonView
alias Pleroma.Web.MastodonAPI.ReportView
- alias Pleroma.Web.MastodonAPI.ScheduledActivityView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.MediaProxy
alias Pleroma.Web.OAuth.App
@@ -44,35 +40,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.TwitterAPI.TwitterAPI
- import Ecto.Query
-
require Logger
require Pleroma.Constants
@rate_limited_relations_actions ~w(follow unfollow)a
- @rate_limited_status_actions ~w(reblog_status unreblog_status fav_status unfav_status
- post_status delete_status)a
-
- plug(
- RateLimiter,
- {:status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]}
- when action in ~w(reblog_status unreblog_status)a
- )
-
- plug(
- RateLimiter,
- {:status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]}
- when action in ~w(fav_status unfav_status)a
- )
-
plug(
RateLimiter,
{:relations_id_action, params: ["id", "uri"]} when action in @rate_limited_relations_actions
)
plug(RateLimiter, :relations_actions when action in @rate_limited_relations_actions)
- plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
plug(RateLimiter, :app_account_creation when action == :account_register)
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
plug(RateLimiter, :password_reset when action == :password_reset)
@@ -362,63 +340,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- def get_statuses(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do
- limit = 100
-
- activities =
- ids
- |> Enum.take(limit)
- |> Activity.all_by_ids_with_object()
- |> Enum.filter(&Visibility.visible_for_user?(&1, user))
-
- conn
- |> put_view(StatusView)
- |> render("index.json", activities: activities, for: user, as: :activity)
- end
-
- def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id_with_object(id),
- true <- Visibility.visible_for_user?(activity, user) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user})
- end
- end
-
- def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id(id),
- activities <-
- ActivityPub.fetch_activities_for_context(activity.data["context"], %{
- "blocking_user" => user,
- "user" => user,
- "exclude_id" => activity.id
- }),
- grouped_activities <- Enum.group_by(activities, fn %{id: id} -> id < activity.id end) do
- result = %{
- ancestors:
- StatusView.render(
- "index.json",
- for: user,
- activities: grouped_activities[true] || [],
- as: :activity
- )
- |> Enum.reverse(),
- # credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart
- descendants:
- StatusView.render(
- "index.json",
- for: user,
- activities: grouped_activities[false] || [],
- as: :activity
- )
- |> Enum.reverse()
- # credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart
- }
-
- json(conn, result)
- end
- end
-
def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
@@ -469,196 +390,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- def scheduled_statuses(%{assigns: %{user: user}} = conn, params) do
- with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
- conn
- |> add_link_headers(scheduled_activities)
- |> put_view(ScheduledActivityView)
- |> render("index.json", %{scheduled_activities: scheduled_activities})
- end
- end
-
- def show_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do
- with %ScheduledActivity{} = scheduled_activity <-
- ScheduledActivity.get(user, scheduled_activity_id) do
- conn
- |> put_view(ScheduledActivityView)
- |> render("show.json", %{scheduled_activity: scheduled_activity})
- else
- _ -> {:error, :not_found}
- end
- end
-
- def update_scheduled_status(
- %{assigns: %{user: user}} = conn,
- %{"id" => scheduled_activity_id} = params
- ) do
- with %ScheduledActivity{} = scheduled_activity <-
- ScheduledActivity.get(user, scheduled_activity_id),
- {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do
- conn
- |> put_view(ScheduledActivityView)
- |> render("show.json", %{scheduled_activity: scheduled_activity})
- else
- nil -> {:error, :not_found}
- error -> error
- end
- end
-
- def delete_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do
- with %ScheduledActivity{} = scheduled_activity <-
- ScheduledActivity.get(user, scheduled_activity_id),
- {:ok, scheduled_activity} <- ScheduledActivity.delete(scheduled_activity) do
- conn
- |> put_view(ScheduledActivityView)
- |> render("show.json", %{scheduled_activity: scheduled_activity})
- else
- nil -> {:error, :not_found}
- error -> error
- end
- end
-
- def post_status(
- %{assigns: %{user: user}} = conn,
- %{"status" => _, "scheduled_at" => scheduled_at} = params
- ) do
- if ScheduledActivity.far_enough?(scheduled_at) do
- with {:ok, scheduled_activity} <-
- ScheduledActivity.create(user, %{"params" => params, "scheduled_at" => scheduled_at}) do
- conn
- |> put_view(ScheduledActivityView)
- |> render("show.json", %{scheduled_activity: scheduled_activity})
- end
- else
- post_status(conn, Map.drop(params, ["scheduled_at"]))
- end
- end
-
- def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
- case CommonAPI.post(user, params) do
- {:ok, activity} ->
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{
- activity: activity,
- for: user,
- as: :activity,
- with_direct_conversation_id: true
- })
-
- {:error, message} ->
- conn
- |> put_status(:unprocessable_entity)
- |> json(%{error: message})
- end
- end
-
- def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
- json(conn, %{})
- else
- _e -> render_error(conn, :forbidden, "Can't delete this post")
- end
- end
-
- def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
- %Activity{} = announce <- Activity.normalize(announce.data) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: announce, for: user, as: :activity})
- end
- end
-
- def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
- %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
- %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
- %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def pin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def unpin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id_with_object(id),
- %User{} = user <- User.get_cached_by_nickname(user.nickname),
- true <- Visibility.visible_for_user?(activity, user),
- {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id_with_object(id),
- %User{} = user <- User.get_cached_by_nickname(user.nickname),
- true <- Visibility.visible_for_user?(activity, user),
- {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- activity = Activity.get_by_id(id)
-
- with {:ok, activity} <- CommonAPI.add_mute(user, activity) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
- def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- activity = Activity.get_by_id(id)
-
- with {:ok, activity} <- CommonAPI.remove_mute(user, activity) do
- conn
- |> put_view(StatusView)
- |> try_render("status.json", %{activity: activity, for: user, as: :activity})
- end
- end
-
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- id = List.wrap(id)
- q = from(u in User, where: u.id in ^id)
- targets = Repo.all(q)
+ targets = User.get_all_by_ids(List.wrap(id))
conn
|> put_view(AccountView)
@@ -668,19 +401,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
# Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array.
def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
- def update_media(%{assigns: %{user: user}} = conn, data) do
- with %Object{} = object <- Repo.get(Object, data["id"]),
+ def update_media(
+ %{assigns: %{user: user}} = conn,
+ %{"id" => id, "description" => description} = _
+ )
+ when is_binary(description) do
+ with %Object{} = object <- Repo.get(Object, id),
true <- Object.authorize_mutation(object, user),
- true <- is_binary(data["description"]),
- description <- data["description"] do
- new_data = %{object.data | "name" => description}
-
- {:ok, _} =
- object
- |> Object.change(%{data: new_data})
- |> Repo.update()
-
- attachment_data = Map.put(new_data, "id", object.id)
+ {:ok, %Object{data: data}} <- Object.update_data(object, %{"name" => description}) do
+ attachment_data = Map.put(data, "id", object.id)
conn
|> put_view(StatusView)
@@ -688,6 +417,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ def update_media(_conn, _data), do: {:error, :bad_request}
+
def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
with {:ok, object} <-
ActivityPub.upload(
@@ -722,46 +453,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def get_mascot(%{assigns: %{user: user}} = conn, _params) do
mascot = User.get_mascot(user)
- conn
- |> json(mascot)
- end
-
- def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id_with_object(id),
- {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
- %Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
- q = from(u in User, where: u.ap_id in ^likes)
-
- users =
- Repo.all(q)
- |> Enum.filter(&(not User.blocks?(user, &1)))
-
- conn
- |> put_view(AccountView)
- |> render("accounts.json", %{for: user, users: users, as: :user})
- else
- {:visible, false} -> {:error, :not_found}
- _ -> json(conn, [])
- end
- end
-
- def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with %Activity{} = activity <- Activity.get_by_id_with_object(id),
- {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
- %Object{data: %{"announcements" => announces}} <- Object.normalize(activity) do
- q = from(u in User, where: u.ap_id in ^announces)
-
- users =
- Repo.all(q)
- |> Enum.filter(&(not User.blocks?(user, &1)))
-
- conn
- |> put_view(AccountView)
- |> render("accounts.json", %{for: user, users: users, as: :user})
- else
- {:visible, false} -> {:error, :not_found}
- _ -> json(conn, [])
- end
+ json(conn, mascot)
end
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
@@ -798,42 +490,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
- follow_requests = User.get_follow_requests(followed)
-
- conn
- |> put_view(AccountView)
- |> render("accounts.json", %{for: followed, users: follow_requests, as: :user})
- end
-
- def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
- with %User{} = follower <- User.get_cached_by_id(id),
- {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do
- conn
- |> put_view(AccountView)
- |> render("relationship.json", %{user: followed, target: follower})
- else
- {:error, message} ->
- conn
- |> put_status(:forbidden)
- |> json(%{error: message})
- end
- end
-
- def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
- with %User{} = follower <- User.get_cached_by_id(id),
- {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
- conn
- |> put_view(AccountView)
- |> render("relationship.json", %{user: followed, target: follower})
- else
- {:error, message} ->
- conn
- |> put_status(:forbidden)
- |> json(%{error: message})
- end
- end
-
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
with {_, %User{} = followed} <- {:followed, User.get_cached_by_id(id)},
{_, true} <- {:followed, follower.id != followed.id},
@@ -963,20 +619,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- def domain_blocks(%{assigns: %{user: %{info: info}}} = conn, _) do
- json(conn, info.domain_blocks || [])
- end
-
- def block_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
- User.block_domain(blocker, domain)
- json(conn, %{})
- end
-
- def unblock_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
- User.unblock_domain(blocker, domain)
- json(conn, %{})
- end
-
def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %User{} = subscription_target <- User.get_cached_by_id(id),
{:ok, subscription_target} = User.subscribe(user, subscription_target) do
@@ -984,10 +626,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> put_view(AccountView)
|> render("relationship.json", %{user: user, target: subscription_target})
else
- {:error, message} ->
- conn
- |> put_status(:forbidden)
- |> json(%{error: message})
+ nil -> {:error, :not_found}
+ e -> e
end
end
@@ -998,10 +638,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> put_view(AccountView)
|> render("relationship.json", %{user: user, target: subscription_target})
else
- {:error, message} ->
- conn
- |> put_status(:forbidden)
- |> json(%{error: message})
+ nil -> {:error, :not_found}
+ e -> e
end
end
@@ -1072,8 +710,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def account_lists(%{assigns: %{user: user}} = conn, %{"id" => account_id}) do
lists = Pleroma.List.get_lists_account_belongs(user, account_id)
- res = ListView.render("lists.json", lists: lists)
- json(conn, res)
+
+ conn
+ |> put_view(ListView)
+ |> render("index.json", %{lists: lists})
end
def index(%{assigns: %{user: user}} = conn, _params) do
@@ -1199,7 +839,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
@doc "Local Mastodon FE login init action"
def login(conn, %{"code" => auth_token}) do
with {:ok, app} <- get_or_make_app(),
- %Authorization{} = auth <- Repo.get_by(Authorization, token: auth_token, app_id: app.id),
+ {:ok, auth} <- Authorization.get_by_token(app, auth_token),
{:ok, token} <- Token.exchange_token(app, auth) do
conn
|> put_session(:oauth_token, token.token)
@@ -1211,9 +851,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def login(conn, _) do
with {:ok, app} <- get_or_make_app() do
path =
- o_auth_path(
- conn,
- :authorize,
+ o_auth_path(conn, :authorize,
response_type: "code",
client_id: app.client_id,
redirect_uri: ".",
@@ -1235,31 +873,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ @spec get_or_make_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
defp get_or_make_app do
- find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."}
- scopes = ["read", "write", "follow", "push"]
-
- with %App{} = app <- Repo.get_by(App, find_attrs) do
- {:ok, app} =
- if app.scopes == scopes do
- {:ok, app}
- else
- app
- |> Changeset.change(%{scopes: scopes})
- |> Repo.update()
- end
-
- {:ok, app}
- else
- _e ->
- cs =
- App.register_changeset(
- %App{},
- Map.put(find_attrs, :scopes, scopes)
- )
-
- Repo.insert(cs)
- end
+ App.get_or_make(
+ %{client_name: @local_mastodon_name, redirect_uris: "."},
+ ["read", "write", "follow", "push"]
+ )
end
def logout(conn, _) do
@@ -1268,16 +887,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> redirect(to: "/")
end
- def relationship_noop(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- Logger.debug("Unimplemented, returning unmodified relationship")
-
- with %User{} = target <- User.get_cached_by_id(id) do
- conn
- |> put_view(AccountView)
- |> render("relationship.json", %{user: user, target: target})
- end
- end
-
+ # Stubs for unimplemented mastodon api
+ #
def empty_array(conn, _) do
Logger.debug("Unimplemented, returning an empty array")
json(conn, [])
@@ -1288,65 +899,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, %{})
end
- def get_filters(%{assigns: %{user: user}} = conn, _) do
- filters = Filter.get_filters(user)
- res = FilterView.render("filters.json", filters: filters)
- json(conn, res)
- end
-
- def create_filter(
- %{assigns: %{user: user}} = conn,
- %{"phrase" => phrase, "context" => context} = params
- ) do
- query = %Filter{
- user_id: user.id,
- phrase: phrase,
- context: context,
- hide: Map.get(params, "irreversible", false),
- whole_word: Map.get(params, "boolean", true)
- # expires_at
- }
-
- {:ok, response} = Filter.create(query)
- res = FilterView.render("filter.json", filter: response)
- json(conn, res)
- end
-
- def get_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
- filter = Filter.get(filter_id, user)
- res = FilterView.render("filter.json", filter: filter)
- json(conn, res)
- end
-
- def update_filter(
- %{assigns: %{user: user}} = conn,
- %{"phrase" => phrase, "context" => context, "id" => filter_id} = params
- ) do
- query = %Filter{
- user_id: user.id,
- filter_id: filter_id,
- phrase: phrase,
- context: context,
- hide: Map.get(params, "irreversible", nil),
- whole_word: Map.get(params, "boolean", true)
- # expires_at
- }
-
- {:ok, response} = Filter.update(query)
- res = FilterView.render("filter.json", filter: response)
- json(conn, res)
- end
-
- def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
- query = %Filter{
- user_id: user.id,
- filter_id: filter_id
- }
-
- {:ok, _} = Filter.delete(query)
- json(conn, %{})
- end
-
def suggestions(%{assigns: %{user: user}} = conn, _) do
suggestions = Config.get(:suggestions)
@@ -1394,22 +946,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- def status_card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do
- with %Activity{} = activity <- Activity.get_by_id(status_id),
- true <- Visibility.visible_for_user?(activity, user) do
- data =
- StatusView.render(
- "card.json",
- Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
- )
-
- json(conn, data)
- else
- _e ->
- %{}
- end
- end
-
def reports(%{assigns: %{user: user}} = conn, params) do
case CommonAPI.report(user, params) do
{:ok, activity} ->
@@ -1459,7 +995,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- def account_register(%{assigns: %{app: _app}} = conn, _params) do
+ def account_register(%{assigns: %{app: _app}} = conn, _) do
render_error(conn, :bad_request, "Missing parameters")
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
new file mode 100644
index 000000000..0a56b10b6
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
@@ -0,0 +1,51 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
+
+ alias Pleroma.ScheduledActivity
+ alias Pleroma.Web.MastodonAPI.MastodonAPI
+
+ plug(:assign_scheduled_activity when action != :index)
+
+ action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+
+ @doc "GET /api/v1/scheduled_statuses"
+ def index(%{assigns: %{user: user}} = conn, params) do
+ with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
+ conn
+ |> add_link_headers(scheduled_activities)
+ |> render("index.json", scheduled_activities: scheduled_activities)
+ end
+ end
+
+ @doc "GET /api/v1/scheduled_statuses/:id"
+ def show(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params) do
+ render(conn, "show.json", scheduled_activity: scheduled_activity)
+ end
+
+ @doc "PUT /api/v1/scheduled_statuses/:id"
+ def update(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, params) do
+ with {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do
+ render(conn, "show.json", scheduled_activity: scheduled_activity)
+ end
+ end
+
+ @doc "DELETE /api/v1/scheduled_statuses/:id"
+ def delete(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params) do
+ with {:ok, scheduled_activity} <- ScheduledActivity.delete(scheduled_activity) do
+ render(conn, "show.json", scheduled_activity: scheduled_activity)
+ end
+ end
+
+ defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do
+ case ScheduledActivity.get(user, id) do
+ %ScheduledActivity{} = activity -> assign(conn, :scheduled_activity, activity)
+ nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
+ end
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
new file mode 100644
index 000000000..f4de9285b
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -0,0 +1,274 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.StatusController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.MastodonAPI.MastodonAPIController, only: [try_render: 3]
+
+ require Ecto.Query
+
+ alias Pleroma.Activity
+ alias Pleroma.Bookmark
+ alias Pleroma.Object
+ alias Pleroma.Plugs.RateLimiter
+ alias Pleroma.Repo
+ alias Pleroma.ScheduledActivity
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.MastodonAPI.AccountView
+ alias Pleroma.Web.MastodonAPI.ScheduledActivityView
+
+ @rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
+
+ plug(
+ RateLimiter,
+ {:status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]}
+ when action in ~w(reblog unreblog)a
+ )
+
+ plug(
+ RateLimiter,
+ {:status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]}
+ when action in ~w(favourite unfavourite)a
+ )
+
+ plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
+
+ action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+
+ @doc """
+ GET `/api/v1/statuses?ids[]=1&ids[]=2`
+
+ `ids` query param is required
+ """
+ def index(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do
+ limit = 100
+
+ activities =
+ ids
+ |> Enum.take(limit)
+ |> Activity.all_by_ids_with_object()
+ |> Enum.filter(&Visibility.visible_for_user?(&1, user))
+
+ render(conn, "index.json", activities: activities, for: user, as: :activity)
+ end
+
+ @doc """
+ POST /api/v1/statuses
+
+ Creates a scheduled status when `scheduled_at` param is present and it's far enough
+ """
+ def create(
+ %{assigns: %{user: user}} = conn,
+ %{"status" => _, "scheduled_at" => scheduled_at} = params
+ ) do
+ params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"])
+
+ if ScheduledActivity.far_enough?(scheduled_at) do
+ with {:ok, scheduled_activity} <-
+ ScheduledActivity.create(user, %{"params" => params, "scheduled_at" => scheduled_at}) do
+ conn
+ |> put_view(ScheduledActivityView)
+ |> render("show.json", scheduled_activity: scheduled_activity)
+ end
+ else
+ create(conn, Map.drop(params, ["scheduled_at"]))
+ end
+ end
+
+ @doc """
+ POST /api/v1/statuses
+
+ Creates a regular status
+ """
+ def create(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
+ params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"])
+
+ with {:ok, activity} <- CommonAPI.post(user, params) do
+ try_render(conn, "show.json",
+ activity: activity,
+ for: user,
+ as: :activity,
+ with_direct_conversation_id: true
+ )
+ else
+ {:error, message} ->
+ conn
+ |> put_status(:unprocessable_entity)
+ |> json(%{error: message})
+ end
+ end
+
+ def create(%{assigns: %{user: _user}} = conn, %{"media_ids" => _} = params) do
+ create(conn, Map.put(params, "status", ""))
+ end
+
+ @doc "GET /api/v1/statuses/:id"
+ def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ true <- Visibility.visible_for_user?(activity, user) do
+ try_render(conn, "show.json", activity: activity, for: user)
+ end
+ end
+
+ @doc "DELETE /api/v1/statuses/:id"
+ def delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
+ json(conn, %{})
+ else
+ _e -> render_error(conn, :forbidden, "Can't delete this post")
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/reblog"
+ def reblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
+ with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user),
+ %Activity{} = announce <- Activity.normalize(announce.data) do
+ try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/unreblog"
+ def unreblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
+ with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user),
+ %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
+ try_render(conn, "show.json", %{activity: activity, for: user, as: :activity})
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/favourite"
+ def favourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
+ with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
+ %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/unfavourite"
+ def unfavourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
+ with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user),
+ %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/pin"
+ def pin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
+ with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/unpin"
+ def unpin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
+ with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/bookmark"
+ def bookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ %User{} = user <- User.get_cached_by_nickname(user.nickname),
+ true <- Visibility.visible_for_user?(activity, user),
+ {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/unbookmark"
+ def unbookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ %User{} = user <- User.get_cached_by_nickname(user.nickname),
+ true <- Visibility.visible_for_user?(activity, user),
+ {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/mute"
+ def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id(id),
+ {:ok, activity} <- CommonAPI.add_mute(user, activity) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "POST /api/v1/statuses/:id/unmute"
+ def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id(id),
+ {:ok, activity} <- CommonAPI.remove_mute(user, activity) do
+ try_render(conn, "show.json", activity: activity, for: user, as: :activity)
+ end
+ end
+
+ @doc "GET /api/v1/statuses/:id/card"
+ @deprecated "https://github.com/tootsuite/mastodon/pull/11213"
+ def card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do
+ with %Activity{} = activity <- Activity.get_by_id(status_id),
+ true <- Visibility.visible_for_user?(activity, user) do
+ data = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+ render(conn, "card.json", data)
+ else
+ _ -> render_error(conn, :not_found, "Record not found")
+ end
+ end
+
+ @doc "GET /api/v1/statuses/:id/favourited_by"
+ def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
+ %Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
+ users =
+ User
+ |> Ecto.Query.where([u], u.ap_id in ^likes)
+ |> Repo.all()
+ |> Enum.filter(&(not User.blocks?(user, &1)))
+
+ conn
+ |> put_view(AccountView)
+ |> render("accounts.json", for: user, users: users, as: :user)
+ else
+ {:visible, false} -> {:error, :not_found}
+ _ -> json(conn, [])
+ end
+ end
+
+ @doc "GET /api/v1/statuses/:id/reblogged_by"
+ def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
+ %Object{data: %{"announcements" => announces}} <- Object.normalize(activity) do
+ users =
+ User
+ |> Ecto.Query.where([u], u.ap_id in ^announces)
+ |> Repo.all()
+ |> Enum.filter(&(not User.blocks?(user, &1)))
+
+ conn
+ |> put_view(AccountView)
+ |> render("accounts.json", for: user, users: users, as: :user)
+ else
+ {:visible, false} -> {:error, :not_found}
+ _ -> json(conn, [])
+ end
+ end
+
+ @doc "GET /api/v1/statuses/:id/context"
+ def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id(id) do
+ activities =
+ ActivityPub.fetch_activities_for_context(activity.data["context"], %{
+ "blocking_user" => user,
+ "user" => user,
+ "exclude_id" => activity.id
+ })
+
+ render(conn, "context.json", activity: activity, activities: activities, user: user)
+ end
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
index 40acc07b3..4aeb79d81 100644
--- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
@@ -24,7 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
activity = Activity.get_by_id_with_object(last_activity_id)
- last_status = StatusView.render("status.json", %{activity: activity, for: user})
+ last_status = StatusView.render("show.json", %{activity: activity, for: user})
# Conversations return all users except the current user.
users =
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index ec8eadcaa..05110a192 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -39,19 +39,19 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
"mention" ->
response
|> Map.merge(%{
- status: StatusView.render("status.json", %{activity: activity, for: user})
+ status: StatusView.render("show.json", %{activity: activity, for: user})
})
"favourite" ->
response
|> Map.merge(%{
- status: StatusView.render("status.json", %{activity: parent_activity, for: user})
+ status: StatusView.render("show.json", %{activity: parent_activity, for: user})
})
"reblog" ->
response
|> Map.merge(%{
- status: StatusView.render("status.json", %{activity: parent_activity, for: user})
+ status: StatusView.render("show.json", %{activity: parent_activity, for: user})
})
"follow" ->
diff --git a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex
index 0aae15ab9..fc042a276 100644
--- a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex
@@ -7,11 +7,10 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
alias Pleroma.ScheduledActivity
alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.MastodonAPI.ScheduledActivityView
alias Pleroma.Web.MastodonAPI.StatusView
def render("index.json", %{scheduled_activities: scheduled_activities}) do
- render_many(scheduled_activities, ScheduledActivityView, "show.json")
+ render_many(scheduled_activities, __MODULE__, "show.json")
end
def render("show.json", %{scheduled_activity: %ScheduledActivity{} = scheduled_activity}) do
@@ -24,12 +23,8 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
end
defp with_media_attachments(data, %{params: %{"media_attachments" => media_attachments}}) do
- try do
- attachments = render_many(media_attachments, StatusView, "attachment.json", as: :attachment)
- Map.put(data, :media_attachments, attachments)
- rescue
- _ -> data
- end
+ attachments = render_many(media_attachments, StatusView, "attachment.json", as: :attachment)
+ Map.put(data, :media_attachments, attachments)
end
defp with_media_attachments(data, _), do: data
@@ -45,13 +40,9 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
in_reply_to_id: params["in_reply_to_id"]
}
- data =
- if media_ids = params["media_ids"] do
- Map.put(data, :media_ids, media_ids)
- else
- data
- end
-
- data
+ case params["media_ids"] do
+ nil -> data
+ media_ids -> Map.put(data, :media_ids, media_ids)
+ end
end
end
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index ef796cddd..2321d0de2 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -73,17 +73,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
def render("index.json", opts) do
replied_to_activities = get_replied_to_activities(opts.activities)
+ opts = Map.put(opts, :replied_to_activities, replied_to_activities)
- opts.activities
- |> safe_render_many(
- StatusView,
- "status.json",
- Map.put(opts, :replied_to_activities, replied_to_activities)
- )
+ safe_render_many(opts.activities, StatusView, "show.json", opts)
end
def render(
- "status.json",
+ "show.json",
%{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts
) do
user = get_user(activity.data["actor"])
@@ -96,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|> Activity.with_set_thread_muted_field(opts[:for])
|> Repo.one()
- reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity))
+ reblogged = render("show.json", Map.put(opts, :activity, reblogged_activity))
favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || [])
@@ -144,7 +140,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
- def render("status.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
+ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
object = Object.normalize(activity)
user = get_user(activity.data["actor"])
@@ -303,7 +299,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
- def render("status.json", _) do
+ def render("show.json", _) do
nil
end
@@ -343,9 +339,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
- def render("card.json", _) do
- nil
- end
+ def render("card.json", _), do: nil
def render("attachment.json", %{attachment: attachment}) do
[attachment_url | _] = attachment["url"]
@@ -443,6 +437,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
end
+ def render("context.json", %{activity: activity, activities: activities, user: user}) do
+ %{ancestors: ancestors, descendants: descendants} =
+ activities
+ |> Enum.reverse()
+ |> Enum.group_by(fn %{id: id} -> if id < activity.id, do: :ancestors, else: :descendants end)
+ |> Map.put_new(:ancestors, [])
+ |> Map.put_new(:descendants, [])
+
+ %{
+ ancestors: render("index.json", for: user, activities: ancestors, as: :activity),
+ descendants: render("index.json", for: user, activities: descendants, as: :activity)
+ }
+ end
+
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
object = Object.normalize(activity)
diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex
index ddcdb1871..cc3fb1ce5 100644
--- a/lib/pleroma/web/oauth/app.ex
+++ b/lib/pleroma/web/oauth/app.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.OAuth.App do
use Ecto.Schema
import Ecto.Changeset
+ alias Pleroma.Repo
@type t :: %__MODULE__{}
@@ -39,4 +40,29 @@ defmodule Pleroma.Web.OAuth.App do
changeset
end
end
+
+ @doc """
+ Gets app by attrs or create new with attrs.
+ And updates the scopes if need.
+ """
+ @spec get_or_make(map(), list(String.t())) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
+ def get_or_make(attrs, scopes) do
+ with %__MODULE__{} = app <- Repo.get_by(__MODULE__, attrs) do
+ update_scopes(app, scopes)
+ else
+ _e ->
+ %__MODULE__{}
+ |> register_changeset(Map.put(attrs, :scopes, scopes))
+ |> Repo.insert()
+ end
+ end
+
+ defp update_scopes(%__MODULE__{} = app, []), do: {:ok, app}
+ defp update_scopes(%__MODULE__{scopes: scopes} = app, scopes), do: {:ok, app}
+
+ defp update_scopes(%__MODULE__{} = app, scopes) do
+ app
+ |> change(%{scopes: scopes})
+ |> Repo.update()
+ end
end
diff --git a/lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex b/lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex
index 0eccbcbb9..fc41a7389 100644
--- a/lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex
@@ -36,19 +36,19 @@ defmodule Pleroma.Web.PleromaAPI.SubscriptionNotificationView do
"mention" ->
response
|> Map.merge(%{
- status: StatusView.render("status.json", %{activity: activity, for: user})
+ status: StatusView.render("show.json", %{activity: activity, for: user})
})
"favourite" ->
response
|> Map.merge(%{
- status: StatusView.render("status.json", %{activity: parent_activity, for: user})
+ status: StatusView.render("show.json", %{activity: parent_activity, for: user})
})
"reblog" ->
response
|> Map.merge(%{
- status: StatusView.render("status.json", %{activity: parent_activity, for: user})
+ status: StatusView.render("show.json", %{activity: parent_activity, for: user})
})
"follow" ->
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 5b744e898..a025474e2 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -323,7 +323,7 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id/lists", MastodonAPIController, :account_lists)
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
- get("/follow_requests", MastodonAPIController, :follow_requests)
+ get("/follow_requests", FollowRequestController, :index)
get("/blocks", MastodonAPIController, :blocks)
get("/mutes", MastodonAPIController, :mutes)
@@ -339,16 +339,16 @@ defmodule Pleroma.Web.Router do
post("/notifications/dismiss", NotificationController, :dismiss)
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
- get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
- get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
+ get("/scheduled_statuses", ScheduledActivityController, :index)
+ get("/scheduled_statuses/:id", ScheduledActivityController, :show)
get("/lists", ListController, :index)
get("/lists/:id", ListController, :show)
get("/lists/:id/accounts", ListController, :list_accounts)
- get("/domain_blocks", MastodonAPIController, :domain_blocks)
+ get("/domain_blocks", DomainBlockController, :index)
- get("/filters", MastodonAPIController, :get_filters)
+ get("/filters", FilterController, :index)
get("/suggestions", MastodonAPIController, :suggestions)
@@ -363,22 +363,22 @@ defmodule Pleroma.Web.Router do
patch("/accounts/update_credentials", MastodonAPIController, :update_credentials)
- post("/statuses", MastodonAPIController, :post_status)
- delete("/statuses/:id", MastodonAPIController, :delete_status)
+ post("/statuses", StatusController, :create)
+ delete("/statuses/:id", StatusController, :delete)
- post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
- post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
- post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
- post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
- post("/statuses/:id/pin", MastodonAPIController, :pin_status)
- post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
- post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status)
- post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status)
- post("/statuses/:id/mute", MastodonAPIController, :mute_conversation)
- post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation)
+ post("/statuses/:id/reblog", StatusController, :reblog)
+ post("/statuses/:id/unreblog", StatusController, :unreblog)
+ post("/statuses/:id/favourite", StatusController, :favourite)
+ post("/statuses/:id/unfavourite", StatusController, :unfavourite)
+ post("/statuses/:id/pin", StatusController, :pin)
+ post("/statuses/:id/unpin", StatusController, :unpin)
+ post("/statuses/:id/bookmark", StatusController, :bookmark)
+ post("/statuses/:id/unbookmark", StatusController, :unbookmark)
+ post("/statuses/:id/mute", StatusController, :mute_conversation)
+ post("/statuses/:id/unmute", StatusController, :unmute_conversation)
- put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status)
- delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status)
+ put("/scheduled_statuses/:id", ScheduledActivityController, :update)
+ delete("/scheduled_statuses/:id", ScheduledActivityController, :delete)
post("/polls/:id/votes", MastodonAPIController, :poll_vote)
@@ -392,10 +392,10 @@ defmodule Pleroma.Web.Router do
post("/lists/:id/accounts", ListController, :add_to_list)
delete("/lists/:id/accounts", ListController, :remove_from_list)
- post("/filters", MastodonAPIController, :create_filter)
- get("/filters/:id", MastodonAPIController, :get_filter)
- put("/filters/:id", MastodonAPIController, :update_filter)
- delete("/filters/:id", MastodonAPIController, :delete_filter)
+ post("/filters", FilterController, :create)
+ get("/filters/:id", FilterController, :show)
+ put("/filters/:id", FilterController, :update)
+ delete("/filters/:id", FilterController, :delete)
patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar)
patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner)
@@ -419,11 +419,11 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/mute", MastodonAPIController, :mute)
post("/accounts/:id/unmute", MastodonAPIController, :unmute)
- post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request)
- post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request)
+ post("/follow_requests/:id/authorize", FollowRequestController, :authorize)
+ post("/follow_requests/:id/reject", FollowRequestController, :reject)
- post("/domain_blocks", MastodonAPIController, :block_domain)
- delete("/domain_blocks", MastodonAPIController, :unblock_domain)
+ post("/domain_blocks", DomainBlockController, :create)
+ delete("/domain_blocks", DomainBlockController, :delete)
post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe)
post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe)
@@ -456,10 +456,10 @@ defmodule Pleroma.Web.Router do
get("/apps/verify_credentials", MastodonAPIController, :verify_app_credentials)
get("/custom_emojis", MastodonAPIController, :custom_emojis)
- get("/statuses/:id/card", MastodonAPIController, :status_card)
+ get("/statuses/:id/card", StatusController, :card)
- get("/statuses/:id/favourited_by", MastodonAPIController, :favourited_by)
- get("/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by)
+ get("/statuses/:id/favourited_by", StatusController, :favourited_by)
+ get("/statuses/:id/reblogged_by", StatusController, :reblogged_by)
get("/trends", MastodonAPIController, :empty_array)
@@ -478,9 +478,9 @@ defmodule Pleroma.Web.Router do
get("/timelines/tag/:tag", TimelineController, :hashtag)
get("/timelines/list/:list_id", TimelineController, :list)
- get("/statuses", MastodonAPIController, :get_statuses)
- get("/statuses/:id", MastodonAPIController, :get_status)
- get("/statuses/:id/context", MastodonAPIController, :get_context)
+ get("/statuses", StatusController, :index)
+ get("/statuses/:id", StatusController, :show)
+ get("/statuses/:id/context", StatusController, :context)
get("/polls/:id", MastodonAPIController, :get_poll)
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index 8eda762c7..bfd838902 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -29,7 +29,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
# true if captcha is disabled or enabled and valid, false otherwise
captcha_ok =
- if !captcha_enabled do
+ if not captcha_enabled do
:ok
else
Pleroma.Captcha.validate(
diff --git a/lib/pleroma/web/views/streamer_view.ex b/lib/pleroma/web/views/streamer_view.ex
index b13030fa0..a9f14d09a 100644
--- a/lib/pleroma/web/views/streamer_view.ex
+++ b/lib/pleroma/web/views/streamer_view.ex
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.StreamerView do
event: "update",
payload:
Pleroma.Web.MastodonAPI.StatusView.render(
- "status.json",
+ "show.json",
activity: activity,
for: user
)
@@ -43,7 +43,7 @@ defmodule Pleroma.Web.StreamerView do
event: "update",
payload:
Pleroma.Web.MastodonAPI.StatusView.render(
- "status.json",
+ "show.json",
activity: activity
)
|> Jason.encode!()