Merge branch 'feature/pinned-posts' into 'develop'
authorkaniini <nenolod@gmail.com>
Thu, 10 Jan 2019 02:39:53 +0000 (02:39 +0000)
committerkaniini <nenolod@gmail.com>
Thu, 10 Jan 2019 02:39:53 +0000 (02:39 +0000)
Pinned Statuses

Closes #440

See merge request pleroma/pleroma!636

config/config.exs
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/rich_media/parser.ex
lib/pleroma/web/twitter_api/representers/activity_representer.ex
lib/pleroma/web/twitter_api/views/activity_view.ex
priv/repo/migrations/20190109152453_add_visibility_function.exs [new file with mode: 0644]
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/twitter_api/representers/activity_representer_test.exs
test/web/twitter_api/views/activity_view_test.exs

index 9301758f04f38627c7329c25e1b1549511038c56..1c55807b7cfdbf629262c4dae2bdfb7b33329ec4 100644 (file)
@@ -238,34 +238,34 @@ config :cors_plug,
 
 config :pleroma, Pleroma.User,
   restricted_nicknames: [
-    "about",
+    ".well-known",
     "~",
-    "main",
-    "users",
-    "settings",
-    "objects",
+    "about",
     "activities",
-    "web",
-    "registration",
-    "friend-requests",
-    "pleroma",
     "api",
-    "tag",
+    "auth",
+    "dev",
+    "friend-requests",
+    "inbox",
+    "internal",
+    "main",
+    "media",
+    "nodeinfo",
     "notice",
-    "status",
-    "user-search",
-    "ostatus_subscribe",
     "oauth",
+    "objects",
+    "ostatus_subscribe",
+    "pleroma",
+    "proxy",
     "push",
+    "registration",
     "relay",
-    "inbox",
-    ".well-known",
-    "nodeinfo",
-    "auth",
-    "proxy",
-    "dev",
-    "internal",
-    "media"
+    "settings",
+    "status",
+    "tag",
+    "user-search",
+    "users",
+    "web"
   ]
 
 config :pleroma, Pleroma.Web.Federator, max_jobs: 50
index 7c2849ce233ff2a67cd76a2eb404a3efc7914dba..a49fa3fcd74c5f72c130c3249a4deb1e1fa19602 100644 (file)
@@ -247,10 +247,7 @@ defmodule Pleroma.User do
       )
       |> Repo.all()
 
-    autofollowed_users
-    |> Enum.reduce({:ok, user}, fn other_user, {:ok, user} ->
-      follow(user, other_user)
-    end)
+    follow_all(user, autofollowed_users)
   end
 
   @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
@@ -307,6 +304,25 @@ defmodule Pleroma.User do
     end
   end
 
+  @doc "A mass follow for local users. Ignores blocks and has no side effects"
+  @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
+  def follow_all(follower, followeds) do
+    following =
+      (follower.following ++ Enum.map(followeds, fn %{follower_address: fa} -> fa end))
+      |> Enum.uniq()
+
+    {:ok, follower} =
+      follower
+      |> follow_changeset(%{following: following})
+      |> update_and_set_cache
+
+    Enum.each(followeds, fn followed ->
+      update_follower_count(followed)
+    end)
+
+    {:ok, follower}
+  end
+
   def follow(%User{} = follower, %User{info: info} = followed) do
     user_config = Application.get_env(:pleroma, :user)
     deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked)
index c5f62c4f8c5702156e6efca2059bfb577d95607f..9c1eb377f48b6e5b644038f70f69a0cbcb1d3f9e 100644 (file)
@@ -364,21 +364,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   @valid_visibilities ~w[direct unlisted public private]
 
-  defp restrict_visibility(query, %{visibility: "direct"}) do
-    public = "https://www.w3.org/ns/activitystreams#Public"
+  defp restrict_visibility(query, %{visibility: visibility})
+       when visibility in @valid_visibilities do
+    query =
+      from(
+        a in query,
+        where:
+          fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
+      )
 
-    from(
-      activity in query,
-      join: sender in User,
-      on: sender.ap_id == activity.actor,
-      # Are non-direct statuses with no to/cc possible?
-      where:
-        fragment(
-          "not (? && ?)",
-          [^public, sender.follower_address],
-          activity.recipients
-        )
-    )
+    Ecto.Adapters.SQL.to_sql(:all, Repo, query)
+
+    query
   end
 
   defp restrict_visibility(_query, %{visibility: visibility})
index 3746feaf688b275b8a70cc5bb15ac91a40191b58..18d9e2df5cdb56f28c8cd31ef788c9a3ae5518fe 100644 (file)
@@ -5,7 +5,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
     def parse(url), do: parse_url(url)
   else
     def parse(url),
-      do: {:commit, Cachex.fetch!(:rich_media_cache, url, fn _ -> parse_url(url) end)}
+      do: Cachex.fetch!(:rich_media_cache, url, fn _ -> parse_url(url) end)
   end
 
   defp parse_url(url) do
index 64f7c00c746d71fecc174c626d0776a6f194ecde..4f8f228ab87c68eee620f66a4ca7fbd0a76aa918 100644 (file)
@@ -182,6 +182,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
 
     reply_user = reply_parent && User.get_cached_by_ap_id(reply_parent.actor)
 
+    summary = HTML.strip_tags(object["summary"])
+
     %{
       "id" => activity.id,
       "uri" => activity.data["object"]["id"],
@@ -209,7 +211,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
       "activity_type" => "post",
       "possibly_sensitive" => possibly_sensitive,
       "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object),
-      "summary" => HTML.strip_tags(object["summary"]) |> Formatter.emojify(object["emoji"])
+      "summary" => summary,
+      "summary_html" => summary |> Formatter.emojify(object["emoji"])
     }
   end
 
index 425df8672ad039778bdfc9f0a18a5767ec2afe94..108e7bfc5208d3d19def3aefc4c525c1a3669b6b 100644 (file)
@@ -280,6 +280,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
 
     reply_user = reply_parent && User.get_cached_by_ap_id(reply_parent.actor)
 
+    summary = HTML.strip_tags(summary)
+
     %{
       "id" => activity.id,
       "uri" => activity.data["object"]["id"],
@@ -307,7 +309,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
       "activity_type" => "post",
       "possibly_sensitive" => possibly_sensitive,
       "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object),
-      "summary" => HTML.strip_tags(summary) |> Formatter.emojify(object["emoji"])
+      "summary" => summary,
+      "summary_html" => summary |> Formatter.emojify(object["emoji"])
     }
   end
 
diff --git a/priv/repo/migrations/20190109152453_add_visibility_function.exs b/priv/repo/migrations/20190109152453_add_visibility_function.exs
new file mode 100644 (file)
index 0000000..3aadabc
--- /dev/null
@@ -0,0 +1,48 @@
+defmodule Pleroma.Repo.Migrations.AddVisibilityFunction do
+  use Ecto.Migration
+  @disable_ddl_transaction true
+
+  def up do
+    definition = """
+    create or replace function activity_visibility(actor varchar, recipients varchar[], data jsonb) returns varchar as $$
+    DECLARE
+      fa varchar;
+      public varchar := 'https://www.w3.org/ns/activitystreams#Public';
+    BEGIN
+      SELECT COALESCE(users.follower_address, '') into fa from users where users.ap_id = actor;
+
+      IF data->'to' ? public THEN
+        RETURN 'public';
+      ELSIF data->'cc' ? public THEN
+        RETURN 'unlisted';
+      ELSIF ARRAY[fa] && recipients THEN
+        RETURN 'private';
+      ELSIF not(ARRAY[fa, public] && recipients) THEN
+        RETURN 'direct';
+      ELSE
+        RETURN 'unknown';
+      END IF;
+    END;
+    $$ LANGUAGE plpgsql IMMUTABLE;
+    """
+
+    execute(definition)
+
+    create(
+      index(:activities, ["activity_visibility(actor, recipients, data)"],
+        name: :activities_visibility_index,
+        concurrently: true
+      )
+    )
+  end
+
+  def down do
+    drop(
+      index(:activities, ["activity_visibility(actor, recipients, data)"],
+        name: :activities_visibility_index
+      )
+    )
+
+    execute("drop function activity_visibility(actor varchar, recipients varchar[], data jsonb)")
+  end
+end
index 5412525391a508c70651a15742181f328eb45fb9..cfccce8d118dadba29cd27299053d615957b89ad 100644 (file)
@@ -48,6 +48,17 @@ defmodule Pleroma.UserTest do
     assert expected_followers_collection == User.ap_followers(user)
   end
 
+  test "follow_all follows mutliple users" do
+    user = insert(:user)
+    followed_one = insert(:user)
+    followed_two = insert(:user)
+
+    {:ok, user} = User.follow_all(user, [followed_one, followed_two])
+
+    assert User.following?(user, followed_one)
+    assert User.following?(user, followed_two)
+  end
+
   test "follow takes a user and another user" do
     user = insert(:user)
     followed = insert(:user)
index e6c998876d96b79d56781e1b2de6ece8217685f6..eafb96f3a303cfd798577e251c3e4754e3d9c878 100644 (file)
@@ -18,6 +18,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     :ok
   end
 
+  describe "fetching restricted by visibility" do
+    test "it restricts by the appropriate visibility" do
+      user = insert(:user)
+
+      {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+
+      {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
+
+      {:ok, unlisted_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
+
+      {:ok, private_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
+
+      assert activities == [direct_activity]
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
+
+      assert activities == [unlisted_activity]
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
+
+      assert activities == [private_activity]
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
+
+      assert activities == [public_activity]
+    end
+  end
+
   describe "building a user from his ap id" do
     test "it returns a user" do
       user_id = "http://mastodon.example.org/users/admin"
index a4f97e0f283edf0abfb36e5b0ce67111841785c2..ef0294140d23d8d1c3eb4bf09aee3c3b67fa8418 100644 (file)
@@ -107,7 +107,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
           "published" => date,
           "type" => "Note",
           "content" => content_html,
-          "summary" => "2hu",
+          "summary" => "2hu :2hu:",
           "inReplyToStatusId" => 213_123,
           "attachment" => [
             object
@@ -129,7 +129,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
     }
 
     expected_html =
-      "<p>2hu</p>alert('YAY')Some <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" /> content mentioning <a href=\"#{
+      "<p>2hu <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" /></p>alert('YAY')Some <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" /> content mentioning <a href=\"#{
         mentioned_user.ap_id
       }\">@shp</a>"
 
@@ -138,7 +138,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
       "user" => UserView.render("show.json", %{user: user, for: follower}),
       "is_local" => false,
       "statusnet_html" => expected_html,
-      "text" => "2hu" <> content,
+      "text" => "2hu :2hu:" <> content,
       "is_post_verb" => true,
       "created_at" => "Tue May 24 13:26:08 +0000 2016",
       "in_reply_to_status_id" => 213_123,
@@ -164,7 +164,9 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
       "possibly_sensitive" => true,
       "uri" => activity.data["object"]["id"],
       "visibility" => "direct",
-      "summary" => "2hu"
+      "summary" => "2hu :2hu:",
+      "summary_html" =>
+        "2hu <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" />"
     }
 
     assert ActivityRepresenter.to_map(activity, %{
index 37444030023aff0921a97762484c337c84d6a245..8b5a16add9b12d30f0cf0be0c067620e01ee469e 100644 (file)
@@ -81,10 +81,13 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
 
     result = ActivityView.render("activity.json", activity: activity)
 
-    expected =
+    expected = ":woollysocks: meow"
+
+    expected_html =
       "<img height=\"32px\" width=\"32px\" alt=\"woollysocks\" title=\"woollysocks\" src=\"http://localhost:4001/finmoji/128px/woollysocks-128.png\" /> meow"
 
     assert result["summary"] == expected
+    assert result["summary_html"] == expected_html
   end
 
   test "a create activity with a summary containing invalid HTML" do
@@ -99,6 +102,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
     expected = "meow"
 
     assert result["summary"] == expected
+    assert result["summary_html"] == expected
   end
 
   test "a create activity with a note" do
@@ -135,6 +139,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
       "pinned" => false,
       "statusnet_conversation_id" => convo_id,
       "summary" => "",
+      "summary_html" => "",
       "statusnet_html" =>
         "Hey <span><a data-user=\"#{other_user.id}\" href=\"#{other_user.ap_id}\">@<span>shp</span></a></span>!",
       "tags" => [],