From 132d815f1f749cec4b54083fc46046c45e7d0d04 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 09:37:11 +0000 Subject: [PATCH 01/19] mastodon api: factor out status card fetching, move status card rendering to statusview, add opengraph extended data --- lib/pleroma/web/mastodon_api/mastodon_api.ex | 19 ++++++++++++++ .../mastodon_api/mastodon_api_controller.ex | 25 +++---------------- .../web/mastodon_api/views/status_view.ex | 23 +++++++++++++++++ .../mastodon_api_controller_test.exs | 12 ++++++++- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 8b1378917..9d1fb22ea 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -1 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright _ 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only +defmodule Pleroma.Web.MastodonAPI do + alias Pleroma.{Repo, Activity, Object, HTML} + alias Pleroma.Web.ActivityPub.ActivityPub + + def get_status_card(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: page_url, rich_media: rich_media} + else + _ -> %{} + end + end +end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index a366a149f..1cdb96989 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -6,8 +6,8 @@ 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 alias Pleroma.Web.MastodonAPI.{ StatusView, AccountView, @@ -1342,27 +1342,10 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do end end - def get_status_card(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) - else - _ -> %{} - end - end - def status_card(conn, %{"id" => status_id}) do - json(conn, get_status_card(status_id)) + data = StatusView.render("card.json", MastodonAPI.get_status_card(status_id)) + + json(conn, data) end def try_render(conn, target, params) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 0f2679444..a9e1e03ba 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -176,6 +176,29 @@ def render("status.json", _) 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], + 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/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index b8f901e6c..02a0eb228 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1663,9 +1663,19 @@ test "Status rich-media Card", %{conn: conn, user: user} 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 From 5a37ddc2dc9c2d1dcecafa8cefbb679da58e6371 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:15:41 +0000 Subject: [PATCH 02/19] mastodon api: embed card in status object --- lib/pleroma/web/mastodon_api/views/status_view.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index a9e1e03ba..ccc954985 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do alias Pleroma.User alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MediaProxy + alias Pleroma.Web.MastodonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView @@ -140,6 +141,8 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity} __MODULE__ ) + card = render("card.json", MastodonAPI.get_status_card(activity.id)) + %{ id: to_string(activity.id), uri: object["id"], @@ -148,6 +151,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity} 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, From 5d895093fd26ce0884957664c1214a66f5a36195 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:16:08 +0000 Subject: [PATCH 03/19] twitter api: embed card in twitterapi posts --- lib/pleroma/web/twitter_api/views/activity_view.ex | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index a01ee0010..77e9af348 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -10,6 +10,8 @@ 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 + alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Activity alias Pleroma.HTML alias Pleroma.Object @@ -274,6 +276,8 @@ def render( summary = HTML.strip_tags(summary) + card = StatusView.render("card.json", MastodonAPI.get_status_card(activity.id)) + %{ "id" => activity.id, "uri" => activity.data["object"]["id"], @@ -300,9 +304,10 @@ def render( "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 From de42646634e65a5216efe2f353352575b97b6390 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:21:05 +0000 Subject: [PATCH 04/19] rich media: add try/rescue to ensure we catch parsing and fetching failures --- lib/pleroma/web/rich_media/parser.ex | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 947dc0c3c..7787bf954 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -21,9 +21,14 @@ def parse(url) do 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"} + end end defp maybe_parse(html) do From 364cf5369b711e15b9ec60c768dadc280969a962 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:21:51 +0000 Subject: [PATCH 05/19] test: update mastodon status view tests --- test/web/mastodon_api/status_view_test.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs index ebf6273e8..f957fedac 100644 --- a/test/web/mastodon_api/status_view_test.exs +++ b/test/web/mastodon_api/status_view_test.exs @@ -84,6 +84,7 @@ test "a note activity" 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, From 07a9a891ad93d7fb21d596deb5211b4570cf1acb Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:27:37 +0000 Subject: [PATCH 06/19] twitter api: fix up activity representer --- .../web/twitter_api/representers/activity_representer.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index 19b723586..364aa7af3 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Formatter alias Pleroma.HTML + alias Pleroma.Web.MastodonAPI + 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 +188,8 @@ def to_map( summary = HTML.strip_tags(object["summary"]) + card = StatusView.render("card.json", MastodonAPI.get_status_card(activity.id)) + %{ "id" => activity.id, "uri" => activity.data["object"]["id"], @@ -214,7 +218,8 @@ def to_map( "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 From 020b3b29d998a6d2b960549c1ab9fb89d60cdd0f Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:27:46 +0000 Subject: [PATCH 07/19] test: update twitterapi tests --- .../web/twitter_api/representers/activity_representer_test.exs | 1 + test/web/twitter_api/views/activity_view_test.exs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/web/twitter_api/representers/activity_representer_test.exs b/test/web/twitter_api/representers/activity_representer_test.exs index ef0294140..ea5813733 100644 --- a/test/web/twitter_api/representers/activity_representer_test.exs +++ b/test/web/twitter_api/representers/activity_representer_test.exs @@ -164,6 +164,7 @@ test "an activity" do "possibly_sensitive" => true, "uri" => activity.data["object"]["id"], "visibility" => "direct", + "card" => nil, "summary" => "2hu :2hu:", "summary_html" => "2hu \"2hu\"" diff --git a/test/web/twitter_api/views/activity_view_test.exs b/test/web/twitter_api/views/activity_view_test.exs index ba053d20d..4f854ecaa 100644 --- a/test/web/twitter_api/views/activity_view_test.exs +++ b/test/web/twitter_api/views/activity_view_test.exs @@ -148,7 +148,8 @@ test "a create activity with a note" 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 From 6096846f5f33e1e81ef72c0f97b30c2745426c5d Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:34:49 +0000 Subject: [PATCH 08/19] API: kill /api/rich_media/parse endpoint --- .../controllers/rich_media_controller.ex | 17 ------- lib/pleroma/web/router.ex | 6 --- .../rich_media_controller_test.exs | 49 ------------------- 3 files changed, 72 deletions(-) delete mode 100644 lib/pleroma/web/rich_media/controllers/rich_media_controller.ex delete mode 100644 test/web/rich_media/controllers/rich_media_controller_test.exs 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 index 91019961d..000000000 --- a/lib/pleroma/web/rich_media/controllers/rich_media_controller.ex +++ /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/router.ex b/lib/pleroma/web/router.ex index 31f739738..68e7a44b6 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -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) 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 index fef126513..000000000 --- a/test/web/rich_media/controllers/rich_media_controller_test.exs +++ /dev/null @@ -1,49 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# 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 From 24a103a1fe199a559a22ddb55c747a89f5576e55 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 27 Jan 2019 12:39:20 +0000 Subject: [PATCH 09/19] mastodon api: formatting --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 1cdb96989..b5231a326 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Web alias Pleroma.Web.MastodonAPI + alias Pleroma.Web.MastodonAPI.{ StatusView, AccountView, @@ -500,7 +501,8 @@ def update_media(%{assigns: %{user: user}} = conn, data) 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 +1103,9 @@ def login(conn, %{"code" => code}) 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: ".", From 8e42251e064b4de6f1d767f28c79d64ca74dc245 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 06:04:54 +0000 Subject: [PATCH 10/19] rich media: add helpers module, use instead of MastodonAPI module --- lib/pleroma/web/mastodon_api/mastodon_api.ex | 19 ------------------- .../mastodon_api/mastodon_api_controller.ex | 16 ++++++++++++---- .../web/mastodon_api/views/status_view.ex | 3 +-- lib/pleroma/web/rich_media/helpers.ex | 18 ++++++++++++++++++ .../representers/activity_representer.ex | 7 +++++-- .../web/twitter_api/views/activity_view.ex | 7 +++++-- 6 files changed, 41 insertions(+), 29 deletions(-) create mode 100644 lib/pleroma/web/rich_media/helpers.ex diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 9d1fb22ea..8b1378917 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -1,20 +1 @@ -# Pleroma: A lightweight social networking server -# Copyright _ 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.MastodonAPI do - alias Pleroma.{Repo, Activity, Object, HTML} - alias Pleroma.Web.ActivityPub.ActivityPub - - def get_status_card(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: page_url, rich_media: rich_media} - else - _ -> %{} - end - end -end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index b5231a326..65b612026 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -7,8 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.{Repo, Object, Activity, User, Notification, Stats} alias Pleroma.Web - alias Pleroma.Web.MastodonAPI - alias Pleroma.Web.MastodonAPI.{ StatusView, AccountView, @@ -1347,9 +1345,19 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do end def status_card(conn, %{"id" => status_id}) do - data = StatusView.render("card.json", MastodonAPI.get_status_card(status_id)) + with %Activity{} = activity <- Repo.get(Activity, status_id), + true <- ActivityPub.is_public?(activity) do + data = + StatusView.render( + "card.json", + Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + ) - json(conn, data) + json(conn, data) + else + _e -> + %{} + end end def try_render(conn, target, params) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index ccc954985..68df1623e 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -11,7 +11,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do alias Pleroma.User alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MediaProxy - alias Pleroma.Web.MastodonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView @@ -141,7 +140,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity} __MODULE__ ) - card = render("card.json", MastodonAPI.get_status_card(activity.id)) + card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)) %{ id: to_string(activity.id), diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex new file mode 100644 index 000000000..e90e35180 --- /dev/null +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright _ 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.Helpers do + alias Pleroma.{Repo, Activity, Object, HTML} + alias Pleroma.Web.RichMedia.Parser + + def fetch_data_for_activity(%Activity{} = activity) do + with %Object{} = object <- Object.normalize(activity.data["object"]), + 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 diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index 364aa7af3..c4025cbd7 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -12,7 +12,6 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Formatter alias Pleroma.HTML - alias Pleroma.Web.MastodonAPI alias Pleroma.Web.MastodonAPI.StatusView defp user_by_ap_id(user_list, ap_id) do @@ -188,7 +187,11 @@ def to_map( summary = HTML.strip_tags(object["summary"]) - card = StatusView.render("card.json", MastodonAPI.get_status_card(activity.id)) + card = + StatusView.render( + "card.json", + Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + ) %{ "id" => activity.id, diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index 77e9af348..d0d1221c3 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -10,7 +10,6 @@ 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 alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Activity alias Pleroma.HTML @@ -276,7 +275,11 @@ def render( summary = HTML.strip_tags(summary) - card = StatusView.render("card.json", MastodonAPI.get_status_card(activity.id)) + card = + StatusView.render( + "card.json", + Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + ) %{ "id" => activity.id, From 91ef64a1ece210d86ad970cd751b5c7fe36df41b Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 06:07:18 +0000 Subject: [PATCH 11/19] activitypub: prime OGP crawler cache when new messages are inserted into the database --- lib/pleroma/web/activity_pub/activity_pub.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index feff22400..0199ac9e7 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -88,6 +88,10 @@ def insert(map, local \\ true) when is_map(map) 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} From ebeabdcc72dffb0a7f66133a511a19025888cfdc Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 06:10:25 +0000 Subject: [PATCH 12/19] rich media: helpers: clean up unused aliases --- lib/pleroma/web/rich_media/helpers.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index e90e35180..91c27f9e0 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Helpers do - alias Pleroma.{Repo, Activity, Object, HTML} + alias Pleroma.{Activity, Object, HTML} alias Pleroma.Web.RichMedia.Parser def fetch_data_for_activity(%Activity{} = activity) do From 339c26e12ba7312d283191196888ca7a5ce81dfe Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 06:19:00 +0000 Subject: [PATCH 13/19] test: add status posting with OGP link preview test --- .../mastodon_api/mastodon_api_controller_test.exs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 02a0eb228..1a5eb090c 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -136,6 +136,20 @@ test "posting a sensitive status", %{conn: conn} 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) From ddc7ae2c1a4e21c5e89a283951ba53e7ab6322ad Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 06:42:27 +0000 Subject: [PATCH 14/19] mastodon api: card: force OGP images through mediaproxy --- lib/pleroma/web/mastodon_api/views/status_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 68df1623e..d3e30b656 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -189,7 +189,7 @@ def render("card.json", %{rich_media: rich_media, page_url: page_url}) do provider_name: site_name, provider_url: page_url_data.scheme <> "://" <> page_url_data.host, url: page_url, - image: rich_media[:image], + image: rich_media[:image] |> MediaProxy.url(), title: rich_media[:title], description: rich_media[:description], pleroma: %{ From 8fb16e9f0f246e459fe6744fcf9b5d382a7c5c3f Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 19:59:36 +0000 Subject: [PATCH 15/19] rich media: parser: add copyright header --- lib/pleroma/web/rich_media/parser.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 7787bf954..bd21d2a0e 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.RichMedia.Parser do @parsers [ Pleroma.Web.RichMedia.Parsers.OGP, From 83b7062634d10bfa91adeb89bac10d854d6213d5 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 20:19:07 +0000 Subject: [PATCH 16/19] rich media: parser: cache negatives --- lib/pleroma/web/rich_media/parser.ex | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index bd21d2a0e..279e27273 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -15,11 +15,13 @@ def parse(nil), do: {:error, "No URL provided"} 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 @@ -30,8 +32,8 @@ defp parse_url(url) do html |> maybe_parse() |> get_parsed_data() rescue - _e -> - {:error, "Parsing error"} + e -> + {:error, "Parsing error: #{inspect(e)}"} end end From 0f11254a065d014d8fbb4f4a48cb03d14d8e02d0 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 20:31:43 +0000 Subject: [PATCH 17/19] rich media: parser: add some basic sanity checks on the returned data with pattern matching --- lib/pleroma/web/rich_media/parser.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 279e27273..76d977ac2 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -46,11 +46,11 @@ defp maybe_parse(html) 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 From ddb554520240bb534a4e968616edebd9299734da Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 20:55:33 +0000 Subject: [PATCH 18/19] rich media: kill some testsuite noise --- lib/pleroma/html.ex | 2 +- lib/pleroma/web/rich_media/helpers.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index fb602d6b6..bf5daa948 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -69,7 +69,7 @@ def extract_first_external_url(object, content) do |> Floki.attribute("a", "href") |> Enum.at(0) - {:commit, result} + {:commit, {:ok, result}} end) end end diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 91c27f9e0..71fdddef9 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do def fetch_data_for_activity(%Activity{} = activity) do with %Object{} = object <- Object.normalize(activity.data["object"]), - page_url <- HTML.extract_first_external_url(object, object.data["content"]), + {: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 From 61d6715714fe9c2ce6bf4fe6afc353f7fe5502a6 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 28 Jan 2019 21:07:36 +0000 Subject: [PATCH 19/19] rich media: oembed: return data in the same format as the other parsers --- .../web/rich_media/parsers/oembed_parser.ex | 6 ++- test/web/rich_media/parser_test.exs | 41 +++++++++---------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex index ca7226faf..efa98bc2c 100644 --- a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex +++ b/lib/pleroma/web/rich_media/parsers/oembed_parser.ex @@ -22,6 +22,10 @@ defp get_oembed_url(nodes) 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 diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index e14b5061a..93a58c528 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -65,28 +65,27 @@ test "parses OEmbed" 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: "\"Bacon", - "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