the_comments_ruby 2.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +30 -0
- data/.ruby-gemset.example +1 -0
- data/.ruby-version.example +1 -0
- data/.rvmrc.example +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +338 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/the_comments.js.coffee +108 -0
- data/app/assets/javascripts/the_comments_manage.js.coffee +27 -0
- data/app/assets/stylesheets/the_comments.css.scss +248 -0
- data/app/controllers/_templates_/comments_controller.rb +44 -0
- data/app/controllers/concerns/the_comments/controller.rb +197 -0
- data/app/controllers/concerns/the_comments/view_token.rb +20 -0
- data/app/helpers/render_comments_tree_helper.rb +111 -0
- data/app/models/_templates_/comment.rb +38 -0
- data/app/models/concerns/the_comments/comment.rb +116 -0
- data/app/models/concerns/the_comments/comment_states.rb +80 -0
- data/app/models/concerns/the_comments/commentable.rb +69 -0
- data/app/models/concerns/the_comments/user.rb +56 -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/haml/_comment.html.haml +1 -0
- data/app/views/the_comments/haml/_comment_body.html.haml +25 -0
- data/app/views/the_comments/haml/_comment_edit.html.haml +26 -0
- data/app/views/the_comments/haml/_form.html.haml +8 -0
- data/app/views/the_comments/haml/_guest_form.html.haml +22 -0
- data/app/views/the_comments/haml/_logined_form.html.haml +18 -0
- data/app/views/the_comments/haml/_manage_controls.html.haml +30 -0
- data/app/views/the_comments/haml/_sidebar.html.haml +9 -0
- data/app/views/the_comments/haml/_sidebar_admin.html.haml +12 -0
- data/app/views/the_comments/haml/_sidebar_backlink.html.haml +3 -0
- data/app/views/the_comments/haml/_sidebar_user.html.haml +29 -0
- data/app/views/the_comments/haml/_tree.html.haml +16 -0
- data/app/views/the_comments/haml/manage.html.haml +26 -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 +24 -0
- data/app/views/the_comments/slim/_comment_edit.html.slim +26 -0
- data/app/views/the_comments/slim/_form.html.slim +8 -0
- data/app/views/the_comments/slim/_guest_form.html.slim +22 -0
- data/app/views/the_comments/slim/_logined_form.html.slim +18 -0
- data/app/views/the_comments/slim/_manage_controls.html.slim +30 -0
- data/app/views/the_comments/slim/_sidebar.html.slim +9 -0
- data/app/views/the_comments/slim/_sidebar_admin.html.slim +12 -0
- data/app/views/the_comments/slim/_sidebar_backlink.html.slim +3 -0
- data/app/views/the_comments/slim/_sidebar_user.html.slim +29 -0
- data/app/views/the_comments/slim/_tree.html.slim +16 -0
- data/app/views/the_comments/slim/index.html.slim +18 -0
- data/app/views/the_comments/slim/manage.html.slim +26 -0
- data/app/views/the_comments/slim/my_comments.html.slim +28 -0
- data/config/initializers/the_comments.rb +14 -0
- data/config/locales/en.yml +68 -0
- data/config/locales/ru.yml +71 -0
- data/config/routes.rb +38 -0
- data/db/migrate/20130101010101_the_comments_change_user.rb +18 -0
- data/db/migrate/20130101010102_the_comments_create_comments.rb +50 -0
- data/db/migrate/20130101010103_the_comments_change_commentable.rb +13 -0
- data/docs/admin_ui_installation.md +145 -0
- data/docs/advanced_installation.md +185 -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 +29 -0
- data/docs/generators.md +74 -0
- data/docs/pagination.md +123 -0
- data/docs/routes.md +77 -0
- data/docs/screencast.jpg +0 -0
- data/docs/the_comments.jpg +0 -0
- data/docs/the_comments_view_1.gif +0 -0
- data/docs/the_comments_view_2.gif +0 -0
- data/docs/the_comments_view_3.gif +0 -0
- data/docs/the_comments_view_4.gif +0 -0
- data/docs/the_comments_view_5.gif +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 +28 -0
- data/docs/where_is_example_application.md +37 -0
- data/gem_version.rb +3 -0
- data/lib/generators/the_comments/USAGE +44 -0
- data/lib/generators/the_comments/the_comments_generator.rb +56 -0
- data/lib/generators/the_comments/views_generator.rb +79 -0
- data/lib/the_comments/config.rb +37 -0
- data/lib/the_comments/version.rb +1 -0
- data/lib/the_comments.rb +28 -0
- data/spec/dummy_app/.gitignore +18 -0
- data/spec/dummy_app/.rspec +1 -0
- data/spec/dummy_app/Gemfile +43 -0
- data/spec/dummy_app/README.md +33 -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/spec/dummy_app/app/controllers/comments_controller.rb +28 -0
- 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 +14 -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 +19 -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/the_comments.gemspec +25 -0
- data/the_comments.yml.teamocil.example +11 -0
- data/views_converter.rb +16 -0
- 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
|