Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
authorlain <lain@soykaf.club>
Sun, 17 May 2020 10:14:49 +0000 (12:14 +0200)
committerlain <lain@soykaf.club>
Sun, 17 May 2020 10:14:49 +0000 (12:14 +0200)
1  2 
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/mastodon_api/views/notification_view.ex
lib/pleroma/web/router.ex
test/web/common_api/common_api_test.exs
test/web/mastodon_api/views/account_view_test.exs
test/web/mastodon_api/views/notification_view_test.exs

index 7008cea4425f1ef6d27470de94a6f4db9e57c420,7c94f16b61b5110139370a505a610db404847698..ce5dad4482ec1cf163b78c1c44b2672c8d9d63b2
@@@ -7,7 -7,6 +7,7 @@@ defmodule Pleroma.Web.CommonAPI d
    alias Pleroma.ActivityExpiration
    alias Pleroma.Conversation.Participation
    alias Pleroma.FollowingRelationship
 +  alias Pleroma.Formatter
    alias Pleroma.Notification
    alias Pleroma.Object
    alias Pleroma.ThreadMute
    require Pleroma.Constants
    require Logger
  
 +  def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
 +    with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
 +         :ok <- validate_chat_content_length(content, !!maybe_attachment),
 +         {_, {:ok, chat_message_data, _meta}} <-
 +           {:build_object,
 +            Builder.chat_message(
 +              user,
 +              recipient.ap_id,
 +              content |> format_chat_content,
 +              attachment: maybe_attachment
 +            )},
 +         {_, {:ok, create_activity_data, _meta}} <-
 +           {:build_create_activity, Builder.create(user, chat_message_data, [recipient.ap_id])},
 +         {_, {:ok, %Activity{} = activity, _meta}} <-
 +           {:common_pipeline,
 +            Pipeline.common_pipeline(create_activity_data,
 +              local: true
 +            )} do
 +      {:ok, activity}
 +    end
 +  end
 +
 +  defp format_chat_content(nil), do: nil
 +
 +  defp format_chat_content(content) do
 +    content |> Formatter.html_escape("text/plain")
 +  end
 +
 +  defp validate_chat_content_length(_, true), do: :ok
 +  defp validate_chat_content_length(nil, false), do: {:error, :no_content}
 +
 +  defp validate_chat_content_length(content, _) do
 +    if String.length(content) <= Pleroma.Config.get([:instance, :chat_limit]) do
 +      :ok
 +    else
 +      {:error, :content_too_long}
 +    end
 +  end
 +
    def unblock(blocker, blocked) do
-     with %Activity{} = block <- Utils.fetch_latest_block(blocker, blocked),
+     with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)},
           {:ok, unblock_data, _} <- Builder.undo(blocker, block),
           {:ok, unblock, _} <- Pipeline.common_pipeline(unblock_data, local: true) do
        {:ok, unblock}
+     else
+       {:fetch_block, nil} ->
+         if User.blocks?(blocker, blocked) do
+           User.unblock(blocker, blocked)
+           {:ok, :no_activity}
+         else
+           {:error, :not_blocking}
+         end
+       e ->
+         e
      end
    end
  
index 19fbf0902c322a84fa085d516d3f1d412b9907de,45fffaad2e5901037088ed9a27a4be168ec26d5f..8ff9f39fd357e2fc4436bec00fcf784fc194988a
@@@ -15,13 -15,12 +15,12 @@@ defmodule Pleroma.Web.MastodonAPI.Accou
    def render("index.json", %{users: users} = opts) do
      reading_user = opts[:for]
  
-     # Note: :skip_relationships option is currently intentionally not supported for accounts
      relationships_opt =
        cond do
          Map.has_key?(opts, :relationships) ->
            opts[:relationships]
  
-         is_nil(reading_user) ->
+         is_nil(reading_user) || !opts[:embed_relationships] ->
            UserRelationship.view_relationships_option(nil, [])
  
          true ->
        end)
  
      relationship =
-       if opts[:skip_relationships] do
-         %{}
-       else
+       if opts[:embed_relationships] do
          render("relationship.json", %{
            user: opts[:for],
            target: user,
            relationships: opts[:relationships]
          })
+       else
+         %{}
        end
  
      %{
  
        # Pleroma extension
        pleroma: %{
 +        ap_id: user.ap_id,
          confirmation_pending: user.confirmation_pending,
          tags: user.tags,
          hide_followers_count: user.hide_followers_count,
index 2a99518317007c6bd52cc1e5a6dedaf149a8b3b3,c46ddcf5578f456b30c18cdce99ff3a9b0baccd0..07d55a3e9c18fd6046e7a6824f0f031c7330e0b2
@@@ -7,14 -7,12 +7,14 @@@ defmodule Pleroma.Web.MastodonAPI.Notif
  
    alias Pleroma.Activity
    alias Pleroma.Notification
 +  alias Pleroma.Object
    alias Pleroma.User
    alias Pleroma.UserRelationship
    alias Pleroma.Web.CommonAPI
    alias Pleroma.Web.MastodonAPI.AccountView
    alias Pleroma.Web.MastodonAPI.NotificationView
    alias Pleroma.Web.MastodonAPI.StatusView
 +  alias Pleroma.Web.PleromaAPI.ChatMessageView
  
    def render("index.json", %{notifications: notifications, for: reading_user} = opts) do
      activities = Enum.map(notifications, & &1.activity)
@@@ -53,9 -51,7 +53,7 @@@
              |> Enum.filter(& &1)
              |> Kernel.++(move_activities_targets)
  
-           UserRelationship.view_relationships_option(reading_user, actors,
-             source_mutes_only: opts[:skip_relationships]
-           )
+           UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes)
        end
  
      opts =
        end
      end
  
 -    mastodon_type = Activity.mastodon_notification_type(activity)
 +    # This returns the notification type by activity, but both chats and statuses
 +    # are in "Create" activities.
 +    mastodon_type =
 +      case Activity.mastodon_notification_type(activity) do
 +        "mention" ->
 +          object = Object.normalize(activity)
 +
 +          case object do
 +            %{data: %{"type" => "ChatMessage"}} -> "pleroma:chat_mention"
 +            _ -> "mention"
 +          end
 +
 +        type ->
 +          type
 +      end
  
-     render_opts = %{
-       relationships: opts[:relationships],
-       skip_relationships: opts[:skip_relationships]
-     }
+     # Note: :relationships contain user mutes (needed for :muted flag in :status)
+     status_render_opts = %{relationships: opts[:relationships]}
  
      with %{id: _} = account <-
             AccountView.render(
               "show.json",
-              Map.merge(render_opts, %{user: actor, for: reading_user})
+              %{user: actor, for: reading_user}
             ) do
        response = %{
          id: to_string(notification.id),
  
        case mastodon_type do
          "mention" ->
-           put_status(response, activity, reading_user, render_opts)
+           put_status(response, activity, reading_user, status_render_opts)
  
          "favourite" ->
-           put_status(response, parent_activity_fn.(), reading_user, render_opts)
+           put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
  
          "reblog" ->
-           put_status(response, parent_activity_fn.(), reading_user, render_opts)
+           put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
  
          "move" ->
-           # Note: :skip_relationships option being applied to _account_ rendering (here)
-           put_target(response, activity, reading_user, render_opts)
+           put_target(response, activity, reading_user, %{})
  
          "pleroma:emoji_reaction" ->
            response
-           |> put_status(parent_activity_fn.(), reading_user, render_opts)
+           |> put_status(parent_activity_fn.(), reading_user, status_render_opts)
            |> put_emoji(activity)
  
-           put_chat_message(response, activity, reading_user, render_opts)
 +        "pleroma:chat_mention" ->
++          put_chat_message(response, activity, reading_user, status_render_opts)
 +
          type when type in ["follow", "follow_request"] ->
            response
  
      Map.put(response, :emoji, activity.data["content"])
    end
  
 +  defp put_chat_message(response, activity, reading_user, opts) do
 +    object = Object.normalize(activity)
 +    author = User.get_cached_by_ap_id(object.data["actor"])
 +    chat = Pleroma.Chat.get(reading_user.id, author.ap_id)
 +    render_opts = Map.merge(opts, %{object: object, for: reading_user, chat: chat})
 +    chat_message_render = ChatMessageView.render("show.json", render_opts)
 +
 +    Map.put(response, :chat_message, chat_message_render)
 +  end
 +
    defp put_status(response, activity, reading_user, opts) do
      status_render_opts = Map.merge(opts, %{activity: activity, for: reading_user})
      status_render = StatusView.render("show.json", status_render_opts)
index 0e4f45869aff082642eb7298aab96afa8379643b,d77a61361285c6e5f992b91298dcb58fe471f6b0..c6e8164058a389862d3d188e071723ed042d323c
@@@ -302,18 -302,6 +302,18 @@@ defmodule Pleroma.Web.Router d
    end
  
    scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
 +    scope [] do
 +      pipe_through(:authenticated_api)
 +
 +      post("/chats/by-account-id/:id", ChatController, :create)
 +      get("/chats", ChatController, :index)
 +      get("/chats/:id", ChatController, :show)
 +      get("/chats/:id/messages", ChatController, :messages)
 +      post("/chats/:id/messages", ChatController, :post_chat_message)
 +      delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
 +      post("/chats/:id/read", ChatController, :mark_as_read)
 +    end
 +
      scope [] do
        pipe_through(:authenticated_api)
  
      post("/markers", MarkerController, :upsert)
  
      post("/media", MediaController, :create)
+     get("/media/:id", MediaController, :show)
      put("/media/:id", MediaController, :update)
  
      get("/notifications", NotificationController, :index)
    scope "/api/v2", Pleroma.Web.MastodonAPI do
      pipe_through(:api)
      get("/search", SearchController, :search2)
+     post("/media", MediaController, :create2)
    end
  
    scope "/api", Pleroma.Web do
index 46ffd2888a198c8b7232407e18399db531608686,fd829901350e17eae2840dd12ee84456403bda48..35396988526b9d037490de1f8799b1c71617bb95
@@@ -5,7 -5,6 +5,7 @@@
  defmodule Pleroma.Web.CommonAPITest do
    use Pleroma.DataCase
    alias Pleroma.Activity
 +  alias Pleroma.Chat
    alias Pleroma.Conversation.Participation
    alias Pleroma.Object
    alias Pleroma.User
    setup do: clear_config([:instance, :limit])
    setup do: clear_config([:instance, :max_pinned_statuses])
  
 +  describe "posting chat messages" do
 +    setup do: clear_config([:instance, :chat_limit])
 +
 +    test "it posts a chat message without content but with an attachment" do
 +      author = insert(:user)
 +      recipient = insert(:user)
 +
 +      file = %Plug.Upload{
 +        content_type: "image/jpg",
 +        path: Path.absname("test/fixtures/image.jpg"),
 +        filename: "an_image.jpg"
 +      }
 +
 +      {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
 +
 +      {:ok, activity} =
 +        CommonAPI.post_chat_message(
 +          author,
 +          recipient,
 +          nil,
 +          media_id: upload.id
 +        )
 +
 +      assert activity
 +    end
 +
 +    test "it posts a chat message" do
 +      author = insert(:user)
 +      recipient = insert(:user)
 +
 +      {:ok, activity} =
 +        CommonAPI.post_chat_message(
 +          author,
 +          recipient,
 +          "a test message <script>alert('uuu')</script> :firefox:"
 +        )
 +
 +      assert activity.data["type"] == "Create"
 +      assert activity.local
 +      object = Object.normalize(activity)
 +
 +      assert object.data["type"] == "ChatMessage"
 +      assert object.data["to"] == [recipient.ap_id]
 +
 +      assert object.data["content"] ==
 +               "a test message &lt;script&gt;alert(&#39;uuu&#39;)&lt;/script&gt; :firefox:"
 +
 +      assert object.data["emoji"] == %{
 +               "firefox" => "http://localhost:4001/emoji/Firefox.gif"
 +             }
 +
 +      assert Chat.get(author.id, recipient.ap_id)
 +      assert Chat.get(recipient.id, author.ap_id)
 +
 +      assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
 +    end
 +
 +    test "it reject messages over the local limit" do
 +      Pleroma.Config.put([:instance, :chat_limit], 2)
 +
 +      author = insert(:user)
 +      recipient = insert(:user)
 +
 +      {:error, message} =
 +        CommonAPI.post_chat_message(
 +          author,
 +          recipient,
 +          "123"
 +        )
 +
 +      assert message == :content_too_long
 +    end
 +  end
 +
+   describe "unblocking" do
+     test "it works even without an existing block activity" do
+       blocked = insert(:user)
+       blocker = insert(:user)
+       User.block(blocker, blocked)
+       assert User.blocks?(blocker, blocked)
+       assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
+       refute User.blocks?(blocker, blocked)
+     end
+   end
    describe "deletion" do
      test "it works with pruned objects" do
        user = insert(:user)
index 035b1f6ac1ae8fd741d369f424260f6d63f3262d,487ec26c2e6894b8ebdc89dece74a815c1ebc4a1..d5d7236a0ffade608d4c54f80cc0aa95d0773f9b
@@@ -72,7 -72,6 +72,7 @@@ defmodule Pleroma.Web.MastodonAPI.Accou
          fields: []
        },
        pleroma: %{
 +        ap_id: user.ap_id,
          background_image: "https://example.com/images/asuka_hospital.png",
          confirmation_pending: false,
          tags: [],
          fields: []
        },
        pleroma: %{
 +        ap_id: user.ap_id,
          background_image: nil,
          confirmation_pending: false,
          tags: [],
      end
    end
  
-   test "represent an embedded relationship" do
-     user =
-       insert(:user, %{
-         follower_count: 0,
-         note_count: 5,
-         actor_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, _user_relationship} = User.block(other_user, user)
-     {:ok, _} = User.follow(insert(: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: 1,
-       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: user.bio,
-         sensitive: false,
-         pleroma: %{
-           actor_type: "Service",
-           discoverable: false
-         },
-         fields: []
-       },
-       pleroma: %{
-         ap_id: user.ap_id,
-         background_image: nil,
-         confirmation_pending: false,
-         tags: [],
-         is_admin: false,
-         is_moderator: false,
-         hide_favorites: true,
-         hide_followers: false,
-         hide_follows: false,
-         hide_followers_count: false,
-         hide_follows_count: false,
-         relationship: %{
-           id: to_string(user.id),
-           following: false,
-           followed_by: false,
-           blocking: true,
-           blocked_by: false,
-           subscribing: false,
-           muting: false,
-           muting_notifications: false,
-           requested: false,
-           domain_blocking: false,
-           showing_reblogs: true,
-           endorsed: false
-         },
-         skip_thread_containment: false
-       }
-     }
-     assert expected ==
-              AccountView.render("show.json", %{user: refresh_record(user), for: other_user})
-   end
    test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
      user = insert(:user, pleroma_settings_store: %{fe: "test"})
  
index f3d269daf2417f8d3cd28ea8be4340c58a780f20,9839e48fc616712356ce57f09be45356509b543d..8db6a237944e25e3260a4df96011c74214464ea0
@@@ -6,9 -6,7 +6,9 @@@ defmodule Pleroma.Web.MastodonAPI.Notif
    use Pleroma.DataCase
  
    alias Pleroma.Activity
 +  alias Pleroma.Chat
    alias Pleroma.Notification
 +  alias Pleroma.Object
    alias Pleroma.Repo
    alias Pleroma.User
    alias Pleroma.Web.CommonAPI
@@@ -16,7 -14,6 +16,7 @@@
    alias Pleroma.Web.MastodonAPI.AccountView
    alias Pleroma.Web.MastodonAPI.NotificationView
    alias Pleroma.Web.MastodonAPI.StatusView
 +  alias Pleroma.Web.PleromaAPI.ChatMessageView
    import Pleroma.Factory
  
    defp test_notifications_rendering(notifications, user, expected_result) do
      assert expected_result == result
    end
  
 +  test "ChatMessage notification" do
 +    user = insert(:user)
 +    recipient = insert(:user)
 +    {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "what's up my dude")
 +
 +    {:ok, [notification]} = Notification.create_notifications(activity)
 +
 +    object = Object.normalize(activity)
 +    chat = Chat.get(recipient.id, user.ap_id)
 +
 +    expected = %{
 +      id: to_string(notification.id),
 +      pleroma: %{is_seen: false},
 +      type: "pleroma:chat_mention",
 +      account: AccountView.render("show.json", %{user: user, for: recipient}),
 +      chat_message:
 +        ChatMessageView.render("show.json", %{object: object, for: recipient, chat: chat}),
 +      created_at: Utils.to_masto_date(notification.inserted_at)
 +    }
 +
 +    test_notifications_rendering([notification], recipient, [expected])
 +  end
 +
    test "Mention notification" do
      user = insert(:user)
      mentioned_user = insert(:user)
        id: to_string(notification.id),
        pleroma: %{is_seen: false},
        type: "mention",
-       account: AccountView.render("show.json", %{user: user, for: mentioned_user}),
+       account:
+         AccountView.render("show.json", %{
+           user: user,
+           for: mentioned_user
+         }),
        status: StatusView.render("show.json", %{activity: activity, for: mentioned_user}),
        created_at: Utils.to_masto_date(notification.inserted_at)
      }