the_comments 1.1.0 → 2.0.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.
- 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
|