about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-10-12 17:10:34 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2023-10-12 17:10:34 -0400
commit835af696703484208882a70cc5dd47c5838ecf58 (patch)
tree8174d5b8c6f2486da56569bad361dd10aa1183f8
parentb24e6f58a525fc576e23ef9d498745e64c16cd6c (diff)
downloadthoughts-835af696703484208882a70cc5dd47c5838ecf58.tar.gz
thoughts-835af696703484208882a70cc5dd47c5838ecf58.tar.bz2
thoughts-835af696703484208882a70cc5dd47c5838ecf58.zip
Added blog post commenting
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/stylesheets/main/entries.scss62
-rw-r--r--app/assets/stylesheets/main/layout.scss4
-rw-r--r--app/controllers/comments_controller.rb59
-rw-r--r--app/helpers/application_helper.rb10
-rw-r--r--app/helpers/comments_helper.rb5
-rw-r--r--app/models/blog.rb2
-rw-r--r--app/models/comment.rb36
-rw-r--r--app/views/blogs/_blog.html.haml2
-rw-r--r--app/views/blogs/show.html.haml5
-rw-r--r--app/views/comments/_comment.html.haml13
-rw-r--r--app/views/comments/_form.html.haml23
-rw-r--r--app/views/comments/_layout.html.haml6
-rw-r--r--config/akismet.yml6
-rw-r--r--config/deploy.rb2
-rw-r--r--config/initializers/akismet.rb2
-rw-r--r--config/routes.rb6
-rw-r--r--db/migrate/20231012190529_create_comments.rb15
-rw-r--r--db/schema.rb16
-rw-r--r--test/controllers/comments_controller_test.rb8
-rw-r--r--test/fixtures/comments.yml17
-rw-r--r--test/models/comment_test.rb7
23 files changed, 299 insertions, 10 deletions
diff --git a/Gemfile b/Gemfile index e467375..47c1ffa 100644 --- a/Gemfile +++ b/Gemfile
@@ -79,3 +79,4 @@ gem 'lingo', git: "https://git.fourisland.com/lingo", glob: "rails/*.gemspec", b
79gem 'will_paginate', '~> 4.0' 79gem 'will_paginate', '~> 4.0'
80gem 'redcarpet' 80gem 'redcarpet'
81gem 'rouge' 81gem 'rouge'
82gem 'akismet'
diff --git a/Gemfile.lock b/Gemfile.lock index 3ad7e17..f60e3d6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock
@@ -100,6 +100,7 @@ GEM
100 public_suffix (>= 2.0.2, < 6.0) 100 public_suffix (>= 2.0.2, < 6.0)
101 airbrussh (1.5.0) 101 airbrussh (1.5.0)
102 sshkit (>= 1.6.1, != 1.7.0) 102 sshkit (>= 1.6.1, != 1.7.0)
103 akismet (3.0.0)
103 audited (5.4.0) 104 audited (5.4.0)
104 activerecord (>= 5.0, < 7.2) 105 activerecord (>= 5.0, < 7.2)
105 request_store (~> 1.2) 106 request_store (~> 1.2)
@@ -339,6 +340,7 @@ PLATFORMS
339 340
340DEPENDENCIES 341DEPENDENCIES
341 acts-as-taggable-on 342 acts-as-taggable-on
343 akismet
342 audited (~> 5.0) 344 audited (~> 5.0)
343 byebug 345 byebug
344 capistrano (~> 3.0) 346 capistrano (~> 3.0)
diff --git a/app/assets/stylesheets/main/entries.scss b/app/assets/stylesheets/main/entries.scss index 56cd221..5fbd4ef 100644 --- a/app/assets/stylesheets/main/entries.scss +++ b/app/assets/stylesheets/main/entries.scss
@@ -219,3 +219,65 @@
219 margin-left: 1.5em; 219 margin-left: 1.5em;
220 } 220 }
221} 221}
222
223.blog-comment {
224 margin: 0 1em;
225
226 .comment-avatar {
227 float: right;
228 margin-top: 1em;
229 }
230}
231
232#comment-form {
233 fieldset {
234 border: 0;
235
236 &#comment-body-field {
237 label {
238 display: none;
239 }
240
241 textarea {
242 margin-top: 1em;
243 width: 99%;
244 height: 6em;
245 resize: none;
246 }
247 }
248
249 &#comment-other-fields {
250 display: table;
251 padding: 1em;
252 width: 75%;
253 margin: 0 auto;
254
255 .comment-field {
256 display: table-row;
257
258 .comment-field-label {
259 text-align: right;
260 display: table-cell;
261 padding-right: 1em;
262 padding-bottom: 1em;
263 width: 50%;
264
265 label:after {
266 content: ":";
267 }
268 }
269
270 .field_with_errors {
271 label {
272 color: red;
273 }
274 }
275
276 .comment-field-input {
277 display: table-cell;
278 width: 50%;
279 }
280 }
281 }
282 }
283}
diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/main/layout.scss index 278113a..9283f36 100644 --- a/app/assets/stylesheets/main/layout.scss +++ b/app/assets/stylesheets/main/layout.scss
@@ -199,3 +199,7 @@ blockquote.bubble.bottom::after {
199 text-decoration: underline; 199 text-decoration: underline;
200 } 200 }
201} 201}
202
203.clear {
204 clear: both;
205}
diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb new file mode 100644 index 0000000..60c8f6a --- /dev/null +++ b/app/controllers/comments_controller.rb
@@ -0,0 +1,59 @@
1class CommentsController < ApplicationController
2 def create
3 @blog = Blog.find_by_slug(params[:slug])
4
5 raise ActiveRecord::RecordNotFound unless @blog
6 raise ActiveRecord::RecordNotFound unless @blog.published
7
8 @comment = @blog.comments.new(comment_params)
9
10 unless @comment.valid?
11 flash.alert = "Error posting comment."
12 render "blogs/show"
13 return
14 end
15
16 akismet_vars = %w{ HTTP_ACCEPT HTTP_ACCEPT_ENCODING }
17 akismet_params = {
18 type: "comment",
19 text: @comment.body,
20 created_at: DateTime.now,
21 author: @comment.username,
22 author_email: @comment.email,
23 author_url: @comment.website,
24 post_url: url_for(@comment.blog),
25 post_modified_at: @comment.blog.updated_at,
26 referrer: request.referrer,
27 env: request.env.slice(*akismet_vars)
28 }
29
30 is_spam, is_blatant = Akismet.check(request.ip, request.user_agent, akismet_params)
31
32 if is_blatant
33 # I am lying.
34 flash.notice = "Comment submitted successfully! It will need to be moderated before it shows up on the blog."
35 else
36 if is_spam
37 @comment.status = :pending
38 flash_message = "Comment submitted successfully! It will need to be moderated before it shows up on the blog."
39 else
40 @comment.status = :published
41 flash_message = "Comment posted successfully!"
42 end
43
44 if @comment.save
45 flash.notice = flash_message
46 else
47 flash.alert = "Error posting comment."
48 end
49 end
50
51 redirect_to @comment.blog
52 end
53
54 private
55
56 def comment_params
57 params.require(:comment).permit(:username, :email, :website, :body)
58 end
59end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 861dd11..14d56cf 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb
@@ -19,9 +19,15 @@ module ApplicationHelper
19 link_to title, {:sort => col, :dir => direction}, {:class => css_class} 19 link_to title, {:sort => col, :dir => direction}, {:class => css_class}
20 end 20 end
21 21
22 def markdown(text) 22 def markdown(text, params = {})
23 options = { fenced_code_blocks: true, highlight: true } 23 options = { fenced_code_blocks: true, highlight: true }
24 Redcarpet::Markdown.new(HTML.new(), options).render(text).html_safe 24
25 html_options = {}
26 if params[:restricted]
27 html_options[:filter_html] = true
28 end
29
30 Redcarpet::Markdown.new(HTML.new(html_options), options).render(text).html_safe
25 end 31 end
26 32
27end 33end
diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb new file mode 100644 index 0000000..5939adc --- /dev/null +++ b/app/helpers/comments_helper.rb
@@ -0,0 +1,5 @@
1module CommentsHelper
2 def show_comments(blog)
3 render "comments/layout", blog: blog
4 end
5end
diff --git a/app/models/blog.rb b/app/models/blog.rb index 415167c..b677e2b 100644 --- a/app/models/blog.rb +++ b/app/models/blog.rb
@@ -3,6 +3,8 @@ class Blog < ApplicationRecord
3 3
4 acts_as_taggable 4 acts_as_taggable
5 5
6 has_many :comments
7
6 validates :title, presence: true 8 validates :title, presence: true
7 validates :body, presence: true, if: :published 9 validates :body, presence: true, if: :published
8 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/, if: :published 10 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/, if: :published
diff --git a/app/models/comment.rb b/app/models/comment.rb new file mode 100644 index 0000000..9697100 --- /dev/null +++ b/app/models/comment.rb
@@ -0,0 +1,36 @@
1class Comment < ApplicationRecord
2 extend Enumerize
3
4 belongs_to :blog
5
6 validates :body, presence: true
7 validates :username, presence: true
8 validates :email, presence: true, format: URI::MailTo::EMAIL_REGEXP
9
10 scope :published_and_ordered, -> { where(status: :published).order(published_at: :asc) }
11
12 enumerize :status,
13 in: [:published, :pending, :rejected],
14 default: :published,
15 predicates: true
16
17 before_save :set_published_at
18
19 def gravatar_url
20 hash = Digest::MD5.hexdigest(email)
21 "https://www.gravatar.com/avatar/#{hash}?size=40&default=identicon&rating=g"
22 end
23
24 private
25
26 def set_published_at
27 if self.published?
28 if self.published_at.blank?
29 self.published_at = DateTime.now
30 end
31 else
32 self.published_at = nil
33 end
34 end
35
36end
diff --git a/app/views/blogs/_blog.html.haml b/app/views/blogs/_blog.html.haml index 26657e7..8972c2e 100644 --- a/app/views/blogs/_blog.html.haml +++ b/app/views/blogs/_blog.html.haml
@@ -15,4 +15,4 @@
15 %cite.bubble 15 %cite.bubble
16 %strong Hatkirby 16 %strong Hatkirby
17 on 17 on
18 = blog.published_at.strftime("%m-%d-%Y") 18 = blog.published_at.strftime("%B #{blog.published_at.day.ordinalize}, %Y at %-I:%M:%S%P")
diff --git a/app/views/blogs/show.html.haml b/app/views/blogs/show.html.haml index 9bff12b..0c549ac 100644 --- a/app/views/blogs/show.html.haml +++ b/app/views/blogs/show.html.haml
@@ -1,7 +1,4 @@
1- title @blog.title 1- title @blog.title
2.breadcrumb= link_to "← Back to home page", root_path 2.breadcrumb= link_to "← Back to home page", root_path
3= render @blog, short: false 3= render @blog, short: false
4%footer#blog-footer 4= show_comments(@blog)
5 This entry was posted on
6 = succeed "." do
7 %time= @blog.published_at.strftime("%B #{@blog.published_at.day.ordinalize}, %Y at %-I:%M:%S%P")
diff --git a/app/views/comments/_comment.html.haml b/app/views/comments/_comment.html.haml new file mode 100644 index 0000000..3e10759 --- /dev/null +++ b/app/views/comments/_comment.html.haml
@@ -0,0 +1,13 @@
1.blog-comment
2 %blockquote.bubble.rounded.bottom
3 = image_tag comment.gravatar_url, class: "comment-avatar"
4 = markdown(comment.body, { restricted: true })
5 .clear
6 %cite.bubble
7 %strong
8 - if comment.website.empty?
9 = comment.username
10 - else
11 = link_to comment.username, comment.website
12 on
13 = comment.published_at.strftime("%B #{comment.published_at.day.ordinalize}, %Y at %-I:%M:%S%P")
diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml new file mode 100644 index 0000000..f6df807 --- /dev/null +++ b/app/views/comments/_form.html.haml
@@ -0,0 +1,23 @@
1= form_for @comment || blog.comments.new, html: { id: "comment-form" } do |f|
2 %fieldset#comment-body-field
3 %blockquote.bubble.rounded.bottom
4 = f.label :body
5 = f.text_area :body
6 %cite.bubble Feel free to post a comment! You may use Markdown.
7 - if @comment and @comment.errors.any?
8 %ul#form-errors
9 - @comment.errors.full_messages.each do |msg|
10 %li= msg
11 %fieldset#comment-other-fields
12 .comment-name-field.comment-field
13 .comment-field-label= f.label :username
14 .comment-field-input= f.text_field :username
15 .comment-email-field.comment-field
16 .comment-field-label= f.label :email
17 .comment-field-input= f.text_field :email, type: :email
18 .comment-website-field.comment-field
19 .comment-field-label= f.label :website, "Website (Optional)"
20 .comment-field-input= f.text_field :website, type: :url
21 .comment-submit-button.comment-field
22 .comment-field-label
23 .comment-field-input= f.submit "Post"
diff --git a/app/views/comments/_layout.html.haml b/app/views/comments/_layout.html.haml new file mode 100644 index 0000000..9acf6b0 --- /dev/null +++ b/app/views/comments/_layout.html.haml
@@ -0,0 +1,6 @@
1%a{ name: "comments" }
2#comments
3 %h2 Comments
4 - blog.comments.published_and_ordered.each do |comment|
5 = render comment
6 = render "comments/form", blog: blog
diff --git a/config/akismet.yml b/config/akismet.yml new file mode 100644 index 0000000..581aa7b --- /dev/null +++ b/config/akismet.yml
@@ -0,0 +1,6 @@
1production:
2 api_key: ""
3 app_url: ""
4development:
5 api_key: ""
6 app_url: ""
diff --git a/config/deploy.rb b/config/deploy.rb index b6cbdf9..cbac23c 100644 --- a/config/deploy.rb +++ b/config/deploy.rb
@@ -21,7 +21,7 @@ set :deploy_to, "/srv/www/thoughts"
21# set :pty, true 21# set :pty, true
22 22
23# Default value for :linked_files is [] 23# Default value for :linked_files is []
24append :linked_files, "config/database.yml", "config/secrets.yml", "config/lingo.yml" 24append :linked_files, "config/database.yml", "config/secrets.yml", "config/lingo.yml", "config/akismet.yml"
25 25
26# Default value for linked_dirs is [] 26# Default value for linked_dirs is []
27append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/uploads" 27append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/uploads"
diff --git a/config/initializers/akismet.rb b/config/initializers/akismet.rb new file mode 100644 index 0000000..325e48f --- /dev/null +++ b/config/initializers/akismet.rb
@@ -0,0 +1,2 @@
1Akismet.api_key = Rails.application.config_for(:akismet)[:api_key]
2Akismet.app_url = Rails.application.config_for(:akismet)[:app_url]
diff --git a/config/routes.rb b/config/routes.rb index 580cce4..2bae007 100644 --- a/config/routes.rb +++ b/config/routes.rb
@@ -24,7 +24,11 @@ Rails.application.routes.draw do
24 24
25 root "blogs#summary" 25 root "blogs#summary"
26 26
27 resources :blogs, only: [:index, :show], param: :slug, path: "blog" 27 resources :blogs, only: [:index, :show], param: :slug, path: "blog" do
28 member do
29 resources :comments, only: [:create]
30 end
31 end
28 32
29 get 'thinks/:slug', to: 'streams#show', as: :stream 33 get 'thinks/:slug', to: 'streams#show', as: :stream
30 34
diff --git a/db/migrate/20231012190529_create_comments.rb b/db/migrate/20231012190529_create_comments.rb new file mode 100644 index 0000000..3073dd5 --- /dev/null +++ b/db/migrate/20231012190529_create_comments.rb
@@ -0,0 +1,15 @@
1class CreateComments < ActiveRecord::Migration[7.0]
2 def change
3 create_table :comments do |t|
4 t.references :blog, null: false, foreign_key: true
5 t.string :username
6 t.string :email
7 t.string :website
8 t.text :body
9 t.string :status
10 t.datetime :published_at
11
12 t.timestamps
13 end
14 end
15end
diff --git a/db/schema.rb b/db/schema.rb index 5b61408..2a9b1df 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.0].define(version: 2022_12_10_175109) do 13ActiveRecord::Schema[7.0].define(version: 2023_10_12_190529) do
14 create_table "audits", force: :cascade do |t| 14 create_table "audits", force: :cascade do |t|
15 t.integer "auditable_id" 15 t.integer "auditable_id"
16 t.string "auditable_type" 16 t.string "auditable_type"
@@ -56,6 +56,19 @@ ActiveRecord::Schema[7.0].define(version: 2022_12_10_175109) do
56 t.index ["type"], name: "index_ckeditor_assets_on_type" 56 t.index ["type"], name: "index_ckeditor_assets_on_type"
57 end 57 end
58 58
59 create_table "comments", force: :cascade do |t|
60 t.integer "blog_id", null: false
61 t.string "username"
62 t.string "email"
63 t.string "website"
64 t.text "body"
65 t.string "status"
66 t.datetime "published_at"
67 t.datetime "created_at", null: false
68 t.datetime "updated_at", null: false
69 t.index ["blog_id"], name: "index_comments_on_blog_id"
70 end
71
59 create_table "games", force: :cascade do |t| 72 create_table "games", force: :cascade do |t|
60 t.string "title" 73 t.string "title"
61 t.string "status" 74 t.string "status"
@@ -345,6 +358,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_12_10_175109) do
345 t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 358 t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
346 end 359 end
347 360
361 add_foreign_key "comments", "blogs"
348 add_foreign_key "pokeviewer_items", "pokeviewer_moves", column: "move_id" 362 add_foreign_key "pokeviewer_items", "pokeviewer_moves", column: "move_id"
349 add_foreign_key "pokeviewer_pokedex_entries", "pokeviewer_species", column: "species_id" 363 add_foreign_key "pokeviewer_pokedex_entries", "pokeviewer_species", column: "species_id"
350 add_foreign_key "pokeviewer_pokedex_entries", "pokeviewer_trainers", column: "trainer_id" 364 add_foreign_key "pokeviewer_pokedex_entries", "pokeviewer_trainers", column: "trainer_id"
diff --git a/test/controllers/comments_controller_test.rb b/test/controllers/comments_controller_test.rb new file mode 100644 index 0000000..3bcb0dc --- /dev/null +++ b/test/controllers/comments_controller_test.rb
@@ -0,0 +1,8 @@
1require "test_helper"
2
3class CommentsControllerTest < ActionDispatch::IntegrationTest
4 test "should get create" do
5 get comments_create_url
6 assert_response :success
7 end
8end
diff --git a/test/fixtures/comments.yml b/test/fixtures/comments.yml new file mode 100644 index 0000000..1d6475a --- /dev/null +++ b/test/fixtures/comments.yml
@@ -0,0 +1,17 @@
1# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
3one:
4 blog: one
5 username: MyString
6 email: MyString
7 website: MyString
8 body: MyText
9 status: MyString
10
11two:
12 blog: two
13 username: MyString
14 email: MyString
15 website: MyString
16 body: MyText
17 status: MyString
diff --git a/test/models/comment_test.rb b/test/models/comment_test.rb new file mode 100644 index 0000000..5a6feda --- /dev/null +++ b/test/models/comment_test.rb
@@ -0,0 +1,7 @@
1require "test_helper"
2
3class CommentTest < ActiveSupport::TestCase
4 # test "the truth" do
5 # assert true
6 # end
7end