Merge branch 'feature/add-pagination-to-users-admin-api' into 'develop'
authorkaniini <nenolod@gmail.com>
Sun, 3 Mar 2019 15:59:15 +0000 (15:59 +0000)
committerkaniini <nenolod@gmail.com>
Sun, 3 Mar 2019 15:59:15 +0000 (15:59 +0000)
Add pagination and search to users

See merge request pleroma/pleroma!873

31 files changed:
config/config.exs
docs/Differences-in-MastodonAPI-Responses.md
lib/pleroma/user.ex
lib/pleroma/user/info.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/auth/authenticator.ex [new file with mode: 0644]
lib/pleroma/web/auth/pleroma_authenticator.ex [new file with mode: 0644]
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/mastodon_api/websocket_handler.ex
lib/pleroma/web/oauth/oauth_controller.ex
lib/pleroma/web/twitter_api/representers/activity_representer.ex
lib/pleroma/web/twitter_api/twitter_api.ex
lib/pleroma/web/twitter_api/twitter_api_controller.ex
lib/pleroma/web/twitter_api/views/activity_view.ex
lib/pleroma/web/twitter_api/views/user_view.ex
lib/pleroma/web/web.ex
mix.exs
mix.lock
priv/repo/migrations/20190301101154_add_default_tags_to_user.exs [new file with mode: 0644]
priv/repo/migrations/20190303120636_update_user_note_counters.exs [new file with mode: 0644]
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/mastodon_api/account_view_test.exs
test/web/mastodon_api/mastodon_api_controller_test.exs
test/web/mastodon_api/status_view_test.exs
test/web/oauth/oauth_controller_test.exs
test/web/twitter_api/representers/activity_representer_test.exs
test/web/twitter_api/twitter_api_controller_test.exs
test/web/twitter_api/views/activity_view_test.exs
test/web/twitter_api/views/user_view_test.exs

index 7e4ac11006220639fd07657a6909f19b858d923e..a620e74517c8f52f350e192d7ac80b2b66d46ad5 100644 (file)
@@ -93,10 +93,11 @@ config :pleroma, Pleroma.Web.Endpoint,
     dispatch: [
       {:_,
        [
-         {"/api/v1/streaming", Elixir.Pleroma.Web.MastodonAPI.WebsocketHandler, []},
-         {"/socket/websocket", Phoenix.Endpoint.CowboyWebSocket,
-          {nil, {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
-         {:_, Plug.Adapters.Cowboy.Handler, {Pleroma.Web.Endpoint, []}}
+         {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+         {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+          {Phoenix.Transports.WebSocket,
+           {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
+         {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
        ]}
     ]
   ],
index f6a5b6461b8d86486508774dfa02c5edd9880872..3026e11738dfb68a9932785f7260c567cc70e3bd 100644 (file)
@@ -9,3 +9,7 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas
 ## Attachment cap
 
 Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting.
+
+## Timelines
+
+Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
index 230155c3375f24b9f054b8b397ac9f77ea04a5c8..78543b4265b24430b12845b9b16eb5ca3e6bd99c 100644 (file)
@@ -607,71 +607,65 @@ defmodule Pleroma.User do
         ),
       where:
         fragment(
-          "? @> ?",
+          "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
           a.data,
-          ^%{"object" => user.ap_id}
+          a.data,
+          ^user.ap_id
         )
     )
   end
 
-  def update_follow_request_count(%User{} = user) do
-    subquery =
+  def get_follow_requests(%User{} = user) do
+    users =
       user
       |> User.get_follow_requests_query()
-      |> select([a], %{count: count(a.id)})
+      |> join(:inner, [a], u in User, a.actor == u.ap_id)
+      |> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address]))
+      |> group_by([a, u], u.id)
+      |> select([a, u], u)
+      |> Repo.all()
+
+    {:ok, users}
+  end
 
+  def increase_note_count(%User{} = user) do
     User
     |> where(id: ^user.id)
-    |> join(:inner, [u], s in subquery(subquery))
-    |> update([u, s],
+    |> update([u],
       set: [
         info:
           fragment(
-            "jsonb_set(?, '{follow_request_count}', ?::varchar::jsonb, true)",
+            "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)",
             u.info,
-            s.count
+            u.info
           )
       ]
     )
     |> Repo.update_all([], returning: true)
     |> case do
-      {1, [user]} -> {:ok, user}
+      {1, [user]} -> set_cache(user)
       _ -> {:error, user}
     end
   end
 
-  def get_follow_requests(%User{} = user) do
-    q = get_follow_requests_query(user)
-    reqs = Repo.all(q)
-
-    users =
-      Enum.map(reqs, fn req -> req.actor end)
-      |> Enum.uniq()
-      |> Enum.map(fn ap_id -> get_by_ap_id(ap_id) end)
-      |> Enum.filter(fn u -> !is_nil(u) end)
-      |> Enum.filter(fn u -> !following?(u, user) end)
-
-    {:ok, users}
-  end
-
-  def increase_note_count(%User{} = user) do
-    info_cng = User.Info.add_to_note_count(user.info, 1)
-
-    cng =
-      change(user)
-      |> put_embed(:info, info_cng)
-
-    update_and_set_cache(cng)
-  end
-
   def decrease_note_count(%User{} = user) do
-    info_cng = User.Info.add_to_note_count(user.info, -1)
-
-    cng =
-      change(user)
-      |> put_embed(:info, info_cng)
-
-    update_and_set_cache(cng)
+    User
+    |> where(id: ^user.id)
+    |> update([u],
+      set: [
+        info:
+          fragment(
+            "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)",
+            u.info,
+            u.info
+          )
+      ]
+    )
+    |> Repo.update_all([], returning: true)
+    |> case do
+      {1, [user]} -> set_cache(user)
+      _ -> {:error, user}
+    end
   end
 
   def update_note_count(%User{} = user) do
@@ -695,24 +689,29 @@ defmodule Pleroma.User do
 
   def update_follower_count(%User{} = user) do
     follower_count_query =
-      from(
-        u in User,
-        where: ^user.follower_address in u.following,
-        where: u.id != ^user.id,
-        select: count(u.id)
-      )
-
-    follower_count = Repo.one(follower_count_query)
-
-    info_cng =
-      user.info
-      |> User.Info.set_follower_count(follower_count)
-
-    cng =
-      change(user)
-      |> put_embed(:info, info_cng)
+      User
+      |> where([u], ^user.follower_address in u.following)
+      |> where([u], u.id != ^user.id)
+      |> select([u], %{count: count(u.id)})
 
-    update_and_set_cache(cng)
+    User
+    |> where(id: ^user.id)
+    |> join(:inner, [u], s in subquery(follower_count_query))
+    |> update([u, s],
+      set: [
+        info:
+          fragment(
+            "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
+            u.info,
+            s.count
+          )
+      ]
+    )
+    |> Repo.update_all([], returning: true)
+    |> case do
+      {1, [user]} -> set_cache(user)
+      _ -> {:error, user}
+    end
   end
 
   def get_users_from_set_query(ap_ids, false) do
@@ -988,6 +987,7 @@ defmodule Pleroma.User do
     update_and_set_cache(cng)
   end
 
+  def mutes?(nil, _), do: false
   def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id)
 
   def blocks?(user, %{ap_id: ap_id}) do
index 00a0f6df3fc05868578965860ad3f5588072b400..818b646454993b1e4fa472bfdbd3f2b692856a02 100644 (file)
@@ -12,7 +12,6 @@ defmodule Pleroma.User.Info do
     field(:source_data, :map, default: %{})
     field(:note_count, :integer, default: 0)
     field(:follower_count, :integer, default: 0)
-    field(:follow_request_count, :integer, default: 0)
     field(:locked, :boolean, default: false)
     field(:confirmation_pending, :boolean, default: false)
     field(:confirmation_token, :string, default: nil)
index cc255cc9e1a7257e7ebe6855625f2f9f1549fba2..783491b67c7950ecd78f07880b22c986572f5c80 100644 (file)
@@ -81,6 +81,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp check_remote_limit(_), do: true
 
+  def increase_note_count_if_public(actor, object) do
+    if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
+  end
+
+  def decrease_note_count_if_public(actor, object) do
+    if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
+  end
+
   def insert(map, local \\ true) when is_map(map) do
     with nil <- Activity.normalize(map),
          map <- lazy_put_activity_defaults(map),
@@ -163,7 +171,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
            ),
          {:ok, activity} <- insert(create_data, local),
          # Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
-         {:ok, _actor} <- User.increase_note_count(actor),
+         {:ok, _actor} <- increase_note_count_if_public(actor, activity),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
     end
@@ -175,8 +183,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
     with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
          {:ok, activity} <- insert(data, local),
-         :ok <- maybe_federate(activity),
-         _ <- User.update_follow_request_count(actor) do
+         :ok <- maybe_federate(activity) do
       {:ok, activity}
     end
   end
@@ -187,8 +194,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
     with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
          {:ok, activity} <- insert(data, local),
-         :ok <- maybe_federate(activity),
-         _ <- User.update_follow_request_count(actor) do
+         :ok <- maybe_federate(activity) do
       {:ok, activity}
     end
   end
@@ -286,8 +292,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   def follow(follower, followed, activity_id \\ nil, local \\ true) do
     with data <- make_follow_data(follower, followed, activity_id),
          {:ok, activity} <- insert(data, local),
-         :ok <- maybe_federate(activity),
-         _ <- User.update_follow_request_count(followed) do
+         :ok <- maybe_federate(activity) do
       {:ok, activity}
     end
   end
@@ -297,8 +302,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
          unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
          {:ok, activity} <- insert(unfollow_data, local),
-         :ok <- maybe_federate(activity),
-         _ <- User.update_follow_request_count(followed) do
+         :ok <- maybe_federate(activity) do
       {:ok, activity}
     end
   end
@@ -316,7 +320,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     with {:ok, _} <- Object.delete(object),
          {:ok, activity} <- insert(data, local),
          # Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
-         {:ok, _actor} <- User.decrease_note_count(user),
+         {:ok, _actor} <- decrease_note_count_if_public(user, object),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
     end
@@ -420,6 +424,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   @valid_visibilities ~w[direct unlisted public private]
 
+  defp restrict_visibility(query, %{visibility: visibility})
+       when is_list(visibility) do
+    if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
+      query =
+        from(
+          a in query,
+          where:
+            fragment(
+              "activity_visibility(?, ?, ?) = ANY (?)",
+              a.actor,
+              a.recipients,
+              a.data,
+              ^visibility
+            )
+        )
+
+      Ecto.Adapters.SQL.to_sql(:all, Repo, query)
+
+      query
+    else
+      Logger.error("Could not restrict visibility to #{visibility}")
+    end
+  end
+
   defp restrict_visibility(query, %{visibility: visibility})
        when visibility in @valid_visibilities do
     query =
@@ -602,6 +630,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   defp restrict_reblogs(query, _), do: query
 
+  defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
+
   defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
     mutes = info.mutes
 
@@ -826,7 +856,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
     date =
       NaiveDateTime.utc_now()
-      |> Timex.format!("{WDshort}, {D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
+      |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
 
     signature =
       Pleroma.Web.HTTPSignatures.sign(actor, %{
diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex
new file mode 100644 (file)
index 0000000..82267c5
--- /dev/null
@@ -0,0 +1,25 @@
+# Pleroma: A lightweight social networking server
+# Copyright Â© 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.Authenticator do
+  alias Pleroma.User
+
+  def implementation do
+    Pleroma.Config.get(
+      Pleroma.Web.Auth.Authenticator,
+      Pleroma.Web.Auth.PleromaAuthenticator
+    )
+  end
+
+  @callback get_user(Plug.Conn.t()) :: {:ok, User.t()} | {:error, any()}
+  def get_user(plug), do: implementation().get_user(plug)
+
+  @callback handle_error(Plug.Conn.t(), any()) :: any()
+  def handle_error(plug, error), do: implementation().handle_error(plug, error)
+
+  @callback auth_template() :: String.t() | nil
+  def auth_template do
+    implementation().auth_template() || Pleroma.Config.get(:auth_template, "show.html")
+  end
+end
diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex
new file mode 100644 (file)
index 0000000..3cc19af
--- /dev/null
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright Â© 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.PleromaAuthenticator do
+  alias Pleroma.User
+  alias Comeonin.Pbkdf2
+
+  @behaviour Pleroma.Web.Auth.Authenticator
+
+  def get_user(%Plug.Conn{} = conn) do
+    %{"authorization" => %{"name" => name, "password" => password}} = conn.params
+
+    with {_, %User{} = user} <- {:user, User.get_by_nickname_or_email(name)},
+         {_, true} <- {:checkpw, Pbkdf2.checkpw(password, user.password_hash)} do
+      {:ok, user}
+    else
+      error ->
+        {:error, error}
+    end
+  end
+
+  def handle_error(%Plug.Conn{} = _conn, error) do
+    error
+  end
+
+  def auth_template, do: nil
+end
index 8fdefdebd167208f522251f6fe41c1097c7b869c..c32f27be2afd75a8cc0f8c64c083230c4b2ca297 100644 (file)
@@ -32,7 +32,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
     }
   end
 
-  def render("relationship.json", %{user: user, target: target}) do
+  def render("relationship.json", %{user: nil, target: _target}) do
+    %{}
+  end
+
+  def render("relationship.json", %{user: %User{} = user, target: %User{} = target}) do
     follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
 
     requested =
@@ -85,6 +89,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
 
     bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
 
+    relationship = render("relationship.json", %{user: opts[:for], target: user})
+
     %{
       id: to_string(user.id),
       username: username_from_nickname(user.nickname),
@@ -115,7 +121,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         confirmation_pending: user_info.confirmation_pending,
         tags: user.tags,
         is_moderator: user.info.is_moderator,
-        is_admin: user.info.is_admin
+        is_admin: user.info.is_admin,
+        relationship: relationship
       }
     }
   end
index b90e4252a3ab43c7d96e728ad5d601be57860a89..3468c0e1c1bd1c6f61c75819d92fdda79eb56344 100644 (file)
@@ -168,7 +168,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
       reblogged: present?(repeated),
       favourited: present?(favorited),
       bookmarked: present?(bookmarked),
-      muted: CommonAPI.thread_muted?(user, activity),
+      muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user),
       pinned: pinned?(activity, user),
       sensitive: sensitive,
       spoiler_text: object["summary"] || "",
index ea75070c441f343e39b4e2c344d6fd869e722859..8efe2efd59f4b2863ac85702888f369b3d6d5e8f 100644 (file)
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
   alias Pleroma.Repo
   alias Pleroma.User
 
-  @behaviour :cowboy_websocket_handler
+  @behaviour :cowboy_websocket
 
   @streams [
     "public",
@@ -26,37 +26,37 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
   # Handled by periodic keepalive in Pleroma.Web.Streamer.
   @timeout :infinity
 
-  def init(_type, _req, _opts) do
-    {:upgrade, :protocol, :cowboy_websocket}
-  end
-
-  def websocket_init(_type, req, _opts) do
-    with {qs, req} <- :cowboy_req.qs(req),
-         params <- :cow_qs.parse_qs(qs),
+  def init(%{qs: qs} = req, state) do
+    with params <- :cow_qs.parse_qs(qs),
          access_token <- List.keyfind(params, "access_token", 0),
          {_, stream} <- List.keyfind(params, "stream", 0),
          {:ok, user} <- allow_request(stream, access_token),
          topic when is_binary(topic) <- expand_topic(stream, params) do
-      send(self(), :subscribe)
-      {:ok, req, %{user: user, topic: topic}, @timeout}
+      {:cowboy_websocket, req, %{user: user, topic: topic}, %{idle_timeout: @timeout}}
     else
       {:error, code} ->
         Logger.debug("#{__MODULE__} denied connection: #{inspect(code)} - #{inspect(req)}")
         {:ok, req} = :cowboy_req.reply(code, req)
-        {:shutdown, req}
+        {:ok, req, state}
 
       error ->
         Logger.debug("#{__MODULE__} denied connection: #{inspect(error)} - #{inspect(req)}")
-        {:shutdown, req}
+        {:ok, req} = :cowboy_req.reply(400, req)
+        {:ok, req, state}
     end
   end
 
+  def websocket_init(state) do
+    send(self(), :subscribe)
+    {:ok, state}
+  end
+
   # We never receive messages.
-  def websocket_handle(_frame, req, state) do
-    {:ok, req, state}
+  def websocket_handle(_frame, state) do
+    {:ok, state}
   end
 
-  def websocket_info(:subscribe, req, state) do
+  def websocket_info(:subscribe, state) do
     Logger.debug(
       "#{__MODULE__} accepted websocket connection for user #{
         (state.user || %{id: "anonymous"}).id
@@ -64,14 +64,14 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
     )
 
     Pleroma.Web.Streamer.add_socket(state.topic, streamer_socket(state))
-    {:ok, req, state}
+    {:ok, state}
   end
 
-  def websocket_info({:text, message}, req, state) do
-    {:reply, {:text, message}, req, state}
+  def websocket_info({:text, message}, state) do
+    {:reply, {:text, message}, state}
   end
 
-  def websocket_terminate(reason, _req, state) do
+  def terminate(reason, _req, state) do
     Logger.debug(
       "#{__MODULE__} terminating websocket connection for user #{
         (state.user || %{id: "anonymous"}).id
index 7c1a3adbd363046ff3606922c113d4518039c41d..36318d69bfc53c4665ccf947c0175c722bebc6a0 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Pleroma.Web.OAuth.OAuthController do
   use Pleroma.Web, :controller
 
+  alias Pleroma.Web.Auth.Authenticator
   alias Pleroma.Web.OAuth.Authorization
   alias Pleroma.Web.OAuth.Token
   alias Pleroma.Web.OAuth.App
@@ -24,27 +25,25 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     available_scopes = (app && app.scopes) || []
     scopes = oauth_scopes(params, nil) || available_scopes
 
-    render(conn, "show.html", %{
+    render(conn, Authenticator.auth_template(), %{
       response_type: params["response_type"],
       client_id: params["client_id"],
       available_scopes: available_scopes,
       scopes: scopes,
       redirect_uri: params["redirect_uri"],
-      state: params["state"]
+      state: params["state"],
+      params: params
     })
   end
 
   def create_authorization(conn, %{
         "authorization" =>
           %{
-            "name" => name,
-            "password" => password,
             "client_id" => client_id,
             "redirect_uri" => redirect_uri
           } = auth_params
       }) do
-    with %User{} = user <- User.get_by_nickname_or_email(name),
-         true <- Pbkdf2.checkpw(password, user.password_hash),
+    with {_, {:ok, %User{} = user}} <- {:get_user, Authenticator.get_user(conn)},
          %App{} = app <- Repo.get_by(App, client_id: client_id),
          true <- redirect_uri in String.split(app.redirect_uris),
          scopes <- oauth_scopes(auth_params, []),
@@ -53,9 +52,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do
          {:missing_scopes, false} <- {:missing_scopes, scopes == []},
          {:auth_active, true} <- {:auth_active, User.auth_active?(user)},
          {:ok, auth} <- Authorization.create_authorization(app, user, scopes) do
-      # Special case: Local MastodonFE.
       redirect_uri =
         if redirect_uri == "." do
+          # Special case: Local MastodonFE
           mastodon_api_url(conn, :login)
         else
           redirect_uri
@@ -97,7 +96,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
         |> authorize(auth_params)
 
       error ->
-        error
+        Authenticator.handle_error(conn, error)
     end
   end
 
@@ -114,7 +113,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
         refresh_token: token.refresh_token,
         created_at: DateTime.to_unix(inserted_at),
         expires_in: 60 * 10,
-        scope: Enum.join(token.scopes)
+        scope: Enum.join(token.scopes, " ")
       }
 
       json(conn, response)
index 192ab7334a069c69350f21c7af9b004c98d87e84..55c612dddecf34ea1feafe3f6d50364b78affc59 100644 (file)
 # THIS MODULE IS DEPRECATED! DON'T USE IT!
 # USE THE Pleroma.Web.TwitterAPI.Views.ActivityView MODULE!
 defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
-  use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter
-  alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
-  alias Pleroma.Activity
-  alias Pleroma.Formatter
-  alias Pleroma.HTML
-  alias Pleroma.User
-  alias Pleroma.Web.TwitterAPI.ActivityView
-  alias Pleroma.Web.TwitterAPI.TwitterAPI
-  alias Pleroma.Web.TwitterAPI.UserView
-  alias Pleroma.Web.CommonAPI.Utils
-  alias Pleroma.Web.MastodonAPI.StatusView
-
-  defp user_by_ap_id(user_list, ap_id) do
-    Enum.find(user_list, fn %{ap_id: user_id} -> ap_id == user_id end)
-  end
-
-  def to_map(
-        %Activity{data: %{"type" => "Announce", "actor" => actor, "published" => created_at}} =
-          activity,
-        %{users: users, announced_activity: announced_activity} = opts
-      ) do
-    user = user_by_ap_id(users, actor)
-    created_at = created_at |> Utils.date_to_asctime()
-
-    text = "#{user.nickname} retweeted a status."
-
-    announced_user = user_by_ap_id(users, announced_activity.data["actor"])
-    retweeted_status = to_map(announced_activity, Map.merge(%{user: announced_user}, opts))
-
-    %{
-      "id" => activity.id,
-      "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
-      "statusnet_html" => text,
-      "text" => text,
-      "is_local" => activity.local,
-      "is_post_verb" => false,
-      "uri" => "tag:#{activity.data["id"]}:objectType=note",
-      "created_at" => created_at,
-      "retweeted_status" => retweeted_status,
-      "statusnet_conversation_id" => conversation_id(announced_activity),
-      "external_url" => activity.data["id"],
-      "activity_type" => "repeat"
-    }
-  end
-
-  def to_map(
-        %Activity{data: %{"type" => "Like", "published" => created_at}} = activity,
-        %{user: user, liked_activity: liked_activity} = opts
-      ) do
-    created_at = created_at |> Utils.date_to_asctime()
-
-    text = "#{user.nickname} favorited a status."
-
-    %{
-      "id" => activity.id,
-      "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
-      "statusnet_html" => text,
-      "text" => text,
-      "is_local" => activity.local,
-      "is_post_verb" => false,
-      "uri" => "tag:#{activity.data["id"]}:objectType=Favourite",
-      "created_at" => created_at,
-      "in_reply_to_status_id" => liked_activity.id,
-      "external_url" => activity.data["id"],
-      "activity_type" => "like"
-    }
-  end
-
-  def to_map(
-        %Activity{data: %{"type" => "Follow", "object" => followed_id}} = activity,
-        %{user: user} = opts
-      ) do
-    created_at = activity.data["published"] || DateTime.to_iso8601(activity.inserted_at)
-    created_at = created_at |> Utils.date_to_asctime()
-
-    followed = User.get_cached_by_ap_id(followed_id)
-    text = "#{user.nickname} started following #{followed.nickname}"
-
-    %{
-      "id" => activity.id,
-      "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
-      "attentions" => [],
-      "statusnet_html" => text,
-      "text" => text,
-      "is_local" => activity.local,
-      "is_post_verb" => false,
-      "created_at" => created_at,
-      "in_reply_to_status_id" => nil,
-      "external_url" => activity.data["id"],
-      "activity_type" => "follow"
-    }
-  end
-
-  # TODO:
-  # Make this more proper. Just a placeholder to not break the frontend.
-  def to_map(
-        %Activity{
-          data: %{"type" => "Undo", "published" => created_at, "object" => undid_activity}
-        } = activity,
-        %{user: user} = opts
-      ) do
-    created_at = created_at |> Utils.date_to_asctime()
-
-    text = "#{user.nickname} undid the action at #{undid_activity["id"]}"
-
-    %{
-      "id" => activity.id,
-      "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
-      "attentions" => [],
-      "statusnet_html" => text,
-      "text" => text,
-      "is_local" => activity.local,
-      "is_post_verb" => false,
-      "created_at" => created_at,
-      "in_reply_to_status_id" => nil,
-      "external_url" => activity.data["id"],
-      "activity_type" => "undo"
-    }
-  end
-
-  def to_map(
-        %Activity{data: %{"type" => "Delete", "published" => created_at, "object" => _}} =
-          activity,
-        %{user: user} = opts
-      ) do
-    created_at = created_at |> Utils.date_to_asctime()
-
-    %{
-      "id" => activity.id,
-      "uri" => activity.data["object"],
-      "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
-      "attentions" => [],
-      "statusnet_html" => "deleted notice {{tag",
-      "text" => "deleted notice {{tag",
-      "is_local" => activity.local,
-      "is_post_verb" => false,
-      "created_at" => created_at,
-      "in_reply_to_status_id" => nil,
-      "external_url" => activity.data["id"],
-      "activity_type" => "delete"
-    }
-  end
-
-  def to_map(
-        %Activity{data: %{"object" => %{"content" => _content} = object}} = activity,
-        %{user: user} = opts
-      ) do
-    created_at = object["published"] |> Utils.date_to_asctime()
-    like_count = object["like_count"] || 0
-    announcement_count = object["announcement_count"] || 0
-    favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
-    repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
-    pinned = activity.id in user.info.pinned_activities
-
-    mentions = opts[:mentioned] || []
-
-    attentions =
-      []
-      |> Utils.maybe_notify_to_recipients(activity)
-      |> Utils.maybe_notify_mentioned_recipients(activity)
-      |> Enum.map(fn ap_id -> Enum.find(mentions, fn user -> ap_id == user.ap_id end) end)
-      |> Enum.filter(& &1)
-      |> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end)
-
-    conversation_id = conversation_id(activity)
-
-    tags = activity.data["object"]["tag"] || []
-    possibly_sensitive = activity.data["object"]["sensitive"] || Enum.member?(tags, "nsfw")
-
-    tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags
-
-    {_summary, content} = ActivityView.render_content(object)
-
-    html =
-      HTML.filter_tags(content, User.html_filter_policy(opts[:for]))
-      |> Formatter.emojify(object["emoji"])
-
-    attachments = object["attachment"] || []
-
-    reply_parent = Activity.get_in_reply_to_activity(activity)
-
-    reply_user = reply_parent && User.get_cached_by_ap_id(reply_parent.actor)
-
-    summary = HTML.strip_tags(object["summary"])
-
-    card =
-      StatusView.render(
-        "card.json",
-        Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
-      )
-
-    %{
-      "id" => activity.id,
-      "uri" => activity.data["object"]["id"],
-      "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
-      "statusnet_html" => html,
-      "text" => HTML.strip_tags(content),
-      "is_local" => activity.local,
-      "is_post_verb" => true,
-      "created_at" => created_at,
-      "in_reply_to_status_id" => object["inReplyToStatusId"],
-      "in_reply_to_screen_name" => reply_user && reply_user.nickname,
-      "in_reply_to_profileurl" => User.profile_url(reply_user),
-      "in_reply_to_ostatus_uri" => reply_user && reply_user.ap_id,
-      "in_reply_to_user_id" => reply_user && reply_user.id,
-      "statusnet_conversation_id" => conversation_id,
-      "attachments" => attachments |> ObjectRepresenter.enum_to_list(opts),
-      "attentions" => attentions,
-      "fave_num" => like_count,
-      "repeat_num" => announcement_count,
-      "favorited" => to_boolean(favorited),
-      "repeated" => to_boolean(repeated),
-      "pinned" => pinned,
-      "external_url" => object["external_url"] || object["id"],
-      "tags" => tags,
-      "activity_type" => "post",
-      "possibly_sensitive" => possibly_sensitive,
-      "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object),
-      "summary" => summary,
-      "summary_html" => summary |> Formatter.emojify(object["emoji"]),
-      "card" => card
-    }
-  end
-
-  def conversation_id(activity) do
-    with context when not is_nil(context) <- activity.data["context"] do
-      TwitterAPI.context_to_conversation_id(context)
-    else
-      _e -> nil
-    end
-  end
-
-  defp to_boolean(false) do
-    false
-  end
-
-  defp to_boolean(nil) do
-    false
-  end
-
-  defp to_boolean(_) do
-    true
+  def to_map(activity, opts) do
+    Pleroma.Web.TwitterAPI.ActivityView.render(
+      "activity.json",
+      Map.put(opts, :activity, activity)
+    )
   end
 end
index efdd0bf435ed853fd5d4640d6d4b0da02a340fc2..ab6470d7847443e060787d41970a12aefb6e49d0 100644 (file)
@@ -229,18 +229,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
     end
   end
 
-  def get_by_id_or_nickname(id_or_nickname) do
-    if !is_integer(id_or_nickname) && :error == Integer.parse(id_or_nickname) do
-      Repo.get_by(User, nickname: id_or_nickname)
-    else
-      Repo.get(User, id_or_nickname)
-    end
-  end
-
   def get_user(user \\ nil, params) do
     case params do
       %{"user_id" => user_id} ->
-        case target = get_by_id_or_nickname(user_id) do
+        case target = User.get_cached_by_nickname_or_id(user_id) do
           nil ->
             {:error, "No user with such user_id"}
 
index 19264a93f37ad78b61c90449ff02c7127f1deb2d..de7b9f24ca29d986395dc4523ce97c0df5cfb5ce 100644 (file)
@@ -167,6 +167,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
       params
       |> Map.put("type", ["Create", "Announce", "Follow", "Like"])
       |> Map.put("blocking_user", user)
+      |> Map.put(:visibility, ~w[unlisted public private])
 
     activities = ActivityPub.fetch_activities([user.ap_id], params)
 
index 661022afa7f9464169c8d520d54fe6fa08d09355..02ca4ee42b8412c3e5c263183b6af23de9f3b028 100644 (file)
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   alias Pleroma.Object
   alias Pleroma.Repo
   alias Pleroma.User
+  alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.TwitterAPI.ActivityView
@@ -309,7 +310,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
       "visibility" => StatusView.get_visibility(object),
       "summary" => summary,
       "summary_html" => summary |> Formatter.emojify(object["emoji"]),
-      "card" => card
+      "card" => card,
+      "muted" => CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user)
     }
   end
 
index df73844761af4cfb3e02044ca8b8da7ef827e6af..0791ed7608e1be3b3318d63a456f067d4d41ea02 100644 (file)
@@ -118,7 +118,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
           "confirmation_pending" => user_info.confirmation_pending,
           "tags" => user.tags
         }
-        |> maybe_with_follow_request_count(user, for_user)
+        |> maybe_with_activation_status(user, for_user)
     }
 
     data =
@@ -134,13 +134,11 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
     end
   end
 
-  defp maybe_with_follow_request_count(data, %User{id: id, info: %{locked: true}} = user, %User{
-         id: id
-       }) do
-    Map.put(data, "follow_request_count", user.info.follow_request_count)
+  defp maybe_with_activation_status(data, user, %User{info: %{is_admin: true}}) do
+    Map.put(data, "deactivated", user.info.deactivated)
   end
 
-  defp maybe_with_follow_request_count(data, _, _), do: data
+  defp maybe_with_activation_status(data, _, _), do: data
 
   defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do
     Map.merge(data, %{"role" => role(user), "show_role" => user.info.show_role})
index 853aa2a87f8d63febb13036e11f00477fbef7f1a..66813e4ddaffa5ad1fd4f807047e639c86a29cc0 100644 (file)
@@ -26,6 +26,12 @@ defmodule Pleroma.Web do
       import Plug.Conn
       import Pleroma.Web.Gettext
       import Pleroma.Web.Router.Helpers
+
+      plug(:set_put_layout)
+
+      defp set_put_layout(conn, _) do
+        put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
+      end
     end
   end
 
diff --git a/mix.exs b/mix.exs
index 5392d94d1aec9f4560c339e4716a3f64202d5a3f..0fe9c6ec351dea854e4df59cbf23dc4a217ee860 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -55,9 +55,8 @@ defmodule Pleroma.Mixfile do
   # Type `mix help deps` for examples and options.
   defp deps do
     [
-      # Until Phoenix 1.4.1 is released
-      {:phoenix, github: "phoenixframework/phoenix", branch: "v1.4"},
-      {:plug_cowboy, "~> 1.0"},
+      {:phoenix, "~> 1.4.1"},
+      {:plug_cowboy, "~> 2.0"},
       {:phoenix_pubsub, "~> 1.1"},
       {:phoenix_ecto, "~> 3.3"},
       {:postgrex, ">= 0.13.5"},
index 91870244456151cd2912e2f77cfebd14f785bb8e..50e742fbd17a1086a9491f9019bebc552137fbfd 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -9,8 +9,8 @@
   "comeonin": {:hex, :comeonin, "4.1.1", "c7304fc29b45b897b34142a91122bc72757bc0c295e9e824999d5179ffc08416", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
   "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
   "cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
-  "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
-  "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
+  "cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
+  "cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"},
   "credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
   "crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
   "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
   "nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"},
   "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
   "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.3", "6706a148809a29c306062862c803406e88f048277f6e85b68faf73291e820b84", [:mix], [], "hexpm"},
-  "phoenix": {:git, "https://github.com/phoenixframework/phoenix.git", "ea22dc50b574178a300ecd19253443960407df93", [branch: "v1.4"]},
+  "phoenix": {:hex, :phoenix, "1.4.1", "801f9d632808657f1f7c657c8bbe624caaf2ba91429123ebe3801598aea4c3d9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
   "phoenix_ecto": {:hex, :phoenix_ecto, "3.3.0", "702f6e164512853d29f9d20763493f2b3bcfcb44f118af2bc37bb95d0801b480", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
   "phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
   "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], [], "hexpm"},
   "plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
-  "plug_cowboy": {:hex, :plug_cowboy, "1.0.0", "2e2a7d3409746d335f451218b8bb0858301c3de6d668c3052716c909936eb57a", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
+  "plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
   "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
   "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
   "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
   "postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
-  "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
+  "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
   "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
   "swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
   "syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
diff --git a/priv/repo/migrations/20190301101154_add_default_tags_to_user.exs b/priv/repo/migrations/20190301101154_add_default_tags_to_user.exs
new file mode 100644 (file)
index 0000000..faeb8f1
--- /dev/null
@@ -0,0 +1,9 @@
+defmodule Pleroma.Repo.Migrations.AddDefaultTagsToUser do
+  use Ecto.Migration
+
+  def up do
+    execute "UPDATE users SET tags = array[]::varchar[] where tags IS NULL"
+  end
+
+  def down, do: :noop
+end
diff --git a/priv/repo/migrations/20190303120636_update_user_note_counters.exs b/priv/repo/migrations/20190303120636_update_user_note_counters.exs
new file mode 100644 (file)
index 0000000..54e68f7
--- /dev/null
@@ -0,0 +1,41 @@
+defmodule Pleroma.Repo.Migrations.UpdateUserNoteCounters do
+  use Ecto.Migration
+
+  @public "https://www.w3.org/ns/activitystreams#Public"
+
+  def up do
+    execute """
+      WITH public_note_count AS (
+        SELECT
+          data->>'actor' AS actor,
+          count(id) AS count
+        FROM objects
+        WHERE data->>'type' = 'Note' AND (
+          data->'cc' ? '#{@public}' OR data->'to' ? '#{@public}'
+        )
+        GROUP BY data->>'actor'
+      )
+      UPDATE users AS u
+      SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true)
+      FROM public_note_count AS o
+      WHERE u.ap_id = o.actor
+    """
+  end
+
+  def down do
+    execute """
+      WITH public_note_count AS (
+        SELECT
+          data->>'actor' AS actor,
+          count(id) AS count
+        FROM objects
+        WHERE data->>'type' = 'Note'
+        GROUP BY data->>'actor'
+      )
+      UPDATE users AS u
+      SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true)
+      FROM public_note_count AS o
+      WHERE u.ap_id = o.actor
+    """
+  end
+end
index 188ab1a5ca44554612bc477203e69a97f1dd120a..390e3ef1392b2e3f37015ca36c7fc0270f61d318 100644 (file)
@@ -50,6 +50,34 @@ defmodule Pleroma.UserTest do
     assert expected_followers_collection == User.ap_followers(user)
   end
 
+  test "returns all pending follow requests" do
+    unlocked = insert(:user)
+    locked = insert(:user, %{info: %{locked: true}})
+    follower = insert(:user)
+
+    Pleroma.Web.TwitterAPI.TwitterAPI.follow(follower, %{"user_id" => unlocked.id})
+    Pleroma.Web.TwitterAPI.TwitterAPI.follow(follower, %{"user_id" => locked.id})
+
+    assert {:ok, []} = User.get_follow_requests(unlocked)
+    assert {:ok, [activity]} = User.get_follow_requests(locked)
+
+    assert activity
+  end
+
+  test "doesn't return already accepted or duplicate follow requests" do
+    locked = insert(:user, %{info: %{locked: true}})
+    pending_follower = insert(:user)
+    accepted_follower = insert(:user)
+
+    Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id})
+    Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id})
+    Pleroma.Web.TwitterAPI.TwitterAPI.follow(accepted_follower, %{"user_id" => locked.id})
+    User.maybe_follow(accepted_follower, locked)
+
+    assert {:ok, [activity]} = User.get_follow_requests(locked)
+    assert activity
+  end
+
   test "follow_all follows mutliple users" do
     user = insert(:user)
     followed_zero = insert(:user)
index 11262c5237d4da84bddea54dc41eae9c13657e46..f4029896cce0ce910790a595f468d5d654080a38 100644 (file)
@@ -55,6 +55,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
         ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
 
       assert activities == [public_activity]
+
+      activities =
+        ActivityPub.fetch_activities([], %{
+          :visibility => ~w[private public],
+          "actor_id" => user.ap_id
+        })
+
+      assert activities == [public_activity, private_activity]
     end
   end
 
@@ -205,6 +213,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       assert activity.actor == user.ap_id
       assert activity.recipients == ["user1", "user2", user.ap_id]
     end
+
+    test "increases user note count only for public activities" do
+      user = insert(:user)
+
+      {:ok, _} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "1", "visibility" => "public"})
+
+      {:ok, _} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "unlisted"})
+
+      {:ok, _} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "private"})
+
+      {:ok, _} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "3", "visibility" => "direct"})
+
+      user = Repo.get(User, user.id)
+      assert user.info.note_count == 2
+    end
   end
 
   describe "fetch activities for recipients" do
@@ -291,6 +318,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     assert Enum.member?(activities, activity_three)
     refute Enum.member?(activities, activity_one)
 
+    # Calling with 'with_muted' will deliver muted activities, too.
+    activities = ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
+
+    assert Enum.member?(activities, activity_two)
+    assert Enum.member?(activities, activity_three)
+    assert Enum.member?(activities, activity_one)
+
     {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
 
     activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
@@ -633,6 +667,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
 
       assert Repo.get(Object, object.id).data["type"] == "Tombstone"
     end
+
+    test "decrements user note count only for public activities" do
+      user = insert(:user, info: %{note_count: 10})
+
+      {:ok, a1} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "public"})
+
+      {:ok, a2} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "unlisted"})
+
+      {:ok, a3} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "private"})
+
+      {:ok, a4} =
+        CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "direct"})
+
+      {:ok, _} = a1.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
+      {:ok, _} = a2.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
+      {:ok, _} = a3.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
+      {:ok, _} = a4.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
+
+      user = Repo.get(User, user.id)
+      assert user.info.note_count == 10
+    end
   end
 
   describe "timeline post-processing" do
index f8cd68173b2d6ad0384d346b676ae96a896b2c9c..6be66ef6367db297ecd7797f45aa48d2f466f6ca 100644 (file)
@@ -63,7 +63,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         confirmation_pending: false,
         tags: [],
         is_admin: false,
-        is_moderator: false
+        is_moderator: false,
+        relationship: %{}
       }
     }
 
@@ -106,7 +107,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         confirmation_pending: false,
         tags: [],
         is_admin: false,
-        is_moderator: false
+        is_moderator: false,
+        relationship: %{}
       }
     }
 
@@ -148,4 +150,64 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
 
     assert expected == AccountView.render("relationship.json", %{user: user, target: other_user})
   end
+
+  test "represent an embedded relationship" do
+    user =
+      insert(:user, %{
+        info: %{note_count: 5, follower_count: 3, source_data: %{"type" => "Service"}},
+        nickname: "shp@shitposter.club",
+        inserted_at: ~N[2017-08-15 15:47:06.597036]
+      })
+
+    other_user = insert(:user)
+
+    {:ok, other_user} = User.follow(other_user, user)
+    {:ok, other_user} = User.block(other_user, user)
+
+    expected = %{
+      id: to_string(user.id),
+      username: "shp",
+      acct: user.nickname,
+      display_name: user.name,
+      locked: false,
+      created_at: "2017-08-15T15:47:06.000Z",
+      followers_count: 3,
+      following_count: 0,
+      statuses_count: 5,
+      note: user.bio,
+      url: user.ap_id,
+      avatar: "http://localhost:4001/images/avi.png",
+      avatar_static: "http://localhost:4001/images/avi.png",
+      header: "http://localhost:4001/images/banner.png",
+      header_static: "http://localhost:4001/images/banner.png",
+      emojis: [],
+      fields: [],
+      bot: true,
+      source: %{
+        note: "",
+        privacy: "public",
+        sensitive: false
+      },
+      pleroma: %{
+        confirmation_pending: false,
+        tags: [],
+        is_admin: false,
+        is_moderator: false,
+        relationship: %{
+          id: to_string(user.id),
+          following: false,
+          followed_by: false,
+          blocking: true,
+          muting: false,
+          muting_notifications: false,
+          requested: false,
+          domain_blocking: false,
+          showing_reblogs: false,
+          endorsed: false
+        }
+      }
+    }
+
+    assert expected == AccountView.render("account.json", %{user: user, for: other_user})
+  end
 end
index b52c2b80570536b19b6658ebf385ba11b5642756..f7f10662a57fd686e86b8e7d8aa4c66101119efa 100644 (file)
@@ -946,7 +946,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       other_user = Repo.get(User, other_user.id)
 
       assert User.following?(other_user, user) == false
-      assert user.info.follow_request_count == 1
 
       conn =
         build_conn()
@@ -960,7 +959,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       other_user = Repo.get(User, other_user.id)
 
       assert User.following?(other_user, user) == true
-      assert user.info.follow_request_count == 0
     end
 
     test "verify_credentials", %{conn: conn} do
@@ -982,7 +980,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       {:ok, _activity} = ActivityPub.follow(other_user, user)
 
       user = Repo.get(User, user.id)
-      assert user.info.follow_request_count == 1
 
       conn =
         build_conn()
@@ -996,7 +993,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       other_user = Repo.get(User, other_user.id)
 
       assert User.following?(other_user, user) == false
-      assert user.info.follow_request_count == 0
     end
   end
 
index 3412a6be2e0c81550fc4f6ee397ebdd2b7e93f69..351dbf673e421c4549024a5cac1a4f84702dd72a 100644 (file)
@@ -126,6 +126,22 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
     assert status == expected
   end
 
+  test "tells if the message is muted for some reason" do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, user} = User.mute(user, other_user)
+
+    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"})
+    status = StatusView.render("status.json", %{activity: activity})
+
+    assert status.muted == false
+
+    status = StatusView.render("status.json", %{activity: activity, for: user})
+
+    assert status.muted == true
+  end
+
   test "a reply" do
     note = insert(:note_activity)
     user = insert(:user)
index 53d83e6e8f9463d3764eda580bc990b7a13e206a..ed94416ff1a7d50f0d856431a9127b9f6ca1fc80 100644 (file)
@@ -165,10 +165,10 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
 
   test "issues a token for request with HTTP basic auth client credentials" do
     user = insert(:user)
-    app = insert(:oauth_app, scopes: ["scope1", "scope2"])
+    app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"])
 
-    {:ok, auth} = Authorization.create_authorization(app, user, ["scope2"])
-    assert auth.scopes == ["scope2"]
+    {:ok, auth} = Authorization.create_authorization(app, user, ["scope1", "scope2"])
+    assert auth.scopes == ["scope1", "scope2"]
 
     app_encoded =
       (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret))
@@ -183,11 +183,13 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
         "redirect_uri" => app.redirect_uris
       })
 
-    assert %{"access_token" => token} = json_response(conn, 200)
+    assert %{"access_token" => token, "scope" => scope} = json_response(conn, 200)
+
+    assert scope == "scope1 scope2"
 
     token = Repo.get_by(Token, token: token)
     assert token
-    assert token.scopes == ["scope2"]
+    assert token.scopes == ["scope1", "scope2"]
   end
 
   test "rejects token exchange with invalid client credentials" do
index 365c7f6599310ea2ead8b2847086222f8d680427..0e554623c511e669bdc138678bcf1fa8e1acbc57 100644 (file)
@@ -13,36 +13,6 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
   alias Pleroma.Web.TwitterAPI.UserView
   import Pleroma.Factory
 
-  test "an announce activity" do
-    user = insert(:user)
-    note_activity = insert(:note_activity)
-    activity_actor = Repo.get_by(User, ap_id: note_activity.data["actor"])
-    object = Object.get_by_ap_id(note_activity.data["object"]["id"])
-
-    {:ok, announce_activity, _object} = ActivityPub.announce(user, object)
-    note_activity = Activity.get_by_ap_id(note_activity.data["id"])
-
-    status =
-      ActivityRepresenter.to_map(announce_activity, %{
-        users: [user, activity_actor],
-        announced_activity: note_activity,
-        for: user
-      })
-
-    assert status["id"] == announce_activity.id
-    assert status["user"] == UserView.render("show.json", %{user: user, for: user})
-
-    retweeted_status =
-      ActivityRepresenter.to_map(note_activity, %{user: activity_actor, for: user})
-
-    assert retweeted_status["repeated"] == true
-    assert retweeted_status["id"] == note_activity.id
-    assert status["statusnet_conversation_id"] == retweeted_status["statusnet_conversation_id"]
-
-    assert status["retweeted_status"] == retweeted_status
-    assert status["activity_type"] == "repeat"
-  end
-
   test "a like activity" do
     user = insert(:user)
     note_activity = insert(:note_activity)
@@ -168,6 +138,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
       "uri" => activity.data["object"]["id"],
       "visibility" => "direct",
       "card" => nil,
+      "muted" => false,
       "summary" => "2hu :2hu:",
       "summary_html" =>
         "2hu <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" />"
@@ -180,18 +151,6 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
            }) == expected_status
   end
 
-  test "an undo for a follow" do
-    follower = insert(:user)
-    followed = insert(:user)
-
-    {:ok, _follow} = ActivityPub.follow(follower, followed)
-    {:ok, unfollow} = ActivityPub.unfollow(follower, followed)
-
-    map = ActivityRepresenter.to_map(unfollow, %{user: follower})
-    assert map["is_post_verb"] == false
-    assert map["activity_type"] == "undo"
-  end
-
   test "a delete activity" do
     object = insert(:note)
     user = User.get_by_ap_id(object.data["actor"])
index 05a832967643438a7a5de21fde6e17540cab6aec..d18b658765393e5437c746e3d2a1bd5e43efe878 100644 (file)
@@ -427,7 +427,10 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
 
     test "with credentials", %{conn: conn, user: current_user} do
       {:ok, activity} =
-        ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user})
+        CommonAPI.post(current_user, %{
+          "status" => "why is tenshi eating a corndog so cute?",
+          "visibility" => "public"
+        })
 
       conn =
         conn
@@ -445,6 +448,23 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
                  mentioned: [current_user]
                })
     end
+
+    test "does not show DMs in mentions timeline", %{conn: conn, user: current_user} do
+      {:ok, _activity} =
+        CommonAPI.post(current_user, %{
+          "status" => "Have you guys ever seen how cute tenshi eating a corndog is?",
+          "visibility" => "direct"
+        })
+
+      conn =
+        conn
+        |> with_credentials(current_user.nickname, "test")
+        |> get("/api/statuses/mentions.json")
+
+      response = json_response(conn, 200)
+
+      assert length(response) == 0
+    end
   end
 
   describe "GET /api/qvitter/statuses/notifications.json" do
@@ -670,7 +690,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       followed = Repo.get(User, followed.id)
 
       refute User.ap_followers(followed) in current_user.following
-      assert followed.info.follow_request_count == 1
 
       assert json_response(conn, 200) ==
                UserView.render("show.json", %{user: followed, for: current_user})
@@ -1737,7 +1756,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       other_user = Repo.get(User, other_user.id)
 
       assert User.following?(other_user, user) == false
-      assert user.info.follow_request_count == 1
 
       conn =
         build_conn()
@@ -1749,7 +1767,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       assert relationship = json_response(conn, 200)
       assert other_user.id == relationship["id"]
       assert relationship["follows_you"] == true
-      assert user.info.follow_request_count == 0
     end
   end
 
@@ -1764,7 +1781,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       other_user = Repo.get(User, other_user.id)
 
       assert User.following?(other_user, user) == false
-      assert user.info.follow_request_count == 1
 
       conn =
         build_conn()
@@ -1776,7 +1792,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       assert relationship = json_response(conn, 200)
       assert other_user.id == relationship["id"]
       assert relationship["follows_you"] == false
-      assert user.info.follow_request_count == 0
     end
   end
 
index 4f854ecaa978eb9807122cff922f306923f7dcc5..0a5384f34b44b0d0f032b272a35fb21d10f7ae81 100644 (file)
@@ -56,6 +56,22 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
     assert result["user"]["id"] == user.id
   end
 
+  test "tells if the message is muted for some reason" do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, user} = User.mute(user, other_user)
+
+    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"})
+    status = ActivityView.render("activity.json", %{activity: activity})
+
+    assert status["muted"] == false
+
+    status = ActivityView.render("activity.json", %{activity: activity, for: user})
+
+    assert status["muted"] == true
+  end
+
   test "a create activity with a html status" do
     text = """
     #Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg
@@ -149,7 +165,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
       "uri" => activity.data["object"]["id"],
       "user" => UserView.render("show.json", %{user: user}),
       "visibility" => "direct",
-      "card" => nil
+      "card" => nil,
+      "muted" => false
     }
 
     assert result == expected
index 95e52ca46a988d7fea21a72f53af9dd44ae5deff..114f24a1c5582a7e3784278c5bc574ff6e22c929 100644 (file)
@@ -239,6 +239,13 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
     assert represented["role"] == nil
   end
 
+  test "A regular user for the admin", %{user: user} do
+    admin = insert(:user, %{info: %{is_admin: true}})
+    represented = UserView.render("show.json", %{user: user, for: admin})
+
+    assert represented["pleroma"]["deactivated"] == false
+  end
+
   test "A blocked user for the blocker" do
     user = insert(:user)
     blocker = insert(:user)