diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | docs/api/differences_in_mastoapi_responses.md | 1 | ||||
| -rw-r--r-- | lib/pleroma/user.ex | 8 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/utils.ex | 24 | ||||
| -rw-r--r-- | lib/pleroma/web/nodeinfo/nodeinfo_controller.ex | 1 | ||||
| -rw-r--r-- | test/user_test.exs | 12 | ||||
| -rw-r--r-- | test/web/common_api/common_api_test.exs | 19 | ||||
| -rw-r--r-- | test/web/common_api/common_api_utils_test.exs | 133 | 
9 files changed, 190 insertions, 13 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4766aef6c..e569c5ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  ## [unreleased]  ### Added  - Add a generic settings store for frontends / clients to use. +- Explicit addressing option for posting.  - Optional SSH access mode. (Needs `erlang-ssh` package on some distributions).  - [MongooseIM](https://github.com/esl/MongooseIM) http authentication support.  - LDAP authentication diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md index fd23fe2d2..fb77ce68d 100644 --- a/docs/api/differences_in_mastoapi_responses.md +++ b/docs/api/differences_in_mastoapi_responses.md @@ -70,6 +70,7 @@ Additional parameters can be added to the JSON body/Form data:  - `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.  - `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint. +- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply.  - `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.  ## PATCH `/api/v1/update_credentials` diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 474cd8c1a..dc534b05c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1441,4 +1441,12 @@ defmodule Pleroma.User do        update_and_set_cache(cng)      end    end + +  def get_ap_ids_by_nicknames(nicknames) do +    from(u in User, +      where: u.nickname in ^nicknames, +      select: u.ap_id +    ) +    |> Repo.all() +  end  end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 536058666..25b5fedb8 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -209,8 +209,10 @@ defmodule Pleroma.Web.CommonAPI do               data,               visibility             ), +         mentioned_users <- for({_, mentioned_user} <- mentions, do: mentioned_user.ap_id), +         addressed_users <- get_addressed_users(mentioned_users, data["to"]),           {poll, poll_emoji} <- make_poll_data(data), -         {to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility), +         {to, cc} <- get_to_and_cc(user, addressed_users, in_reply_to, visibility),           bcc <- bcc_for_list(user, visibility),           context <- make_context(in_reply_to),           cw <- data["spoiler_text"] || "", diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 9c92c6cea..22ce1ea90 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -61,9 +61,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do      end)    end -  def to_for_user_and_mentions(user, mentions, inReplyTo, "public") do -    mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end) - +  @spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) :: +          {list(String.t()), list(String.t())} +  def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do      to = ["https://www.w3.org/ns/activitystreams#Public" | mentioned_users]      cc = [user.follower_address] @@ -74,9 +74,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do      end    end -  def to_for_user_and_mentions(user, mentions, inReplyTo, "unlisted") do -    mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end) - +  def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted") do      to = [user.follower_address | mentioned_users]      cc = ["https://www.w3.org/ns/activitystreams#Public"] @@ -87,14 +85,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do      end    end -  def to_for_user_and_mentions(user, mentions, inReplyTo, "private") do -    {to, cc} = to_for_user_and_mentions(user, mentions, inReplyTo, "direct") +  def get_to_and_cc(user, mentioned_users, inReplyTo, "private") do +    {to, cc} = get_to_and_cc(user, mentioned_users, inReplyTo, "direct")      {[user.follower_address | to], cc}    end -  def to_for_user_and_mentions(_user, mentions, inReplyTo, "direct") do -    mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end) - +  def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct") do      if inReplyTo do        {Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}      else @@ -102,7 +98,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do      end    end -  def to_for_user_and_mentions(_user, _mentions, _inReplyTo, _), do: {[], []} +  def get_addressed_users(_, to) when is_list(to) do +    User.get_ap_ids_by_nicknames(to) +  end + +  def get_addressed_users(mentioned_users, _), do: mentioned_users    def bcc_for_list(user, {:list, list_id}) do      list = Pleroma.List.get(list_id, user) diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 57f5b61bb..32be430b7 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -98,6 +98,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do          "mastodon_api",          "mastodon_api_streaming",          "polls", +        "pleroma_explicit_addressing",          if Config.get([:media_proxy, :enabled]) do            "media_proxy"          end, diff --git a/test/user_test.exs b/test/user_test.exs index 019f2b56d..d7473ef43 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1266,4 +1266,16 @@ defmodule Pleroma.UserTest do        assert user.info.keys == "xxx"      end    end + +  describe "get_ap_ids_by_nicknames" do +    test "it returns a list of AP ids for a given set of nicknames" do +      user = insert(:user) +      user_two = insert(:user) + +      ap_ids = User.get_ap_ids_by_nicknames([user.nickname, user_two.nickname, "nonexistent"]) +      assert length(ap_ids) == 2 +      assert user.ap_id in ap_ids +      assert user_two.ap_id in ap_ids +    end +  end  end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 58305d09b..9ef79e9c9 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -56,6 +56,25 @@ defmodule Pleroma.Web.CommonAPITest do    end    describe "posting" do +    test "it supports explicit addressing" do +      user = insert(:user) +      user_two = insert(:user) +      user_three = insert(:user) +      user_four = insert(:user) + +      {:ok, activity} = +        CommonAPI.post(user, %{ +          "status" => +            "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.", +          "to" => [user_two.nickname, user_four.nickname, "nonexistent"] +        }) + +      assert user.ap_id in activity.recipients +      assert user_two.ap_id in activity.recipients +      assert user_four.ap_id in activity.recipients +      refute user_three.ap_id in activity.recipients +    end +      test "it filters out obviously bad tags when accepting a post as HTML" do        user = insert(:user) diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index ab4c62b35..b3a334d50 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -5,10 +5,15 @@  defmodule Pleroma.Web.CommonAPI.UtilsTest do    alias Pleroma.Builders.UserBuilder    alias Pleroma.Object +  alias Pleroma.Web.CommonAPI    alias Pleroma.Web.CommonAPI.Utils    alias Pleroma.Web.Endpoint    use Pleroma.DataCase +  import Pleroma.Factory + +  @public_address "https://www.w3.org/ns/activitystreams#Public" +    test "it adds attachment links to a given text and attachment set" do      name =        "Sakura%20Mana%20%E2%80%93%20Turned%20on%20by%20a%20Senior%20OL%20with%20a%20Temptating%20Tight%20Skirt-s%20Full%20Hipline%20and%20Panty%20Shot-%20Beautiful%20Thick%20Thighs-%20and%20Erotic%20Ass-%20-2015-%20--%20Oppaitime%208-28-2017%206-50-33%20PM.png" @@ -214,4 +219,132 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do        assert Utils.date_to_asctime(nil) == expected      end    end + +  describe "get_to_and_cc" do +    test "for public posts, not a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "public") + +      assert length(to) == 2 +      assert length(cc) == 1 + +      assert @public_address in to +      assert mentioned_user.ap_id in to +      assert user.follower_address in cc +    end + +    test "for public posts, a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      third_user = insert(:user) +      {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "public") + +      assert length(to) == 3 +      assert length(cc) == 1 + +      assert @public_address in to +      assert mentioned_user.ap_id in to +      assert third_user.ap_id in to +      assert user.follower_address in cc +    end + +    test "for unlisted posts, not a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "unlisted") + +      assert length(to) == 2 +      assert length(cc) == 1 + +      assert @public_address in cc +      assert mentioned_user.ap_id in to +      assert user.follower_address in to +    end + +    test "for unlisted posts, a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      third_user = insert(:user) +      {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "unlisted") + +      assert length(to) == 3 +      assert length(cc) == 1 + +      assert @public_address in cc +      assert mentioned_user.ap_id in to +      assert third_user.ap_id in to +      assert user.follower_address in to +    end + +    test "for private posts, not a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private") + +      assert length(to) == 2 +      assert length(cc) == 0 + +      assert mentioned_user.ap_id in to +      assert user.follower_address in to +    end + +    test "for private posts, a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      third_user = insert(:user) +      {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private") + +      assert length(to) == 3 +      assert length(cc) == 0 + +      assert mentioned_user.ap_id in to +      assert third_user.ap_id in to +      assert user.follower_address in to +    end + +    test "for direct posts, not a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct") + +      assert length(to) == 1 +      assert length(cc) == 0 + +      assert mentioned_user.ap_id in to +    end + +    test "for direct posts, a reply" do +      user = insert(:user) +      mentioned_user = insert(:user) +      third_user = insert(:user) +      {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) +      mentions = [mentioned_user.ap_id] + +      {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct") + +      assert length(to) == 2 +      assert length(cc) == 0 + +      assert mentioned_user.ap_id in to +      assert third_user.ap_id in to +    end +  end  end  | 
