diff options
Diffstat (limited to 'app')
| -rw-r--r-- | app/assets/stylesheets/main/entries.scss | 62 | ||||
| -rw-r--r-- | app/assets/stylesheets/main/layout.scss | 4 | ||||
| -rw-r--r-- | app/controllers/comments_controller.rb | 59 | ||||
| -rw-r--r-- | app/helpers/application_helper.rb | 10 | ||||
| -rw-r--r-- | app/helpers/comments_helper.rb | 5 | ||||
| -rw-r--r-- | app/models/blog.rb | 2 | ||||
| -rw-r--r-- | app/models/comment.rb | 36 | ||||
| -rw-r--r-- | app/views/blogs/_blog.html.haml | 2 | ||||
| -rw-r--r-- | app/views/blogs/show.html.haml | 5 | ||||
| -rw-r--r-- | app/views/comments/_comment.html.haml | 13 | ||||
| -rw-r--r-- | app/views/comments/_form.html.haml | 23 | ||||
| -rw-r--r-- | app/views/comments/_layout.html.haml | 6 |
12 files changed, 220 insertions, 7 deletions
| 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 @@ | |||
| 1 | class 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 | ||
| 59 | 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 | |||
| 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 | ||
| 27 | end | 33 | 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 @@ | |||
| 1 | module CommentsHelper | ||
| 2 | def show_comments(blog) | ||
| 3 | render "comments/layout", blog: blog | ||
| 4 | end | ||
| 5 | 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 | |||
| 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 @@ | |||
| 1 | class 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 | |||
| 36 | 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 @@ | |||
| 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 | ||
