the_comments 0.9.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 (43) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +248 -0
  5. data/Rakefile +1 -0
  6. data/app/assets/javascripts/the_comments.js.coffee +104 -0
  7. data/app/assets/javascripts/the_comments_manage.js.coffee +59 -0
  8. data/app/assets/stylesheets/the_comments.css.scss +251 -0
  9. data/app/controllers/concerns/the_comments_controller.rb +227 -0
  10. data/app/controllers/concerns/the_comments_ip_controller.rb +17 -0
  11. data/app/controllers/concerns/the_comments_user_agent_controller.rb +17 -0
  12. data/app/helpers/render_comments_tree_helper.rb +113 -0
  13. data/app/models/concerns/the_comments_base.rb +64 -0
  14. data/app/models/concerns/the_comments_black_ip.rb +10 -0
  15. data/app/models/concerns/the_comments_black_user_agent.rb +10 -0
  16. data/app/models/concerns/the_comments_commentable.rb +50 -0
  17. data/app/models/concerns/the_comments_states.rb +64 -0
  18. data/app/models/concerns/the_comments_user.rb +25 -0
  19. data/app/views/ip_black_lists/index.html.haml +17 -0
  20. data/app/views/the_comments/_comment.html.haml +1 -0
  21. data/app/views/the_comments/_comment_body.html.haml +30 -0
  22. data/app/views/the_comments/_form.html.haml +25 -0
  23. data/app/views/the_comments/_manage_controls.html.haml +4 -0
  24. data/app/views/the_comments/_tree.html.haml +4 -0
  25. data/app/views/the_comments/index.html.haml +19 -0
  26. data/app/views/the_comments/manage.html.haml +29 -0
  27. data/app/views/user_agent_black_lists/index.html.haml +17 -0
  28. data/config/locales/en.yml +43 -0
  29. data/config/routes.rb +24 -0
  30. data/db/migrate/20130101010101_create_comments.rb +81 -0
  31. data/lib/generators/the_comments/USAGE +29 -0
  32. data/lib/generators/the_comments/templates/comments_controller.rb +25 -0
  33. data/lib/generators/the_comments/templates/ip_black_lists_controller.rb +10 -0
  34. data/lib/generators/the_comments/templates/the_comments.rb +7 -0
  35. data/lib/generators/the_comments/templates/user_agent_black_lists_controller.rb +10 -0
  36. data/lib/generators/the_comments/the_comments_generator.rb +32 -0
  37. data/lib/generators/the_comments/views_generator.rb +41 -0
  38. data/lib/the_comments/config.rb +21 -0
  39. data/lib/the_comments/version.rb +3 -0
  40. data/lib/the_comments.rb +10 -0
  41. data/the_comments.gemspec +23 -0
  42. data/the_comments.jpg +0 -0
  43. metadata +120 -0
@@ -0,0 +1,227 @@
1
+ module TheCommentsController
2
+ COMMENTS_COOKIES_TOKEN = 'JustTheCommentsCookies'
3
+
4
+ # View token for Commentable controller
5
+ #
6
+ # class PagesController < ApplicationController
7
+ # include TheCommentsController::ViewToken
8
+ # end
9
+ module ViewToken
10
+ def comments_view_token
11
+ cookies[:comments_view_token] = { :value => SecureRandom.hex, :expires => 7.days.from_now } unless cookies[:comments_view_token]
12
+ cookies[:comments_view_token]
13
+ end
14
+ end
15
+
16
+ # Cookies for spam protection
17
+ #
18
+ # class ApplicationController < ActionController::Base
19
+ # include TheCommentsController::Cookies
20
+ # end
21
+ module Cookies
22
+ extend ActiveSupport::Concern
23
+ included { before_action :set_the_comments_cookies }
24
+
25
+ private
26
+
27
+ def set_the_comments_cookies
28
+ cookies[:the_comment_cookies] = { :value => TheCommentsController::COMMENTS_COOKIES_TOKEN, :expires => 1.year.from_now }
29
+ end
30
+ end
31
+
32
+ # Base functionality of Comments Controller
33
+ #
34
+ # class CommentsController < ApplicationController
35
+ # include TheCommentsController::Base
36
+ # end
37
+ module Base
38
+ extend ActiveSupport::Concern
39
+
40
+ included do
41
+ include TheCommentsController::ViewToken
42
+
43
+ before_action -> { @errors = [] }
44
+
45
+ # Attention! We should not set TheComments cookie before create
46
+ skip_before_action :set_the_comments_cookies, only: [:create]
47
+
48
+ # Black lists
49
+ before_action :stop_black_ip, only: [:create]
50
+ before_action :stop_black_user_agent, only: [:create]
51
+
52
+ # Spam protection
53
+ before_action :ajax_requests_required, only: [:create]
54
+ before_action :cookies_required, only: [:create]
55
+ before_action :empty_trap_required, only: [:create]
56
+ before_action :tolerance_time_required, only: [:create]
57
+
58
+ # preparation
59
+ before_action :define_commentable, only: [:create]
60
+ end
61
+
62
+ # App side methods (overwrite it)
63
+ def index
64
+ @comments = Comment.with_state(:published).order('created_at DESC').page(params[:page])
65
+ render template: 'the_comments/index'
66
+ end
67
+
68
+ # Methods based on *current_user* helper
69
+ def edit
70
+ @comments = current_user.comcoms.where(id: params[:id]).page(params[:page])
71
+ render template: 'the_comments/manage'
72
+ end
73
+
74
+ def my
75
+ @comments = current_user.comments.with_state(:draft, :published).order('created_at DESC').page(params[:page])
76
+ render template: 'the_comments/index'
77
+ end
78
+
79
+ def incoming
80
+ @comments = current_user.comcoms.with_state(:draft, :published).order('created_at DESC').page(params[:page])
81
+ render template: 'the_comments/manage'
82
+ end
83
+
84
+ def trash
85
+ @comments = current_user.comcoms.with_state(:deleted).order('created_at DESC').page(params[:page])
86
+ render template: 'the_comments/manage'
87
+ end
88
+
89
+ # Base methods
90
+
91
+ def update
92
+ comment = Comment.where(id: params[:id]).first
93
+ comment.update_attributes!(patch_comment_params)
94
+ render(layout: false, partial: 'the_comments/comment_body', locals: { comment: comment })
95
+ end
96
+
97
+ def create
98
+ @comment = @commentable.comments.new comment_params
99
+ if @comment.valid?
100
+ @comment.save
101
+ return render layout: false, partial: 'the_comments/comment', locals: { tree: @comment }
102
+ end
103
+ render json: { errors: @comment.errors.full_messages }
104
+ end
105
+
106
+ def to_published
107
+ Comment.where(id: params[:id]).first.to_published
108
+ render nothing: :true
109
+ end
110
+
111
+ def to_draft
112
+ Comment.where(id: params[:id]).first.to_draft
113
+ render nothing: :true
114
+ end
115
+
116
+ def to_spam
117
+ comment = Comment.where(id: params[:id]).first
118
+ IpBlackList.where(ip: comment.ip).first_or_create.increment!(:count)
119
+ UserAgentBlackList.where(user_agent: comment.user_agent).first_or_create.increment!(:count)
120
+ comment.to_deleted
121
+ render nothing: :true
122
+ end
123
+
124
+ def to_trash
125
+ Comment.where(id: params[:id]).first.to_deleted
126
+ render nothing: :true
127
+ end
128
+
129
+ private
130
+
131
+ def denormalized_fields
132
+ title = @commentable.commentable_title
133
+ url = @commentable.commentable_url
134
+ @commentable ? { commentable_title: title, commentable_url: url } : {}
135
+ end
136
+
137
+ def request_data_for_black_lists
138
+ r = request
139
+ { ip: r.ip, referer: CGI::unescape(r.referer || 'direct_visit'), user_agent: r.user_agent }
140
+ end
141
+
142
+ def define_commentable
143
+ commentable_klass = params[:comment][:commentable_type].constantize
144
+ commentable_id = params[:comment][:commentable_id]
145
+
146
+ @commentable = commentable_klass.where(id: commentable_id).first
147
+ return render(json: { errors: ['Commentable object is undefined'] }) unless @commentable
148
+ end
149
+
150
+ def comment_params
151
+ params
152
+ .require(:comment)
153
+ .permit(:title, :contacts, :raw_content, :parent_id)
154
+ .merge(user: current_user, view_token: comments_view_token)
155
+ .merge(denormalized_fields)
156
+ .merge( tolerance_time: params[:tolerance_time].to_i )
157
+ .merge(request_data_for_black_lists)
158
+ end
159
+
160
+ def patch_comment_params
161
+ params
162
+ .require(:comment)
163
+ .permit(:title, :contacts, :raw_content, :parent_id)
164
+ end
165
+
166
+ # Protection tricks
167
+ def cookies_required
168
+ unless cookies[:the_comment_cookies] == TheCommentsController::COMMENTS_COOKIES_TOKEN
169
+ @errors << [t('the_comments.cookies'), t('the_comments.cookies_required')].join(' ')
170
+ return render(json: { errors: @errors })
171
+ end
172
+ end
173
+
174
+ def ajax_requests_required
175
+ unless request.xhr?
176
+ IpBlackList.where(ip: request.ip).first_or_create.increment!(:count)
177
+ UserAgentBlackList.where(user_agent: request.user_agent).first_or_create.increment!(:count)
178
+ return render(text: t('the_comments.ajax_requests_required'))
179
+ end
180
+ end
181
+
182
+ def empty_trap_required
183
+ # TODO: 1) inject ?, 2) fields can be removed on client site
184
+ is_user = true
185
+ params.slice(*TheComments.config.empty_inputs).values.each{|v| is_user = is_user && v.blank? }
186
+
187
+ unless is_user
188
+ IpBlackList.where(ip: request.ip).first_or_create.increment!(:count)
189
+ UserAgentBlackList.where(user_agent: request.user_agent).first_or_create.increment!(:count)
190
+ @errors << [t('the_comments.trap'), t('the_comments.trap_message')].join(' ')
191
+ return render(json: { errors: @errors })
192
+ end
193
+ end
194
+
195
+ def tolerance_time_required
196
+ min_time = TheComments.config.tolerance_time
197
+ this_time = params[:tolerance_time].to_i
198
+ if this_time < min_time
199
+ @errors << [t('the_comments.tolerance_time'), t('the_comments.tolerance_time_message', time: min_time - this_time )].join(' ')
200
+ return render(json: { errors: @errors })
201
+ end
202
+ end
203
+
204
+ def stop_black_ip
205
+ if IpBlackList.where(ip: request.ip, state: :banned).first
206
+ @errors << [t('the_comments.black_ip'), t('the_comments.black_ip_message')].join(' ')
207
+ return render(json: { errors: @errors })
208
+ end
209
+ end
210
+
211
+ def stop_black_user_agent
212
+ if UserAgentBlackList.where(user_agent: request.user_agent, state: :banned).first
213
+ @errors << [t('the_comments.black_user_agent'), t('the_comments.black_user_agent_message')].join(' ')
214
+ return render(json: { errors: @errors })
215
+ end
216
+ end
217
+
218
+ def time_tolerance_required
219
+ if params[:time_tolerance].to_i < TheComments.config.tolerance_time
220
+ errors = {}
221
+ errors[t('the_comments.time_tolerance')] = [t('the_comments.time_tolerance_message')]
222
+ return render(json: { errors: errors })
223
+ end
224
+ end
225
+
226
+ end#Base
227
+ end
@@ -0,0 +1,17 @@
1
+ module TheCommentsIpController
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ def index
6
+ @ip_black_lists = if params[:ip]
7
+ IpBlackList.where(ip: params[:ip])
8
+ else
9
+ IpBlackList.order('count DESC').page(params[:page])
10
+ end
11
+ end
12
+
13
+ def to_state
14
+ return render text: IpBlackList.where(id: params[:ip_black_list_id]).first.update!(state: params[:state])
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module TheCommentsUserAgentController
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ def index
6
+ @ip_black_lists = if params[:ip]
7
+ UserAgentBlackList.where(ip: params[:ip])
8
+ else
9
+ UserAgentBlackList.order('count DESC').page(params[:page])
10
+ end
11
+ end
12
+
13
+ def to_state
14
+ return render text: UserAgentBlackList.where(id: params[:user_agent_black_list_id]).first.update!(state: params[:state])
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,113 @@
1
+ # coding: UTF-8
2
+ # DOC:
3
+ # We use Helper Methods for tree building,
4
+ # because it's faster than View Templates and Partials
5
+
6
+ # SECURITY note
7
+ # Prepare your data on server side for rendering
8
+ # or use h.html_escape(node.content)
9
+ # for escape potentially dangerous content
10
+ module RenderCommentsTreeHelper
11
+ module Render
12
+ class << self
13
+ attr_accessor :h, :options
14
+
15
+ # Main Helpers
16
+ def controller
17
+ @options[:controller]
18
+ end
19
+
20
+ def t str
21
+ controller.t str
22
+ end
23
+
24
+ # Render Helpers
25
+ def visible_draft?
26
+ controller.try(:comments_view_token) == @comment.view_token
27
+ end
28
+
29
+ def moderator?
30
+ controller.try(:current_user).try(:comment_moderator?, @comment)
31
+ end
32
+
33
+ # Render Methods
34
+ def render_node(h, options)
35
+ @h, @options = h, options
36
+ @comment = options[:node]
37
+
38
+ @max_reply_depth = options[:max_reply_depth] || TheComments.config.max_reply_depth
39
+
40
+ if @comment.draft?
41
+ draft_comment
42
+ elsif @comment.published?
43
+ published_comment
44
+ else
45
+ deleted_comment
46
+ end
47
+ end
48
+
49
+ def draft_comment
50
+ if visible_draft? || moderator?
51
+ published_comment
52
+ else
53
+ "<li class='draft'>
54
+ <div class='comment draft' id='comment_#{@comment.anchor}'>
55
+ #{ t('the_comments.waiting_for_moderation') }
56
+ #{ h.link_to '#', '#comment_' + @comment.anchor }
57
+ </div>
58
+ #{ children }
59
+ </li>"
60
+ end
61
+ end
62
+
63
+ def published_comment
64
+ "<li>
65
+ <div id='comment_#{@comment.anchor}' class='comment #{@comment.state}' data-comment-id='#{@comment.to_param}'>
66
+ <div>
67
+ #{ avatar }
68
+ #{ userbar }
69
+ <div class='cbody'>#{ @comment.content }</div>
70
+ #{ reply }
71
+ </div>
72
+ </div>
73
+
74
+ <div class='form_holder'></div>
75
+ #{ children }
76
+ </li>"
77
+ end
78
+
79
+ def avatar
80
+ "<div class='userpic'>
81
+ <img src='#{ @comment.avatar_url }' alt='userpic' />
82
+ #{ controls }
83
+ </div>"
84
+ end
85
+
86
+ def userbar
87
+ anchor = h.link_to('#', '#comment_' + @comment.anchor)
88
+ title = @comment.title.blank? ? t('the_comments.guest_name') : @comment.title
89
+ "<div class='userbar'>#{ title } #{ anchor }</div>"
90
+ end
91
+
92
+ def moderator_controls
93
+ if moderator?
94
+ h.link_to t('the_comments.edit'), h.edit_comment_url(@comment), class: :edit
95
+ end
96
+ end
97
+
98
+ def reply
99
+ if @comment.depth < @max_reply_depth
100
+ "<p class='reply'><a href='#' class='reply_link'>#{ t('the_comments.reply') }</a>"
101
+ end
102
+ end
103
+
104
+ def controls
105
+ "<div class='controls'>#{ moderator_controls }</div>"
106
+ end
107
+
108
+ def children
109
+ "<ol class='nested_set'>#{ options[:children] }</ol>"
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,64 @@
1
+ module TheCommentsBase
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # Nested Set
6
+ attr_accessible :parent_id
7
+ acts_as_nested_set scope: [:commentable_type, :commentable_id]
8
+
9
+ # Comments State Machine
10
+ include TheCommentsStates
11
+
12
+ # TheSortableTree
13
+ include TheSortableTree::Scopes
14
+
15
+ attr_accessible :user, :title, :contacts, :raw_content, :view_token, :state
16
+ attr_accessible :ip, :referer, :user_agent, :tolerance_time
17
+
18
+ validates :raw_content, presence: true
19
+
20
+ # relations
21
+ belongs_to :user
22
+ belongs_to :holder, class_name: :User
23
+ belongs_to :commentable, polymorphic: true
24
+
25
+ # callbacks
26
+ before_create :define_holder, :define_anchor, :denormalize_commentable
27
+ after_create :update_cache_counters
28
+ before_save :prepare_content
29
+
30
+ def avatar_url
31
+ src = id.to_s
32
+ src = title unless title.blank?
33
+ src = contacts if !contacts.blank? && /@/ =~ contacts
34
+ hash = Digest::MD5.hexdigest(src)
35
+ "http://www.gravatar.com/avatar/#{hash}?s=40&d=identicon"
36
+ end
37
+
38
+ private
39
+
40
+ def define_anchor
41
+ self.anchor = SecureRandom.hex[0..5]
42
+ end
43
+
44
+ def define_holder
45
+ self.holder = self.commentable.user
46
+ end
47
+
48
+ def denormalize_commentable
49
+ self.commentable_title = self.commentable.try :commentable_title
50
+ self.commentable_url = self.commentable.try :commentable_url
51
+ self.commentable_state = self.commentable.try :state
52
+ end
53
+
54
+ def prepare_content
55
+ self.content = self.raw_content
56
+ end
57
+
58
+ def update_cache_counters
59
+ self.user.try :increment!, :draft_comments_count
60
+ self.holder.try :increment!, :draft_comcoms_count
61
+ self.commentable.try :increment!, :draft_comments_count
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,10 @@
1
+ module TheCommentsBlackIp
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ attr_accessible :ip, :state
6
+
7
+ validates :ip, presence: true
8
+ validates :ip, uniqueness: true
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module TheCommentsBlackUserAgent
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ attr_accessible :ip, :state
6
+
7
+ validates :user_agent, presence: true
8
+ validates :user_agent, uniqueness: true
9
+ end
10
+ end
@@ -0,0 +1,50 @@
1
+ module TheCommentsCommentable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :comments, as: :commentable
6
+
7
+ # TODO: update requests reduction
8
+ # *define_denormalize_flags* - should be placed before
9
+ # title or url builder filters
10
+ before_validation :define_denormalize_flags
11
+ after_save :denormalize_for_comments
12
+
13
+ def commentable_title
14
+ try(:title) || 'Undefined title'
15
+ end
16
+
17
+ def commentable_url
18
+ # /pages/1
19
+ ['', self.class.to_s.tableize, self.to_param].join('/')
20
+ end
21
+
22
+ def comments_sum
23
+ published_comments_count + draft_comments_count
24
+ end
25
+
26
+ def recalculate_comments_counters
27
+ self.draft_comments_count = comments.with_state(:draft).count
28
+ self.published_comments_count = comments.with_state(:published).count
29
+ self.deleted_comments_count = comments.with_state(:deleted).count
30
+ save
31
+ end
32
+
33
+ private
34
+
35
+ def define_denormalize_flags
36
+ @_commentable_title = commentable_title
37
+ @_commentable_url = commentable_url
38
+ end
39
+
40
+ def denormalize_for_comments
41
+ title_changed = @_commentable_title != commentable_title
42
+ url_changed = @_commentable_url != commentable_url
43
+ cstate = try(:state_changed?) ? { commentable_state: state } : {}
44
+
45
+ if title_changed || url_changed || !cstate.blank?
46
+ comments.update_all({ commentable_title: commentable_title, commentable_url: commentable_url}.merge(cstate))
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ module TheCommentsStates
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # :draft | :published | :deleted
6
+ state_machine :state, :initial => :draft do
7
+
8
+ # events
9
+ event :to_draft do
10
+ transition all - :draft => :draft
11
+ end
12
+
13
+ event :to_published do
14
+ transition all - :published => :published
15
+ end
16
+
17
+ event :to_deleted do
18
+ transition any - :deleted => :deleted
19
+ end
20
+
21
+ # transition callbacks
22
+ after_transition any => any do |comment|
23
+ @comment = comment
24
+ @owner = comment.user
25
+ @holder = comment.holder
26
+ @commentable = comment.commentable
27
+ end
28
+
29
+ # between draft and published
30
+ after_transition [:draft, :published] => [:draft, :published] do |comment, transition|
31
+ from = transition.from_name
32
+ to = transition.to_name
33
+
34
+ @holder.try :increment!, :"#{to}_comcoms_count"
35
+ @holder.try :decrement!, :"#{from}_comcoms_count"
36
+
37
+ [@owner, @commentable].each do |obj|
38
+ obj.try :increment!, "#{to}_comments_count"
39
+ obj.try :decrement!, "#{from}_comments_count"
40
+ end
41
+ end
42
+
43
+ # to deleted (cascade like query)
44
+ after_transition [:draft, :published] => :deleted do |comment|
45
+ ids = comment.self_and_descendants.map(&:id)
46
+ Comment.where(id: ids).update_all(state: :deleted)
47
+ [@holder, @owner, @commentable].each{|o| o.try :recalculate_comments_counters }
48
+ end
49
+
50
+ # from deleted
51
+ after_transition :deleted => [:draft, :published] do |comment, transition|
52
+ to = transition.to_name
53
+
54
+ @holder.try :decrement!, :deleted_comcoms_count
55
+ @holder.try :increment!, "#{to}_comcoms_count"
56
+
57
+ [@owner, @commentable].each do |obj|
58
+ obj.try :decrement!, :deleted_comments_count
59
+ obj.try :increment!, "#{to}_comments_count"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,25 @@
1
+ module TheCommentsUser
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :comments
6
+ has_many :comcoms, class_name: :Comment, foreign_key: :holder_id
7
+
8
+ def recalculate_comments_counters
9
+ [:comments, :comcoms].each do |name|
10
+ send "draft_#{name}_count=", send(name).with_state(:draft).count
11
+ send "published_#{name}_count=", send(name).with_state(:published).count
12
+ send "deleted_#{name}_count=", send(name).with_state(:deleted).count
13
+ end
14
+ save
15
+ end
16
+
17
+ def comments_sum
18
+ published_comments_count + draft_comments_count
19
+ end
20
+
21
+ def comcoms_sum
22
+ published_comcoms_count + draft_comcoms_count
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ - unless @ip_black_lists.blank?
2
+ %ol.black_list
3
+ - @ip_black_lists.each do |black|
4
+ %li{ class: black.state }
5
+ %p
6
+ %b ip:
7
+ = black.ip
8
+ %b count:
9
+ = black.count
10
+ %b state:
11
+ %span.state= black.state
12
+ %p
13
+ %b action:
14
+ = link_to :warning, ip_black_list_to_state_path(black, state: :warning), remote: true, method: :patch, class: :to_warning
15
+ = link_to :banned, ip_black_list_to_state_path(black, state: :banned), remote: true, method: :patch, class: :to_banned
16
+ - else
17
+ %p= t('the_comments.item_not_found')
@@ -0,0 +1 @@
1
+ = build_server_tree(tree, render_module: RenderCommentsTreeHelper, controller: controller)
@@ -0,0 +1,30 @@
1
+ .comment
2
+ .commentable
3
+ = comment.commentable_type
4
+ &rarr;
5
+ = link_to comment.commentable_title, comment.commentable_url
6
+ = comment.try(:commentable_state) ? "(#{comment.try(:commentable_state)})" : nil
7
+ .title
8
+ %label from:
9
+ = comment.title.blank? ? t('the_comments.guest_name') : comment.title
10
+ .contacts
11
+ %label contacts:
12
+ = comment.contacts
13
+ .content
14
+ = comment.content
15
+ .params
16
+ %b tolerance time:
17
+ = comment.tolerance_time || 'none'
18
+ \|
19
+ %b ip:
20
+ = link_to(comment.ip, ip_black_lists_url(ip: comment.ip)) || 'none'
21
+ \|
22
+ %b UA:
23
+ = link_to(comment.user_agent, user_agent_black_lists_url(agent: comment.user_agent)) || 'none'
24
+ \|
25
+ %b referer:
26
+ = comment.referer || 'none'
27
+
28
+ .controls
29
+ = link_to :Edit, '#', class: :edit
30
+ = render partial: 'the_comments/manage_controls', locals: { comment: comment }
@@ -0,0 +1,25 @@
1
+ %h3
2
+ = link_to 'New Comment', '#', id: :new_root_comment
3
+
4
+ = form_for Comment.new, remote: true, authenticity_token: true do |f|
5
+ .error_notifier{ style: "display:none" }
6
+ %label title:
7
+ %p= f.text_field :title
8
+
9
+ %label contacts:
10
+ %p= f.text_field :contacts
11
+
12
+ %label content*:
13
+ %p= f.text_area :raw_content
14
+
15
+ %p.trap
16
+ - TheComments.config.empty_inputs.each do |name|
17
+ = text_field_tag name, nil, autocomplete: :off, tabindex: -1, id: nil
18
+
19
+ = hidden_field_tag :tolerance_time, 0, id: nil, class: :tolerance_time
20
+
21
+ = f.hidden_field :commentable_type, value: commentable.class
22
+ = f.hidden_field :commentable_id, value: commentable.id
23
+ = f.hidden_field :parent_id, class: :parent_id
24
+
25
+ %p= f.submit 'Create Comment', class: :btn
@@ -0,0 +1,4 @@
1
+ = link_to t('the_comments.to_published'), to_published_comment_url(comment), remote: true, class: :to_published, method: :post
2
+ = link_to t('the_comments.to_draft'), to_draft_comment_url(comment), remote: true, class: :to_draft, method: :post
3
+ = link_to t('the_comments.to_spam'), to_spam_comment_url(comment), remote: true, class: :to_spam, method: :post
4
+ = link_to t('the_comments.to_deleted'), to_trash_comment_url(comment), remote: true, class: :to_deleted, method: :delete, data: { confirm: t('the_comments.delete_confirm') }