Merge branch 'feature/rich-media-part-2-electric-boogaloo' into 'develop'
authorHaelwenn <contact+git.pleroma.social@hacktivis.me>
Tue, 29 Jan 2019 05:11:08 +0000 (05:11 +0000)
committerHaelwenn <contact+git.pleroma.social@hacktivis.me>
Tue, 29 Jan 2019 05:11:08 +0000 (05:11 +0000)
Rich Media support, part 2.

See merge request pleroma/pleroma!719

17 files changed:
lib/pleroma/html.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/rich_media/controllers/rich_media_controller.ex [deleted file]
lib/pleroma/web/rich_media/helpers.ex [new file with mode: 0644]
lib/pleroma/web/rich_media/parser.ex
lib/pleroma/web/rich_media/parsers/oembed_parser.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/representers/activity_representer.ex
lib/pleroma/web/twitter_api/views/activity_view.ex
test/web/mastodon_api/mastodon_api_controller_test.exs
test/web/mastodon_api/status_view_test.exs
test/web/rich_media/controllers/rich_media_controller_test.exs [deleted file]
test/web/rich_media/parser_test.exs
test/web/twitter_api/representers/activity_representer_test.exs
test/web/twitter_api/views/activity_view_test.exs

index fb602d6b6186335f1b4174f59fd6e70de9036648..bf5daa9482bdb1da279502b5a8c5b937f805d5fc 100644 (file)
@@ -69,7 +69,7 @@ defmodule Pleroma.HTML do
         |> Floki.attribute("a", "href")
         |> Enum.at(0)
 
-      {:commit, result}
+      {:commit, {:ok, result}}
     end)
   end
 end
index feff2240071707ceda6e47aa191937eb648fde51..0199ac9e7c0558a5db0721fd2ffc71a17ff486b9 100644 (file)
@@ -88,6 +88,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
           recipients: recipients
         })
 
+      Task.start(fn ->
+        Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+      end)
+
       Notification.create_notifications(activity)
       stream_out(activity)
       {:ok, activity}
index a366a149f4eb8e2751274e6a96b899d51f51e56a..65b61202624a2bb76bb4ac9862074960901516ab 100644 (file)
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   use Pleroma.Web, :controller
   alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
   alias Pleroma.Web
-  alias Pleroma.HTML
 
   alias Pleroma.Web.MastodonAPI.{
     StatusView,
@@ -500,7 +499,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
 
   def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
     with {:ok, object} <-
-           ActivityPub.upload(file,
+           ActivityPub.upload(
+             file,
              actor: User.ap_id(user),
              description: Map.get(data, "description")
            ) do
@@ -1101,7 +1101,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   def login(conn, _) do
     with {:ok, app} <- get_or_make_app() do
       path =
-        o_auth_path(conn, :authorize,
+        o_auth_path(
+          conn,
+          :authorize,
           response_type: "code",
           client_id: app.client_id,
           redirect_uri: ".",
@@ -1342,29 +1344,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     end
   end
 
-  def get_status_card(status_id) do
+  def status_card(conn, %{"id" => status_id}) do
     with %Activity{} = activity <- Repo.get(Activity, status_id),
-         true <- ActivityPub.is_public?(activity),
-         %Object{} = object <- Object.normalize(activity.data["object"]),
-         page_url <- HTML.extract_first_external_url(object, object.data["content"]),
-         {:ok, rich_media} <- Pleroma.Web.RichMedia.Parser.parse(page_url) do
-      page_url = rich_media[:url] || page_url
-      site_name = rich_media[:site_name] || URI.parse(page_url).host
-
-      rich_media
-      |> Map.take([:image, :title, :description])
-      |> Map.put(:type, "link")
-      |> Map.put(:provider_name, site_name)
-      |> Map.put(:url, page_url)
+         true <- ActivityPub.is_public?(activity) do
+      data =
+        StatusView.render(
+          "card.json",
+          Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+        )
+
+      json(conn, data)
     else
-      _ -> %{}
+      _e ->
+        %{}
     end
   end
 
-  def status_card(conn, %{"id" => status_id}) do
-    json(conn, get_status_card(status_id))
-  end
-
   def try_render(conn, target, params)
       when is_binary(target) do
     res = render(conn, target, params)
index ddfe6788cd5a0b864bb320e29bf1eedbd4c1ad22..b14ca9f5d65a59a4dab62b89868424182979486a 100644 (file)
@@ -139,6 +139,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         __MODULE__
       )
 
+    card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity))
+
     %{
       id: to_string(activity.id),
       uri: object["id"],
@@ -147,6 +149,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
       in_reply_to_id: reply_to && to_string(reply_to.id),
       in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
       reblog: nil,
+      card: card,
       content: content,
       created_at: created_at,
       reblogs_count: announcement_count,
@@ -175,6 +178,29 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     nil
   end
 
+  def render("card.json", %{rich_media: rich_media, page_url: page_url}) do
+    page_url = rich_media[:url] || page_url
+    page_url_data = URI.parse(page_url)
+    site_name = rich_media[:site_name] || page_url_data.host
+
+    %{
+      type: "link",
+      provider_name: site_name,
+      provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
+      url: page_url,
+      image: rich_media[:image] |> MediaProxy.url(),
+      title: rich_media[:title],
+      description: rich_media[:description],
+      pleroma: %{
+        opengraph: rich_media
+      }
+    }
+  end
+
+  def render("card.json", _) do
+    nil
+  end
+
   def render("attachment.json", %{attachment: attachment}) do
     [attachment_url | _] = attachment["url"]
     media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image"
diff --git a/lib/pleroma/web/rich_media/controllers/rich_media_controller.ex b/lib/pleroma/web/rich_media/controllers/rich_media_controller.ex
deleted file mode 100644 (file)
index 9101996..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-defmodule Pleroma.Web.RichMedia.RichMediaController do
-  use Pleroma.Web, :controller
-
-  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
-
-  def parse(conn, %{"url" => url}) do
-    case Pleroma.Web.RichMedia.Parser.parse(url) do
-      {:ok, data} ->
-        conn
-        |> json_response(200, data)
-
-      {:error, msg} ->
-        conn
-        |> json_response(404, msg)
-    end
-  end
-end
diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex
new file mode 100644 (file)
index 0000000..71fddde
--- /dev/null
@@ -0,0 +1,18 @@
+# Pleroma: A lightweight social networking server
+# Copyright _ 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.RichMedia.Helpers do
+  alias Pleroma.{Activity, Object, HTML}
+  alias Pleroma.Web.RichMedia.Parser
+
+  def fetch_data_for_activity(%Activity{} = activity) do
+    with %Object{} = object <- Object.normalize(activity.data["object"]),
+         {:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
+         {:ok, rich_media} <- Parser.parse(page_url) do
+      %{page_url: page_url, rich_media: rich_media}
+    else
+      _ -> %{}
+    end
+  end
+end
index 947dc0c3c2fef989d334f431cc4c9f29c2c9275a..76d977ac22a68f02a5f841f072fe998a161211cb 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.RichMedia.Parser do
   @parsers [
     Pleroma.Web.RichMedia.Parsers.OGP,
@@ -11,19 +15,26 @@ defmodule Pleroma.Web.RichMedia.Parser do
     def parse(url), do: parse_url(url)
   else
     def parse(url) do
-      with {:ok, data} <- Cachex.fetch(:rich_media_cache, url, fn _ -> parse_url(url) end) do
-        data
-      else
-        _e ->
-          {:error, "Parsing error"}
+      try do
+        Cachex.fetch!(:rich_media_cache, url, fn _ ->
+          {:commit, parse_url(url)}
+        end)
+      rescue
+        e ->
+          {:error, "Cachex error: #{inspect(e)}"}
       end
     end
   end
 
   defp parse_url(url) do
-    {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url)
+    try do
+      {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url)
 
-    html |> maybe_parse() |> get_parsed_data()
+      html |> maybe_parse() |> get_parsed_data()
+    rescue
+      e ->
+        {:error, "Parsing error: #{inspect(e)}"}
+    end
   end
 
   defp maybe_parse(html) do
@@ -35,11 +46,11 @@ defmodule Pleroma.Web.RichMedia.Parser do
     end)
   end
 
-  defp get_parsed_data(data) when data == %{} do
-    {:error, "No metadata found"}
+  defp get_parsed_data(%{title: title} = data) when is_binary(title) and byte_size(title) > 0 do
+    {:ok, data}
   end
 
   defp get_parsed_data(data) do
-    {:ok, data}
+    {:error, "Found metadata was invalid or incomplete: #{inspect(data)}"}
   end
 end
index ca7226fafd88f7b21dec7d2da49d479c6d6bc4b8..efa98bc2c16fe47a4c788cb663e28c13dcf88cf9 100644 (file)
@@ -22,6 +22,10 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
   defp get_oembed_data(url) do
     {:ok, %Tesla.Env{body: json}} = Pleroma.HTTP.get(url)
 
-    {:ok, Poison.decode!(json)}
+    {:ok, data} = Jason.decode(json)
+
+    data = data |> Map.new(fn {k, v} -> {String.to_atom(k), v} end)
+
+    {:ok, data}
   end
 end
index 31f739738d32793e0e3f773269ee3e4c527d5801..68e7a44b623ea704b965ebeb394750a6cab2ce59 100644 (file)
@@ -239,12 +239,6 @@ defmodule Pleroma.Web.Router do
     put("/settings", MastodonAPIController, :put_settings)
   end
 
-  scope "/api", Pleroma.Web.RichMedia do
-    pipe_through(:authenticated_api)
-
-    get("/rich_media/parse", RichMediaController, :parse)
-  end
-
   scope "/api/v1", Pleroma.Web.MastodonAPI do
     pipe_through(:api)
     get("/instance", MastodonAPIController, :masto_instance)
index 19b723586bd804f8382156594ad469d2531b6147..c4025cbd78c47e3597e1232f97f7d40b4f1f0acb 100644 (file)
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
   alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Formatter
   alias Pleroma.HTML
+  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)
@@ -186,6 +187,12 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
 
     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"],
@@ -214,7 +221,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
       "possibly_sensitive" => possibly_sensitive,
       "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object),
       "summary" => summary,
-      "summary_html" => summary |> Formatter.emojify(object["emoji"])
+      "summary_html" => summary |> Formatter.emojify(object["emoji"]),
+      "card" => card
     }
   end
 
index a01ee00106f47fbd761c92fbdb0ecfe032431a59..d0d1221c347111d176107bd78de79b24281d1759 100644 (file)
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   alias Pleroma.Web.TwitterAPI.ActivityView
   alias Pleroma.Web.TwitterAPI.TwitterAPI
   alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
+  alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Activity
   alias Pleroma.HTML
   alias Pleroma.Object
@@ -274,6 +275,12 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
 
     summary = HTML.strip_tags(summary)
 
+    card =
+      StatusView.render(
+        "card.json",
+        Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+      )
+
     %{
       "id" => activity.id,
       "uri" => activity.data["object"]["id"],
@@ -300,9 +307,10 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
       "tags" => tags,
       "activity_type" => "post",
       "possibly_sensitive" => possibly_sensitive,
-      "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object),
+      "visibility" => StatusView.get_visibility(object),
       "summary" => summary,
-      "summary_html" => summary |> Formatter.emojify(object["emoji"])
+      "summary_html" => summary |> Formatter.emojify(object["emoji"]),
+      "card" => card
     }
   end
 
index b8f901e6c31efc21109ff4bfe33ba316452d10ae..1a5eb090c44e1a95fea85c7d19be9b6d964d4305 100644 (file)
@@ -136,6 +136,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert Repo.get(Activity, id)
   end
 
+  test "posting a status with OGP link preview", %{conn: conn} do
+    user = insert(:user)
+
+    conn =
+      conn
+      |> assign(:user, user)
+      |> post("/api/v1/statuses", %{
+        "status" => "http://example.com/ogp"
+      })
+
+    assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
+    assert Repo.get(Activity, id)
+  end
+
   test "posting a direct status", %{conn: conn} do
     user1 = insert(:user)
     user2 = insert(:user)
@@ -1663,9 +1677,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       assert response == %{
                "image" => "http://ia.media-imdb.com/images/rock.jpg",
                "provider_name" => "www.imdb.com",
+               "provider_url" => "http://www.imdb.com",
                "title" => "The Rock",
                "type" => "link",
-               "url" => "http://www.imdb.com/title/tt0117500/"
+               "url" => "http://www.imdb.com/title/tt0117500/",
+               "description" => nil,
+               "pleroma" => %{
+                 "opengraph" => %{
+                   "image" => "http://ia.media-imdb.com/images/rock.jpg",
+                   "title" => "The Rock",
+                   "type" => "video.movie",
+                   "url" => "http://www.imdb.com/title/tt0117500/"
+                 }
+               }
              }
     end
   end
index ebf6273e8985dd85212cdbc2662f06cf36fbafa9..f957fedac6bd8d1e6bbdd039dc0d8dd1f515a42c 100644 (file)
@@ -84,6 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
       account: AccountView.render("account.json", %{user: user}),
       in_reply_to_id: nil,
       in_reply_to_account_id: nil,
+      card: nil,
       reblog: nil,
       content: HtmlSanitizeEx.basic_html(note.data["object"]["content"]),
       created_at: created_at,
diff --git a/test/web/rich_media/controllers/rich_media_controller_test.exs b/test/web/rich_media/controllers/rich_media_controller_test.exs
deleted file mode 100644 (file)
index fef1265..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.RichMedia.RichMediaControllerTest do
-  use Pleroma.Web.ConnCase
-  import Pleroma.Factory
-  import Tesla.Mock
-
-  setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  describe "GET /api/rich_media/parse" do
-    setup do
-      user = insert(:user)
-
-      [user: user]
-    end
-
-    test "returns 404 if not metadata found", %{user: user} do
-      build_conn()
-      |> with_credentials(user.nickname, "test")
-      |> get("/api/rich_media/parse", %{"url" => "http://example.com/empty"})
-      |> json_response(404)
-    end
-
-    test "returns OGP metadata", %{user: user} do
-      response =
-        build_conn()
-        |> with_credentials(user.nickname, "test")
-        |> get("/api/rich_media/parse", %{"url" => "http://example.com/ogp"})
-        |> json_response(200)
-
-      assert response == %{
-               "image" => "http://ia.media-imdb.com/images/rock.jpg",
-               "title" => "The Rock",
-               "type" => "video.movie",
-               "url" => "http://www.imdb.com/title/tt0117500/"
-             }
-    end
-  end
-
-  defp with_credentials(conn, username, password) do
-    header_content = "Basic " <> Base.encode64("#{username}:#{password}")
-    put_req_header(conn, "authorization", header_content)
-  end
-end
index e14b5061aeffe615ba2063c112170de515f444bf..93a58c52842e738028b01f7886bf78e4e40addb9 100644 (file)
@@ -65,28 +65,27 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
     assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/oembed") ==
              {:ok,
               %{
-                "author_name" => "‮‭‬bees‬",
-                "author_url" => "https://www.flickr.com/photos/bees/",
-                "cache_age" => 3600,
-                "flickr_type" => "photo",
-                "height" => "768",
-                "html" =>
+                author_name: "‮‭‬bees‬",
+                author_url: "https://www.flickr.com/photos/bees/",
+                cache_age: 3600,
+                flickr_type: "photo",
+                height: "768",
+                html:
                   "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by ‮‭‬bees‬, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
-                "license" => "All Rights Reserved",
-                "license_id" => 0,
-                "provider_name" => "Flickr",
-                "provider_url" => "https://www.flickr.com/",
-                "thumbnail_height" => 150,
-                "thumbnail_url" =>
-                  "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
-                "thumbnail_width" => 150,
-                "title" => "Bacon Lollys",
-                "type" => "photo",
-                "url" => "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg",
-                "version" => "1.0",
-                "web_page" => "https://www.flickr.com/photos/bees/2362225867/",
-                "web_page_short_url" => "https://flic.kr/p/4AK2sc",
-                "width" => "1024"
+                license: "All Rights Reserved",
+                license_id: 0,
+                provider_name: "Flickr",
+                provider_url: "https://www.flickr.com/",
+                thumbnail_height: 150,
+                thumbnail_url: "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
+                thumbnail_width: 150,
+                title: "Bacon Lollys",
+                type: "photo",
+                url: "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg",
+                version: "1.0",
+                web_page: "https://www.flickr.com/photos/bees/2362225867/",
+                web_page_short_url: "https://flic.kr/p/4AK2sc",
+                width: "1024"
               }}
   end
 end
index ef0294140d23d8d1c3eb4bf09aee3c3b67fa8418..ea5813733d6575c382f864176567e50b6d35f67c 100644 (file)
@@ -164,6 +164,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
       "possibly_sensitive" => true,
       "uri" => activity.data["object"]["id"],
       "visibility" => "direct",
+      "card" => nil,
       "summary" => "2hu :2hu:",
       "summary_html" =>
         "2hu <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" />"
index ba053d20d04d276c7904ffc4fdfc573c503e509c..4f854ecaa978eb9807122cff922f306923f7dcc5 100644 (file)
@@ -148,7 +148,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
       "text" => "Hey @shp!",
       "uri" => activity.data["object"]["id"],
       "user" => UserView.render("show.json", %{user: user}),
-      "visibility" => "direct"
+      "visibility" => "direct",
+      "card" => nil
     }
 
     assert result == expected