From 835af696703484208882a70cc5dd47c5838ecf58 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 12 Oct 2023 17:10:34 -0400 Subject: Added blog post commenting --- Gemfile | 1 + Gemfile.lock | 2 + app/assets/stylesheets/main/entries.scss | 62 ++++++++++++++++++++++++++++ app/assets/stylesheets/main/layout.scss | 4 ++ app/controllers/comments_controller.rb | 59 ++++++++++++++++++++++++++ app/helpers/application_helper.rb | 10 ++++- app/helpers/comments_helper.rb | 5 +++ app/models/blog.rb | 2 + app/models/comment.rb | 36 ++++++++++++++++ app/views/blogs/_blog.html.haml | 2 +- app/views/blogs/show.html.haml | 5 +-- app/views/comments/_comment.html.haml | 13 ++++++ app/views/comments/_form.html.haml | 23 +++++++++++ app/views/comments/_layout.html.haml | 6 +++ config/akismet.yml | 6 +++ config/deploy.rb | 2 +- config/initializers/akismet.rb | 2 + config/routes.rb | 6 ++- db/migrate/20231012190529_create_comments.rb | 15 +++++++ db/schema.rb | 16 ++++++- test/controllers/comments_controller_test.rb | 8 ++++ test/fixtures/comments.yml | 17 ++++++++ test/models/comment_test.rb | 7 ++++ 23 files changed, 299 insertions(+), 10 deletions(-) create mode 100644 app/controllers/comments_controller.rb create mode 100644 app/helpers/comments_helper.rb create mode 100644 app/models/comment.rb create mode 100644 app/views/comments/_comment.html.haml create mode 100644 app/views/comments/_form.html.haml create mode 100644 app/views/comments/_layout.html.haml create mode 100644 config/akismet.yml create mode 100644 config/initializers/akismet.rb create mode 100644 db/migrate/20231012190529_create_comments.rb create mode 100644 test/controllers/comments_controller_test.rb create mode 100644 test/fixtures/comments.yml create mode 100644 test/models/comment_test.rb 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 gem 'will_paginate', '~> 4.0' gem 'redcarpet' gem 'rouge' +gem 'akismet' diff --git a/Gemfile.lock b/Gemfile.lock index 3ad7e17..f60e3d6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,6 +100,7 @@ GEM public_suffix (>= 2.0.2, < 6.0) airbrussh (1.5.0) sshkit (>= 1.6.1, != 1.7.0) + akismet (3.0.0) audited (5.4.0) activerecord (>= 5.0, < 7.2) request_store (~> 1.2) @@ -339,6 +340,7 @@ PLATFORMS DEPENDENCIES acts-as-taggable-on + akismet audited (~> 5.0) byebug 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 @@ margin-left: 1.5em; } } + +.blog-comment { + margin: 0 1em; + + .comment-avatar { + float: right; + margin-top: 1em; + } +} + +#comment-form { + fieldset { + border: 0; + + &#comment-body-field { + label { + display: none; + } + + textarea { + margin-top: 1em; + width: 99%; + height: 6em; + resize: none; + } + } + + &#comment-other-fields { + display: table; + padding: 1em; + width: 75%; + margin: 0 auto; + + .comment-field { + display: table-row; + + .comment-field-label { + text-align: right; + display: table-cell; + padding-right: 1em; + padding-bottom: 1em; + width: 50%; + + label:after { + content: ":"; + } + } + + .field_with_errors { + label { + color: red; + } + } + + .comment-field-input { + display: table-cell; + width: 50%; + } + } + } + } +} 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 { text-decoration: underline; } } + +.clear { + clear: both; +} 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 @@ +class CommentsController < ApplicationController + def create + @blog = Blog.find_by_slug(params[:slug]) + + raise ActiveRecord::RecordNotFound unless @blog + raise ActiveRecord::RecordNotFound unless @blog.published + + @comment = @blog.comments.new(comment_params) + + unless @comment.valid? + flash.alert = "Error posting comment." + render "blogs/show" + return + end + + akismet_vars = %w{ HTTP_ACCEPT HTTP_ACCEPT_ENCODING } + akismet_params = { + type: "comment", + text: @comment.body, + created_at: DateTime.now, + author: @comment.username, + author_email: @comment.email, + author_url: @comment.website, + post_url: url_for(@comment.blog), + post_modified_at: @comment.blog.updated_at, + referrer: request.referrer, + env: request.env.slice(*akismet_vars) + } + + is_spam, is_blatant = Akismet.check(request.ip, request.user_agent, akismet_params) + + if is_blatant + # I am lying. + flash.notice = "Comment submitted successfully! It will need to be moderated before it shows up on the blog." + else + if is_spam + @comment.status = :pending + flash_message = "Comment submitted successfully! It will need to be moderated before it shows up on the blog." + else + @comment.status = :published + flash_message = "Comment posted successfully!" + end + + if @comment.save + flash.notice = flash_message + else + flash.alert = "Error posting comment." + end + end + + redirect_to @comment.blog + end + + private + + def comment_params + params.require(:comment).permit(:username, :email, :website, :body) + end +end 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 link_to title, {:sort => col, :dir => direction}, {:class => css_class} end - def markdown(text) + def markdown(text, params = {}) options = { fenced_code_blocks: true, highlight: true } - Redcarpet::Markdown.new(HTML.new(), options).render(text).html_safe + + html_options = {} + if params[:restricted] + html_options[:filter_html] = true + end + + Redcarpet::Markdown.new(HTML.new(html_options), options).render(text).html_safe end end 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 @@ +module CommentsHelper + def show_comments(blog) + render "comments/layout", blog: blog + end +end 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 acts_as_taggable + has_many :comments + validates :title, presence: true validates :body, presence: true, if: :published 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 @@ +class Comment < ApplicationRecord + extend Enumerize + + belongs_to :blog + + validates :body, presence: true + validates :username, presence: true + validates :email, presence: true, format: URI::MailTo::EMAIL_REGEXP + + scope :published_and_ordered, -> { where(status: :published).order(published_at: :asc) } + + enumerize :status, + in: [:published, :pending, :rejected], + default: :published, + predicates: true + + before_save :set_published_at + + def gravatar_url + hash = Digest::MD5.hexdigest(email) + "https://www.gravatar.com/avatar/#{hash}?size=40&default=identicon&rating=g" + end + + private + + def set_published_at + if self.published? + if self.published_at.blank? + self.published_at = DateTime.now + end + else + self.published_at = nil + end + end + +end 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 @@ %cite.bubble %strong Hatkirby on - = blog.published_at.strftime("%m-%d-%Y") + = 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 @@ - title @blog.title .breadcrumb= link_to "← Back to home page", root_path = render @blog, short: false -%footer#blog-footer - This entry was posted on - = succeed "." do - %time= @blog.published_at.strftime("%B #{@blog.published_at.day.ordinalize}, %Y at %-I:%M:%S%P") += show_comments(@blog) 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 @@ +.blog-comment + %blockquote.bubble.rounded.bottom + = image_tag comment.gravatar_url, class: "comment-avatar" + = markdown(comment.body, { restricted: true }) + .clear + %cite.bubble + %strong + - if comment.website.empty? + = comment.username + - else + = link_to comment.username, comment.website + on + = 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 @@ += form_for @comment || blog.comments.new, html: { id: "comment-form" } do |f| + %fieldset#comment-body-field + %blockquote.bubble.rounded.bottom + = f.label :body + = f.text_area :body + %cite.bubble Feel free to post a comment! You may use Markdown. + - if @comment and @comment.errors.any? + %ul#form-errors + - @comment.errors.full_messages.each do |msg| + %li= msg + %fieldset#comment-other-fields + .comment-name-field.comment-field + .comment-field-label= f.label :username + .comment-field-input= f.text_field :username + .comment-email-field.comment-field + .comment-field-label= f.label :email + .comment-field-input= f.text_field :email, type: :email + .comment-website-field.comment-field + .comment-field-label= f.label :website, "Website (Optional)" + .comment-field-input= f.text_field :website, type: :url + .comment-submit-button.comment-field + .comment-field-label + .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 @@ +%a{ name: "comments" } +#comments + %h2 Comments + - blog.comments.published_and_ordered.each do |comment| + = render comment + = 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 @@ +production: + api_key: "" + app_url: "" +development: + api_key: "" + 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" # set :pty, true # Default value for :linked_files is [] -append :linked_files, "config/database.yml", "config/secrets.yml", "config/lingo.yml" +append :linked_files, "config/database.yml", "config/secrets.yml", "config/lingo.yml", "config/akismet.yml" # Default value for linked_dirs is [] append :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 @@ +Akismet.api_key = Rails.application.config_for(:akismet)[:api_key] +Akismet.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 root "blogs#summary" - resources :blogs, only: [:index, :show], param: :slug, path: "blog" + resources :blogs, only: [:index, :show], param: :slug, path: "blog" do + member do + resources :comments, only: [:create] + end + end get 'thinks/:slug', to: 'streams#show', as: :stream 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 @@ +class CreateComments < ActiveRecord::Migration[7.0] + def change + create_table :comments do |t| + t.references :blog, null: false, foreign_key: true + t.string :username + t.string :email + t.string :website + t.text :body + t.string :status + t.datetime :published_at + + t.timestamps + end + end +end 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 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_12_10_175109) do +ActiveRecord::Schema[7.0].define(version: 2023_10_12_190529) do create_table "audits", force: :cascade do |t| t.integer "auditable_id" t.string "auditable_type" @@ -56,6 +56,19 @@ ActiveRecord::Schema[7.0].define(version: 2022_12_10_175109) do t.index ["type"], name: "index_ckeditor_assets_on_type" end + create_table "comments", force: :cascade do |t| + t.integer "blog_id", null: false + t.string "username" + t.string "email" + t.string "website" + t.text "body" + t.string "status" + t.datetime "published_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["blog_id"], name: "index_comments_on_blog_id" + end + create_table "games", force: :cascade do |t| t.string "title" t.string "status" @@ -345,6 +358,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_12_10_175109) do t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + add_foreign_key "comments", "blogs" add_foreign_key "pokeviewer_items", "pokeviewer_moves", column: "move_id" add_foreign_key "pokeviewer_pokedex_entries", "pokeviewer_species", column: "species_id" 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 @@ +require "test_helper" + +class CommentsControllerTest < ActionDispatch::IntegrationTest + test "should get create" do + get comments_create_url + assert_response :success + end +end 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 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + blog: one + username: MyString + email: MyString + website: MyString + body: MyText + status: MyString + +two: + blog: two + username: MyString + email: MyString + website: MyString + body: MyText + 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 @@ +require "test_helper" + +class CommentTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end -- cgit 1.4.1