Add webfinger and basic feed support.
authorRoger Braun <roger@rogerbraun.net>
Tue, 18 Apr 2017 16:41:51 +0000 (18:41 +0200)
committerRoger Braun <roger@rogerbraun.net>
Tue, 18 Apr 2017 16:41:51 +0000 (18:41 +0200)
lib/pleroma/web/ostatus/feed_representer.ex [new file with mode: 0644]
lib/pleroma/web/ostatus/ostatus.ex [new file with mode: 0644]
lib/pleroma/web/ostatus/ostatus_controller.ex [new file with mode: 0644]
lib/pleroma/web/ostatus/user_representer.ex
lib/pleroma/web/router.ex
lib/pleroma/web/web_finger/web_finger.ex
test/web/ostatus/feed_representer_test.exs [new file with mode: 0644]
test/web/ostatus/ostatus_controller_test.exs [new file with mode: 0644]
test/web/ostatus/user_representer_test.exs

diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex
new file mode 100644 (file)
index 0000000..cb76022
--- /dev/null
@@ -0,0 +1,26 @@
+defmodule Pleroma.Web.OStatus.FeedRepresenter do
+  alias Pleroma.Web.OStatus
+  alias Pleroma.Web.OStatus.UserRepresenter
+
+  def to_simple_form(user, activities, users) do
+    most_recent_update = List.first(activities).updated_at
+    |> NaiveDateTime.to_iso8601
+
+    h = fn(str) -> [to_charlist(str)] end
+
+    entries = []
+    [{
+      :feed, [
+        xmlns: 'http://www.w3.org/2005/Atom',
+        "xmlns:activity": 'http://activitystrea.ms/spec/1.0/'
+      ], [
+        {:id, h.(OStatus.feed_path(user))},
+        {:title, ['#{user.nickname}\'s timeline']},
+        {:updated, h.(most_recent_update)},
+        {:entries, []},
+        {:link, [rel: 'hub', href: h.(OStatus.pubsub_path)], []},
+        {:author, UserRepresenter.to_simple_form(user)}
+      ]
+    }]
+  end
+end
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
new file mode 100644 (file)
index 0000000..9fcbe6c
--- /dev/null
@@ -0,0 +1,14 @@
+defmodule Pleroma.Web.OStatus do
+  alias Pleroma.Web
+
+  def feed_path(user) do
+    "#{user.ap_id}/feed.atom"
+  end
+
+  def pubsub_path() do
+    "#{Web.base_url}/push/hub"
+  end
+
+  def user_path(user) do
+  end
+end
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
new file mode 100644 (file)
index 0000000..ff6d730
--- /dev/null
@@ -0,0 +1,26 @@
+defmodule Pleroma.Web.OStatus.OStatusController do
+  use Pleroma.Web, :controller
+
+  alias Pleroma.{User, Activity}
+  alias Pleroma.Web.OStatus.FeedRepresenter
+  alias Pleroma.Repo
+  import Ecto.Query
+
+  def feed(conn, %{"nickname" => nickname}) do
+    user = User.get_cached_by_nickname(nickname)
+    query = from activity in Activity,
+      where: fragment("? @> ?", activity.data, ^%{actor: user.ap_id}),
+      limit: 20,
+      order_by: [desc: :inserted_at]
+
+    activities = query
+    |> Repo.all
+
+    response = FeedRepresenter.to_simple_form(user, activities, [user])
+    |> :xmerl.export_simple(:xmerl_xml)
+
+    conn
+    |> put_resp_content_type("application/atom+xml")
+    |> send_resp(200, response)
+  end
+end
index 66fc6e05388ad3ebf3200c70ac15ad0485b8431c..e7ee4cfeb88db7f6acc8cf6943a55c60e9aaffce 100644 (file)
@@ -1,14 +1,15 @@
 defmodule Pleroma.Web.OStatus.UserRepresenter do
   alias Pleroma.User
-  def to_tuple(user, wrapper \\ :author) do
-    {
-      wrapper, [
-        { :id, user.ap_id },
-        { :"activity:object", "http://activitystrea.ms/schema/1.0/person" },
-        { :uri, user.ap_id },
-        { :name, user.nickname },
-        { :link, %{rel: "avatar", href: User.avatar_url(user)}}
-      ]
-    }
+  def to_simple_form(user) do
+    ap_id = to_charlist(user.ap_id)
+    nickname = to_charlist(user.nickname)
+    avatar_url = to_charlist(User.avatar_url(user))
+    [
+      { :id, [ap_id] },
+      { :"activity:object", ['http://activitystrea.ms/schema/1.0/person'] },
+      { :uri, [ap_id] },
+      { :name, [nickname] },
+      { :link, [rel: 'avatar', href: avatar_url], []}
+    ]
   end
 end
index 99d1f69c269273bb581fef248e395536ed56b9fa..cc1f0e165e6293c6d03e1e9b0c3a33b1441f88fc 100644 (file)
@@ -54,6 +54,16 @@ defmodule Pleroma.Web.Router do
     post "/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar
   end
 
+  pipeline :ostatus do
+    plug :accepts, ["xml", "atom"]
+  end
+
+  scope "/users", Pleroma.Web do
+    pipe_through :ostatus
+
+    get "/:nickname/feed", OStatus.OStatusController, :feed
+  end
+
   scope "/.well-known", Pleroma.Web do
     pipe_through :well_known
 
index 258ff76714b4ee94c3d4f4cb8d0be6a25ef0919e..eb540e92ac011e3bc629f68f10df047961967788 100644 (file)
@@ -1,6 +1,7 @@
 defmodule Pleroma.Web.WebFinger do
   alias Pleroma.XmlBuilder
   alias Pleroma.User
+  alias Pleroma.Web.OStatus
 
   def host_meta() do
     base_url  = Pleroma.Web.base_url
@@ -30,7 +31,7 @@ defmodule Pleroma.Web.WebFinger do
       [
         {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.host}"},
         {:Alias, user.ap_id},
-        {:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: "#{user.ap_id}.atom"}}
+        {:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: OStatus.feed_path(user)}}
       ]
     }
     |> XmlBuilder.to_doc
diff --git a/test/web/ostatus/feed_representer_test.exs b/test/web/ostatus/feed_representer_test.exs
new file mode 100644 (file)
index 0000000..e252eca
--- /dev/null
@@ -0,0 +1,39 @@
+defmodule Pleroma.Web.OStatus.FeedRepresenterTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  alias Pleroma.User
+  alias Pleroma.Web.OStatus.{FeedRepresenter, UserRepresenter}
+  alias Pleroma.Web.OStatus
+
+  test "returns a feed of the last 20 items of the user" do
+    note_activity = insert(:note_activity)
+    user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+    tuple = FeedRepresenter.to_simple_form(user, [note_activity], [user])
+
+    most_recent_update = note_activity.updated_at
+    |> NaiveDateTime.to_iso8601
+
+    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary
+    user_xml = UserRepresenter.to_simple_form(user)
+    |> :xmerl.export_simple_content(:xmerl_xml)
+
+    expected = """
+    <feed xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
+      <id>#{OStatus.feed_path(user)}</id>
+      <title>#{user.nickname}'s timeline</title>
+      <updated>#{most_recent_update}</updated>
+      <entries />
+      <link rel="hub" href="#{OStatus.pubsub_path}" />
+      <author>
+        #{user_xml}
+      </author>
+    </feed>
+    """
+    assert clean(res) == clean(expected)
+  end
+
+  defp clean(string) do
+    String.replace(string, ~r/\s/, "")
+  end
+end
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
new file mode 100644 (file)
index 0000000..229cd9b
--- /dev/null
@@ -0,0 +1,15 @@
+defmodule Pleroma.Web.OStatus.OStatusControllerTest do
+  use Pleroma.Web.ConnCase
+  import Pleroma.Factory
+  alias Pleroma.User
+
+  test "gets a feed", %{conn: conn} do
+    note_activity = insert(:note_activity)
+    user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+    conn = conn
+    |> get("/users/#{user.nickname}/feed.atom")
+
+    assert response(conn, 200)
+  end
+end
index 02a4b5b141e6beb3e51559835c72efed7533dfc0..a401a56daecc4514bcbf82552e92952522964d44 100644 (file)
@@ -3,15 +3,26 @@ defmodule Pleroma.Web.OStatus.UserRepresenterTest do
   alias Pleroma.Web.OStatus.UserRepresenter
 
   import Pleroma.Factory
+  alias Pleroma.User
 
   test "returns a user with id, uri, name and link" do
     user = build(:user)
-    tuple = UserRepresenter.to_tuple(user)
-    {:author, author} = tuple
+    tuple = UserRepresenter.to_simple_form(user)
 
-    [:id, :uri, :name, :link]
-    |> Enum.each(fn (tag) ->
-      assert Enum.find(author, fn(e) -> tag == elem(e, 0) end)
-    end)
+    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary
+
+    expected = """
+    <id>#{user.ap_id}</id>
+    <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
+    <uri>#{user.ap_id}</uri>
+    <name>#{user.nickname}</name>
+    <link rel="avatar" href="#{User.avatar_url(user)}" />
+    """
+
+    assert clean(res) == clean(expected)
+  end
+
+  defp clean(string) do
+    String.replace(string, ~r/\s/, "")
   end
 end