diff options
17 files changed, 159 insertions, 131 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 @@ defmodule Pleroma.HTML do          |> Floki.attribute("a", "href")          |> Enum.at(0) -      {:commit, result} +      {:commit, {:ok, result}}      end)    end  end 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 @@ 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} diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index a366a149f..65b612026 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -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) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index ddfe6788c..b14ca9f5d 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -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 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/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex new file mode 100644 index 000000000..71fdddef9 --- /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 <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 diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 947dc0c3c..76d977ac2 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 <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 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 @@ 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 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/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index 19b723586..c4025cbd7 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -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 diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index a01ee0010..d0d1221c3 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -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 diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index b8f901e6c..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 @@ 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 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 @@ 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 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 <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 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 @@ 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 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 @@ 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\" />" 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 @@ 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  | 
