1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @package Comments |
4
|
|
|
* @category modules |
5
|
|
|
* @author Nazar Mokrynskyi <[email protected]> |
6
|
|
|
* @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi |
7
|
|
|
* @license MIT License, see license.txt |
8
|
|
|
*/ |
9
|
|
|
namespace cs\modules\Comments; |
10
|
|
|
use |
11
|
|
|
h, |
12
|
|
|
cs\Cache, |
13
|
|
|
cs\Config, |
14
|
|
|
cs\Language, |
15
|
|
|
cs\Request, |
16
|
|
|
cs\User, |
17
|
|
|
cs\CRUD_helpers, |
18
|
|
|
cs\Singleton; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @method static $this instance($check = false) |
22
|
|
|
*/ |
23
|
|
|
class Comments { |
24
|
|
|
use |
25
|
|
|
CRUD_helpers, |
26
|
|
|
Singleton; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var Cache\Prefix |
30
|
|
|
*/ |
31
|
|
|
protected $cache; |
32
|
|
|
/** |
33
|
|
|
* @var int Avatar size in px, can be redefined |
34
|
|
|
*/ |
35
|
|
|
public $avatar_size = 36; |
36
|
|
|
|
37
|
|
|
protected $data_model = [ |
38
|
|
|
'id' => 'int:1', |
39
|
|
|
'parent' => 'int:0', |
40
|
|
|
'module' => 'text', |
41
|
|
|
'item' => 'int:1', |
42
|
|
|
'user' => 'int:1', |
43
|
|
|
'date' => 'int:1', |
44
|
|
|
'text' => 'html', |
45
|
|
|
'lang' => 'text' |
46
|
|
|
]; |
47
|
|
|
|
48
|
|
|
protected $table = '[prefix]comments'; |
49
|
|
|
|
50
|
|
|
protected function construct () { |
51
|
|
|
$this->cache = Cache::prefix('Comments'); |
52
|
|
|
} |
53
|
|
|
/** |
54
|
|
|
* Returns database index |
55
|
|
|
* |
56
|
|
|
* @return int |
57
|
|
|
*/ |
58
|
|
|
protected function cdb () { |
59
|
|
|
return Config::instance()->module('Comments')->db('comments'); |
60
|
|
|
} |
61
|
|
|
/** |
62
|
|
|
* Get comment data |
63
|
|
|
* |
64
|
|
|
* @param int|int[] $id |
65
|
|
|
* |
66
|
|
|
* @return array|false |
67
|
|
|
*/ |
68
|
|
|
function get ($id) { |
69
|
|
|
return $this->read($id); |
70
|
|
|
} |
71
|
|
|
/** |
72
|
|
|
* @param string $module |
73
|
|
|
* @param int $item |
74
|
|
|
* |
75
|
|
|
* @return int[] |
|
|
|
|
76
|
|
|
*/ |
77
|
|
|
function get_for_module_item ($module, $item) { |
78
|
|
|
$search_parameters = [ |
79
|
|
|
'module' => $module, |
80
|
|
|
'item' => $item |
81
|
|
|
]; |
82
|
|
|
return $this->search($search_parameters, 1, PHP_INT_MAX, 'id', true) ?: []; |
83
|
|
|
} |
84
|
|
|
/** |
85
|
|
|
* Add new comment |
86
|
|
|
* |
87
|
|
|
* @param int $item Item id |
88
|
|
|
* @param string $module Module name |
89
|
|
|
* @param string $text Comment text |
90
|
|
|
* @param int $parent Parent comment id |
91
|
|
|
* |
92
|
|
|
* @return false|int |
93
|
|
|
*/ |
94
|
|
|
function add ($item, $module, $text, $parent = 0) { |
95
|
|
|
$L = Language::instance(); |
96
|
|
|
$User = User::instance(); |
97
|
|
|
$text = xap($text, true); |
98
|
|
|
if (!$text) { |
99
|
|
|
return false; |
100
|
|
|
} |
101
|
|
|
if ($parent) { |
102
|
|
|
$parent_comment = $this->read($parent); |
103
|
|
|
if ($parent_comment['item'] != $item || $parent_comment['module'] != $module) { |
104
|
|
|
return false; |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
$id = $this->create($parent, $module, $item, $User->id, time(), $text, $L->clang); |
108
|
|
|
if ($id) { |
109
|
|
|
$this->cache->del("$module/$item"); |
110
|
|
|
} |
111
|
|
|
return $id; |
112
|
|
|
} |
113
|
|
|
/** |
114
|
|
|
* Set comment text |
115
|
|
|
* |
116
|
|
|
* @param int $id |
117
|
|
|
* @param string $text |
118
|
|
|
* |
119
|
|
|
* @return bool |
120
|
|
|
*/ |
121
|
|
|
function set ($id, $text) { |
122
|
|
|
$text = xap($text, true); |
123
|
|
|
if (!$text) { |
124
|
|
|
return false; |
125
|
|
|
} |
126
|
|
|
$comment = $this->get($id); |
127
|
|
|
if (!$comment) { |
128
|
|
|
return false; |
129
|
|
|
} |
130
|
|
|
$comment['text'] = $text; |
131
|
|
|
$result = $this->update($comment); |
132
|
|
|
if ($result) { |
133
|
|
|
$this->cache->del("$comment[module]/$comment[item]"); |
134
|
|
|
} |
135
|
|
|
return $result; |
136
|
|
|
} |
137
|
|
|
/** |
138
|
|
|
* Delete comment |
139
|
|
|
* |
140
|
|
|
* @param int $id |
141
|
|
|
* |
142
|
|
|
* @return bool |
143
|
|
|
*/ |
144
|
|
|
function del ($id) { |
145
|
|
|
$comment = $this->read($id); |
146
|
|
|
if ( |
147
|
|
|
!$comment || |
148
|
|
|
$this->search( |
149
|
|
|
[ |
150
|
|
|
'parent' => $id, |
151
|
|
|
'total_count' => true |
152
|
|
|
] |
153
|
|
|
) |
154
|
|
|
) { |
155
|
|
|
return false; |
156
|
|
|
} |
157
|
|
|
$result = $this->delete($id); |
158
|
|
|
if ($result) { |
159
|
|
|
$this->cache->del("$comment[module]/$comment[item]"); |
160
|
|
|
} |
161
|
|
|
return $result; |
162
|
|
|
} |
163
|
|
|
/** |
164
|
|
|
* Delete all comments of specified item |
165
|
|
|
* |
166
|
|
|
* @param int $item Item id |
167
|
|
|
* @param string $module Module name |
168
|
|
|
* |
169
|
|
|
* @return bool |
170
|
|
|
*/ |
171
|
|
|
function del_all ($item, $module) { |
172
|
|
|
$item = (int)$item; |
173
|
|
|
$result = $this->db_prime()->q( |
174
|
|
|
"DELETE FROM `[prefix]comments` |
175
|
|
|
WHERE |
176
|
|
|
`module` = '%s' AND |
177
|
|
|
`item` = '%d'", |
178
|
|
|
$module, |
179
|
|
|
$item |
180
|
|
|
); |
181
|
|
|
if ($result) { |
182
|
|
|
$this->cache->del("$module/$item"); |
183
|
|
|
} |
184
|
|
|
return (bool)$result; |
185
|
|
|
} |
186
|
|
|
/** |
187
|
|
|
* Count of comments for specified item |
188
|
|
|
* |
189
|
|
|
* @param int $item Item id |
190
|
|
|
* @param string $module Module name |
191
|
|
|
* |
192
|
|
|
* @return int |
193
|
|
|
*/ |
194
|
|
|
function count ($item, $module) { |
195
|
|
|
$item = (int)$item; |
196
|
|
|
$L = Language::instance(); |
197
|
|
|
return $this->cache->get( |
198
|
|
|
"$module/$item/count/$L->clang", |
199
|
|
|
function () use ($item, $module, $L) { |
200
|
|
|
return $this->search( |
201
|
|
|
[ |
202
|
|
|
'module' => $module, |
203
|
|
|
'item' => $item, |
204
|
|
|
'lang' => $L->clang, |
205
|
|
|
'total_count' => true |
206
|
|
|
] |
207
|
|
|
); |
208
|
|
|
} |
209
|
|
|
); |
210
|
|
|
} |
211
|
|
|
/** |
212
|
|
|
* Get comments tree in html format for specified item (look at ::block() method before usage) |
213
|
|
|
* |
214
|
|
|
* @param int $item Item id |
215
|
|
|
* @param string $module Module name |
216
|
|
|
* |
217
|
|
|
* @return string |
218
|
|
|
*/ |
219
|
|
|
function tree ($item, $module) { |
220
|
|
|
return $this->tree_html($this->tree_data($item, $module) ?: []); |
221
|
|
|
} |
222
|
|
|
/** |
223
|
|
|
* Get comments structure of specified item |
224
|
|
|
* |
225
|
|
|
* @param int $item |
226
|
|
|
* @param string $module |
227
|
|
|
* @param int $parent |
228
|
|
|
* |
229
|
|
|
* @return false|array |
230
|
|
|
*/ |
231
|
|
|
function tree_data ($item, $module, $parent = 0) { |
232
|
|
|
$Cache = $this->cache; |
233
|
|
|
$L = Language::instance(); |
234
|
|
|
if (($comments = $Cache->{"$module/$item/$L->clang"}) === false) { |
235
|
|
|
$item = (int)$item; |
236
|
|
|
$parent = (int)$parent; |
237
|
|
|
$comments = $this->db()->qfa( |
238
|
|
|
"SELECT |
239
|
|
|
`id`, |
240
|
|
|
`parent`, |
241
|
|
|
`user`, |
242
|
|
|
`date`, |
243
|
|
|
`text`, |
244
|
|
|
`lang` |
245
|
|
|
FROM `[prefix]comments` |
246
|
|
|
WHERE |
247
|
|
|
`parent` = '%d' AND |
248
|
|
|
`item` = '%d' AND |
249
|
|
|
`module` = '%s' AND |
250
|
|
|
`lang` = '%s'", |
251
|
|
|
$parent, |
252
|
|
|
$item, |
253
|
|
|
$module, |
254
|
|
|
$L->clang |
255
|
|
|
) ?: []; |
256
|
|
|
foreach ($comments as &$comment) { |
257
|
|
|
$comment['comments'] = $this->tree_data($item, $module, $comment['id']); |
258
|
|
|
} |
259
|
|
|
unset($comment); |
260
|
|
|
/** |
261
|
|
|
* Cache only root tree data |
262
|
|
|
*/ |
263
|
|
|
if ($parent == 0) { |
264
|
|
|
$Cache->{"$module/$item/$L->clang"} = $comments; |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
return $comments; |
268
|
|
|
} |
269
|
|
|
/** |
270
|
|
|
* Get comments tree in html format for given data structure (usually uses ::tree_data() method) |
271
|
|
|
* |
272
|
|
|
* @param array[] $comments |
273
|
|
|
* |
274
|
|
|
* @return string |
275
|
|
|
*/ |
276
|
|
|
function tree_html ($comments) { |
277
|
|
|
$L = Language::instance(); |
278
|
|
|
$User = User::instance(); |
279
|
|
|
if (!is_array($comments) || !$comments) { |
280
|
|
|
return ''; |
281
|
|
|
} |
282
|
|
|
$content = ''; |
283
|
|
|
foreach ($comments as $comment) { |
284
|
|
|
$uniqid = uniqid('comment_', true); |
285
|
|
|
$content .= str_replace($uniqid, $comment['text'], h::{'article.cs-comments-comment'}( |
286
|
|
|
h::{'img.cs-comments-comment-avatar'}([ |
287
|
|
|
'src' => $User->avatar($this->avatar_size, $comment['user']), |
288
|
|
|
'alt' => $User->username($comment['user']), |
289
|
|
|
'title' => $User->username($comment['user']) |
290
|
|
|
]). |
291
|
|
|
h::span($User->username($comment['user'])). |
292
|
|
|
h::{'time.cs-comments-comment-date'}( |
293
|
|
|
date('dmY', time()) == date('dmY', $comment['date']) ? |
294
|
|
|
date($L->_time, $comment['date']) : $L->to_locale(date($L->_datetime, $comment['date'])), |
295
|
|
|
[ |
296
|
|
|
'datetime' => date('c', $comment['date']) |
297
|
|
|
] |
298
|
|
|
). |
299
|
|
|
h::{'a.cs-comments-comment-link'}( |
300
|
|
|
h::icon('anchor'), |
301
|
|
|
[ |
302
|
|
|
'href' => "#comment_$comment[id]" |
303
|
|
|
] |
304
|
|
|
). |
305
|
|
|
( |
306
|
|
|
$comment['parent'] ? h::{'a.cs-comments-comment-parent'}( |
307
|
|
|
h::icon('level-up'), |
308
|
|
|
[ |
309
|
|
|
'href' => "#comment_$comment[parent]" |
310
|
|
|
] |
311
|
|
|
) : '' |
312
|
|
|
). |
313
|
|
|
( |
314
|
|
|
$User->id == $comment['user'] || $User->admin() ? h::{'icon.cs-comments-comment-edit.cs-cursor-pointer'}('pencil') : '' |
315
|
|
|
). |
316
|
|
|
( |
317
|
|
|
!$comment['comments'] && |
318
|
|
|
( |
319
|
|
|
$User->id == $comment['user'] || $User->admin() |
320
|
|
|
) ? h::{'icon.cs-comments-comment-delete.cs-cursor-pointer'}('trash-o') : '' |
321
|
|
|
). |
322
|
|
|
h::{'div.cs-comments-comment-text'}($uniqid). |
323
|
|
|
( |
324
|
|
|
$comment['comments'] ? $this->tree_html($comment['comments']) : '' |
325
|
|
|
), |
326
|
|
|
[ |
327
|
|
|
'id' => "comment_$comment[id]" |
328
|
|
|
] |
329
|
|
|
)); |
330
|
|
|
} |
331
|
|
|
return $content; |
332
|
|
|
} |
333
|
|
|
/** |
334
|
|
|
* Get comments block with comments tree and comments sending form |
335
|
|
|
* |
336
|
|
|
* @param int $item Item id |
337
|
|
|
* @param string $module Module name |
338
|
|
|
* |
339
|
|
|
* @return string |
340
|
|
|
*/ |
341
|
|
|
function block ($item, $module) { |
342
|
|
|
$L = Language::prefix('comments_'); |
343
|
|
|
return h::{'section#comments.cs-comments-comments'}( |
344
|
|
|
$L->comments.':'. |
345
|
|
|
( |
346
|
|
|
$this->tree($item, $module) ?: h::{'article.cs-blogs-no-comments'}($L->no_comments_yet) |
347
|
|
|
) |
348
|
|
|
). |
349
|
|
|
h::{'p.cs-comments-add-comment'}("$L->add_comment:"). |
350
|
|
|
( |
351
|
|
|
User::instance()->user() ? h::{'section.cs-comments-comment-write'}( |
352
|
|
|
h::{'cs-editor-simple textarea.cs-comments-comment-write-text[is=cs-textarea][autosize]'}( |
353
|
|
|
'', |
354
|
|
|
[ |
355
|
|
|
'data-item' => $item, |
356
|
|
|
'data-parent' => 0, |
357
|
|
|
'data-id' => 0, |
358
|
|
|
'data-module' => $module |
359
|
|
|
] |
360
|
|
|
). |
361
|
|
|
h::br(). |
362
|
|
|
h::{'button.cs-comments-comment-write-send[is=cs-button]'}( |
363
|
|
|
$L->send_comment |
364
|
|
|
). |
365
|
|
|
h::{'button.cs-comments-comment-write-edit[is=cs-button]'}( |
366
|
|
|
$L->save, |
367
|
|
|
[ |
368
|
|
|
'style' => 'display: none' |
369
|
|
|
] |
370
|
|
|
). |
371
|
|
|
h::{'button.cs-comments-comment-write-cancel[is=cs-button]'}( |
372
|
|
|
$L->cancel, |
373
|
|
|
[ |
374
|
|
|
'style' => 'display: none' |
375
|
|
|
] |
376
|
|
|
) |
377
|
|
|
) : h::p($L->register_for_comments_sending) |
378
|
|
|
); |
379
|
|
|
} |
380
|
|
|
} |
381
|
|
|
|
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.If the return type contains the type array, this check recommends the use of a more specific type like
String[]
orarray<String>
.