Browse Source

fix(webmention): Update logic for outgoing.

jackyalcine 8 months ago
parent
commit
dc7a49a5cb
Signed by: Jacky Alciné <yo@jacky.wtf> GPG Key ID: 537A4F904B15268D

+ 1
- 0
.gitignore View File

@@ -28,3 +28,4 @@ priv/static
28 28
 .env
29 29
 test/snapshots/*.png
30 30
 priv/mnesia
31
+.directory

+ 1
- 1
config/test.exs View File

@@ -24,7 +24,7 @@ config :exvcr,
24 24
 config :hound,
25 25
   app_host: {:system, :string, "TEST_HOST", "localhost"},
26 26
   app_port: {:system, :integer, "TEST_PORT", 5001},
27
-  retry_time: 5_000,
27
+  retry_time: 10_000,
28 28
   genserver_timeout: 480_000,
29 29
   driver: "chrome_driver",
30 30
   host: "http://webdriver",

+ 4
- 1
lib/indieweb/micropub/entry.ex View File

@@ -37,7 +37,10 @@ defmodule IndieWeb.Micropub.Entry do
37 37
         if length(links) != 0 do
38 38
           Logger.info("Found links for #{entry_uri} to dispatch Webmentions to: " <> Enum.join(links, "; "))
39 39
 
40
-          Enum.each(links, &Koype.Webmention.send(target: &1, source: model))
40
+          Enum.each(
41
+            links,
42
+            &Koype.Webmention.send(target: IndieWeb.Http.make_absolute_uri(&1, Koype.host()), source: model)
43
+          )
41 44
         else
42 45
           Logger.info("No links found for #{entry_uri}; not sending any Webmentions.")
43 46
           :ok

+ 18
- 13
lib/indieweb/syndication.ex View File

@@ -59,33 +59,38 @@ defmodule IndieWeb.Syndication do
59 59
     entry_uri = Koype.Repo.Entry.get_uri(entry)
60 60
 
61 61
     with(
62
-      {:ok, syndication_result} <- Koype.Repo.Syndication.Result.create(%{target_id: target.id, status: "sent"}),
62
+      {:ok, syndication_result} <- Koype.Repo.Syndication.Result.create(%{target_id: target.id, status: "processed"}),
63 63
       {:ok, model} <-
64 64
         entry
65 65
         |> Ecto.build_assoc(:syndication_results, %{syndication_result_id: syndication_result.id, entry_id: entry.id})
66 66
         |> Koype.Repo.insert(),
67 67
       result <- Koype.Repo.preload(model, :syndication_result),
68
-      {:ok, resp} <- Koype.Webmention.send!(source: entry_uri, target: target.endpoint),
69
-      {:ok, updated_result} <- Koype.Repo.Syndication.Result.update(result.syndication_result, %{status: "processed"})
68
+      {:ok, resp} <- Koype.Webmention.send!(target: target.endpoint, source: URI.parse(entry_uri)),
69
+      {:ok, updated_result} <- Koype.Repo.Syndication.Result.update(result.syndication_result, %{status: "sent"})
70 70
     ) do
71
-      Logger.info("Sent request to syndication target #{target.endpoint} / '#{target.name}' for #{entry_uri}")
72
-      # TODO: Check the response type.
71
+      Logger.info(
72
+        "Sent syndication Webmention to syndication target #{target.endpoint} / '#{target.name}' for #{entry_uri}."
73
+      )
73 74
 
74
-      case Jason.decode(resp.body) do
75
-        {:ok, %{"url" => url}} ->
76
-          Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed", result: url})
75
+      if resp.body != "" do
76
+        case Jason.decode(resp.body) do
77
+          {:ok, %{"url" => url}} ->
78
+            Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed", result: url})
77 79
 
78
-        error ->
79
-          Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed"})
80
-          error
80
+          error ->
81
+            Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed"})
82
+            error
83
+        end
84
+      else
85
+        Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed"})
81 86
       end
82 87
     else
83 88
       {:ok, %Koype.Http.Response{body: body} = resp} ->
84 89
         Logger.info(fn -> "Server failed to accept syndication webmention sent to #{target.endpoint}: #{body}" end)
85 90
         {:error, resp}
86 91
 
87
-      {:error, issue} = err ->
88
-        Logger.warn("Failed to send syndication webmention to #{target.endpoint}: #{inspect(issue)}")
92
+      err ->
93
+        Logger.warn("Failed to send syndication webmention to #{target.endpoint}: #{inspect(err)}")
89 94
         err
90 95
     end
91 96
   end

+ 17
- 3
lib/job.ex View File

@@ -34,14 +34,28 @@ defmodule Koype.Job do
34 34
     def info(args \\ [])
35 35
   end
36 36
 
37
+  defmodule WorkerFailedException do
38
+    defexception [:message, :job, :reason]
39
+
40
+    def exception(job: job, reason: reason) do
41
+      msg = "The job '#{job.name()}' failed to complete with args '#{inspect(job.args)}': #{inspect(reason)}"
42
+      %__MODULE__{message: msg, job: job, reason: reason}
43
+    end
44
+  end
45
+
37 46
   defmodule Worker do
38 47
     use Que.Worker
39 48
 
40 49
     # TODO: Do exception handling around this.
41 50
     def perform(kind: kind, job: job) do
42 51
       Logger.info("Starting job #{job.name()}##{job.id}...")
43
-      kind.execute(job.args)
52
+      result = kind.execute(job.args)
44 53
       Logger.info("Completed job #{job.name()}##{job.id}.")
54
+
55
+      case result do
56
+        {:ok, _} -> result
57
+        _ -> raise Koype.Job.WorkerFailedException, job: job, reason: result
58
+      end
45 59
     end
46 60
 
47 61
     def on_success(args) do
@@ -49,9 +63,9 @@ defmodule Koype.Job do
49 63
       Logger.info("Job #{job.name()}##{job.id} was successful.")
50 64
     end
51 65
 
52
-    def on_failure(args) do
66
+    def on_failure(args, error) do
53 67
       job = args[:job]
54
-      Logger.info("Job #{job.name()}##{job.id} failed.")
68
+      Logger.info("Job #{job.name()}##{job.id} failed: #{inspect(error)}.")
55 69
     end
56 70
   end
57 71
 

+ 2
- 2
lib/job/webmention.ex View File

@@ -28,7 +28,7 @@ defmodule Koype.Job.Webmention do
28 28
 
29 29
     def info(args) do
30 30
       %{
31
-        description: "Recevied a Webmention to #{args[:target]} from #{args[:source].name}",
31
+        description: "Recevied a Webmention to #{args[:target]} from #{args[:source]}",
32 32
         info: "Webmention Received"
33 33
       }
34 34
     end
@@ -45,7 +45,7 @@ defmodule Koype.Job.Webmention do
45 45
 
46 46
     def info(args) do
47 47
       %{
48
-        description: "Sent a Webmention to #{args[:target]} from #{args[:source].name}",
48
+        description: "Sent a Webmention to #{args[:target]} from #{args[:source]}",
49 49
         info: "Webmention Sent"
50 50
       }
51 51
     end

+ 6
- 1
lib/post.ex View File

@@ -133,7 +133,7 @@ defmodule Koype.Post do
133 133
       first_entry =
134 134
         cond do
135 135
           is_list(entries) -> entries |> List.first()
136
-          is_map(entries) -> entries
136
+          true -> entries
137 137
         end
138 138
 
139 139
       title =
@@ -164,5 +164,10 @@ defmodule Koype.Post do
164 164
   def determine_title(_, %{"content" => %{"plain" => plain_content}}) when plain_content != "",
165 165
     do: truncate_title(plain_content)
166 166
 
167
+  def determine_title(t, d) do
168
+    Apex.ap([t, d])
169
+    "Entry"
170
+  end
171
+
167 172
   def truncate_title(text), do: T.truncate(text, length: @max_title_length)
168 173
 end

+ 1
- 0
lib/repo/entry.ex View File

@@ -31,6 +31,7 @@ defmodule Koype.Repo.Entry do
31 31
   @route_token "post"
32 32
 
33 33
   defmodule EntrySyndicationResult do
34
+    @moduledoc false
34 35
     use Ecto.Schema
35 36
 
36 37
     schema "abstract table: syndication result table" do

+ 2
- 2
lib/template/format.ex View File

@@ -60,14 +60,14 @@ defimpl Koype.Template.Formatter, for: Koype.Repo.Entry do
60 60
       {:ok, json} <- Koype.Repo.Entry.Json.find(entry),
61 61
       props when is_map(props) <- IndieWeb.Micropub.Content.expand_properties(json, entry)
62 62
     ) do
63
-      uri = Koype.Repo.Entry.get_uri(entry)
63
+      uri = entry |> Koype.Repo.Entry.get_uri()
64 64
       reloaded_entry = Koype.Repo.preload(entry, [:categories, :syndication_results])
65 65
 
66 66
       entry
67 67
       |> Map.drop([:__meta__, :__struct__])
68 68
       |> Map.new()
69 69
       |> Map.put("uri", uri)
70
-      |> Map.put("title", Koype.Post.determine_title(String.to_existing_atom(entry.type), json))
70
+      |> Map.put("title", entry.name)
71 71
       |> Map.put("interaction_count", Koype.Repo.Webmention.count_of(uri))
72 72
       |> Map.put("icon", Koype.Web.EntryView.icon_for_entry(entry))
73 73
       |> Map.put("json", props)

+ 2
- 1
lib/template/render.ex View File

@@ -90,6 +90,7 @@ defmodule Koype.Template.Renderer do
90 90
       card = %{
91 91
         "uri" => Koype.host(),
92 92
         "preferred_name" => Koype.Profile.displayed_name(),
93
+        "displayed_name" => Koype.Profile.displayed_name(),
93 94
         "photo" => Koype.Profile.photo(),
94 95
         "note" => Koype.Profile.note(),
95 96
         "name" => Koype.Profile.name(),
@@ -99,7 +100,7 @@ defmodule Koype.Template.Renderer do
99 100
 
100 101
       Map.merge(data, %{
101 102
         "domain" => Koype.host(),
102
-        "host" => URI.parse(Koype.host()) |> Map.get(:host),
103
+        "host" => Koype.host() |> URI.parse() |> Map.get(:host),
103 104
         "form" => %{
104 105
           "csrf_token" => Phoenix.Controller.get_csrf_token()
105 106
         },

+ 1
- 1
lib/template/tags.ex View File

@@ -65,7 +65,7 @@ defmodule Koype.Template.Tags do
65 65
       path =
66 66
         case markup do
67 67
           "current" ->
68
-            Koype.host() <> current_path
68
+            current_path
69 69
 
70 70
           "home" ->
71 71
             RouteHelpers.page_path(Koype.Web.Endpoint, :index)

+ 30
- 7
lib/webmention.ex View File

@@ -23,13 +23,19 @@ defmodule Koype.Webmention do
23 23
   require Logger
24 24
 
25 25
   defmodule URIAdapter do
26
+    @moduledoc false
26 27
     @behaviour IndieWeb.Webmention.URIAdapter
27 28
 
28 29
     @impl true
29 30
     def to_source_url(target_url)
30
-    def to_source_url(target_url) when is_binary(target_url), do: target_url
31
+    def to_source_url(target_url) when is_binary(target_url), do: URI.parse(target_url)
32
+    def to_source_url(%Koype.Repo.Entry{} = entry), do: to_source_url(Koype.Repo.Entry.get_uri(entry))
33
+    def to_source_url(%URI{} = target_url), do: target_url
31 34
     def to_source_url(_), do: nil
32 35
 
36
+    @impl true
37
+    def from_source_url(%URI{} = target_url), do: from_source_url(URI.to_string(target_url))
38
+
33 39
     @impl true
34 40
     def from_source_url(target_url) do
35 41
       Enum.reduce_while(
@@ -57,7 +63,7 @@ defmodule Koype.Webmention do
57 63
           {:ok, :homepage}
58 64
 
59 65
         true ->
60
-          Logger.debug("The URI #{target_url} is not the homepage (#{current_host}).")
66
+          Logger.debug(fn -> "The URI #{target_url} is not the homepage (#{current_host})." end)
61 67
           {:error, :not_homepage}
62 68
       end
63 69
     end
@@ -90,7 +96,7 @@ defmodule Koype.Webmention do
90 96
 
91 97
     case IndieWeb.Webmention.send(target, source) do
92 98
       {:ok, resp} ->
93
-        Logger.debug("Webmention for #{inspect(source)} to #{target}!")
99
+        Logger.debug("Webmention for #{inspect(source)} to #{target} sent!")
94 100
         {:ok, resp}
95 101
 
96 102
       error ->
@@ -140,9 +146,10 @@ defmodule Koype.Webmention do
140 146
   end
141 147
 
142 148
   defp do_deletion_of_webmention(args) do
143
-    Koype.Repo.Webmention
144
-    |> Koype.Repo.all(source: args[:source], target: args[:target_url])
145
-    |> Enum.map(&Koype.Repo.delete(&1))
149
+    _mentions =
150
+      Koype.Repo.Webmention
151
+      |> Koype.Repo.all(source: args[:source], target: args[:target_url])
152
+      |> Enum.map(&Koype.Repo.delete(&1))
146 153
 
147 154
     Logger.info("Dropped all relating Webmentions from the database.")
148 155
     {:ok, :gone}
@@ -163,7 +170,7 @@ defmodule Koype.Webmention do
163 170
         "Detected #{source} to have #{inspect(types)} types and be a #{type} at its base."
164 171
       end)
165 172
 
166
-      Logger.info("Saving Webmention from #{source} to #{target_url}")
173
+      Logger.info("Saving Webmention from #{source} to #{target_url}.")
167 174
 
168 175
       case Koype.Repo.Webmention.upsert(
169 176
              source: source,
@@ -195,4 +202,20 @@ defmodule Koype.Webmention do
195 202
         err
196 203
     end
197 204
   end
205
+
206
+  def fetch_for(entry, type \\ :all) do
207
+    entry
208
+    |> Koype.Repo.Entry.get_uri()
209
+    |> Koype.Repo.Webmention.fetch(type)
210
+    |> Enum.map(fn model ->
211
+      with({:ok, json} <- Koype.Repo.Webmention.Json.find(model)) do
212
+        json
213
+        |> Map.put("url", model.source)
214
+        |> Map.put("type", model.type)
215
+      else
216
+        {:error, _} -> nil
217
+      end
218
+    end)
219
+    |> Enum.filter(&is_map/1)
220
+  end
198 221
 end

+ 1
- 1
mix.lock View File

@@ -64,7 +64,7 @@
64 64
   "ibrowse": {:hex, :ibrowse, "4.4.0", "2d923325efe0d2cb09b9c6a047b2835a5eda69d8a47ed6ff8bc03628b764e991", [:rebar3], [], "hexpm"},
65 65
   "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
66 66
   "inch_ex": {:git, "https://github.com/rrrene/inch_ex.git", "45ecbc4b57fa8b943b9e4cd0d4da72da95c9dd4a", []},
67
-  "indieweb": {:hex, :indieweb, "0.0.38", "847eb165daf7c3a30e442679685218bf7c4d89aec71edd3c8e9bd2cf414d9caa", [:mix], [{:cachex, "~> 3.1", [hex: :cachex, repo: "hexpm", optional: false]}, {:microformats2, "~> 0.2.0", [hex: :microformats2, repo: "hexpm", optional: false]}], "hexpm"},
67
+  "indieweb": {:hex, :indieweb, "0.0.40", "f68b887916ba8e24eb77faf38f4e96eac0cdb1e08ba4e76bfaecb3054bbfe7f2", [:mix], [{:cachex, "~> 3.1", [hex: :cachex, repo: "hexpm", optional: false]}, {:microformats2, "~> 0.2.0", [hex: :microformats2, repo: "hexpm", optional: false]}], "hexpm"},
68 68
   "inflex": {:hex, :inflex, "1.10.0", "8366a7696e70e1813aca102e61274addf85d99f4a072b2f9c7984054ea1b9d29", [:mix], [], "hexpm"},
69 69
   "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
70 70
   "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},

+ 6
- 1
priv/themes/default/entry/view/response.html.liquid View File

@@ -14,7 +14,12 @@
14 14
 <form method="post">
15 15
   <input type=hidden name=_csrf_token value="{{ form.csrf_token }}" />
16 16
   <input type=hidden name=action value="mp-action" />
17
-  <button type="submit">Reparse</button>
17
+  <button type="submit">Reinvoke Micropub Actions</button>
18
+</form>
19
+<form method="post">
20
+  <input type=hidden name=_csrf_token value="{{ form.csrf_token }}" />
21
+  <input type=hidden name=action value="webmention" />
22
+  <button type="submit">Resend Webmentions</button>
18 23
 </form>
19 24
 {% endif %}
20 25
 <div class="w-100 order-3 self-end mt2 mt3-l bt b--moon-gray flex flex-wrap flex-row justify-between self-start pt3 pv1">

+ 1
- 0
test/features/entry_test.exs View File

@@ -12,6 +12,7 @@ defmodule Koype.Feature.EntryTest do
12 12
       end
13 13
 
14 14
     uri = Koype.Web.BrowserCase.rewrite_url(URI.parse(url))
15
+    Apex.ap(uri)
15 16
     navigate_to(uri)
16 17
   end
17 18
 end

+ 2
- 2
test/integration/controllers/indie/auth_controller_test.exs View File

@@ -288,7 +288,7 @@ defmodule Koype.Web.Indie.AuthControllerTest do
288 288
           Code,
289 289
           [],
290 290
           [
291
-            verify: fn _code, _client, _redirect -> :ok end
291
+            verify: fn _code, _client, _redirect, _args -> :ok end
292 292
           ]
293 293
         }
294 294
       ]) do
@@ -320,7 +320,7 @@ defmodule Koype.Web.Indie.AuthControllerTest do
320 320
           Code,
321 321
           [],
322 322
           [
323
-            verify: fn _code, _client, _redirect -> {:error, :invalid_code} end
323
+            verify: fn _code, _client, _redirect, _args -> {:error, :invalid_code} end
324 324
           ]
325 325
         }
326 326
       ]) do

+ 3
- 3
test/integration/controllers/indie/micropub_controller_test.exs View File

@@ -44,7 +44,7 @@ defmodule Koype.Web.Indie.MicropubControllerTest do
44 44
         |> doc(description: "Creates a new form-encoded post", operation_id: "micropub_form_encoded_post")
45 45
 
46 46
       assert text_response(conn, :created)
47
-      assert get_resp_header(conn, "location") |> List.first() =~ "/post/"
47
+      assert conn |> get_resp_header("location") |> List.first() =~ "/post/"
48 48
     end
49 49
 
50 50
     test "201 creates a new entry form-encoded with syndication targets" do
@@ -64,7 +64,7 @@ defmodule Koype.Web.Indie.MicropubControllerTest do
64 64
         |> doc(description: "Creates a new form-encoded post", operation_id: "micropub_form_encoded_post")
65 65
 
66 66
       assert text_response(conn, :created)
67
-      location = get_resp_header(conn, "location") |> List.first()
67
+      location = conn |> get_resp_header("location") |> List.first()
68 68
       assert location =~ "/post/"
69 69
       {:ok, entry} = Koype.Repo.Entry.resolve_from_uri(location)
70 70
       refute [] == entry |> Koype.Repo.preload(:syndication_results) |> Map.get(:syndication_results)
@@ -85,7 +85,7 @@ defmodule Koype.Web.Indie.MicropubControllerTest do
85 85
         |> doc(description: "Creates a new entry with JSON", operation_id: "micropub_json_post")
86 86
 
87 87
       assert text_response(conn, :created)
88
-      assert List.first(get_resp_header(conn, "location")) =~ "/post/"
88
+      assert conn |> get_resp_header("location") |> List.first() =~ "/post/"
89 89
     end
90 90
 
91 91
     test "201 creates a new entry json with categories" do

+ 2
- 1
test/support/steps/data.ex View File

@@ -15,7 +15,8 @@ defmodule Koype.Feature.Steps.Shared.Data do
15 15
         result =
16 16
           case record do
17 17
             %Koype.Repo.Entry{} ->
18
-              Koype.Repo.Entry.Json.persist(record, build(:entry_json))
18
+              entry_json = build(:entry_json, %{"name" => Faker.Lorem.sentence()})
19
+              Koype.Repo.Entry.Json.persist(record, entry_json)
19 20
           end
20 21
 
21 22
         assert {:ok, _} = result

+ 43
- 23
test/unit/indieweb/micropub/entry_test.exs View File

@@ -240,6 +240,29 @@ defmodule IndieWeb.Micropub.EntryTest do
240 240
                )
241 241
     end
242 242
 
243
+    test "successfully creates a new HTML entry" do
244
+      target = Faker.Internet.url()
245
+
246
+      html = """
247
+      <html>
248
+      <body>
249
+      <div class='h-entry'>
250
+      This is a sample <a href='#{target}'>target</a>.
251
+      </div>
252
+      </body>
253
+      </html>
254
+      """
255
+
256
+      assert {:ok, [state: :created, model: model, uri: _]} =
257
+               Subject.invoke(
258
+                 "create",
259
+                 scope: ~w(create),
260
+                 params: [
261
+                   reserved: %{"content" => %{"html" => html}, "h" => "entry"}
262
+                 ]
263
+               )
264
+    end
265
+
243 266
     for {type, property} <- [
244 267
           {"like", "like-of"},
245 268
           {"repost", "repost-of"},
@@ -274,29 +297,26 @@ defmodule IndieWeb.Micropub.EntryTest do
274 297
     end
275 298
 
276 299
     test "successfully creates a new rsvp" do
277
-      content = %{"rsvp" => ["yes"], "in-reply-to" => [Faker.Internet.url()]}
278
-      entry_json = params_for(:entry_json)
279
-      html = "<html><body><div class='h-event'><a href='/' class='h-card p-name u-url'>Hello</a></div></body></html>"
280
-
281
-      use_cassette :stub, uri: "~r/*/", method: "get", body: html do
282
-        with_mock(
283
-          Model.Json,
284
-          [],
285
-          persist: fn _, _ -> {:ok, Faker.File.file_name(:text)} end,
286
-          find: fn _ -> {:ok, entry_json} end
287
-        ) do
288
-          assert {:ok, state: :created, model: model, uri: _} =
289
-                   Subject.invoke(
290
-                     "create",
291
-                     scope: ~w(create),
292
-                     params: [
293
-                       reserved: %{"h" => "entry"},
294
-                       content: content
295
-                     ]
296
-                   )
297
-
298
-          assert model.type == "rsvp"
299
-        end
300
+      source_uri = Faker.Internet.url()
301
+      content = %{"rsvp" => ["yes"], "in-reply-to" => [source_uri]}
302
+
303
+      html =
304
+        "<html><body><div class='h-event'><a href='/' class='h-card p-name u-url'>Hello</a><time class='dt-start' datetime='#{
305
+          Calendar.DateTime.Format.rfc3339(Calendar.DateTime.now_utc())
306
+        }'>now</time></div></body></html>"
307
+
308
+      use_cassette :stub, uri: source_uri, method: "get", body: html do
309
+        assert {:ok, state: :created, model: model, uri: _} =
310
+                 Subject.invoke(
311
+                   "create",
312
+                   scope: ~w(create),
313
+                   params: [
314
+                     reserved: %{"h" => "entry"},
315
+                     content: content
316
+                   ]
317
+                 )
318
+
319
+        assert model.type == "rsvp"
300 320
       end
301 321
     end
302 322
 

+ 10
- 3
test/unit/webmention_test.exs View File

@@ -72,7 +72,6 @@ defmodule Koype.WebmentionTest do
72 72
       end
73 73
     end
74 74
 
75
-    @tag skip: true
76 75
     test "updates existing - refreshes content" do
77 76
       model = insert(:entry)
78 77
       source_uri = Faker.Internet.url()
@@ -129,8 +128,16 @@ defmodule Koype.WebmentionTest do
129 128
       source_uri = Faker.Internet.url()
130 129
       target_uri = Koype.Repo.Entry.get_uri(model)
131 130
 
132
-      html =
133
-        "<html><body><div class='h-entry'><a href='/' class='h-card u-url'><span class='p-name'>Hello</span></a><p class='p-content'>Wow</p></div></body></html>"
131
+      html = """
132
+      <html>
133
+      <body>
134
+      <div class="h-entry">
135
+      <a class="p-author h-card" href="/"><span class="p-name">#{Faker.Name.name()}</span></a>
136
+      <a class="u-like-of" href="#{target_uri}">Wow</a>
137
+      </div>
138
+      </body>
139
+      </html>
140
+      """
134 141
 
135 142
       use_cassette :stub, url: source_uri, body: html, status_code: 200 do
136 143
         with_mock(Koype.Storage.Json, persist: fn _, _ -> {:error, :test_storage_error} end) do

+ 24
- 21
web/controllers/entry_controller.ex View File

@@ -22,40 +22,43 @@ defmodule Koype.Web.EntryController do
22 22
   require Logger
23 23
   plug(Logster.Plugs.ChangeLogLevel, to: :debug)
24 24
 
25
-  defp do_decorate_webmentions_of_entry(entry, type) do
26
-    entry
27
-    |> Koype.Repo.Entry.get_uri()
28
-    |> Koype.Repo.Webmention.fetch(String.to_existing_atom(type))
29
-    |> Enum.map(fn model ->
30
-      with({:ok, json} <- Koype.Repo.Webmention.Json.find(model)) do
31
-        json
32
-        |> Map.put("url", model.source)
33
-        |> Map.put("type", model.type)
34
-      else
35
-        {:error, _} -> nil
36
-      end
37
-    end)
38
-    |> Enum.filter(&is_map/1)
25
+  def reparse(conn, %{"id" => id, "action" => "webmention"}) do
26
+    Logger.info("Beginning the resending of Webmentions for #{id}...")
27
+
28
+    with(
29
+      {:ok, entry} <- Entry.fetch(id),
30
+      :ok <- IndieWeb.Micropub.Entry.send_webmentions(entry)
31
+    ) do
32
+      conn
33
+      |> put_flash(:success, "Resent Webmentions for #{entry.name}.")
34
+      |> redirect(to: entry_path(conn, :view, entry.id))
35
+    else
36
+      {:error, error} ->
37
+        Logger.warn("Failed to resend Webmentions for entry #{id}: #{inspect(error)}.")
38
+
39
+        conn
40
+        |> put_flash(:error, "Failed to resend Webmentions: #{inspect(error)}")
41
+        |> redirect(to: entry_path(conn, :view, id))
42
+    end
39 43
   end
40 44
 
41 45
   def reparse(conn, %{"id" => id, "action" => "mp-action"}) do
42
-    Logger.info("Beginning the reparsing of #{id}...")
46
+    Logger.info("Beginning the re-invoking of Micropub actions for #{id}...")
43 47
 
44 48
     with(
45 49
       {:ok, entry} <- Entry.fetch(id),
46 50
       {:ok, json} <- Entry.Json.find(entry),
47
-      _ <- Logger.info(inspect(json)),
48 51
       {:ok, updated_model} <- IndieWeb.Micropub.Content.parse_extensions(entry, json)
49 52
     ) do
50 53
       conn
51
-      |> put_flash(:success, "Reparsed post #{updated_model.id}.")
54
+      |> put_flash(:success, "Invoked stored Micropub actions for #{updated_model.name}.")
52 55
       |> redirect(to: entry_path(conn, :view, updated_model.id))
53 56
     else
54 57
       {:error, error} ->
55
-        Logger.warn("Failed to reparse entry #{id}: #{inspect(error)}.")
58
+        Logger.warn("Failed to reinvoke actions for entry #{id}: #{inspect(error)}.")
56 59
 
57 60
         conn
58
-        |> put_flash(:error, "Failed to re-parse post: #{inspect(error)}")
61
+        |> put_flash(:error, "Failed to reinvoke actions for entry: #{inspect(error)}")
59 62
         |> redirect(to: entry_path(conn, :view, id))
60 63
     end
61 64
   end
@@ -84,12 +87,12 @@ defmodule Koype.Web.EntryController do
84 87
               "facepile" => ~w(like repost bookmark),
85 88
               "of" =>
86 89
                 Enum.map(~w(like repost bookmark reply), fn type ->
87
-                  {type, do_decorate_webmentions_of_entry(entry, type)}
90
+                  {type, Koype.Webmention.fetch_for(entry, type)}
88 91
                 end)
89 92
                 |> Map.new()
90 93
                 |> Map.put(
91 94
                   "other",
92
-                  List.flatten(Enum.map(~w(note article), &do_decorate_webmentions_of_entry(entry, &1)))
95
+                  List.flatten(Enum.map(~w(note article), &Koype.Webmention.fetch_for(entry, &1)))
93 96
                 )
94 97
             }
95 98
           }

+ 8
- 0
web/templates/error/403.html.eex View File

@@ -0,0 +1,8 @@
1
+<section class="flex-auto pa2 flex-grow w-100 h-100 flex flex-column justify-center items-center">
2
+  <h1 class="f1 fw1 gray ttu tracked dark-gray lh-title mv1 measure">Foribbden</h1>
3
+  <img src="<%= asset_path_for(:core, "/images/ouch/pluto-page-not-found.png") %>"
4
+       class="content-stretch items-stretch w-100 mw7 h-auto"
5
+       alt="Image of person floating in space with a rocket to the top right" />
6
+  <p class="lh-solid f4 measure serif">Something ventured, nothing gained (in this case). Not your fault!</p>
7
+  <p class="lh-solid f5 measure-narrow serif">Go to the <a href="<%= page_path(Koype.Web.Endpoint, :index) %>" class="link underline blue">home page</a>.</p>
8
+</section>

+ 4
- 4
web/templates/layout/_footer.html.eex View File

@@ -2,7 +2,7 @@
2 2
   <div class="mw8 flex flex-column flex-row-l justify-around center">
3 3
     <%= if Koype.Profile.complete? do %>
4 4
       <div class="h-card pa2 items-center flex flex-column flex-row-l flex-auto flex-grow items-start">
5
-        <a href="<%= Koype.host %>" rel="author me" class="u-url db pa2 link light-gray">
5
+        <a href="/" rel="author me" class="u-url db pa2 link light-gray">
6 6
           <%= if Koype.Profile.photo do %>
7 7
             <img alt="<%= Koype.Profile.displayed_name %>"
8 8
                 class="center mw5 ba bw1 b--moon-gray-20 bg--moon-gray-20 br-100 w-auto u-photo db"
@@ -15,7 +15,7 @@
15 15
       </div>
16 16
       <ul class="list pv2 ph4 f7 center measure-narrow">
17 17
         <li class="lh-copy mb1">
18
-          <a href="<%= Koype.host %>" class="moon-gray link">
18
+          <a href="/" class="moon-gray link">
19 19
             <i class="hover-orange h1 w1 pa1 v-mid" data-feather="home"></i>
20 20
             <span class="v-mid underline-hover">Home</span>
21 21
           </a>
@@ -34,7 +34,7 @@
34 34
             </a>
35 35
           </li>
36 36
           <li class="lh-copy mb1">
37
-            <a href="https://quill.p3k.io/?me=<%= Koype.host %>" class="moon-gray link">
37
+            <a href="https://quill.p3k.io/?me=<%= Enum.join([@conn.scheme, "://", @conn.host, ":", @conn.port]) %>" class="moon-gray link">
38 38
               <i class="h1 w1 pa1 v-mid" data-feather="edit"></i>
39 39
               <span class="v-mid underline-hover">Compose</span>
40 40
             </a>
@@ -46,7 +46,7 @@
46 46
         Koype
47 47
         <code>v<%= Koype.version() %></code>
48 48
         &mdash;
49
-        <a class="link lightest-blue fw7" href="<%= Koype.host %>">
49
+        <a class="link lightest-blue fw7" href="/">
50 50
           <code><%= Koype.host %></code>
51 51
         </a>
52 52
         &mdash;

+ 2
- 1
web/views/error_view.ex View File

@@ -23,7 +23,8 @@ defmodule Koype.Web.ErrorView do
23 23
   def tag(_, _, _), do: nil
24 24
 
25 25
   def title("400.html", _), do: "I Don't Understand (Bad Request)"
26
-  def title("401.html", _), do: "You Shall Not Pass (Unauthorized)"
26
+  def title("401.html", _), do: "No Cash? No Entry. (Unauthorized)"
27
+  def title("403.html", _), do: "You Shall Not Pass (Forbidden)"
27 28
   def title("404.html", _), do: "I Think You Might be Mistaken (Not Found)"
28 29
   def title("500.html", _), do: "Bleep Bloop BLE- (Internal Server Error)"
29 30
 

Loading…
Cancel
Save