adva_comments 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +2 -0
  7. data/adva_comments.gemspec +20 -0
  8. data/app/assets/javascripts/adva_comments/jquery.comments.js +41 -0
  9. data/app/assets/stylesheets/adva_comments/admin/comments.scss +34 -0
  10. data/app/assets/stylesheets/adva_comments/comments.scss +50 -0
  11. data/app/controllers/admin/comments_controller.rb +86 -0
  12. data/app/controllers/comments_controller.rb +109 -0
  13. data/app/helpers/admin/comments_helper.rb +32 -0
  14. data/app/helpers/comments_helper.rb +71 -0
  15. data/app/mailers/comment_mailer.rb +8 -0
  16. data/app/models/comment.rb +94 -0
  17. data/app/views/admin/articles/_comments_settings.html.erb +1 -0
  18. data/app/views/admin/comments/_form.html.erb +12 -0
  19. data/app/views/admin/comments/edit.html.erb +9 -0
  20. data/app/views/admin/comments/index.html.erb +43 -0
  21. data/app/views/admin/sections/_comments_settings.html.erb +7 -0
  22. data/app/views/admin/sites/_comments_settings.html.erb +7 -0
  23. data/app/views/comment_mailer/comment_notification.html.erb +12 -0
  24. data/app/views/comments/_comment.html.erb +13 -0
  25. data/app/views/comments/_form.html.erb +44 -0
  26. data/app/views/comments/_list.html.erb +8 -0
  27. data/app/views/comments/comments.atom.builder +16 -0
  28. data/app/views/comments/preview.html.erb +3 -0
  29. data/app/views/comments/show.html.erb +12 -0
  30. data/config/initializers/article.rb +13 -0
  31. data/config/initializers/content.rb +17 -0
  32. data/config/initializers/controllers.rb +25 -0
  33. data/config/initializers/menus.rb +24 -0
  34. data/config/initializers/section.rb +15 -0
  35. data/config/initializers/site.rb +11 -0
  36. data/config/routes.rb +9 -0
  37. data/db/migrate/20080401000007_create_comments_table.rb +22 -0
  38. data/db/migrate/20080721141112_add_comment_board_id.rb +9 -0
  39. data/lib/action_controller/acts_as_commentable.rb +43 -0
  40. data/lib/active_record/has_many_comments.rb +49 -0
  41. data/lib/adva_comments/version.rb +3 -0
  42. data/lib/adva_comments.rb +19 -0
  43. data/lib/format.rb +3 -0
  44. data/test/contexts.rb +29 -0
  45. data/test/functional/admin/comments_controller_test.rb +200 -0
  46. data/test/functional/comments_controller_test.rb +133 -0
  47. data/test/functional/comments_routes_test.rb +17 -0
  48. data/test/test_helper.rb +4 -0
  49. data/test/unit/helpers/admin/comments_helper_test.rb +23 -0
  50. data/test/unit/helpers/comments_helper_test.rb +147 -0
  51. data/test/unit/models/comment_test.rb +150 -0
  52. data/test/unit/models/commentable_test.rb +30 -0
  53. data/test/unit/observers/activities_comment_observer_test.rb +45 -0
  54. metadata +132 -0
@@ -0,0 +1,43 @@
1
+ <%= content_for :sidebar do %>
2
+ <!--
3
+ <div class="tabs">
4
+ <ul>
5
+ <li class="active"><a href="#filters" onclick="return false;"><%= t(:'adva.titles.filters') %></a></li>
6
+ </ul>
7
+ <div class="tab active" id="tab_filters">
8
+ <%= filter_for(Comment) %>
9
+ </div>
10
+ </div>
11
+ -->
12
+ <% end %>
13
+
14
+ <%= will_paginate @comments %>
15
+
16
+ <ul id="comments_list">
17
+ <% if @comments.present? %>
18
+ <% @comments.each do |comment, i| %>
19
+ <li class="<%= cycle 'alt', '' %>" id="<%= dom_id(comment) %>">
20
+ <h4><%= link_to comment.commentable.title, [comment.commentable.section, comment.commentable] if comment.commentable %></h4>
21
+ <div class="actions">
22
+ <%= comment.approved? ?
23
+ link_to(t(:'adva.comments.links.unapprove'), admin_comment_path(comment, "comment[approved]" => 0, :return_to => request.fullpath), :class => 'text', :method => :put) :
24
+ link_to(t(:'adva.comments.links.approve'), admin_comment_path(comment, "comment[approved]" => 1, :return_to => request.fullpath), :class => 'text', :method => :put) %>
25
+ <%= link_to_edit(comment, :url => edit_admin_comment_path(comment, :return_to => request.fullpath)) %>
26
+ <%= link_to_delete(comment, :url => admin_comment_path(comment, :return_to => request.fullpath)) %>
27
+ </div>
28
+ <cite>
29
+ <%= link_to_author(comment) %>
30
+ <%= "(#{comment.author_email})" %>
31
+ <%= t(:'adva.comments.said_time_ago', :time => time_ago_in_words(comment.created_at)) %>
32
+ </cite>
33
+ <blockquote>
34
+ <%= strip_tags(comment.body) %>
35
+ </blockquote>
36
+ </li>
37
+ <% end %>
38
+ <% else %>
39
+ <li class="alt"><%= params[:filters] ? t(:'adva.comments.no_filtered_comments') : t(:'adva.comments.no_comments') %></li>
40
+ <% end %>
41
+ </ul>
42
+
43
+ <%= will_paginate @comments %>
@@ -0,0 +1,7 @@
1
+ <h2><%= t(:'adva.titles.comments') %></h2>
2
+
3
+ <% f.field_set :comments_settings do %>
4
+ <% column do %>
5
+ <%= f.select :comment_age, comment_expiration_options, {}, :label => :'adva.comments.labels.comments' %>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <h2><%= t(:'adva.titles.comments') %></h2>
2
+
3
+ <% f.field_set :comments_settings do %>
4
+ <% column do %>
5
+ <%= f.select :comment_filter, filter_options, {}, :label => true, :hint => :'adva.comments.hints.comments_filter' %>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <p>Hello,</p>
2
+
3
+ <p>A new comment has been posted on <%= @comment.site.name %>. Please <%= link_to "log in", admin_comments_url(@comment.site, :host => @comment.site.host) %> and approve or delete it.</p>
4
+
5
+ <p>
6
+ Page: <%= @comment.commentable.title %><br />
7
+ Name: <%= @comment.author_name %><br />
8
+ Email: <%= @comment.author_email %><br />
9
+ Homepage: <%= @comment.author_homepage %><br />
10
+ </p>
11
+
12
+ <p><%= @comment.body %></p>
@@ -0,0 +1,13 @@
1
+ <li id="<%= dom_id(comment) %>" class="<%= cycle 'alt', '' %> clearing">
2
+ <div class="meta">
3
+ <p><%= link_to_author(comment) %></p>
4
+ <p><%= datetime_with_microformat(comment.created_at, :format => :long) %></p>
5
+ <% unless comment.approved? %>
6
+ <p><%= t(:'adva.comments.info.under_review') %></p>
7
+ <% end %>
8
+ </div>
9
+ <div class="comment">
10
+ <%= comment.body_html %>
11
+ <a href="#<%= dom_id(comment) %>" title="" style="display:none;">#</a>
12
+ </div>
13
+ </li>
@@ -0,0 +1,44 @@
1
+ <%# @comment ||= commentable.comments.build %>
2
+ <% if @comment %>
3
+ <% authorized_tag(:div, :create, @comment, :id => 'comment_form_wrapper') do %>
4
+ <div id="registered_author" style="display: none;">
5
+ <p><%= t(:'adva.info.greeting_logged_in') %></p>
6
+ </div>
7
+
8
+ <h2><%= @comment.new_record? ? t(:'adva.comments.titles.add_reply') : t(:'adva.comments.titles.edit') %></h2>
9
+
10
+ <%= form_for :comment, @comment, :url => (@comment.new_record? ? comments_path : comment_path(@comment)), :html => { :id => 'comment_form', :method => (@comment.new_record? ? :post : :put) } do |f| -%>
11
+ <%= invisible_captcha :homepage, :comment %>
12
+ <%= comment_form_hidden_fields commentable %>
13
+
14
+ <%= fields_for @comment.author do |author| %>
15
+ <div id="anonymous_author">
16
+ <p>
17
+ <%= author.label :name, 'Name' %>
18
+ <%= author.text_field :name %>
19
+ </p>
20
+ <p>
21
+ <%= author.label :email, 'E-Mail' %>
22
+ <%= author.email_field :email %>
23
+ </p>
24
+ <p>
25
+ <%= author.label :homepage %>
26
+ <%= author.text_field :homepage %>
27
+ </p>
28
+ </div>
29
+ <% end %>
30
+
31
+ <p>
32
+ <%= f.text_area :body %>
33
+ </p>
34
+
35
+ <p id="preview"></p>
36
+
37
+ <p>
38
+ <input type="text" name="comment[url]" style="background-color: transparent; border: 0px" />
39
+ <%= submit_tag (@comment.new_record? ? t(:'adva.comments.titles.submit_comment') : t(:'adva.comments.titles.save_comment')) %>
40
+ <%= link_to_remote_comment_preview %>
41
+ </p>
42
+ <% end %>
43
+ <% end %>
44
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <% if comments.present? %>
2
+ <div id="comments">
3
+ <h2><%= t(:'adva.comments.titles.comment_with_count', :count => comments.size) %></h2>
4
+ <ol>
5
+ <%= render :partial => 'comments/comment', :collection => comments %>
6
+ </ol>
7
+ </div>
8
+ <% end %>
@@ -0,0 +1,16 @@
1
+ atom_feed :url => request.url do |feed|
2
+ feed.title comments_feed_title(@site, @section, @commentable)
3
+ feed.updated @comments.present? ? @comments.first.created_at : Time.now
4
+
5
+ @comments.each do |comment|
6
+ url = show_url(comment.commentable, :anchor => dom_id(comment))
7
+ feed.entry comment, :url => url do |entry|
8
+ entry.title I18n.t(:'adva.comments.titles.comment_on_by', :on => comment.commentable.title, :by => comment.author_name)
9
+ entry.content comment.body_html, :type => 'html'
10
+ entry.author do |author|
11
+ author.name comment.author_name
12
+ author.email comment.author_email
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ <div id="comment_preview">
2
+ <%= @comment.body_html %>
3
+ </div>
@@ -0,0 +1,12 @@
1
+ <div id="comments">
2
+ <h2><%= t(:'adva.comments.titles.comment_number', :number => @comment.id) %></h2>
3
+ <p>
4
+ <%= t(:'adva.comments.labels.on') %>: <%= link_to_content @comment.commentable %>
5
+ </p>
6
+ <ol>
7
+ <%= render :partial => 'comments/comment', :object => @comment %>
8
+ </ol>
9
+ </div>
10
+ <% if !@comment.approved? && current_user.admin? %>
11
+ <%= render :partial => 'comments/form', :locals => { :commentable => @comment.commentable } %>
12
+ <% end %>
@@ -0,0 +1,13 @@
1
+ ActiveSupport::Reloader.to_prepare do
2
+ Article.class_eval do
3
+ def accept_comments?
4
+ published? && (comment_age > -1) && (comment_age == 0 || comments_expired_at > Time.zone.now)
5
+ end
6
+ end
7
+ end
8
+
9
+ class ArticleFormBuilder < ExtensibleFormBuilder
10
+ after(:article, :filter) do |f|
11
+ render :partial => 'admin/articles/comments_settings', :locals => { :f => f }
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ ActiveSupport::Reloader.to_prepare do
2
+ Content.class_eval do
3
+ # has_many_comments :polymorphic => true
4
+ has_many_comments :as => :commentable
5
+
6
+ delegate :comment_filter, :to => :site
7
+ delegate :accept_comments?, :to => :section
8
+
9
+ def comments_expired_at
10
+ if comment_age == -1
11
+ 9999.years.from_now
12
+ else
13
+ (published_at || Time.zone.now) + comment_age.days
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ ActiveSupport::Reloader.to_prepare do
2
+ BaseController.class_eval do
3
+ helper :comments
4
+
5
+ def comments # FIXME why isn't this in acts_as_commentable ?
6
+ @comments = @commentable.approved_comments
7
+ respond_to do |format|
8
+ format.atom { render :template => 'comments/comments', :layout => false }
9
+ end
10
+ end
11
+ end
12
+
13
+ Admin::BaseController.helper :comments, :'admin/comments'
14
+
15
+ ArticlesController.class_eval do
16
+ acts_as_commentable
17
+
18
+ private
19
+
20
+ def set_commentable
21
+ set_article if params[:permalink]
22
+ super
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ module Menus
2
+ module Admin
3
+ # will be concatenated onto existing sites menu via javascript
4
+ class Sites < Menu::Group
5
+ define do
6
+ namespace :admin
7
+ menu :left, :class => 'main' do
8
+ item :comments, :action => :index, :resource => :comment
9
+ end
10
+ end
11
+ end
12
+
13
+ class Comments < Menu::Group
14
+ define do
15
+ id :main
16
+ parent Sites.new.build(scope).find(:comments)
17
+
18
+ menu :left, :class => 'left' do
19
+ item :comments, :action => :index, :resource => :comment
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ class SectionFormBuilder < ExtensibleFormBuilder
2
+ before(:section, :submit_buttons) do |f|
3
+ unless @section.type == 'Forum'
4
+ render :partial => 'admin/sections/comments_settings', :locals => { :f => f }
5
+ end
6
+ end
7
+
8
+ ActiveSupport::Reloader.to_prepare do
9
+ Section.class_eval do
10
+ def accept_comments?
11
+ true
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ class SiteFormBuilder < ExtensibleFormBuilder
2
+ after(:site, :default_fields) do |f|
3
+ render :partial => 'admin/sites/comments_settings', :locals => { :f => f }
4
+ end
5
+ end
6
+
7
+ ActiveSupport::Reloader.to_prepare do
8
+ Site.class_eval do
9
+ has_many_comments
10
+ end
11
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Rails.application.routes.draw do
2
+ resources :comments, :except => :index do
3
+ post :preview, :on => :collection
4
+ end
5
+
6
+ namespace :admin do
7
+ resources :comments
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ class CreateCommentsTable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :comments, :force => true do |t|
4
+ t.references :site
5
+ t.references :section
6
+ # t.references :topic # TODO can we use the commentable reference as the topic reference?
7
+ t.references :commentable, :polymorphic => true
8
+ t.references :author, :polymorphic => true
9
+ t.string :author_name, :limit => 40
10
+ t.string :author_email, :limit => 40
11
+ t.string :author_homepage
12
+ t.text :body
13
+ t.text :body_html
14
+ t.integer :approved, :null => false, :default => 0
15
+ t.timestamps
16
+ end
17
+ end
18
+
19
+ def self.down
20
+ drop_table :comments
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ class AddCommentBoardId < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :comments, :board_id, :integer
4
+ end
5
+
6
+ def self.down
7
+ remove_column :comments, :board_id
8
+ end
9
+ end
@@ -0,0 +1,43 @@
1
+ module ActionController
2
+ module ActsAsCommentable
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend ActMacro
6
+ end
7
+ end
8
+
9
+ module ActMacro
10
+ def acts_as_commentable(options = {})
11
+ return if acts_as_commentable?
12
+ include InstanceMethods
13
+
14
+ before_action :set_commentable, :only => :comments
15
+ before_action :set_comment, :except => :comments
16
+ helper :comments
17
+ end
18
+
19
+ def acts_as_commentable?
20
+ included_modules.include?(ActionController::ActsAsCommentable::InstanceMethods)
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ def comments
26
+ @comments = @commentable.approved_comments
27
+ respond_to do |format|
28
+ format.atom do
29
+ render :template => 'comments/comments', :layout => false
30
+ end
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ def set_comment
37
+ @comment = Comment.new params[:comment]
38
+ @comment.author = User.anonymous
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,49 @@
1
+ module ActiveRecord
2
+ module HasManyComments
3
+ def self.included(base)
4
+ base.extend ActMacro
5
+ end
6
+
7
+ module ActMacro
8
+ def has_many_comments(options = {})
9
+ return if has_many_comments?
10
+
11
+ order_options = options.delete(:order) || [:created_at, :id]
12
+ options[:class_name] ||= 'Comment'
13
+
14
+ has_counter :comments,
15
+ :as => options[:as]
16
+
17
+ has_counter :approved_comments,
18
+ :as => options[:as],
19
+ :class_name => 'Comment',
20
+ :callbacks => {
21
+ :after_approve => :increment!,
22
+ :after_unapprove => :decrement!,
23
+ :after_destroy => lambda { |record| :decrement! if record.approved? }
24
+ }
25
+
26
+ options.delete(:as) unless options[:as] == :commentable
27
+ with_options options do |c|
28
+ c.has_many :comments, -> { order(order_options) }, dependent: :delete_all do
29
+ def by_author(author)
30
+ find_all_by_author_id_and_author_type(author.id, author.class.name)
31
+ end
32
+ end
33
+ c.has_many :approved_comments, -> { where(["comments.approved = ?", 1]).order(order_options) }
34
+ c.has_many :unapproved_comments, -> { where(["comments.approved = ?", 0]).order(order_options) }
35
+ end
36
+
37
+ include InstanceMethods
38
+ end
39
+
40
+ def has_many_comments?
41
+ included_modules.include? ActiveRecord::HasManyComments::InstanceMethods
42
+ end
43
+ end
44
+
45
+ module InstanceMethods
46
+ end
47
+ end
48
+ end
49
+ ActiveRecord::Base.send :include, ActiveRecord::HasManyComments
@@ -0,0 +1,3 @@
1
+ module AdvaComments
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,19 @@
1
+ # require "adva_comments/version"
2
+ require "rails"
3
+
4
+ require "active_record/has_many_comments"
5
+ require "action_controller/acts_as_commentable"
6
+ require "invisible_captcha"
7
+
8
+ module AdvaComments
9
+ class Engine < Rails::Engine
10
+ initializer "add assets to precompilation list" do |app|
11
+ app.config.assets.precompile += %w(adva_comments/admin/comments.css)
12
+ end
13
+
14
+ initializer "adva_comments.init" do
15
+ ActiveRecord::Base.send :include, ActiveRecord::HasManyComments
16
+ ActionController::Base.send :include, ActionController::ActsAsCommentable
17
+ end
18
+ end
19
+ end
data/lib/format.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Format
2
+ EMAIL = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
3
+ end
data/test/contexts.rb ADDED
@@ -0,0 +1,29 @@
1
+ class Test::Unit::TestCase
2
+ share :valid_comment_params do
3
+ before do
4
+ @params = { :comment => { :commentable_id => @article.id,
5
+ :commentable_type => 'Article',
6
+ :body => 'the comment body' } }
7
+ end
8
+ end
9
+
10
+ share :invalid_comment_params do
11
+ before do
12
+ @params = { :comment => { :commentable_id => @article.id,
13
+ :commentable_type => 'Article',
14
+ :body => nil } }
15
+ end
16
+ end
17
+
18
+ share :an_approved_comment do
19
+ before do
20
+ @comment = @section.approved_comments.first
21
+ end
22
+ end
23
+
24
+ share :an_unapproved_comment do
25
+ before do
26
+ @comment = @section.unapproved_comments.first
27
+ end
28
+ end
29
+ end