the_comments_ruby 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +30 -0
  3. data/.ruby-gemset.example +1 -0
  4. data/.ruby-version.example +1 -0
  5. data/.rvmrc.example +1 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +338 -0
  10. data/Rakefile +1 -0
  11. data/app/assets/javascripts/the_comments.js.coffee +108 -0
  12. data/app/assets/javascripts/the_comments_manage.js.coffee +27 -0
  13. data/app/assets/stylesheets/the_comments.css.scss +248 -0
  14. data/app/controllers/_templates_/comments_controller.rb +44 -0
  15. data/app/controllers/concerns/the_comments/controller.rb +197 -0
  16. data/app/controllers/concerns/the_comments/view_token.rb +20 -0
  17. data/app/helpers/render_comments_tree_helper.rb +111 -0
  18. data/app/models/_templates_/comment.rb +38 -0
  19. data/app/models/concerns/the_comments/comment.rb +116 -0
  20. data/app/models/concerns/the_comments/comment_states.rb +80 -0
  21. data/app/models/concerns/the_comments/commentable.rb +69 -0
  22. data/app/models/concerns/the_comments/user.rb +56 -0
  23. data/app/views/the_comments/_tree.html.erb +3 -0
  24. data/app/views/the_comments/haml/_additional_info.html.haml +13 -0
  25. data/app/views/the_comments/haml/_comment.html.haml +1 -0
  26. data/app/views/the_comments/haml/_comment_body.html.haml +25 -0
  27. data/app/views/the_comments/haml/_comment_edit.html.haml +26 -0
  28. data/app/views/the_comments/haml/_form.html.haml +8 -0
  29. data/app/views/the_comments/haml/_guest_form.html.haml +22 -0
  30. data/app/views/the_comments/haml/_logined_form.html.haml +18 -0
  31. data/app/views/the_comments/haml/_manage_controls.html.haml +30 -0
  32. data/app/views/the_comments/haml/_sidebar.html.haml +9 -0
  33. data/app/views/the_comments/haml/_sidebar_admin.html.haml +12 -0
  34. data/app/views/the_comments/haml/_sidebar_backlink.html.haml +3 -0
  35. data/app/views/the_comments/haml/_sidebar_user.html.haml +29 -0
  36. data/app/views/the_comments/haml/_tree.html.haml +16 -0
  37. data/app/views/the_comments/haml/manage.html.haml +26 -0
  38. data/app/views/the_comments/slim/_additional_info.html.slim +13 -0
  39. data/app/views/the_comments/slim/_comment.html.slim +1 -0
  40. data/app/views/the_comments/slim/_comment_body.html.slim +24 -0
  41. data/app/views/the_comments/slim/_comment_edit.html.slim +26 -0
  42. data/app/views/the_comments/slim/_form.html.slim +8 -0
  43. data/app/views/the_comments/slim/_guest_form.html.slim +22 -0
  44. data/app/views/the_comments/slim/_logined_form.html.slim +18 -0
  45. data/app/views/the_comments/slim/_manage_controls.html.slim +30 -0
  46. data/app/views/the_comments/slim/_sidebar.html.slim +9 -0
  47. data/app/views/the_comments/slim/_sidebar_admin.html.slim +12 -0
  48. data/app/views/the_comments/slim/_sidebar_backlink.html.slim +3 -0
  49. data/app/views/the_comments/slim/_sidebar_user.html.slim +29 -0
  50. data/app/views/the_comments/slim/_tree.html.slim +16 -0
  51. data/app/views/the_comments/slim/index.html.slim +18 -0
  52. data/app/views/the_comments/slim/manage.html.slim +26 -0
  53. data/app/views/the_comments/slim/my_comments.html.slim +28 -0
  54. data/config/initializers/the_comments.rb +14 -0
  55. data/config/locales/en.yml +68 -0
  56. data/config/locales/ru.yml +71 -0
  57. data/config/routes.rb +38 -0
  58. data/db/migrate/20130101010101_the_comments_change_user.rb +18 -0
  59. data/db/migrate/20130101010102_the_comments_create_comments.rb +50 -0
  60. data/db/migrate/20130101010103_the_comments_change_commentable.rb +13 -0
  61. data/docs/admin_ui_installation.md +145 -0
  62. data/docs/advanced_installation.md +185 -0
  63. data/docs/comment_api.md +58 -0
  64. data/docs/commentable_api.md +59 -0
  65. data/docs/config_file.md +27 -0
  66. data/docs/content_preprocessors.md +73 -0
  67. data/docs/customazation_of_views.md +30 -0
  68. data/docs/denormalization_and_recent_comments.md +40 -0
  69. data/docs/documentation.md +29 -0
  70. data/docs/generators.md +74 -0
  71. data/docs/pagination.md +123 -0
  72. data/docs/routes.md +77 -0
  73. data/docs/screencast.jpg +0 -0
  74. data/docs/the_comments.jpg +0 -0
  75. data/docs/the_comments_view_1.gif +0 -0
  76. data/docs/the_comments_view_2.gif +0 -0
  77. data/docs/the_comments_view_3.gif +0 -0
  78. data/docs/the_comments_view_4.gif +0 -0
  79. data/docs/the_comments_view_5.gif +0 -0
  80. data/docs/user_api.md +75 -0
  81. data/docs/what_is_comcoms.md +63 -0
  82. data/docs/whats_wrong_with_other_gems.md +28 -0
  83. data/docs/where_is_example_application.md +37 -0
  84. data/gem_version.rb +3 -0
  85. data/lib/generators/the_comments/USAGE +44 -0
  86. data/lib/generators/the_comments/the_comments_generator.rb +56 -0
  87. data/lib/generators/the_comments/views_generator.rb +79 -0
  88. data/lib/the_comments/config.rb +37 -0
  89. data/lib/the_comments/version.rb +1 -0
  90. data/lib/the_comments.rb +28 -0
  91. data/spec/dummy_app/.gitignore +18 -0
  92. data/spec/dummy_app/.rspec +1 -0
  93. data/spec/dummy_app/Gemfile +43 -0
  94. data/spec/dummy_app/README.md +33 -0
  95. data/spec/dummy_app/Rakefile +6 -0
  96. data/spec/dummy_app/app/assets/images/.keep +0 -0
  97. data/spec/dummy_app/app/assets/javascripts/admin_panel.js +5 -0
  98. data/spec/dummy_app/app/assets/javascripts/application.js +16 -0
  99. data/spec/dummy_app/app/assets/stylesheets/admin_panel.css +3 -0
  100. data/spec/dummy_app/app/assets/stylesheets/app.css.scss +4 -0
  101. data/spec/dummy_app/app/assets/stylesheets/application.css +16 -0
  102. data/spec/dummy_app/app/controllers/application_controller.rb +7 -0
  103. data/spec/dummy_app/app/controllers/comments_controller.rb +28 -0
  104. data/spec/dummy_app/app/controllers/concerns/.keep +0 -0
  105. data/spec/dummy_app/app/controllers/posts_controller.rb +13 -0
  106. data/spec/dummy_app/app/controllers/users_controller.rb +7 -0
  107. data/spec/dummy_app/app/helpers/application_helper.rb +2 -0
  108. data/spec/dummy_app/app/mailers/.keep +0 -0
  109. data/spec/dummy_app/app/models/.keep +0 -0
  110. data/spec/dummy_app/app/models/comment.rb +32 -0
  111. data/spec/dummy_app/app/models/concerns/.keep +0 -0
  112. data/spec/dummy_app/app/models/post.rb +17 -0
  113. data/spec/dummy_app/app/models/user.rb +21 -0
  114. data/spec/dummy_app/app/views/layouts/admin.html.haml +25 -0
  115. data/spec/dummy_app/app/views/layouts/application.html.haml +20 -0
  116. data/spec/dummy_app/app/views/posts/index.html.haml +22 -0
  117. data/spec/dummy_app/app/views/posts/show.html.haml +7 -0
  118. data/spec/dummy_app/bin/bundle +3 -0
  119. data/spec/dummy_app/bin/rails +4 -0
  120. data/spec/dummy_app/bin/rake +4 -0
  121. data/spec/dummy_app/config/application.rb +23 -0
  122. data/spec/dummy_app/config/boot.rb +4 -0
  123. data/spec/dummy_app/config/database.yml +11 -0
  124. data/spec/dummy_app/config/environment.rb +5 -0
  125. data/spec/dummy_app/config/environments/development.rb +29 -0
  126. data/spec/dummy_app/config/environments/production.rb +80 -0
  127. data/spec/dummy_app/config/environments/test.rb +36 -0
  128. data/spec/dummy_app/config/initializers/backtrace_silencers.rb +7 -0
  129. data/spec/dummy_app/config/initializers/filter_parameter_logging.rb +4 -0
  130. data/spec/dummy_app/config/initializers/inflections.rb +16 -0
  131. data/spec/dummy_app/config/initializers/mime_types.rb +5 -0
  132. data/spec/dummy_app/config/initializers/secret_token.rb +12 -0
  133. data/spec/dummy_app/config/initializers/session_store.rb +3 -0
  134. data/spec/dummy_app/config/initializers/sorcery.rb +437 -0
  135. data/spec/dummy_app/config/initializers/the_comments.rb +14 -0
  136. data/spec/dummy_app/config/initializers/wrap_parameters.rb +14 -0
  137. data/spec/dummy_app/config/locales/en.yml +23 -0
  138. data/spec/dummy_app/config/routes.rb +19 -0
  139. data/spec/dummy_app/config.ru +4 -0
  140. data/spec/dummy_app/db/migrate/20130712061503_sorcery_core.rb +16 -0
  141. data/spec/dummy_app/db/migrate/20130712065951_create_posts.rb +11 -0
  142. data/spec/dummy_app/db/migrate/20131027185332_change_user.the_comments_engine.rb +19 -0
  143. data/spec/dummy_app/db/migrate/20131027185333_create_comments.the_comments_engine.rb +51 -0
  144. data/spec/dummy_app/db/migrate/20131027185334_change_commentable.the_comments_engine.rb +14 -0
  145. data/spec/dummy_app/db/schema.rb +74 -0
  146. data/spec/dummy_app/db/seeds.rb +42 -0
  147. data/spec/dummy_app/lib/assets/.keep +0 -0
  148. data/spec/dummy_app/lib/tasks/.keep +0 -0
  149. data/spec/dummy_app/lib/tasks/app_bootstrap.rake +15 -0
  150. data/spec/dummy_app/log/.keep +0 -0
  151. data/spec/dummy_app/public/404.html +58 -0
  152. data/spec/dummy_app/public/422.html +58 -0
  153. data/spec/dummy_app/public/500.html +57 -0
  154. data/spec/dummy_app/public/favicon.ico +0 -0
  155. data/spec/dummy_app/public/robots.txt +5 -0
  156. data/spec/dummy_app/spec/factories/post.rb +6 -0
  157. data/spec/dummy_app/spec/factories/user.rb +6 -0
  158. data/spec/dummy_app/spec/models/user_counters_spec.rb +339 -0
  159. data/spec/dummy_app/spec/spec_helper.rb +29 -0
  160. data/spec/dummy_app/test/controllers/.keep +0 -0
  161. data/spec/dummy_app/test/fixtures/.keep +0 -0
  162. data/spec/dummy_app/test/helpers/.keep +0 -0
  163. data/spec/dummy_app/test/integration/.keep +0 -0
  164. data/spec/dummy_app/test/mailers/.keep +0 -0
  165. data/spec/dummy_app/test/models/.keep +0 -0
  166. data/spec/dummy_app/test/test_helper.rb +15 -0
  167. data/spec/dummy_app/vendor/assets/javascripts/.keep +0 -0
  168. data/spec/dummy_app/vendor/assets/stylesheets/.keep +0 -0
  169. data/the_comments.gemspec +25 -0
  170. data/the_comments.yml.teamocil.example +11 -0
  171. data/views_converter.rb +16 -0
  172. metadata +333 -0
@@ -0,0 +1,248 @@
1
+ .comments_tree, .comments_list{
2
+ font-family: Arial;
3
+
4
+ margin:0;
5
+ padding:0;
6
+ margin-bottom: 30px;
7
+
8
+ *{ margin: 0; padding: 0; font-size: inherit; }
9
+
10
+ a{ text-decoration: none; }
11
+ a:hover{ text-decoration: underline; }
12
+
13
+ ol{
14
+ margin: 0;
15
+ padding: 0 0 0 20px;
16
+ list-style: none outside none;
17
+ }
18
+ li{
19
+ margin-bottom: 5px;
20
+ position: relative;
21
+ list-style: none outside none;
22
+ }
23
+ }
24
+
25
+ .action_btns a{ margin-right: 15px; }
26
+
27
+ .comments, .comments_tree{
28
+ font-family: Arial;
29
+ font-size: 13px;
30
+
31
+ h3{ font-size: 1.6em; }
32
+
33
+ .error_notifier{
34
+ background-color: #F2DEDE;
35
+ border: 1px solid #B94A48;
36
+ color: #B94A48;
37
+
38
+ border-radius: 4px;
39
+ margin: 0 0 15px 0;
40
+ padding: 10px 10px 0 10px;
41
+ overflow: hidden;
42
+
43
+ p{ margin: 0 0 10px 0; }
44
+ }
45
+ form{
46
+
47
+ background: #e0e4f5;
48
+
49
+ border: 1px solid #c6cff5;
50
+ border-radius: 5px;
51
+ padding: 10px;
52
+
53
+ p{ margin: 0 0 10px 0; }
54
+
55
+ input[type=text]{
56
+ border: 1px solid gray;
57
+ padding: 4px;
58
+ width: 75%;
59
+ }
60
+ label{ font-size: 15px; }
61
+ textarea{
62
+ border: 1px solid gray;
63
+ font-family: Arial;
64
+ font-size: 13px;
65
+ height: 150px;
66
+ padding: 4px;
67
+ width: 75%;
68
+ }
69
+ .trap{
70
+ margin: 0; padding: 0;
71
+ filter: alpha(opacity=0.001);
72
+ height: 0.1px;
73
+ opacity: 0.001;
74
+ overflow: hidden;
75
+ }
76
+ }
77
+ }
78
+
79
+ .comments_tree{
80
+ .nested_set{
81
+ border-left: 1px dotted lightGray;
82
+ }
83
+
84
+ li{
85
+ .comment.draft{
86
+ border: 1px solid gray;
87
+ background: #eff5f3;
88
+ padding: 10px;
89
+ }
90
+ }
91
+
92
+ .form_holder{ margin-left: 40px; }
93
+
94
+ .edit, .delete{
95
+ margin-bottom: 3px;
96
+ text-align:center;
97
+ line-height: 130%;
98
+ background: #336;
99
+ color: white;
100
+ padding: 1px;
101
+ }
102
+ .delete{ background: gray; }
103
+
104
+ .comment{
105
+ overflow: hidden; zoom: 1;
106
+
107
+ .userpic{
108
+ overflow: hidden; zoom: 1;
109
+ float: left;
110
+ width: 50px;
111
+
112
+ img{ margin-bottom: 10px; width: 42px; height: 42px; }
113
+ }
114
+ .userbar, .cbody{
115
+ margin: 0 0 5px 55px;
116
+ padding: 3px;
117
+ }
118
+ .userbar{
119
+ background: #eff5f3;
120
+ border-radius: 3px;
121
+ padding-left: 7px;
122
+ }
123
+ &.draft{
124
+ .userbar{ background: #ffa768; }
125
+ .to_draft{ display: none; }
126
+ }
127
+ &.published{
128
+ .to_published{ display: none; }
129
+ }
130
+ .cbody{
131
+ font-size: 15px;
132
+ border-bottom: 1px solid #eee;
133
+ padding-bottom: 10px;
134
+ line-height: 135%;
135
+ margin-bottom: 3px;
136
+ overflow: hidden;
137
+ }
138
+ .reply{
139
+ margin: 0 0 5px 55px;
140
+ font-size: 12px;
141
+ }
142
+ }
143
+
144
+ .controls{
145
+ position: absolute;
146
+ top: 53px; left: 5px;
147
+
148
+ a{
149
+ font-size:11px;
150
+ display:block;
151
+ }
152
+ }
153
+
154
+ .comment{
155
+ margin-bottom: 10px;
156
+
157
+ &.published, &.draft{
158
+ margin-bottom: 10px;
159
+ border-radius: 3px;
160
+ padding: 5px;
161
+ }
162
+ &.highlighted{ border: 1px dashed #ff6633 !important; }
163
+
164
+ }
165
+
166
+ .form_holder{
167
+ form{ margin: 10px 0; }
168
+ }
169
+ }
170
+
171
+ .new_comment{
172
+ .btn{
173
+ padding: 7px;
174
+ margin-top: 5px;
175
+ cursor: pointer;
176
+ }
177
+ }
178
+
179
+ .comments_list{
180
+ li{
181
+ margin-bottom: 20px;
182
+
183
+ .item{
184
+ border: 1px solid gray;
185
+ border-radius: 5px;
186
+ padding: 10px;
187
+ }
188
+
189
+ .draft{
190
+ border-left: 5px solid orange;
191
+
192
+ .controls{
193
+ a.to_draft{ display: none }
194
+ }
195
+ }
196
+ .published{
197
+ border-left: 5px solid green;
198
+
199
+ .controls{
200
+ a.to_published{ display: none; }
201
+ }
202
+ }
203
+
204
+ .deleted{
205
+ border-left: 5px solid red;
206
+
207
+ .controls{
208
+ a.to_deleted, a.to_spam{ display: none; }
209
+ }
210
+ }
211
+
212
+ .comment{
213
+ div{ margin: 0 0 10px 0; }
214
+
215
+ label{
216
+ width: 75px;
217
+ font-weight: bold;
218
+ display: inline-block;
219
+ }
220
+ input[type=text]{
221
+ width: 70%;
222
+ padding: 4px;
223
+ }
224
+ input[type=submit]{
225
+ padding: 5px;
226
+ cursor: pointer;
227
+ }
228
+ textarea{
229
+ width: 70%;
230
+ padding: 4px;
231
+ height: 150px;
232
+ }
233
+ .content{
234
+ line-height: 130%;
235
+ background: #ddd;
236
+ padding: 10px;
237
+ }
238
+ .commentable{
239
+ margin-bottom: 10px;
240
+ }
241
+ .controls{
242
+ background: lightgray;
243
+ padding: 3px;
244
+ a{ margin-right: 15px; }
245
+ }
246
+ }
247
+ }
248
+ }
@@ -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,197 @@
1
+ module TheComments
2
+ # Base functionality of Comments Controller
3
+ # class CommentsController < ApplicationController
4
+ # include TheComments::Controller
5
+ # end
6
+ module Controller
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ include TheComments::ViewToken
11
+
12
+ # Attention! We should not set TheComments cookie before create
13
+ skip_before_action :set_the_comments_cookies, only: [:create]
14
+
15
+ # Spam protection
16
+ before_action -> { @errors = [] }, only: [:create]
17
+
18
+ before_action :ajax_requests_required, only: [:create]
19
+ before_action :cookies_required, only: [:create]
20
+
21
+ before_action :empty_trap_required, only: [:create], if: -> { TheComments.config.empty_trap_protection }
22
+ before_action :tolerance_time_required, only: [:create], if: -> { TheComments.config.tolerance_time_protection }
23
+
24
+ # preparation
25
+ before_action :define_commentable, only: [:create]
26
+
27
+ # raise an errors
28
+ before_action -> { return render(json: { errors: @errors }) unless @errors.blank? }, only: [:create]
29
+ end
30
+
31
+ # App side methods (you can overwrite them)
32
+
33
+ def manage
34
+ @comments = current_user.comcoms.with_users.active.recent.page(params[:page])
35
+ render comment_template(:manage)
36
+ end
37
+
38
+ def my_comments
39
+ @comments = current_user.my_comments.with_users.active.recent.page(params[:page])
40
+ render comment_template(:manage)
41
+ end
42
+
43
+ # Methods based on *current_user* helper
44
+ # Methods for admin
45
+ %w[draft published deleted].each do |state|
46
+ define_method "#{state}" do
47
+ @comments = current_user.comcoms.with_users.with_state(state).recent.page(params[:page])
48
+ render comment_template(:manage)
49
+ end
50
+
51
+ define_method "total_#{state}" do
52
+ @comments = ::Comment.with_state(state).with_users.recent.page(params[:page])
53
+ render comment_template(:manage)
54
+ end
55
+
56
+ define_method "my_#{state}" do
57
+ @comments = current_user.my_comments.with_users.with_state(state).recent.page(params[:page])
58
+ render comment_template(:manage)
59
+ end
60
+ end
61
+
62
+ def spam
63
+ @comments = current_user.comcoms.with_users.where(spam: true).recent.page(params[:page])
64
+ render comment_template(:manage)
65
+ end
66
+
67
+ def my_spam
68
+ @comments = current_user.my_comments.with_users.where(spam: true).recent.page(params[:page])
69
+ render comment_template(:manage)
70
+ end
71
+
72
+ def total_spam
73
+ @comments = ::Comment.where(spam: true).with_users.recent.page(params[:page])
74
+ render comment_template(:manage)
75
+ end
76
+
77
+ # BASE METHODS
78
+
79
+ # Public methods
80
+
81
+ def create
82
+ @comment = @commentable.comments.new comment_params
83
+ if @comment.valid?
84
+ @comment.save
85
+ return render layout: false, partial: comment_partial(:comment), locals: { tree: @comment }
86
+ end
87
+ render json: { errors: @comment.errors }
88
+ end
89
+
90
+ # Restricted area
91
+
92
+ def edit
93
+ @comments = current_user.comcoms.where(id: params[:id]).page(params[:page])
94
+ render comment_template(:manage)
95
+ end
96
+
97
+ def update
98
+ comment = ::Comment.find(params[:id])
99
+ comment.update_attributes!(patch_comment_params)
100
+ render(layout: false, partial: comment_partial(:comment_body), locals: { comment: comment })
101
+ end
102
+
103
+ %w[draft published deleted].each do |state|
104
+ define_method "to_#{state}" do
105
+ ::Comment.find(params[:id]).try "to_#{state}"
106
+ render nothing: true
107
+ end
108
+ end
109
+
110
+ def to_spam
111
+ comment = ::Comment.find(params[:id])
112
+ comment.to_spam
113
+ comment.to_deleted
114
+ render nothing: true
115
+ end
116
+
117
+ private
118
+
119
+ def comment_template template
120
+ { template: "the_comments/#{TheComments.config.template_engine}/#{template}" }
121
+ end
122
+
123
+ def comment_partial partial
124
+ "the_comments/#{TheComments.config.template_engine}/#{partial}"
125
+ end
126
+
127
+ def denormalized_fields
128
+ title = @commentable.commentable_title
129
+ url = @commentable.commentable_url
130
+ @commentable ? { commentable_title: title, commentable_url: url } : {}
131
+ end
132
+
133
+ def request_data_for_comment
134
+ r = request
135
+ { ip: r.ip, referer: CGI::unescape(r.referer || 'direct_visit'), user_agent: r.user_agent }
136
+ end
137
+
138
+ def define_commentable
139
+ commentable_klass = params[:comment][:commentable_type].constantize
140
+ commentable_id = params[:comment][:commentable_id]
141
+
142
+ @commentable = commentable_klass.find(commentable_id)
143
+ return render(json: { errors: [t('the_comments.undefined_commentable')] }) unless @commentable
144
+ end
145
+
146
+ def comment_params
147
+ params
148
+ .require(:comment)
149
+ .permit(:title, :contacts, :raw_content, :parent_id)
150
+ .merge(denormalized_fields)
151
+ .merge(request_data_for_comment)
152
+ .merge(tolerance_time: params[:tolerance_time].to_i)
153
+ .merge(user: current_user, view_token: comments_view_token)
154
+ end
155
+
156
+ def patch_comment_params
157
+ params
158
+ .require(:comment)
159
+ .permit(:title, :contacts, :raw_content, :parent_id)
160
+ end
161
+
162
+ # Protection hooks
163
+ def ajax_requests_required
164
+ unless request.xhr?
165
+ return render(text: t('the_comments.ajax_requests_required'))
166
+ end
167
+ end
168
+
169
+ def cookies_required
170
+ if cookies[:the_comment_cookies] != TheComments::COMMENTS_COOKIES_TOKEN
171
+ @errors << [t('the_comments.cookies'), t('the_comments.cookies_required')].join(': ')
172
+ end
173
+ end
174
+
175
+ # TODO:
176
+ # 1) inject ?
177
+ # 2) fields can be removed on client side
178
+ def empty_trap_required
179
+ is_human = true
180
+ params.slice(*TheComments.config.empty_inputs).values.each{|v| is_human = (is_human && v.blank?) }
181
+
182
+ if !is_human
183
+ @errors << [t('the_comments.trap'), t('the_comments.trap_message')].join(': ')
184
+ end
185
+ end
186
+
187
+ def tolerance_time_required
188
+ this_time = params[:tolerance_time].to_i
189
+ min_time = TheComments.config.tolerance_time.to_i
190
+
191
+ if this_time < min_time
192
+ tdiff = min_time - this_time
193
+ @errors << [t('the_comments.tolerance_time'), t('the_comments.tolerance_time_message', time: tdiff )].join(': ')
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,20 @@
1
+ module TheComments
2
+ # Cookies and View token for spam protection
3
+ # include TheComments::ViewToken
4
+ module ViewToken
5
+ extend ActiveSupport::Concern
6
+
7
+ included { before_action :set_the_comments_cookies }
8
+
9
+ def comments_view_token
10
+ cookies[:comments_view_token]
11
+ end
12
+
13
+ private
14
+
15
+ def set_the_comments_cookies
16
+ cookies[:the_comment_cookies] = { value: TheComments::COMMENTS_COOKIES_TOKEN, expires: 1.year.from_now }
17
+ cookies[:comments_view_token] = { value: SecureRandom.hex, expires: 7.days.from_now } unless cookies[:comments_view_token]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,111 @@
1
+ # coding: UTF-8
2
+ # DOC:
3
+ # We use Helper Methods for tree building,
4
+ # because it's faster than View Templates and Partials
5
+
6
+ # SECURITY note
7
+ # Prepare your data on server side for rendering
8
+ # or use h.html_escape(node.content)
9
+ # for escape potentially dangerous content
10
+ module RenderCommentsTreeHelper
11
+ module Render
12
+ class << self
13
+ attr_accessor :h, :options
14
+
15
+ # Main Helpers
16
+ def controller
17
+ @options[:controller]
18
+ end
19
+
20
+ def t str
21
+ controller.t str
22
+ end
23
+
24
+ # Render Helpers
25
+ def visible_draft?
26
+ controller.try(:comments_view_token) == @comment.view_token
27
+ end
28
+
29
+ def moderator?
30
+ controller.try(:current_user).try(:comments_moderator?, @comment)
31
+ end
32
+
33
+ # Render Methods
34
+ def render_node(h, options)
35
+ @h, @options = h, options
36
+ @comment = options[:node]
37
+
38
+ @max_reply_depth = options[:max_reply_depth] || TheComments.config.max_reply_depth
39
+
40
+ if @comment.draft?
41
+ draft_comment
42
+ else @comment.published?
43
+ published_comment
44
+ end
45
+ end
46
+
47
+ def draft_comment
48
+ if visible_draft? || moderator?
49
+ published_comment
50
+ else
51
+ "<li class='draft'>
52
+ <div class='comment draft' id='comment_#{@comment.anchor}'>
53
+ #{ t('the_comments.waiting_for_moderation') }
54
+ #{ h.link_to '#', '#comment_' + @comment.anchor }
55
+ </div>
56
+ #{ children }
57
+ </li>"
58
+ end
59
+ end
60
+
61
+ def published_comment
62
+ "<li>
63
+ <div id='comment_#{@comment.anchor}' class='comment #{@comment.state}' data-comment-id='#{@comment.to_param}'>
64
+ <div>
65
+ #{ avatar }
66
+ #{ userbar }
67
+ <div class='cbody'>#{ @comment.content }</div>
68
+ #{ reply }
69
+ </div>
70
+ </div>
71
+
72
+ <div class='form_holder'></div>
73
+ #{ children }
74
+ </li>"
75
+ end
76
+
77
+ def avatar
78
+ "<div class='userpic'>
79
+ <img src='#{ @comment.avatar_url }' alt='userpic' />
80
+ #{ controls }
81
+ </div>"
82
+ end
83
+
84
+ def userbar
85
+ anchor = h.link_to('#', '#comment_' + @comment.anchor)
86
+ title = @comment.title.blank? ? t('the_comments.guest_name') : @comment.title
87
+ "<div class='userbar'>#{ title } #{ anchor }</div>"
88
+ end
89
+
90
+ def moderator_controls
91
+ if moderator?
92
+ h.link_to(t('the_comments.edit'), h.edit_comment_url(@comment), class: :edit)
93
+ end
94
+ end
95
+
96
+ def reply
97
+ if @comment.depth < (@max_reply_depth - 1)
98
+ "<p class='reply'><a href='#' class='reply_link'>#{ t('the_comments.reply') }</a>"
99
+ end
100
+ end
101
+
102
+ def controls
103
+ "<div class='controls'>#{ moderator_controls }</div>"
104
+ end
105
+
106
+ def children
107
+ "<ol class='nested_set'>#{ options[:children] }</ol>"
108
+ end
109
+ end
110
+ end
111
+ 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