the_comments 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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') }