Merge remote-tracking branch 'origin/develop' into reactions
[akkoma] / lib / pleroma / web / mastodon_api / controllers / mastodon_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
9
10 alias Pleroma.Bookmark
11 alias Pleroma.Config
12 alias Pleroma.Pagination
13 alias Pleroma.User
14 alias Pleroma.Web
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.AccountView
18 alias Pleroma.Web.MastodonAPI.MastodonView
19 alias Pleroma.Web.MastodonAPI.StatusView
20
21 require Logger
22
23 action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
24
25 defp mastodonized_emoji do
26 Pleroma.Emoji.get_all()
27 |> Enum.map(fn {shortcode, %Pleroma.Emoji{file: relative_url, tags: tags}} ->
28 url = to_string(URI.merge(Web.base_url(), relative_url))
29
30 %{
31 "shortcode" => shortcode,
32 "static_url" => url,
33 "visible_in_picker" => true,
34 "url" => url,
35 "tags" => tags,
36 # Assuming that a comma is authorized in the category name
37 "category" => (tags -- ["Custom"]) |> Enum.join(",")
38 }
39 end)
40 end
41
42 def custom_emojis(conn, _params) do
43 mastodon_emoji = mastodonized_emoji()
44 json(conn, mastodon_emoji)
45 end
46
47 def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
48 with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
49 {_, true} <- {:followed, follower.id != followed.id},
50 {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
51 conn
52 |> put_view(AccountView)
53 |> render("show.json", %{user: followed, for: follower})
54 else
55 {:followed, _} ->
56 {:error, :not_found}
57
58 {:error, message} ->
59 conn
60 |> put_status(:forbidden)
61 |> json(%{error: message})
62 end
63 end
64
65 def mutes(%{assigns: %{user: user}} = conn, _) do
66 with muted_accounts <- User.muted_users(user) do
67 res = AccountView.render("index.json", users: muted_accounts, for: user, as: :user)
68 json(conn, res)
69 end
70 end
71
72 def blocks(%{assigns: %{user: user}} = conn, _) do
73 with blocked_accounts <- User.blocked_users(user) do
74 res = AccountView.render("index.json", users: blocked_accounts, for: user, as: :user)
75 json(conn, res)
76 end
77 end
78
79 def favourites(%{assigns: %{user: user}} = conn, params) do
80 params =
81 params
82 |> Map.put("type", "Create")
83 |> Map.put("favorited_by", user.ap_id)
84 |> Map.put("blocking_user", user)
85
86 activities =
87 ActivityPub.fetch_activities([], params)
88 |> Enum.reverse()
89
90 conn
91 |> add_link_headers(activities)
92 |> put_view(StatusView)
93 |> render("index.json", %{activities: activities, for: user, as: :activity})
94 end
95
96 def bookmarks(%{assigns: %{user: user}} = conn, params) do
97 user = User.get_cached_by_id(user.id)
98
99 bookmarks =
100 Bookmark.for_user_query(user.id)
101 |> Pagination.fetch_paginated(params)
102
103 activities =
104 bookmarks
105 |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end)
106
107 conn
108 |> add_link_headers(bookmarks)
109 |> put_view(StatusView)
110 |> render("index.json", %{activities: activities, for: user, as: :activity})
111 end
112
113 def index(%{assigns: %{user: user}} = conn, _params) do
114 token = get_session(conn, :oauth_token)
115
116 if user && token do
117 mastodon_emoji = mastodonized_emoji()
118
119 limit = Config.get([:instance, :limit])
120
121 accounts = Map.put(%{}, user.id, AccountView.render("show.json", %{user: user, for: user}))
122
123 initial_state =
124 %{
125 meta: %{
126 streaming_api_base_url: Pleroma.Web.Endpoint.websocket_url(),
127 access_token: token,
128 locale: "en",
129 domain: Pleroma.Web.Endpoint.host(),
130 admin: "1",
131 me: "#{user.id}",
132 unfollow_modal: false,
133 boost_modal: false,
134 delete_modal: true,
135 auto_play_gif: false,
136 display_sensitive_media: false,
137 reduce_motion: false,
138 max_toot_chars: limit,
139 mascot: User.get_mascot(user)["url"]
140 },
141 poll_limits: Config.get([:instance, :poll_limits]),
142 rights: %{
143 delete_others_notice: present?(user.info.is_moderator),
144 admin: present?(user.info.is_admin)
145 },
146 compose: %{
147 me: "#{user.id}",
148 default_privacy: user.info.default_scope,
149 default_sensitive: false,
150 allow_content_types: Config.get([:instance, :allowed_post_formats])
151 },
152 media_attachments: %{
153 accept_content_types: [
154 ".jpg",
155 ".jpeg",
156 ".png",
157 ".gif",
158 ".webm",
159 ".mp4",
160 ".m4v",
161 "image\/jpeg",
162 "image\/png",
163 "image\/gif",
164 "video\/webm",
165 "video\/mp4"
166 ]
167 },
168 settings:
169 user.info.settings ||
170 %{
171 onboarded: true,
172 home: %{
173 shows: %{
174 reblog: true,
175 reply: true
176 }
177 },
178 notifications: %{
179 alerts: %{
180 follow: true,
181 favourite: true,
182 reblog: true,
183 mention: true
184 },
185 shows: %{
186 follow: true,
187 favourite: true,
188 reblog: true,
189 mention: true
190 },
191 sounds: %{
192 follow: true,
193 favourite: true,
194 reblog: true,
195 mention: true
196 }
197 }
198 },
199 push_subscription: nil,
200 accounts: accounts,
201 custom_emojis: mastodon_emoji,
202 char_limit: limit
203 }
204 |> Jason.encode!()
205
206 conn
207 |> put_layout(false)
208 |> put_view(MastodonView)
209 |> render("index.html", %{initial_state: initial_state})
210 else
211 conn
212 |> put_session(:return_to, conn.request_path)
213 |> redirect(to: "/web/login")
214 end
215 end
216
217 def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
218 with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do
219 json(conn, %{})
220 else
221 e ->
222 conn
223 |> put_status(:internal_server_error)
224 |> json(%{error: inspect(e)})
225 end
226 end
227
228 # Stubs for unimplemented mastodon api
229 #
230 def empty_array(conn, _) do
231 Logger.debug("Unimplemented, returning an empty array")
232 json(conn, [])
233 end
234
235 def empty_object(conn, _) do
236 Logger.debug("Unimplemented, returning an empty object")
237 json(conn, %{})
238 end
239
240 defp present?(nil), do: false
241 defp present?(false), do: false
242 defp present?(_), do: true
243 end