Allow to mark a single notification as read
[akkoma] / lib / pleroma / web / twitter_api / controllers / util_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.TwitterAPI.UtilController do
6 use Pleroma.Web, :controller
7
8 require Logger
9
10 alias Comeonin.Pbkdf2
11 alias Pleroma.Emoji
12 alias Pleroma.Notification
13 alias Pleroma.PasswordResetToken
14 alias Pleroma.Repo
15 alias Pleroma.User
16 alias Pleroma.Web
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.OStatus
20 alias Pleroma.Web.WebFinger
21
22 def show_password_reset(conn, %{"token" => token}) do
23 with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
24 %User{} = user <- Repo.get(User, token.user_id) do
25 render(conn, "password_reset.html", %{
26 token: token,
27 user: user
28 })
29 else
30 _e -> render(conn, "invalid_token.html")
31 end
32 end
33
34 def password_reset(conn, %{"data" => data}) do
35 with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
36 render(conn, "password_reset_success.html")
37 else
38 _e -> render(conn, "password_reset_failed.html")
39 end
40 end
41
42 def help_test(conn, _params) do
43 json(conn, "ok")
44 end
45
46 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
47 with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do
48 conn
49 |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
50 else
51 _e ->
52 render(conn, "subscribe.html", %{
53 nickname: nick,
54 avatar: nil,
55 error: "Could not find user"
56 })
57 end
58 end
59
60 def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
61 with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
62 %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
63 conn
64 |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
65 else
66 _e ->
67 render(conn, "subscribe.html", %{
68 nickname: nick,
69 avatar: nil,
70 error: "Something went wrong."
71 })
72 end
73 end
74
75 def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
76 {err, followee} = OStatus.find_or_make_user(acct)
77 avatar = User.avatar_url(followee)
78 name = followee.nickname
79 id = followee.id
80
81 if !!user do
82 conn
83 |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
84 else
85 conn
86 |> render("follow_login.html", %{
87 error: false,
88 acct: acct,
89 avatar: avatar,
90 name: name,
91 id: id
92 })
93 end
94 end
95
96 def do_remote_follow(conn, %{
97 "authorization" => %{"name" => username, "password" => password, "id" => id}
98 }) do
99 followee = Repo.get(User, id)
100 avatar = User.avatar_url(followee)
101 name = followee.nickname
102
103 with %User{} = user <- User.get_cached_by_nickname(username),
104 true <- Pbkdf2.checkpw(password, user.password_hash),
105 %User{} = _followed <- Repo.get(User, id),
106 {:ok, follower} <- User.follow(user, followee),
107 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
108 conn
109 |> render("followed.html", %{error: false})
110 else
111 # Was already following user
112 {:error, "Could not follow user:" <> _rest} ->
113 render(conn, "followed.html", %{error: false})
114
115 _e ->
116 conn
117 |> render("follow_login.html", %{
118 error: "Wrong username or password",
119 id: id,
120 name: name,
121 avatar: avatar
122 })
123 end
124 end
125
126 def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
127 with %User{} = followee <- Repo.get(User, id),
128 {:ok, follower} <- User.follow(user, followee),
129 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
130 conn
131 |> render("followed.html", %{error: false})
132 else
133 # Was already following user
134 {:error, "Could not follow user:" <> _rest} ->
135 conn
136 |> render("followed.html", %{error: false})
137
138 e ->
139 Logger.debug("Remote follow failed with error #{inspect(e)}")
140
141 conn
142 |> render("followed.html", %{error: inspect(e)})
143 end
144 end
145
146 def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
147 with {:ok, _} <- Notification.read_one(user, notification_id) do
148 json(conn, %{status: "success"})
149 else
150 {:error, message} ->
151 conn
152 |> put_resp_content_type("application/json")
153 |> send_resp(403, Jason.encode!(%{"error" => message}))
154 end
155 end
156
157 def config(conn, _params) do
158 instance = Pleroma.Config.get(:instance)
159 instance_fe = Pleroma.Config.get(:fe)
160 instance_chat = Pleroma.Config.get(:chat)
161
162 case get_format(conn) do
163 "xml" ->
164 response = """
165 <config>
166 <site>
167 <name>#{Keyword.get(instance, :name)}</name>
168 <site>#{Web.base_url()}</site>
169 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
170 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
171 </site>
172 </config>
173 """
174
175 conn
176 |> put_resp_content_type("application/xml")
177 |> send_resp(200, response)
178
179 _ ->
180 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
181
182 uploadlimit = %{
183 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
184 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
185 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
186 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
187 }
188
189 data = %{
190 name: Keyword.get(instance, :name),
191 description: Keyword.get(instance, :description),
192 server: Web.base_url(),
193 textlimit: to_string(Keyword.get(instance, :limit)),
194 uploadlimit: uploadlimit,
195 closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
196 private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
197 vapidPublicKey: vapid_public_key,
198 accountActivationRequired:
199 if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
200 invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0")
201 }
202
203 pleroma_fe =
204 if instance_fe do
205 %{
206 theme: Keyword.get(instance_fe, :theme),
207 background: Keyword.get(instance_fe, :background),
208 logo: Keyword.get(instance_fe, :logo),
209 logoMask: Keyword.get(instance_fe, :logo_mask),
210 logoMargin: Keyword.get(instance_fe, :logo_margin),
211 redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
212 redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
213 chatDisabled: !Keyword.get(instance_chat, :enabled),
214 showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
215 scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
216 formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
217 collapseMessageWithSubject:
218 Keyword.get(instance_fe, :collapse_message_with_subject),
219 hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
220 hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
221 scopeCopy: Keyword.get(instance_fe, :scope_copy),
222 subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
223 alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
224 }
225 else
226 Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
227 end
228
229 managed_config = Keyword.get(instance, :managed_config)
230
231 data =
232 if managed_config do
233 data |> Map.put("pleromafe", pleroma_fe)
234 else
235 data
236 end
237
238 json(conn, %{site: data})
239 end
240 end
241
242 def frontend_configurations(conn, _params) do
243 config =
244 Pleroma.Config.get(:frontend_configurations, %{})
245 |> Enum.into(%{})
246
247 json(conn, config)
248 end
249
250 def version(conn, _params) do
251 version = Pleroma.Application.named_version()
252
253 case get_format(conn) do
254 "xml" ->
255 response = "<version>#{version}</version>"
256
257 conn
258 |> put_resp_content_type("application/xml")
259 |> send_resp(200, response)
260
261 _ ->
262 json(conn, version)
263 end
264 end
265
266 def emoji(conn, _params) do
267 json(conn, Enum.into(Emoji.get_all(), %{}))
268 end
269
270 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
271 follow_import(conn, %{"list" => File.read!(listfile.path)})
272 end
273
274 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
275 with followed_identifiers <- String.split(list),
276 {:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
277 json(conn, "job started")
278 end
279 end
280
281 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
282 blocks_import(conn, %{"list" => File.read!(listfile.path)})
283 end
284
285 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
286 with blocked_identifiers <- String.split(list),
287 {:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
288 json(conn, "job started")
289 end
290 end
291
292 def change_password(%{assigns: %{user: user}} = conn, params) do
293 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
294 {:ok, user} ->
295 with {:ok, _user} <-
296 User.reset_password(user, %{
297 password: params["new_password"],
298 password_confirmation: params["new_password_confirmation"]
299 }) do
300 json(conn, %{status: "success"})
301 else
302 {:error, changeset} ->
303 {_, {error, _}} = Enum.at(changeset.errors, 0)
304 json(conn, %{error: "New password #{error}."})
305
306 _ ->
307 json(conn, %{error: "Unable to change password."})
308 end
309
310 {:error, msg} ->
311 json(conn, %{error: msg})
312 end
313 end
314
315 def delete_account(%{assigns: %{user: user}} = conn, params) do
316 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
317 {:ok, user} ->
318 Task.start(fn -> User.delete(user) end)
319 json(conn, %{status: "success"})
320
321 {:error, msg} ->
322 json(conn, %{error: msg})
323 end
324 end
325
326 def captcha(conn, _params) do
327 json(conn, Pleroma.Captcha.new())
328 end
329 end