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.
Files changed (172) hide show
  1. data/.rvmrc.example +1 -0
  2. data/.travis.yml +5 -0
  3. data/README.md +105 -425
  4. data/app/assets/javascripts/the_comments.js.coffee +12 -9
  5. data/app/assets/javascripts/the_comments_manage.js.coffee +19 -49
  6. data/app/assets/stylesheets/the_comments.css.scss +20 -29
  7. data/app/controllers/_templates_/comments_controller.rb +44 -0
  8. data/app/controllers/concerns/controller.rb +216 -0
  9. data/app/helpers/render_comments_tree_helper.rb +4 -7
  10. data/app/models/_templates_/comment.rb +38 -0
  11. data/app/models/concerns/comment.rb +103 -0
  12. data/app/models/concerns/comment_states.rb +80 -0
  13. data/app/models/concerns/commentable.rb +69 -0
  14. data/app/models/concerns/user.rb +52 -0
  15. data/app/views/the_comments/_tree.html.erb +3 -0
  16. data/app/views/the_comments/haml/_additional_info.html.haml +13 -0
  17. data/app/views/the_comments/{_comment.html.haml → haml/_comment.html.haml} +0 -0
  18. data/app/views/the_comments/haml/_comment_body.html.haml +20 -0
  19. data/app/views/the_comments/haml/_comment_edit.html.haml +26 -0
  20. data/app/views/the_comments/{_form.html.haml → haml/_form.html.haml} +8 -6
  21. data/app/views/the_comments/haml/_manage_controls.html.haml +27 -0
  22. data/app/views/the_comments/haml/_sidebar.html.haml +28 -0
  23. data/app/views/the_comments/haml/_tree.html.haml +4 -0
  24. data/app/views/the_comments/haml/index.html.haml +18 -0
  25. data/app/views/the_comments/haml/manage.html.haml +25 -0
  26. data/app/views/the_comments/haml/my_comments.html.haml +28 -0
  27. data/app/views/the_comments/slim/_additional_info.html.slim +13 -0
  28. data/app/views/the_comments/slim/_comment.html.slim +1 -0
  29. data/app/views/the_comments/slim/_comment_body.html.slim +20 -0
  30. data/app/views/the_comments/slim/_comment_edit.html.slim +26 -0
  31. data/app/views/the_comments/slim/_form.html.slim +27 -0
  32. data/app/views/the_comments/slim/_manage_controls.html.slim +27 -0
  33. data/app/views/the_comments/slim/_sidebar.html.slim +28 -0
  34. data/app/views/the_comments/slim/_tree.html.slim +4 -0
  35. data/app/views/the_comments/slim/index.html.slim +18 -0
  36. data/app/views/the_comments/slim/manage.html.slim +25 -0
  37. data/app/views/the_comments/slim/my_comments.html.slim +28 -0
  38. data/{lib/generators/the_comments/templates → config/initializers}/the_comments.rb +3 -0
  39. data/config/locales/en.yml +39 -14
  40. data/config/locales/ru.yml +67 -0
  41. data/config/routes.rb +17 -13
  42. data/db/migrate/20130101010101_change_user.rb +18 -0
  43. data/db/migrate/20130101010102_create_comments.rb +50 -0
  44. data/db/migrate/20130101010103_change_commentable.rb +13 -0
  45. data/docs/admin_ui_installation.md +145 -0
  46. data/docs/advanced_installation.md +182 -0
  47. data/docs/comment_api.md +58 -0
  48. data/docs/commentable_api.md +59 -0
  49. data/docs/config_file.md +27 -0
  50. data/docs/content_preprocessors.md +73 -0
  51. data/docs/customazation_of_views.md +30 -0
  52. data/docs/denormalization_and_recent_comments.md +40 -0
  53. data/docs/documentation.md +28 -0
  54. data/docs/mountable_routes.md +80 -0
  55. data/docs/pagination.md +123 -0
  56. data/docs/screencast.jpg +0 -0
  57. data/docs/user_api.md +75 -0
  58. data/docs/what_is_comcoms.md +63 -0
  59. data/docs/whats_wrong_with_other_gems.md +18 -0
  60. data/docs/where_is_example_application.md +37 -0
  61. data/gem_version.rb +3 -0
  62. data/lib/generators/the_comments/USAGE +31 -20
  63. data/lib/generators/the_comments/the_comments_generator.rb +35 -18
  64. data/lib/generators/the_comments/views_generator.rb +52 -16
  65. data/lib/the_comments/config.rb +14 -1
  66. data/lib/the_comments/version.rb +1 -3
  67. data/lib/the_comments.rb +10 -0
  68. data/spec/dummy_app/.gitignore +17 -0
  69. data/spec/dummy_app/.rspec +1 -0
  70. data/spec/dummy_app/.ruby-gemset +1 -0
  71. data/spec/dummy_app/.ruby-version +1 -0
  72. data/spec/dummy_app/Gemfile +43 -0
  73. data/spec/dummy_app/README.md +50 -0
  74. data/spec/dummy_app/Rakefile +6 -0
  75. data/spec/dummy_app/app/assets/images/.keep +0 -0
  76. data/spec/dummy_app/app/assets/javascripts/admin_panel.js +5 -0
  77. data/spec/dummy_app/app/assets/javascripts/application.js +16 -0
  78. data/spec/dummy_app/app/assets/stylesheets/admin_panel.css +3 -0
  79. data/spec/dummy_app/app/assets/stylesheets/app.css.scss +4 -0
  80. data/spec/dummy_app/app/assets/stylesheets/application.css +16 -0
  81. data/spec/dummy_app/app/controllers/application_controller.rb +7 -0
  82. data/{lib/generators/the_comments/templates → spec/dummy_app/app/controllers}/comments_controller.rb +3 -1
  83. data/spec/dummy_app/app/controllers/concerns/.keep +0 -0
  84. data/spec/dummy_app/app/controllers/posts_controller.rb +13 -0
  85. data/spec/dummy_app/app/controllers/users_controller.rb +7 -0
  86. data/spec/dummy_app/app/helpers/application_helper.rb +2 -0
  87. data/spec/dummy_app/app/mailers/.keep +0 -0
  88. data/spec/dummy_app/app/models/.keep +0 -0
  89. data/spec/dummy_app/app/models/comment.rb +32 -0
  90. data/spec/dummy_app/app/models/concerns/.keep +0 -0
  91. data/spec/dummy_app/app/models/post.rb +17 -0
  92. data/spec/dummy_app/app/models/user.rb +21 -0
  93. data/spec/dummy_app/app/views/layouts/admin.html.haml +25 -0
  94. data/spec/dummy_app/app/views/layouts/application.html.haml +20 -0
  95. data/spec/dummy_app/app/views/posts/index.html.haml +22 -0
  96. data/spec/dummy_app/app/views/posts/show.html.haml +7 -0
  97. data/spec/dummy_app/bin/bundle +3 -0
  98. data/spec/dummy_app/bin/rails +4 -0
  99. data/spec/dummy_app/bin/rake +4 -0
  100. data/spec/dummy_app/config/application.rb +23 -0
  101. data/spec/dummy_app/config/boot.rb +4 -0
  102. data/spec/dummy_app/config/database.yml +11 -0
  103. data/spec/dummy_app/config/environment.rb +5 -0
  104. data/spec/dummy_app/config/environments/development.rb +29 -0
  105. data/spec/dummy_app/config/environments/production.rb +80 -0
  106. data/spec/dummy_app/config/environments/test.rb +36 -0
  107. data/spec/dummy_app/config/initializers/backtrace_silencers.rb +7 -0
  108. data/spec/dummy_app/config/initializers/filter_parameter_logging.rb +4 -0
  109. data/spec/dummy_app/config/initializers/inflections.rb +16 -0
  110. data/spec/dummy_app/config/initializers/mime_types.rb +5 -0
  111. data/spec/dummy_app/config/initializers/secret_token.rb +12 -0
  112. data/spec/dummy_app/config/initializers/session_store.rb +3 -0
  113. data/spec/dummy_app/config/initializers/sorcery.rb +437 -0
  114. data/spec/dummy_app/config/initializers/the_comments.rb +13 -0
  115. data/spec/dummy_app/config/initializers/wrap_parameters.rb +14 -0
  116. data/spec/dummy_app/config/locales/en.yml +23 -0
  117. data/spec/dummy_app/config/routes.rb +15 -0
  118. data/spec/dummy_app/config.ru +4 -0
  119. data/spec/dummy_app/db/migrate/20130712061503_sorcery_core.rb +16 -0
  120. data/spec/dummy_app/db/migrate/20130712065951_create_posts.rb +11 -0
  121. data/spec/dummy_app/db/migrate/20131027185332_change_user.the_comments_engine.rb +19 -0
  122. data/spec/dummy_app/db/migrate/20131027185333_create_comments.the_comments_engine.rb +51 -0
  123. data/spec/dummy_app/db/migrate/20131027185334_change_commentable.the_comments_engine.rb +14 -0
  124. data/spec/dummy_app/db/schema.rb +74 -0
  125. data/spec/dummy_app/db/seeds.rb +42 -0
  126. data/spec/dummy_app/lib/assets/.keep +0 -0
  127. data/spec/dummy_app/lib/tasks/.keep +0 -0
  128. data/spec/dummy_app/lib/tasks/app_bootstrap.rake +15 -0
  129. data/spec/dummy_app/log/.keep +0 -0
  130. data/spec/dummy_app/public/404.html +58 -0
  131. data/spec/dummy_app/public/422.html +58 -0
  132. data/spec/dummy_app/public/500.html +57 -0
  133. data/spec/dummy_app/public/favicon.ico +0 -0
  134. data/spec/dummy_app/public/robots.txt +5 -0
  135. data/spec/dummy_app/spec/factories/post.rb +6 -0
  136. data/spec/dummy_app/spec/factories/user.rb +6 -0
  137. data/spec/dummy_app/spec/models/user_counters_spec.rb +339 -0
  138. data/spec/dummy_app/spec/spec_helper.rb +29 -0
  139. data/spec/dummy_app/test/controllers/.keep +0 -0
  140. data/spec/dummy_app/test/fixtures/.keep +0 -0
  141. data/spec/dummy_app/test/helpers/.keep +0 -0
  142. data/spec/dummy_app/test/integration/.keep +0 -0
  143. data/spec/dummy_app/test/mailers/.keep +0 -0
  144. data/spec/dummy_app/test/models/.keep +0 -0
  145. data/spec/dummy_app/test/test_helper.rb +15 -0
  146. data/spec/dummy_app/vendor/assets/javascripts/.keep +0 -0
  147. data/spec/dummy_app/vendor/assets/stylesheets/.keep +0 -0
  148. data/views_converter.rb +16 -0
  149. metadata +223 -45
  150. data/app/controllers/concerns/the_comments_controller.rb +0 -229
  151. data/app/controllers/concerns/the_comments_ip_controller.rb +0 -17
  152. data/app/controllers/concerns/the_comments_user_agent_controller.rb +0 -15
  153. data/app/models/concerns/the_comments_base.rb +0 -69
  154. data/app/models/concerns/the_comments_black_ip.rb +0 -9
  155. data/app/models/concerns/the_comments_black_user_agent.rb +0 -9
  156. data/app/models/concerns/the_comments_commentable.rb +0 -66
  157. data/app/models/concerns/the_comments_states.rb +0 -65
  158. data/app/models/concerns/the_comments_user.rb +0 -32
  159. data/app/views/ip_black_lists/index.html.haml +0 -17
  160. data/app/views/the_comments/_comment_body.html.haml +0 -30
  161. data/app/views/the_comments/_manage_controls.html.haml +0 -4
  162. data/app/views/the_comments/_tree.html.haml +0 -4
  163. data/app/views/the_comments/index.html.haml +0 -19
  164. data/app/views/the_comments/manage.html.haml +0 -29
  165. data/app/views/user_agent_black_lists/index.html.haml +0 -17
  166. data/db/migrate/20130101010101_create_comments.rb +0 -90
  167. data/lib/generators/the_comments/templates/ip_black_list.rb +0 -3
  168. data/lib/generators/the_comments/templates/ip_black_lists_controller.rb +0 -10
  169. data/lib/generators/the_comments/templates/the_comments_black_ip.rb +0 -9
  170. data/lib/generators/the_comments/templates/the_comments_black_user_agent.rb +0 -9
  171. data/lib/generators/the_comments/templates/user_agent_black_list.rb +0 -3
  172. data/lib/generators/the_comments/templates/user_agent_black_lists_controller.rb +0 -10
@@ -1,5 +1,5 @@
1
1
  # ERROR MSG BUILDER
2
- @error_text_builder = (errors) ->
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
- # JUST HELPER
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 = error_text_builder(["Please wait #{delta} secs"])
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
- # AJAX ERROR
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 = error_text_builder(["Server Error: #{response.status}"])
56
+ error_msgs = comments_errors_builder(["Server Error: #{response.status}"])
55
57
  comments_error_notifier(form, error_msgs)
56
58
 
57
- # COMMENT FORMS => SUCCESS
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 = error_text_builder(response.errors)
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
- # CONTROLS
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
- # INPLACE EDIT
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
- # FOR MANAGE SECTION
28
- list = $('.comments_list')
29
-
30
- list.on 'click', '.controls a.view', ->
31
- form = $(@).parents('div.form')
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
- list.on 'click', '.controls a.edit', ->
38
- body = $(@).parents('div.body')
39
- form = body.siblings('.form')
40
- body.hide()
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
- # BLACK LIST
45
- holder = $('.black_list')
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
- holder.on 'ajax:success', '.to_banned', (request, response, status) ->
54
- link = $ @
55
- li = link.parents('li').first()
56
- li.attr 'class', 'banned'
57
- li.find('.state').html 'banned'
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, .black_list{
1
+ .comments_tree, .comments_list{
2
2
  font-family: Arial;
3
3
 
4
- margin:0; padding:0;
5
- *{ margin: 0; padding: 0; }
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-color: #EED3D7;
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
- background: white;
40
- border: 1px solid gray;
41
- border-radius: 3px;
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
- border-bottom: 1px solid LightGray;
129
+ font-size: 15px;
130
+ border-bottom: 1px solid #eee;
122
131
  padding-bottom: 10px;
123
132
  line-height: 135%;
124
- margin-bottom: 0;
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(:comment_moderator?, @comment)
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
- t += h.link_to(t('the_comments.edit'), h.edit_comment_url(@comment), class: :edit)
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