the_comments 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc.example +1 -0
- data/.travis.yml +5 -0
- data/README.md +105 -425
- data/app/assets/javascripts/the_comments.js.coffee +12 -9
- data/app/assets/javascripts/the_comments_manage.js.coffee +19 -49
- data/app/assets/stylesheets/the_comments.css.scss +20 -29
- data/app/controllers/_templates_/comments_controller.rb +44 -0
- data/app/controllers/concerns/controller.rb +216 -0
- data/app/helpers/render_comments_tree_helper.rb +4 -7
- data/app/models/_templates_/comment.rb +38 -0
- data/app/models/concerns/comment.rb +103 -0
- data/app/models/concerns/comment_states.rb +80 -0
- data/app/models/concerns/commentable.rb +69 -0
- data/app/models/concerns/user.rb +52 -0
- data/app/views/the_comments/_tree.html.erb +3 -0
- data/app/views/the_comments/haml/_additional_info.html.haml +13 -0
- data/app/views/the_comments/{_comment.html.haml → haml/_comment.html.haml} +0 -0
- data/app/views/the_comments/haml/_comment_body.html.haml +20 -0
- data/app/views/the_comments/haml/_comment_edit.html.haml +26 -0
- data/app/views/the_comments/{_form.html.haml → haml/_form.html.haml} +8 -6
- data/app/views/the_comments/haml/_manage_controls.html.haml +27 -0
- data/app/views/the_comments/haml/_sidebar.html.haml +28 -0
- data/app/views/the_comments/haml/_tree.html.haml +4 -0
- data/app/views/the_comments/haml/index.html.haml +18 -0
- data/app/views/the_comments/haml/manage.html.haml +25 -0
- data/app/views/the_comments/haml/my_comments.html.haml +28 -0
- data/app/views/the_comments/slim/_additional_info.html.slim +13 -0
- data/app/views/the_comments/slim/_comment.html.slim +1 -0
- data/app/views/the_comments/slim/_comment_body.html.slim +20 -0
- data/app/views/the_comments/slim/_comment_edit.html.slim +26 -0
- data/app/views/the_comments/slim/_form.html.slim +27 -0
- data/app/views/the_comments/slim/_manage_controls.html.slim +27 -0
- data/app/views/the_comments/slim/_sidebar.html.slim +28 -0
- data/app/views/the_comments/slim/_tree.html.slim +4 -0
- data/app/views/the_comments/slim/index.html.slim +18 -0
- data/app/views/the_comments/slim/manage.html.slim +25 -0
- data/app/views/the_comments/slim/my_comments.html.slim +28 -0
- data/{lib/generators/the_comments/templates → config/initializers}/the_comments.rb +3 -0
- data/config/locales/en.yml +39 -14
- data/config/locales/ru.yml +67 -0
- data/config/routes.rb +17 -13
- data/db/migrate/20130101010101_change_user.rb +18 -0
- data/db/migrate/20130101010102_create_comments.rb +50 -0
- data/db/migrate/20130101010103_change_commentable.rb +13 -0
- data/docs/admin_ui_installation.md +145 -0
- data/docs/advanced_installation.md +182 -0
- data/docs/comment_api.md +58 -0
- data/docs/commentable_api.md +59 -0
- data/docs/config_file.md +27 -0
- data/docs/content_preprocessors.md +73 -0
- data/docs/customazation_of_views.md +30 -0
- data/docs/denormalization_and_recent_comments.md +40 -0
- data/docs/documentation.md +28 -0
- data/docs/mountable_routes.md +80 -0
- data/docs/pagination.md +123 -0
- data/docs/screencast.jpg +0 -0
- data/docs/user_api.md +75 -0
- data/docs/what_is_comcoms.md +63 -0
- data/docs/whats_wrong_with_other_gems.md +18 -0
- data/docs/where_is_example_application.md +37 -0
- data/gem_version.rb +3 -0
- data/lib/generators/the_comments/USAGE +31 -20
- data/lib/generators/the_comments/the_comments_generator.rb +35 -18
- data/lib/generators/the_comments/views_generator.rb +52 -16
- data/lib/the_comments/config.rb +14 -1
- data/lib/the_comments/version.rb +1 -3
- data/lib/the_comments.rb +10 -0
- data/spec/dummy_app/.gitignore +17 -0
- data/spec/dummy_app/.rspec +1 -0
- data/spec/dummy_app/.ruby-gemset +1 -0
- data/spec/dummy_app/.ruby-version +1 -0
- data/spec/dummy_app/Gemfile +43 -0
- data/spec/dummy_app/README.md +50 -0
- data/spec/dummy_app/Rakefile +6 -0
- data/spec/dummy_app/app/assets/images/.keep +0 -0
- data/spec/dummy_app/app/assets/javascripts/admin_panel.js +5 -0
- data/spec/dummy_app/app/assets/javascripts/application.js +16 -0
- data/spec/dummy_app/app/assets/stylesheets/admin_panel.css +3 -0
- data/spec/dummy_app/app/assets/stylesheets/app.css.scss +4 -0
- data/spec/dummy_app/app/assets/stylesheets/application.css +16 -0
- data/spec/dummy_app/app/controllers/application_controller.rb +7 -0
- data/{lib/generators/the_comments/templates → spec/dummy_app/app/controllers}/comments_controller.rb +3 -1
- data/spec/dummy_app/app/controllers/concerns/.keep +0 -0
- data/spec/dummy_app/app/controllers/posts_controller.rb +13 -0
- data/spec/dummy_app/app/controllers/users_controller.rb +7 -0
- data/spec/dummy_app/app/helpers/application_helper.rb +2 -0
- data/spec/dummy_app/app/mailers/.keep +0 -0
- data/spec/dummy_app/app/models/.keep +0 -0
- data/spec/dummy_app/app/models/comment.rb +32 -0
- data/spec/dummy_app/app/models/concerns/.keep +0 -0
- data/spec/dummy_app/app/models/post.rb +17 -0
- data/spec/dummy_app/app/models/user.rb +21 -0
- data/spec/dummy_app/app/views/layouts/admin.html.haml +25 -0
- data/spec/dummy_app/app/views/layouts/application.html.haml +20 -0
- data/spec/dummy_app/app/views/posts/index.html.haml +22 -0
- data/spec/dummy_app/app/views/posts/show.html.haml +7 -0
- data/spec/dummy_app/bin/bundle +3 -0
- data/spec/dummy_app/bin/rails +4 -0
- data/spec/dummy_app/bin/rake +4 -0
- data/spec/dummy_app/config/application.rb +23 -0
- data/spec/dummy_app/config/boot.rb +4 -0
- data/spec/dummy_app/config/database.yml +11 -0
- data/spec/dummy_app/config/environment.rb +5 -0
- data/spec/dummy_app/config/environments/development.rb +29 -0
- data/spec/dummy_app/config/environments/production.rb +80 -0
- data/spec/dummy_app/config/environments/test.rb +36 -0
- data/spec/dummy_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy_app/config/initializers/inflections.rb +16 -0
- data/spec/dummy_app/config/initializers/mime_types.rb +5 -0
- data/spec/dummy_app/config/initializers/secret_token.rb +12 -0
- data/spec/dummy_app/config/initializers/session_store.rb +3 -0
- data/spec/dummy_app/config/initializers/sorcery.rb +437 -0
- data/spec/dummy_app/config/initializers/the_comments.rb +13 -0
- data/spec/dummy_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy_app/config/locales/en.yml +23 -0
- data/spec/dummy_app/config/routes.rb +15 -0
- data/spec/dummy_app/config.ru +4 -0
- data/spec/dummy_app/db/migrate/20130712061503_sorcery_core.rb +16 -0
- data/spec/dummy_app/db/migrate/20130712065951_create_posts.rb +11 -0
- data/spec/dummy_app/db/migrate/20131027185332_change_user.the_comments_engine.rb +19 -0
- data/spec/dummy_app/db/migrate/20131027185333_create_comments.the_comments_engine.rb +51 -0
- data/spec/dummy_app/db/migrate/20131027185334_change_commentable.the_comments_engine.rb +14 -0
- data/spec/dummy_app/db/schema.rb +74 -0
- data/spec/dummy_app/db/seeds.rb +42 -0
- data/spec/dummy_app/lib/assets/.keep +0 -0
- data/spec/dummy_app/lib/tasks/.keep +0 -0
- data/spec/dummy_app/lib/tasks/app_bootstrap.rake +15 -0
- data/spec/dummy_app/log/.keep +0 -0
- data/spec/dummy_app/public/404.html +58 -0
- data/spec/dummy_app/public/422.html +58 -0
- data/spec/dummy_app/public/500.html +57 -0
- data/spec/dummy_app/public/favicon.ico +0 -0
- data/spec/dummy_app/public/robots.txt +5 -0
- data/spec/dummy_app/spec/factories/post.rb +6 -0
- data/spec/dummy_app/spec/factories/user.rb +6 -0
- data/spec/dummy_app/spec/models/user_counters_spec.rb +339 -0
- data/spec/dummy_app/spec/spec_helper.rb +29 -0
- data/spec/dummy_app/test/controllers/.keep +0 -0
- data/spec/dummy_app/test/fixtures/.keep +0 -0
- data/spec/dummy_app/test/helpers/.keep +0 -0
- data/spec/dummy_app/test/integration/.keep +0 -0
- data/spec/dummy_app/test/mailers/.keep +0 -0
- data/spec/dummy_app/test/models/.keep +0 -0
- data/spec/dummy_app/test/test_helper.rb +15 -0
- data/spec/dummy_app/vendor/assets/javascripts/.keep +0 -0
- data/spec/dummy_app/vendor/assets/stylesheets/.keep +0 -0
- data/views_converter.rb +16 -0
- metadata +223 -45
- data/app/controllers/concerns/the_comments_controller.rb +0 -229
- data/app/controllers/concerns/the_comments_ip_controller.rb +0 -17
- data/app/controllers/concerns/the_comments_user_agent_controller.rb +0 -15
- data/app/models/concerns/the_comments_base.rb +0 -69
- data/app/models/concerns/the_comments_black_ip.rb +0 -9
- data/app/models/concerns/the_comments_black_user_agent.rb +0 -9
- data/app/models/concerns/the_comments_commentable.rb +0 -66
- data/app/models/concerns/the_comments_states.rb +0 -65
- data/app/models/concerns/the_comments_user.rb +0 -32
- data/app/views/ip_black_lists/index.html.haml +0 -17
- data/app/views/the_comments/_comment_body.html.haml +0 -30
- data/app/views/the_comments/_manage_controls.html.haml +0 -4
- data/app/views/the_comments/_tree.html.haml +0 -4
- data/app/views/the_comments/index.html.haml +0 -19
- data/app/views/the_comments/manage.html.haml +0 -29
- data/app/views/user_agent_black_lists/index.html.haml +0 -17
- data/db/migrate/20130101010101_create_comments.rb +0 -90
- data/lib/generators/the_comments/templates/ip_black_list.rb +0 -3
- data/lib/generators/the_comments/templates/ip_black_lists_controller.rb +0 -10
- data/lib/generators/the_comments/templates/the_comments_black_ip.rb +0 -9
- data/lib/generators/the_comments/templates/the_comments_black_user_agent.rb +0 -9
- data/lib/generators/the_comments/templates/user_agent_black_list.rb +0 -3
- data/lib/generators/the_comments/templates/user_agent_black_lists_controller.rb +0 -10
@@ -1,5 +1,5 @@
|
|
1
1
|
# ERROR MSG BUILDER
|
2
|
-
@
|
2
|
+
@comments_errors_builder = (errors) ->
|
3
3
|
error_msgs = ''
|
4
4
|
for error in errors
|
5
5
|
error_msgs += "<p><b>#{ error }</b></p>"
|
@@ -15,7 +15,7 @@
|
|
15
15
|
@comments_error_notifier = (form, text) ->
|
16
16
|
form.children('.error_notifier').empty().hide().append(text).show()
|
17
17
|
|
18
|
-
#
|
18
|
+
# TIME HELPER
|
19
19
|
@unixsec = (t) -> Math.round(t.getTime() / 1000)
|
20
20
|
|
21
21
|
# HIGHTLIGHT ANCHOR
|
@@ -26,12 +26,11 @@
|
|
26
26
|
|
27
27
|
$ ->
|
28
28
|
window.tolerance_time_start = unixsec(new Date)
|
29
|
-
|
30
|
-
comment_forms = "#new_comment, .reply_comments_form"
|
31
29
|
tolerance_time = $('[data-comments-tolarance-time]').first().data('comments-tolarance-time')
|
32
30
|
|
33
31
|
# Button Click => AJAX Before Send
|
34
32
|
submits = '#new_comment input[type=submit], .reply_comments_form input[type=submit]'
|
33
|
+
|
35
34
|
$(document).on 'click', submits, (e) ->
|
36
35
|
button = $ e.target
|
37
36
|
form = button.parents('form').first()
|
@@ -39,7 +38,7 @@ $ ->
|
|
39
38
|
|
40
39
|
if tolerance_time && (time_diff < tolerance_time)
|
41
40
|
delta = tolerance_time - time_diff
|
42
|
-
error_msgs =
|
41
|
+
error_msgs = comments_errors_builder(["Please wait #{delta} secs"])
|
43
42
|
comments_error_notifier(form, error_msgs)
|
44
43
|
return false
|
45
44
|
|
@@ -47,14 +46,17 @@ $ ->
|
|
47
46
|
button.hide()
|
48
47
|
true
|
49
48
|
|
50
|
-
|
49
|
+
################ COMMENTS FORMS ################
|
50
|
+
comment_forms = "#new_comment, .reply_comments_form"
|
51
|
+
|
52
|
+
# ERROR
|
51
53
|
$(document).on 'ajax:error', comment_forms, (request, response, status) ->
|
52
54
|
form = $ @
|
53
55
|
$('input[type=submit]', form).show()
|
54
|
-
error_msgs =
|
56
|
+
error_msgs = comments_errors_builder(["Server Error: #{response.status}"])
|
55
57
|
comments_error_notifier(form, error_msgs)
|
56
58
|
|
57
|
-
#
|
59
|
+
# SUCCESS
|
58
60
|
$(document).on 'ajax:success', comment_forms, (request, response, status) ->
|
59
61
|
form = $ @
|
60
62
|
$('input[type=submit]', form).show()
|
@@ -70,7 +72,7 @@ $ ->
|
|
70
72
|
tree.append(response)
|
71
73
|
document.location.hash = anchor
|
72
74
|
else
|
73
|
-
error_msgs =
|
75
|
+
error_msgs = comments_errors_builder(response.errors)
|
74
76
|
comments_error_notifier(form, error_msgs)
|
75
77
|
|
76
78
|
# NEW ROOT BUTTON
|
@@ -92,6 +94,7 @@ $ ->
|
|
92
94
|
$('.parent_id', form).val comment_id
|
93
95
|
|
94
96
|
comment.siblings('.form_holder').html(form)
|
97
|
+
$('.error_notifier', form).empty().hide()
|
95
98
|
form.fadeIn()
|
96
99
|
false
|
97
100
|
|
@@ -1,57 +1,27 @@
|
|
1
1
|
$ ->
|
2
|
-
|
3
|
-
holder = $('.comments_list')
|
4
|
-
|
5
|
-
holder.on 'ajax:success', '.to_published', (request, response, status) ->
|
6
|
-
link = $ @
|
7
|
-
link.parents('.item').first().attr('class', 'item published')
|
8
|
-
|
9
|
-
holder.on 'ajax:success', '.to_draft', (request, response, status) ->
|
10
|
-
link = $ @
|
11
|
-
link.parents('.item').first().attr('class', 'item draft')
|
12
|
-
|
13
|
-
holder.on 'ajax:success', '.to_spam, .to_deleted', (request, response, status) ->
|
14
|
-
$(@).parents('li').first().hide()
|
15
|
-
|
16
|
-
$('.comments_tree').on 'ajax:success', '.delete', (request, response, status) ->
|
17
|
-
$(@).parents('li').first().hide()
|
2
|
+
hide_comment_panel = (btn) -> $(btn).parents('.panel').slideUp()
|
18
3
|
|
19
|
-
|
20
|
-
inplace_forms = '.comments_list .form form'
|
21
|
-
$(document).on 'ajax:success', inplace_forms, (request, response, status) ->
|
22
|
-
form = $ @
|
23
|
-
item = form.parents('.item')
|
24
|
-
item.children('.body').html(response).show()
|
25
|
-
item.children('.form').hide()
|
4
|
+
comments = $ '.comments'
|
26
5
|
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
body = form.siblings('.body')
|
33
|
-
body.show()
|
34
|
-
form.hide()
|
6
|
+
# CONTROLS
|
7
|
+
comments.on 'click', 'a.additional_info', ->
|
8
|
+
btn = $ @
|
9
|
+
holder = btn.parents('.panel-body')
|
10
|
+
holder.find('div.additional_info').slideToggle()
|
35
11
|
false
|
36
12
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
form.show()
|
13
|
+
comments.on 'click', 'a.edit', ->
|
14
|
+
btn = $ @
|
15
|
+
holder = btn.parents('.panel-body')
|
16
|
+
holder.find('.edit_form, .comment_body, a.edit').toggle()
|
42
17
|
false
|
43
18
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
holder.on 'ajax:success', '.to_warning', (request, response, status) ->
|
48
|
-
link = $ @
|
49
|
-
li = link.parents('li').first()
|
50
|
-
li.attr 'class', 'warning'
|
51
|
-
li.find('.state').html 'warning'
|
19
|
+
comments.on 'ajax:success', '.to_published, .to_draft, .to_spam, .to_deleted', ->
|
20
|
+
hide_comment_panel @
|
52
21
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
22
|
+
# Edit form
|
23
|
+
comments.on 'ajax:success', '.edit_comment', (request, response, status) ->
|
24
|
+
form = $ @
|
25
|
+
holder = form.parents('.panel-body')
|
26
|
+
holder.find('.edit_form, .comment_body, a.edit').toggle()
|
27
|
+
holder.find('.comment_body').replaceWith response
|
@@ -1,8 +1,11 @@
|
|
1
|
-
.comments_tree, .comments_list
|
1
|
+
.comments_tree, .comments_list{
|
2
2
|
font-family: Arial;
|
3
3
|
|
4
|
-
margin:0;
|
5
|
-
|
4
|
+
margin:0;
|
5
|
+
padding:0;
|
6
|
+
margin-bottom: 30px;
|
7
|
+
|
8
|
+
*{ margin: 0; padding: 0; font-size: inherit; }
|
6
9
|
|
7
10
|
a{ text-decoration: none; }
|
8
11
|
a:hover{ text-decoration: underline; }
|
@@ -23,9 +26,11 @@
|
|
23
26
|
font-family: Arial;
|
24
27
|
font-size: 13px;
|
25
28
|
|
29
|
+
h3{ font-size: 1.6em; }
|
30
|
+
|
26
31
|
.error_notifier{
|
27
32
|
background-color: #F2DEDE;
|
28
|
-
border
|
33
|
+
border: 1px solid #B94A48;
|
29
34
|
color: #B94A48;
|
30
35
|
|
31
36
|
border-radius: 4px;
|
@@ -36,9 +41,11 @@
|
|
36
41
|
p{ margin: 0 0 10px 0; }
|
37
42
|
}
|
38
43
|
form{
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
|
45
|
+
background: #e0e4f5;
|
46
|
+
|
47
|
+
border: 1px solid #c6cff5;
|
48
|
+
border-radius: 5px;
|
42
49
|
padding: 10px;
|
43
50
|
|
44
51
|
p{ margin: 0 0 10px 0; }
|
@@ -48,6 +55,7 @@
|
|
48
55
|
padding: 4px;
|
49
56
|
width: 75%;
|
50
57
|
}
|
58
|
+
label{ font-size: 15px; }
|
51
59
|
textarea{
|
52
60
|
border: 1px solid gray;
|
53
61
|
font-family: Arial;
|
@@ -99,7 +107,7 @@
|
|
99
107
|
float: left;
|
100
108
|
width: 50px;
|
101
109
|
|
102
|
-
img{ margin-bottom: 10px; }
|
110
|
+
img{ margin-bottom: 10px; width: 42px; height: 42px; }
|
103
111
|
}
|
104
112
|
.userbar, .cbody{
|
105
113
|
margin: 0 0 5px 55px;
|
@@ -118,10 +126,12 @@
|
|
118
126
|
.to_published{ display: none; }
|
119
127
|
}
|
120
128
|
.cbody{
|
121
|
-
|
129
|
+
font-size: 15px;
|
130
|
+
border-bottom: 1px solid #eee;
|
122
131
|
padding-bottom: 10px;
|
123
132
|
line-height: 135%;
|
124
|
-
margin-bottom:
|
133
|
+
margin-bottom: 3px;
|
134
|
+
overflow: hidden;
|
125
135
|
}
|
126
136
|
.reply{
|
127
137
|
margin: 0 0 5px 55px;
|
@@ -230,23 +240,4 @@
|
|
230
240
|
}
|
231
241
|
}
|
232
242
|
}
|
233
|
-
}
|
234
|
-
|
235
|
-
.black_list{
|
236
|
-
li{
|
237
|
-
border: 1px solid gray;
|
238
|
-
border-radius: 5px;
|
239
|
-
padding: 10px;
|
240
|
-
&.warning{
|
241
|
-
border-left: 5px solid orange;
|
242
|
-
.to_warning{ display: none; }
|
243
|
-
}
|
244
|
-
&.banned{
|
245
|
-
border-left: 5px solid black;
|
246
|
-
.to_banned{ display: none; }
|
247
|
-
}
|
248
|
-
}
|
249
|
-
p{
|
250
|
-
margin-bottom: 10px;
|
251
|
-
}
|
252
243
|
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class CommentsController < ApplicationController
|
2
|
+
# layout 'admin'
|
3
|
+
|
4
|
+
# Define your restrict methods and use them like this:
|
5
|
+
#
|
6
|
+
# before_action :user_required, except: %w[index create]
|
7
|
+
# before_action :owner_required, except: %w[index create]
|
8
|
+
# before_action :admin_required, only: %w[total_draft total_published total_deleted total_spam]
|
9
|
+
|
10
|
+
include TheComments::Controller
|
11
|
+
|
12
|
+
# >>> include TheComments::Controller <<<
|
13
|
+
# (!) Almost all methods based on *current_user* method
|
14
|
+
#
|
15
|
+
# 1. Controller's public methods list:
|
16
|
+
# You can redifine it for your purposes
|
17
|
+
# public
|
18
|
+
# %w[ manage index create edit update ]
|
19
|
+
# %w[ my_comments my_draft my_published ]
|
20
|
+
# %w[ draft published deleted spam ]
|
21
|
+
# %w[ to_draft to_published to_deleted to_spam ]
|
22
|
+
# %w[ total_draft total_published total_deleted total_spam ]
|
23
|
+
#
|
24
|
+
#
|
25
|
+
# 2. Controller's private methods list:
|
26
|
+
# You can redifine it for your purposes
|
27
|
+
#
|
28
|
+
# private
|
29
|
+
# %w[ comment_template comment_partial ]
|
30
|
+
# %w[ denormalized_fields request_data_for_comment define_commentable ]
|
31
|
+
# %w[ comment_params patch_comment_params ]
|
32
|
+
# %w[ ajax_requests_required cookies_required ]
|
33
|
+
# %w[ empty_trap_required tolerance_time_required ]
|
34
|
+
|
35
|
+
# KAMINARI pagination:
|
36
|
+
# following methods based on gem "kaminari"
|
37
|
+
# You should redefine them if you use something else
|
38
|
+
#
|
39
|
+
# public
|
40
|
+
# %w[ manage index edit ]
|
41
|
+
# %w[ draft published deleted spam ]
|
42
|
+
# %w[ my_comments my_draft my_published ]
|
43
|
+
# %w[ total_draft total_published total_deleted total_spam ]
|
44
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
module TheComments
|
2
|
+
COMMENTS_COOKIES_TOKEN = 'JustTheCommentsCookies'
|
3
|
+
|
4
|
+
# Cookies and View token for spam protection
|
5
|
+
# include TheComments::ViewToken
|
6
|
+
module ViewToken
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included { before_action :set_the_comments_cookies }
|
10
|
+
|
11
|
+
def comments_view_token
|
12
|
+
cookies[:comments_view_token]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def set_the_comments_cookies
|
18
|
+
cookies[:the_comment_cookies] = { value: TheComments::COMMENTS_COOKIES_TOKEN, expires: 1.year.from_now }
|
19
|
+
cookies[:comments_view_token] = { value: SecureRandom.hex, expires: 7.days.from_now } unless cookies[:comments_view_token]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Base functionality of Comments Controller
|
24
|
+
# class CommentsController < ApplicationController
|
25
|
+
# include TheComments::Controller
|
26
|
+
# end
|
27
|
+
module Controller
|
28
|
+
extend ActiveSupport::Concern
|
29
|
+
|
30
|
+
included do
|
31
|
+
include TheComments::ViewToken
|
32
|
+
|
33
|
+
# Attention! We should not set TheComments cookie before create
|
34
|
+
skip_before_action :set_the_comments_cookies, only: [:create]
|
35
|
+
|
36
|
+
# Spam protection
|
37
|
+
before_action -> { @errors = [] }, only: [:create]
|
38
|
+
|
39
|
+
before_action :ajax_requests_required, only: [:create]
|
40
|
+
before_action :cookies_required, only: [:create]
|
41
|
+
|
42
|
+
before_action :empty_trap_required, only: [:create], if: -> { TheComments.config.empty_trap_protection }
|
43
|
+
before_action :tolerance_time_required, only: [:create], if: -> { TheComments.config.tolerance_time_protection }
|
44
|
+
|
45
|
+
# preparation
|
46
|
+
before_action :define_commentable, only: [:create]
|
47
|
+
|
48
|
+
# raise an errors
|
49
|
+
before_action -> { return render(json: { errors: @errors }) unless @errors.blank? }, only: [:create]
|
50
|
+
end
|
51
|
+
|
52
|
+
# App side methods (you can overwrite them)
|
53
|
+
def index
|
54
|
+
@comments = ::Comment.with_state(:published).recent.page(params[:page])
|
55
|
+
render comment_template(:index)
|
56
|
+
end
|
57
|
+
|
58
|
+
def manage
|
59
|
+
@comments = current_user.comcoms.active.recent.page(params[:page])
|
60
|
+
render comment_template(:manage)
|
61
|
+
end
|
62
|
+
|
63
|
+
def my_comments
|
64
|
+
@comments = current_user.my_comments.active.recent.page(params[:page])
|
65
|
+
render comment_template(:my_comments)
|
66
|
+
end
|
67
|
+
|
68
|
+
def edit
|
69
|
+
@comments = current_user.comcoms.where(id: params[:id]).page(params[:page])
|
70
|
+
render comment_template(:manage)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Methods based on *current_user* helper
|
74
|
+
# Methods for admin
|
75
|
+
%w[draft published deleted].each do |state|
|
76
|
+
define_method "#{state}" do
|
77
|
+
@comments = current_user.comcoms.with_state(state).recent.page(params[:page])
|
78
|
+
render comment_template(:manage)
|
79
|
+
end
|
80
|
+
|
81
|
+
define_method "total_#{state}" do
|
82
|
+
@comments = ::Comment.with_state(state).recent.page(params[:page])
|
83
|
+
render comment_template(:manage)
|
84
|
+
end
|
85
|
+
|
86
|
+
unless state == 'deleted'
|
87
|
+
define_method "my_#{state}" do
|
88
|
+
@comments = current_user.my_comments.with_state(state).recent.page(params[:page])
|
89
|
+
render comment_template(:my_comments)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def spam
|
95
|
+
@comments = current_user.comcoms.where(spam: true).recent.page(params[:page])
|
96
|
+
render comment_template(:manage)
|
97
|
+
end
|
98
|
+
|
99
|
+
def total_spam
|
100
|
+
@comments = ::Comment.where(spam: true).recent.page(params[:page])
|
101
|
+
render comment_template(:manage)
|
102
|
+
end
|
103
|
+
|
104
|
+
# BASE METHODS
|
105
|
+
# Public methods
|
106
|
+
def update
|
107
|
+
comment = ::Comment.find(params[:id])
|
108
|
+
comment.update_attributes!(patch_comment_params)
|
109
|
+
render(layout: false, partial: comment_partial(:comment_body), locals: { comment: comment })
|
110
|
+
end
|
111
|
+
|
112
|
+
def create
|
113
|
+
@comment = @commentable.comments.new comment_params
|
114
|
+
if @comment.valid?
|
115
|
+
@comment.save
|
116
|
+
return render layout: false, partial: comment_partial(:comment), locals: { tree: @comment }
|
117
|
+
end
|
118
|
+
render json: { errors: @comment.errors.full_messages }
|
119
|
+
end
|
120
|
+
|
121
|
+
# Restricted area
|
122
|
+
%w[draft published deleted].each do |state|
|
123
|
+
define_method "to_#{state}" do
|
124
|
+
::Comment.find(params[:id]).try "to_#{state}"
|
125
|
+
render nothing: true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_spam
|
130
|
+
comment = ::Comment.find(params[:id])
|
131
|
+
comment.to_spam
|
132
|
+
comment.to_deleted
|
133
|
+
render nothing: true
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def comment_template template
|
139
|
+
{ template: "the_comments/#{TheComments.config.template_engine}/#{template}" }
|
140
|
+
end
|
141
|
+
|
142
|
+
def comment_partial partial
|
143
|
+
"the_comments/#{TheComments.config.template_engine}/#{partial}"
|
144
|
+
end
|
145
|
+
|
146
|
+
def denormalized_fields
|
147
|
+
title = @commentable.commentable_title
|
148
|
+
url = @commentable.commentable_url
|
149
|
+
@commentable ? { commentable_title: title, commentable_url: url } : {}
|
150
|
+
end
|
151
|
+
|
152
|
+
def request_data_for_comment
|
153
|
+
r = request
|
154
|
+
{ ip: r.ip, referer: CGI::unescape(r.referer || 'direct_visit'), user_agent: r.user_agent }
|
155
|
+
end
|
156
|
+
|
157
|
+
def define_commentable
|
158
|
+
commentable_klass = params[:comment][:commentable_type].constantize
|
159
|
+
commentable_id = params[:comment][:commentable_id]
|
160
|
+
|
161
|
+
@commentable = commentable_klass.find(commentable_id)
|
162
|
+
return render(json: { errors: [t('the_comments.undefined_commentable')] }) unless @commentable
|
163
|
+
end
|
164
|
+
|
165
|
+
def comment_params
|
166
|
+
params
|
167
|
+
.require(:comment)
|
168
|
+
.permit(:title, :contacts, :raw_content, :parent_id)
|
169
|
+
.merge(denormalized_fields)
|
170
|
+
.merge(request_data_for_comment)
|
171
|
+
.merge(tolerance_time: params[:tolerance_time].to_i)
|
172
|
+
.merge(user: current_user, view_token: comments_view_token)
|
173
|
+
end
|
174
|
+
|
175
|
+
def patch_comment_params
|
176
|
+
params
|
177
|
+
.require(:comment)
|
178
|
+
.permit(:title, :contacts, :raw_content, :parent_id)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Protection hooks
|
182
|
+
def ajax_requests_required
|
183
|
+
unless request.xhr?
|
184
|
+
return render(text: t('the_comments.ajax_requests_required'))
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def cookies_required
|
189
|
+
if cookies[:the_comment_cookies] != TheComments::COMMENTS_COOKIES_TOKEN
|
190
|
+
@errors << [t('the_comments.cookies'), t('the_comments.cookies_required')].join(': ')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# TODO:
|
195
|
+
# 1) inject ?
|
196
|
+
# 2) fields can be removed on client side
|
197
|
+
def empty_trap_required
|
198
|
+
is_human = true
|
199
|
+
params.slice(*TheComments.config.empty_inputs).values.each{|v| is_human = (is_human && v.blank?) }
|
200
|
+
|
201
|
+
if !is_human
|
202
|
+
@errors << [t('the_comments.trap'), t('the_comments.trap_message')].join(': ')
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def tolerance_time_required
|
207
|
+
this_time = params[:tolerance_time].to_i
|
208
|
+
min_time = TheComments.config.tolerance_time.to_i
|
209
|
+
|
210
|
+
if this_time < min_time
|
211
|
+
tdiff = min_time - this_time
|
212
|
+
@errors << [t('the_comments.tolerance_time'), t('the_comments.tolerance_time_message', time: tdiff )].join(': ')
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -27,7 +27,7 @@ module RenderCommentsTreeHelper
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def moderator?
|
30
|
-
controller.try(:current_user).try(:
|
30
|
+
controller.try(:current_user).try(:comments_moderator?, @comment)
|
31
31
|
end
|
32
32
|
|
33
33
|
# Render Methods
|
@@ -87,17 +87,14 @@ module RenderCommentsTreeHelper
|
|
87
87
|
"<div class='userbar'>#{ title } #{ anchor }</div>"
|
88
88
|
end
|
89
89
|
|
90
|
-
def moderator_controls
|
91
|
-
t = ''
|
90
|
+
def moderator_controls
|
92
91
|
if moderator?
|
93
|
-
|
94
|
-
t += h.link_to(t('the_comments.to_deleted'), h.to_trash_comment_url(@comment), class: :delete, method: :delete, remote: true, confirm: t('the_comments.delete_confirm'))
|
92
|
+
h.link_to(t('the_comments.edit'), h.comments.edit_comment_url(@comment), class: :edit)
|
95
93
|
end
|
96
|
-
t
|
97
94
|
end
|
98
95
|
|
99
96
|
def reply
|
100
|
-
if @comment.depth < @max_reply_depth
|
97
|
+
if @comment.depth < (@max_reply_depth - 1)
|
101
98
|
"<p class='reply'><a href='#' class='reply_link'>#{ t('the_comments.reply') }</a>"
|
102
99
|
end
|
103
100
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Comment < ActiveRecord::Base
|
2
|
+
include TheComments::Comment
|
3
|
+
# ---------------------------------------------------
|
4
|
+
# Define comment's avatar url
|
5
|
+
# Usually we use Comment#user (owner of comment) to define avatar
|
6
|
+
# @blog.comments.includes(:user) <= use includes(:user) to decrease queries count
|
7
|
+
# comment#user.avatar_url
|
8
|
+
# ---------------------------------------------------
|
9
|
+
|
10
|
+
# public
|
11
|
+
# ---------------------------------------------------
|
12
|
+
# Simple way to define avatar url
|
13
|
+
#
|
14
|
+
# def avatar_url
|
15
|
+
# src = id.to_s
|
16
|
+
# src = title unless title.blank?
|
17
|
+
# src = contacts if !contacts.blank? && /@/ =~ contacts
|
18
|
+
# hash = Digest::MD5.hexdigest(src)
|
19
|
+
# "https://2.gravatar.com/avatar/#{hash}?s=42&d=https://identicons.github.com/#{hash}.png"
|
20
|
+
# end
|
21
|
+
# ---------------------------------------------------
|
22
|
+
|
23
|
+
# private
|
24
|
+
# ---------------------------------------------------
|
25
|
+
# Define your content filters
|
26
|
+
# gem 'RedCloth'
|
27
|
+
# gem 'sanitize'
|
28
|
+
# gem 'MySmilesProcessor'
|
29
|
+
#
|
30
|
+
# def prepare_content
|
31
|
+
# text = self.raw_content
|
32
|
+
# text = RedCloth.new(text).to_html
|
33
|
+
# text = MySmilesProcessor.new(text)
|
34
|
+
# text = Sanitize.clean(text, Sanitize::Config::RELAXED)
|
35
|
+
# self.content = text
|
36
|
+
# end
|
37
|
+
# ---------------------------------------------------
|
38
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module TheComments
|
2
|
+
module Comment
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
scope :active, -> { with_state [:draft, :published] }
|
7
|
+
scope :recent, -> { order('created_at DESC') }
|
8
|
+
|
9
|
+
# Nested Set
|
10
|
+
acts_as_nested_set scope: [:commentable_type, :commentable_id]
|
11
|
+
|
12
|
+
# Comments State Machine
|
13
|
+
include TheComments::CommentStates
|
14
|
+
|
15
|
+
# TheSortableTree
|
16
|
+
include TheSortableTree::Scopes
|
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_default_state, :define_anchor, :denormalize_commentable
|
27
|
+
after_create :update_cache_counters
|
28
|
+
before_save :prepare_content
|
29
|
+
end
|
30
|
+
|
31
|
+
def avatar_url
|
32
|
+
src = id.to_s
|
33
|
+
src = title unless title.blank?
|
34
|
+
src = contacts if !contacts.blank? && /@/ =~ contacts
|
35
|
+
hash = Digest::MD5.hexdigest(src)
|
36
|
+
"https://2.gravatar.com/avatar/#{hash}?s=42&d=https://identicons.github.com/#{hash}.png"
|
37
|
+
end
|
38
|
+
|
39
|
+
def mark_as_spam
|
40
|
+
count = self_and_descendants.update_all({spam: true})
|
41
|
+
update_spam_counter
|
42
|
+
count
|
43
|
+
end
|
44
|
+
|
45
|
+
def mark_as_not_spam
|
46
|
+
count = self_and_descendants.update_all({spam: false})
|
47
|
+
update_spam_counter
|
48
|
+
count
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_spam
|
52
|
+
mark_as_spam
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def update_spam_counter
|
58
|
+
holder.try :update_comcoms_spam_counter
|
59
|
+
end
|
60
|
+
|
61
|
+
def define_anchor
|
62
|
+
self.anchor = SecureRandom.hex[0..5]
|
63
|
+
end
|
64
|
+
|
65
|
+
def define_holder
|
66
|
+
c = self.commentable
|
67
|
+
self.holder = c.is_a?(User) ? c : c.try(:user)
|
68
|
+
end
|
69
|
+
|
70
|
+
def define_default_state
|
71
|
+
self.state = TheComments.config.default_owner_state if user && user == holder
|
72
|
+
end
|
73
|
+
|
74
|
+
def denormalize_commentable
|
75
|
+
self.commentable_title = commentable.try :commentable_title
|
76
|
+
self.commentable_state = commentable.try :commentable_state
|
77
|
+
self.commentable_url = commentable.try :commentable_url
|
78
|
+
end
|
79
|
+
|
80
|
+
def prepare_content
|
81
|
+
self.content = self.raw_content
|
82
|
+
end
|
83
|
+
|
84
|
+
# Warn: increment! doesn't call validation =>
|
85
|
+
# before_validation filters doesn't work =>
|
86
|
+
# We have few unuseful requests
|
87
|
+
# I impressed that I found it and reduce DB requests
|
88
|
+
# Awesome logic pazzl! I'm really pedant :D
|
89
|
+
def update_cache_counters
|
90
|
+
user.try :recalculate_my_comments_counter!
|
91
|
+
|
92
|
+
if holder
|
93
|
+
holder.send :try, :define_denormalize_flags
|
94
|
+
holder.increment! "#{state}_comcoms_count"
|
95
|
+
end
|
96
|
+
|
97
|
+
if commentable
|
98
|
+
commentable.send :define_denormalize_flags
|
99
|
+
commentable.increment! "#{state}_comments_count"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|