about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2024-12-07 15:54:39 -0500
committerStar Rauchenberger <fefferburbia@gmail.com>2024-12-07 15:54:39 -0500
commita996f3bd05fc480247fd112f23fa3e67f7d5d7b5 (patch)
tree2df0a56e8cc42ccd632db5b2c022b7dbf8d74444
parent59f5508d5bc0cee856105a5bd52e1deaee44b842 (diff)
downloadthoughts-a996f3bd05fc480247fd112f23fa3e67f7d5d7b5.tar.gz
thoughts-a996f3bd05fc480247fd112f23fa3e67f7d5d7b5.tar.bz2
thoughts-a996f3bd05fc480247fd112f23fa3e67f7d5d7b5.zip
Added support for liking blog posts via webmention
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock31
-rw-r--r--app/controllers/blogs_controller.rb34
-rw-r--r--app/models/concerns/votable.rb8
-rw-r--r--app/models/vote.rb1
-rw-r--r--app/views/blogs/_blog.html.haml2
-rw-r--r--app/views/blogs/show.html.haml1
-rw-r--r--app/views/layouts/application.html.haml2
-rw-r--r--config/routes.rb1
-rw-r--r--db/migrate/20241207200746_add_like_fields_to_vote.rb8
-rw-r--r--db/schema.rb4
11 files changed, 91 insertions, 3 deletions
diff --git a/Gemfile b/Gemfile index d0f9528..d6ca5e9 100644 --- a/Gemfile +++ b/Gemfile
@@ -85,3 +85,5 @@ gem "image_processing", ">= 1.2"
85gem "meta-tags" 85gem "meta-tags"
86gem 'rails_autolink' 86gem 'rails_autolink'
87gem 'whenever', "~> 1.0.0", require: false 87gem 'whenever', "~> 1.0.0", require: false
88gem "webmention"
89gem 'microformats', '~> 4.0', '>= 4.2.1'
diff --git a/Gemfile.lock b/Gemfile.lock index a3408d7..cc60005 100644 --- a/Gemfile.lock +++ b/Gemfile.lock
@@ -137,6 +137,7 @@ GEM
137 railties (>= 4.1.0) 137 railties (>= 4.1.0)
138 responders 138 responders
139 warden (~> 1.2.3) 139 warden (~> 1.2.3)
140 domain_name (0.6.20240107)
140 drb (2.2.0) 141 drb (2.2.0)
141 ruby2_keywords 142 ruby2_keywords
142 ed25519 (1.3.0) 143 ed25519 (1.3.0)
@@ -145,6 +146,9 @@ GEM
145 erubi (1.12.0) 146 erubi (1.12.0)
146 execjs (2.9.1) 147 execjs (2.9.1)
147 ffi (1.16.3) 148 ffi (1.16.3)
149 ffi-compiler (1.3.2)
150 ffi (>= 1.15.5)
151 rake
148 globalid (1.2.1) 152 globalid (1.2.1)
149 activesupport (>= 6.1) 153 activesupport (>= 6.1)
150 haml (6.2.3) 154 haml (6.2.3)
@@ -152,11 +156,24 @@ GEM
152 thor 156 thor
153 tilt 157 tilt
154 highline (2.1.0) 158 highline (2.1.0)
159 http (5.2.0)
160 addressable (~> 2.8)
161 base64 (~> 0.1)
162 http-cookie (~> 1.0)
163 http-form_data (~> 2.2)
164 llhttp-ffi (~> 0.5.0)
165 http-cookie (1.0.8)
166 domain_name (~> 0.5)
167 http-form_data (2.3.0)
155 i18n (1.14.1) 168 i18n (1.14.1)
156 concurrent-ruby (~> 1.0) 169 concurrent-ruby (~> 1.0)
157 image_processing (1.12.2) 170 image_processing (1.12.2)
158 mini_magick (>= 4.9.5, < 5) 171 mini_magick (>= 4.9.5, < 5)
159 ruby-vips (>= 2.0.17, < 3) 172 ruby-vips (>= 2.0.17, < 3)
173 indieweb-endpoints (8.0.0)
174 http (~> 5.0)
175 link-header-parser (~> 5.0)
176 nokogiri (>= 1.13)
160 io-console (0.6.0) 177 io-console (0.6.0)
161 irb (1.8.3) 178 irb (1.8.3)
162 rdoc 179 rdoc
@@ -172,10 +189,15 @@ GEM
172 railties (>= 3.2.16) 189 railties (>= 3.2.16)
173 js-routes (2.2.7) 190 js-routes (2.2.7)
174 railties (>= 4) 191 railties (>= 4)
192 json (2.9.0)
175 libv8-node (18.16.0.0) 193 libv8-node (18.16.0.0)
194 link-header-parser (5.1.1)
176 listen (3.0.8) 195 listen (3.0.8)
177 rb-fsevent (~> 0.9, >= 0.9.4) 196 rb-fsevent (~> 0.9, >= 0.9.4)
178 rb-inotify (~> 0.9, >= 0.9.7) 197 rb-inotify (~> 0.9, >= 0.9.7)
198 llhttp-ffi (0.5.0)
199 ffi-compiler (~> 1.0)
200 rake (~> 13.0)
179 loofah (2.21.4) 201 loofah (2.21.4)
180 crass (~> 1.0.2) 202 crass (~> 1.0.2)
181 nokogiri (>= 1.12.0) 203 nokogiri (>= 1.12.0)
@@ -187,6 +209,9 @@ GEM
187 marcel (1.0.2) 209 marcel (1.0.2)
188 meta-tags (2.19.0) 210 meta-tags (2.19.0)
189 actionpack (>= 3.2.0, < 7.2) 211 actionpack (>= 3.2.0, < 7.2)
212 microformats (4.5.0)
213 json
214 nokogiri
190 mime-types (3.5.1) 215 mime-types (3.5.1)
191 mime-types-data (~> 3.2015) 216 mime-types-data (~> 3.2015)
192 mime-types-data (3.2023.1003) 217 mime-types-data (3.2023.1003)
@@ -339,6 +364,10 @@ GEM
339 activemodel (>= 6.0.0) 364 activemodel (>= 6.0.0)
340 bindex (>= 0.4.0) 365 bindex (>= 0.4.0)
341 railties (>= 6.0.0) 366 railties (>= 6.0.0)
367 webmention (7.0.0)
368 http (~> 5.0)
369 indieweb-endpoints (~> 8.0)
370 nokogiri (>= 1.13)
342 webrick (1.8.1) 371 webrick (1.8.1)
343 websocket (1.2.10) 372 websocket (1.2.10)
344 websocket-driver (0.7.6) 373 websocket-driver (0.7.6)
@@ -380,6 +409,7 @@ DEPENDENCIES
380 js-routes 409 js-routes
381 listen (>= 3.0.5, < 3.2) 410 listen (>= 3.0.5, < 3.2)
382 meta-tags 411 meta-tags
412 microformats (~> 4.0, >= 4.2.1)
383 mini_racer 413 mini_racer
384 mysql2 414 mysql2
385 normalize-rails 415 normalize-rails
@@ -398,6 +428,7 @@ DEPENDENCIES
398 turbolinks (~> 5) 428 turbolinks (~> 5)
399 tzinfo-data 429 tzinfo-data
400 web-console (>= 3.3.0) 430 web-console (>= 3.3.0)
431 webmention
401 webrick (~> 1.7) 432 webrick (~> 1.7)
402 whenever (~> 1.0.0) 433 whenever (~> 1.0.0)
403 will_paginate (~> 4.0) 434 will_paginate (~> 4.0)
diff --git a/app/controllers/blogs_controller.rb b/app/controllers/blogs_controller.rb index 033d6bb..4bbbc28 100644 --- a/app/controllers/blogs_controller.rb +++ b/app/controllers/blogs_controller.rb
@@ -1,6 +1,9 @@
1require 'microformats'
1require 'redcarpet/render_strip' 2require 'redcarpet/render_strip'
3require 'webmention'
2 4
3class BlogsController < ApplicationController 5class BlogsController < ApplicationController
6 skip_before_action :verify_authenticity_token, only: [:webmention]
4 7
5 def summary 8 def summary
6 @blogs = Blog.where(published: true).order(published_at: :desc).paginate(page: params[:page], per_page: 10) 9 @blogs = Blog.where(published: true).order(published_at: :desc).paginate(page: params[:page], per_page: 10)
@@ -89,4 +92,35 @@ class BlogsController < ApplicationController
89 end 92 end
90 end 93 end
91 94
95 def webmention
96 @blog = Blog.find_by_slug(params[:slug])
97
98 raise ActiveRecord::RecordNotFound unless @blog
99 raise ActiveRecord::RecordNotFound unless @blog.published
100
101 permalink = url_for(@blog)
102
103 target = params[:target]
104 unless target == permalink
105 render json: { error: "Incorrect target for webmention endpoint (#{target} != #{permalink})." }
106 return
107 end
108
109 source = params[:source]
110 verification = Webmention.verify_webmention(source, target)
111 unless verification.verified?
112 render json: { error: "Webmention could not be verified." }
113 return
114 end
115
116 response = Webmention::Request.get(source)
117 parsed = Microformats.parse(response.body.to_s)
118
119 if parsed.entry.properties.to_hash.include?("like-of") and parsed.entry.like_of(:all).map(&:to_s).include? permalink
120 @blog.like!(parsed.entry.author.url, parsed.entry.author.name)
121 end
122
123 head :ok
124 end
125
92end 126end
diff --git a/app/models/concerns/votable.rb b/app/models/concerns/votable.rb index ba6e6d5..40b3a2a 100644 --- a/app/models/concerns/votable.rb +++ b/app/models/concerns/votable.rb
@@ -39,5 +39,13 @@ module Votable
39 save! 39 save!
40 end 40 end
41 end 41 end
42
43 def like!(url, name)
44 return false unless votes.where(liker_url: url).empty?
45
46 votes.create(liker_url: url, liker_name: name, upvote: 1).save
47 self.upvotes += 1
48 save!
49 end
42 end 50 end
43end 51end
diff --git a/app/models/vote.rb b/app/models/vote.rb index e2d8386..fced5bd 100644 --- a/app/models/vote.rb +++ b/app/models/vote.rb
@@ -2,5 +2,4 @@ class Vote < ApplicationRecord
2 belongs_to :votable, polymorphic: true 2 belongs_to :votable, polymorphic: true
3 3
4 validates :upvote, presence: true, inclusion: { in: [0, 1] } 4 validates :upvote, presence: true, inclusion: { in: [0, 1] }
5 validates :ip, presence: true
6end 5end
diff --git a/app/views/blogs/_blog.html.haml b/app/views/blogs/_blog.html.haml index d0b81d1..878b1a2 100644 --- a/app/views/blogs/_blog.html.haml +++ b/app/views/blogs/_blog.html.haml
@@ -19,7 +19,7 @@
19 %cite.bubble.blog-cite 19 %cite.bubble.blog-cite
20 %span.p-author.h-card 20 %span.p-author.h-card
21 %strong.p-name= blog.user.login.capitalize 21 %strong.p-name= blog.user.login.capitalize
22 %data.u-url{ value: "/" } 22 %data.u-url{ value: root_url }
23 on 23 on
24 %time.dt-published{ datetime: blog.visible_date.strftime("%Y-%m-%dT%H:%M:%SZ%z") }= blog.visible_date.strftime("%B #{blog.visible_date.day.ordinalize}, %Y at %-I:%M:%S%P") 24 %time.dt-published{ datetime: blog.visible_date.strftime("%Y-%m-%dT%H:%M:%SZ%z") }= blog.visible_date.strftime("%B #{blog.visible_date.day.ordinalize}, %Y at %-I:%M:%S%P")
25 .post-vote{ id: "blog-vote-section-#{blog.id}" } 25 .post-vote{ id: "blog-vote-section-#{blog.id}" }
diff --git a/app/views/blogs/show.html.haml b/app/views/blogs/show.html.haml index 7558257..c849143 100644 --- a/app/views/blogs/show.html.haml +++ b/app/views/blogs/show.html.haml
@@ -1,4 +1,5 @@
1- title @blog.title 1- title @blog.title
2- content_for :webmention_endpoint, webmention_blog_url(@blog)
2- unless @prev.nil? 3- unless @prev.nil?
3 .back-post= link_to "← #{@prev.title}", @prev 4 .back-post= link_to "← #{@prev.title}", @prev
4- unless @next.nil? 5- unless @next.nil?
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 69cd6ba..ec37f73 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml
@@ -6,6 +6,8 @@
6 = stylesheet_link_tag 'main', media: 'all', 'data-turbolinks-track': 'reload' 6 = stylesheet_link_tag 'main', media: 'all', 'data-turbolinks-track': 'reload'
7 = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' 7 = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
8 = display_meta_tags og: { site_name: "Four Island" } 8 = display_meta_tags og: { site_name: "Four Island" }
9 - if content_for?(:webmention_endpoint)
10 %link{rel: "webmention", href: yield(:webmention_endpoint)}
9 %body#main-body 11 %body#main-body
10 - if flash[:alert] 12 - if flash[:alert]
11 %div#flash.flash-alert= flash[:alert] 13 %div#flash.flash-alert= flash[:alert]
diff --git a/config/routes.rb b/config/routes.rb index 6f7b7de..21e1517 100644 --- a/config/routes.rb +++ b/config/routes.rb
@@ -51,6 +51,7 @@ Rails.application.routes.draw do
51 member do 51 member do
52 post 'upvote' 52 post 'upvote'
53 post 'downvote' 53 post 'downvote'
54 post 'webmention'
54 55
55 resources :comments, only: [:create] 56 resources :comments, only: [:create]
56 end 57 end
diff --git a/db/migrate/20241207200746_add_like_fields_to_vote.rb b/db/migrate/20241207200746_add_like_fields_to_vote.rb new file mode 100644 index 0000000..eae2816 --- /dev/null +++ b/db/migrate/20241207200746_add_like_fields_to_vote.rb
@@ -0,0 +1,8 @@
1class AddLikeFieldsToVote < ActiveRecord::Migration[7.1]
2 def change
3 change_table :votes do |t|
4 t.string :liker_url
5 t.string :liker_name
6 end
7 end
8end
diff --git a/db/schema.rb b/db/schema.rb index 9cfdf57..b01ed43 100644 --- a/db/schema.rb +++ b/db/schema.rb
@@ -10,7 +10,7 @@
10# 10#
11# It's strongly recommended that you check this file into your version control system. 11# It's strongly recommended that you check this file into your version control system.
12 12
13ActiveRecord::Schema[7.1].define(version: 2024_03_20_145033) do 13ActiveRecord::Schema[7.1].define(version: 2024_12_07_200746) do
14 create_table "active_storage_attachments", force: :cascade do |t| 14 create_table "active_storage_attachments", force: :cascade do |t|
15 t.string "name", null: false 15 t.string "name", null: false
16 t.string "record_type", null: false 16 t.string "record_type", null: false
@@ -421,6 +421,8 @@ ActiveRecord::Schema[7.1].define(version: 2024_03_20_145033) do
421 t.string "ip" 421 t.string "ip"
422 t.datetime "created_at", null: false 422 t.datetime "created_at", null: false
423 t.datetime "updated_at", null: false 423 t.datetime "updated_at", null: false
424 t.string "liker_url"
425 t.string "liker_name"
424 t.index ["votable_type", "votable_id"], name: "index_votes_on_votable" 426 t.index ["votable_type", "votable_id"], name: "index_votes_on_votable"
425 end 427 end
426 428