1 | <?php |
||||
2 | |||||
3 | namespace SilverStripe\Comments\Model; |
||||
4 | |||||
5 | use HTMLPurifier; |
||||
0 ignored issues
–
show
|
|||||
6 | use HTMLPurifier_Config; |
||||
0 ignored issues
–
show
The type
HTMLPurifier_Config was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||
7 | use SilverStripe\Comments\Controllers\CommentingController; |
||||
8 | use SilverStripe\Comments\Extensions\CommentsExtension; |
||||
9 | use SilverStripe\Comments\Model\Comment\SecurityToken; |
||||
10 | use SilverStripe\Control\Controller; |
||||
11 | use SilverStripe\Control\Director; |
||||
12 | use SilverStripe\Core\Injector\Injector; |
||||
13 | use SilverStripe\Core\TempFolder; |
||||
14 | use SilverStripe\Forms\CheckboxField; |
||||
15 | use SilverStripe\Forms\EmailField; |
||||
16 | use SilverStripe\Forms\FieldGroup; |
||||
17 | use SilverStripe\Forms\FieldList; |
||||
18 | use SilverStripe\Forms\Form; |
||||
19 | use SilverStripe\Forms\HeaderField; |
||||
20 | use SilverStripe\Forms\HTMLEditor\HTMLEditorField; |
||||
21 | use SilverStripe\Forms\TextareaField; |
||||
22 | use SilverStripe\Forms\TextField; |
||||
23 | use SilverStripe\ORM\ArrayList; |
||||
24 | use SilverStripe\ORM\DataObject; |
||||
25 | use SilverStripe\ORM\FieldType\DBDatetime; |
||||
26 | use SilverStripe\ORM\PaginatedList; |
||||
27 | use SilverStripe\ORM\SS_List; |
||||
28 | use SilverStripe\Security\Member; |
||||
29 | use SilverStripe\Security\Permission; |
||||
30 | |||||
31 | /** |
||||
32 | * Represents a single comment object. |
||||
33 | * |
||||
34 | * @property string $Name |
||||
35 | * @property string $Comment |
||||
36 | * @property string $Email |
||||
37 | * @property string $URL |
||||
38 | * @property string $BaseClass |
||||
39 | * @property boolean $Moderated |
||||
40 | * @property boolean $IsSpam True if the comment is known as spam |
||||
41 | * @property integer $ParentID ID of the parent page / dataobject |
||||
42 | * @property boolean $AllowHtml If true, treat $Comment as HTML instead of plain text |
||||
43 | * @property string $SecretToken Secret admin token required to provide moderation links between sessions |
||||
44 | * @property integer $Depth Depth of this comment in the nested chain |
||||
45 | * |
||||
46 | * @method HasManyList ChildComments() List of child comments |
||||
47 | * @method Member Author() Member object who created this comment |
||||
48 | * @method Comment ParentComment() Parent comment this is a reply to |
||||
49 | * @package comments |
||||
50 | */ |
||||
51 | class Comment extends DataObject |
||||
52 | { |
||||
53 | /** |
||||
54 | * {@inheritDoc} |
||||
55 | */ |
||||
56 | private static $db = array( |
||||
0 ignored issues
–
show
|
|||||
57 | 'Name' => 'Varchar(200)', |
||||
58 | 'Comment' => 'Text', |
||||
59 | 'Email' => 'Varchar(200)', |
||||
60 | 'URL' => 'Varchar(255)', |
||||
61 | 'Moderated' => 'Boolean(0)', |
||||
62 | 'IsSpam' => 'Boolean(0)', |
||||
63 | 'AllowHtml' => 'Boolean', |
||||
64 | 'SecretToken' => 'Varchar(255)', |
||||
65 | 'Depth' => 'Int' |
||||
66 | ); |
||||
67 | |||||
68 | /** |
||||
69 | * {@inheritDoc} |
||||
70 | */ |
||||
71 | private static $has_one = array( |
||||
0 ignored issues
–
show
|
|||||
72 | 'Author' => Member::class, |
||||
73 | 'ParentComment' => self::class, |
||||
74 | 'Parent' => DataObject::class |
||||
75 | ); |
||||
76 | |||||
77 | /** |
||||
78 | * {@inheritDoc} |
||||
79 | */ |
||||
80 | private static $has_many = array( |
||||
0 ignored issues
–
show
|
|||||
81 | 'ChildComments' => self::class |
||||
82 | ); |
||||
83 | |||||
84 | /** |
||||
85 | * {@inheritDoc} |
||||
86 | */ |
||||
87 | private static $default_sort = '"Created" DESC'; |
||||
0 ignored issues
–
show
|
|||||
88 | |||||
89 | /** |
||||
90 | * {@inheritDoc} |
||||
91 | */ |
||||
92 | private static $defaults = array( |
||||
0 ignored issues
–
show
|
|||||
93 | 'Moderated' => 0, |
||||
94 | 'IsSpam' => 0, |
||||
95 | ); |
||||
96 | |||||
97 | /** |
||||
98 | * {@inheritDoc} |
||||
99 | */ |
||||
100 | private static $casting = array( |
||||
0 ignored issues
–
show
|
|||||
101 | 'Title' => 'Varchar', |
||||
102 | 'ParentTitle' => 'Varchar', |
||||
103 | 'ParentClassName' => 'Varchar', |
||||
104 | 'AuthorName' => 'Varchar', |
||||
105 | 'RSSName' => 'Varchar', |
||||
106 | 'DeleteLink' => 'Varchar', |
||||
107 | 'Date' => 'Datetime', |
||||
108 | 'SpamLink' => 'Varchar', |
||||
109 | 'HamLink' => 'Varchar', |
||||
110 | 'ApproveLink' => 'Varchar', |
||||
111 | 'Permalink' => 'Varchar' |
||||
112 | ); |
||||
113 | |||||
114 | /** |
||||
115 | * {@inheritDoc} |
||||
116 | */ |
||||
117 | private static $searchable_fields = array( |
||||
0 ignored issues
–
show
|
|||||
118 | 'Name', |
||||
119 | 'Email', |
||||
120 | 'Comment', |
||||
121 | 'Created' |
||||
122 | ); |
||||
123 | |||||
124 | /** |
||||
125 | * {@inheritDoc} |
||||
126 | */ |
||||
127 | private static $summary_fields = array( |
||||
0 ignored issues
–
show
|
|||||
128 | 'getAuthorName' => 'Submitted By', |
||||
129 | 'getAuthorEmail' => 'Email', |
||||
130 | 'Comment.LimitWordCount' => 'Comment', |
||||
131 | 'Created' => 'Date Posted', |
||||
132 | 'Parent.Title' => 'Post', |
||||
133 | 'IsSpam' => 'Is Spam' |
||||
134 | ); |
||||
135 | |||||
136 | /** |
||||
137 | * {@inheritDoc} |
||||
138 | */ |
||||
139 | private static $field_labels = array( |
||||
0 ignored issues
–
show
|
|||||
140 | 'Author' => 'Author Member' |
||||
141 | ); |
||||
142 | |||||
143 | /** |
||||
144 | * {@inheritDoc} |
||||
145 | */ |
||||
146 | private static $table_name = 'Comment'; |
||||
0 ignored issues
–
show
|
|||||
147 | |||||
148 | /** |
||||
149 | * {@inheritDoc} |
||||
150 | */ |
||||
151 | public function onBeforeWrite() |
||||
152 | { |
||||
153 | parent::onBeforeWrite(); |
||||
154 | |||||
155 | // Sanitize HTML, because its expected to be passed to the template unescaped later |
||||
156 | if ($this->AllowHtml) { |
||||
157 | $this->Comment = $this->purifyHtml($this->Comment); |
||||
158 | } |
||||
159 | |||||
160 | // Check comment depth |
||||
161 | $this->updateDepth(); |
||||
162 | } |
||||
163 | |||||
164 | /** |
||||
165 | * {@inheritDoc} |
||||
166 | */ |
||||
167 | public function onBeforeDelete() |
||||
168 | { |
||||
169 | parent::onBeforeDelete(); |
||||
170 | |||||
171 | // Delete all children |
||||
172 | foreach ($this->ChildComments() as $comment) { |
||||
173 | $comment->delete(); |
||||
174 | } |
||||
175 | } |
||||
176 | |||||
177 | /** |
||||
178 | * @return Comment_SecurityToken |
||||
0 ignored issues
–
show
The type
SilverStripe\Comments\Model\Comment_SecurityToken was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||
179 | */ |
||||
180 | public function getSecurityToken() |
||||
181 | { |
||||
182 | return Injector::inst()->createWithArgs(SecurityToken::class, array($this)); |
||||
183 | } |
||||
184 | |||||
185 | /** |
||||
186 | * Return a link to this comment |
||||
187 | * |
||||
188 | * @param string $action |
||||
189 | * |
||||
190 | * @return string link to this comment. |
||||
191 | */ |
||||
192 | public function Link($action = '') |
||||
193 | { |
||||
194 | if ($parent = $this->Parent()) { |
||||
0 ignored issues
–
show
The method
Parent() does not exist on SilverStripe\Comments\Model\Comment . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
195 | return $parent->Link($action) . '#' . $this->Permalink(); |
||||
196 | } |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * Returns the permalink for this {@link Comment}. Inserted into |
||||
201 | * the ID tag of the comment |
||||
202 | * |
||||
203 | * @return string |
||||
204 | */ |
||||
205 | public function Permalink() |
||||
206 | { |
||||
207 | $prefix = $this->getOption('comment_permalink_prefix'); |
||||
208 | return $prefix . $this->ID; |
||||
209 | } |
||||
210 | |||||
211 | /** |
||||
212 | * Translate the form field labels for the CMS administration |
||||
213 | * |
||||
214 | * @param boolean $includerelations |
||||
215 | * |
||||
216 | * @return array |
||||
217 | */ |
||||
218 | public function fieldLabels($includerelations = true) |
||||
219 | { |
||||
220 | $labels = parent::fieldLabels($includerelations); |
||||
221 | |||||
222 | $labels['Name'] = _t(__CLASS__ . '.NAME', 'Author name'); |
||||
223 | $labels['Comment'] = _t(__CLASS__ . '.COMMENT', 'Comment'); |
||||
224 | $labels['Email'] = _t(__CLASS__ . '.EMAIL', 'Email'); |
||||
225 | $labels['URL'] = _t(__CLASS__ . '.URL', 'URL'); |
||||
226 | $labels['IsSpam'] = _t(__CLASS__ . '.ISSPAM', 'Spam?'); |
||||
227 | $labels['Moderated'] = _t(__CLASS__ . '.MODERATED', 'Moderated?'); |
||||
228 | $labels['ParentTitle'] = _t(__CLASS__ . '.PARENTTITLE', 'Parent'); |
||||
229 | $labels['Created'] = _t(__CLASS__ . '.CREATED', 'Date posted'); |
||||
230 | |||||
231 | return $labels; |
||||
232 | } |
||||
233 | |||||
234 | /** |
||||
235 | * Get the commenting option |
||||
236 | * |
||||
237 | * @param string $key |
||||
238 | * |
||||
239 | * @return mixed Result if the setting is available, or null otherwise |
||||
240 | */ |
||||
241 | public function getOption($key) |
||||
242 | { |
||||
243 | // If possible use the current record |
||||
244 | $record = $this->Parent(); |
||||
245 | |||||
246 | if (!$record && $this->Parent()) { |
||||
247 | // Otherwise a singleton of that record |
||||
248 | $record = singleton($this->Parent()->dataClass()); |
||||
249 | } elseif (!$record) { |
||||
250 | // Otherwise just use the default options |
||||
251 | $record = singleton(CommentsExtension::class); |
||||
252 | } |
||||
253 | |||||
254 | return ($record instanceof CommentsExtension || $record->hasExtension(CommentsExtension::class)) |
||||
255 | ? $record->getCommentsOption($key) |
||||
256 | : null; |
||||
257 | } |
||||
258 | |||||
259 | /** |
||||
260 | * Returns the parent {@link DataObject} this comment is attached too |
||||
261 | * |
||||
262 | * @deprecated 4.0.0 Use $this->Parent() instead |
||||
263 | * @return DataObject |
||||
264 | */ |
||||
265 | public function getParent() |
||||
266 | { |
||||
267 | return $this->BaseClass && $this->ParentID |
||||
268 | ? DataObject::get_by_id($this->BaseClass, $this->ParentID, true) |
||||
269 | : null; |
||||
270 | } |
||||
271 | |||||
272 | |||||
273 | /** |
||||
274 | * Returns a string to help identify the parent of the comment |
||||
275 | * |
||||
276 | * @return string |
||||
277 | */ |
||||
278 | public function getParentTitle() |
||||
279 | { |
||||
280 | if ($parent = $this->Parent()) { |
||||
281 | return $parent->Title ?: ($parent->ClassName . ' #' . $parent->ID); |
||||
282 | } |
||||
283 | } |
||||
284 | |||||
285 | /** |
||||
286 | * Comment-parent classnames obviously vary, return the parent classname |
||||
287 | * |
||||
288 | * @return string |
||||
289 | */ |
||||
290 | public function getParentClassName() |
||||
291 | { |
||||
292 | return $this->Parent()->getClassName(); |
||||
293 | } |
||||
294 | |||||
295 | /** |
||||
296 | * {@inheritDoc} |
||||
297 | */ |
||||
298 | public function castingHelper($field) |
||||
299 | { |
||||
300 | // Safely escape the comment |
||||
301 | if (in_array($field, ['EscapedComment', 'Comment'], true)) { |
||||
302 | return $this->AllowHtml ? 'HTMLText' : 'Text'; |
||||
303 | } |
||||
304 | return parent::castingHelper($field); |
||||
305 | } |
||||
306 | |||||
307 | /** |
||||
308 | * Content to be safely escaped on the frontend |
||||
309 | * |
||||
310 | * @return string |
||||
311 | */ |
||||
312 | public function getEscapedComment() |
||||
313 | { |
||||
314 | return $this->Comment; |
||||
315 | } |
||||
316 | |||||
317 | /** |
||||
318 | * Return whether this comment is a preview (has not been written to the db) |
||||
319 | * |
||||
320 | * @return boolean |
||||
321 | */ |
||||
322 | public function isPreview() |
||||
323 | { |
||||
324 | return !$this->exists(); |
||||
325 | } |
||||
326 | |||||
327 | /** |
||||
328 | * @todo needs to compare to the new {@link Commenting} configuration API |
||||
329 | * |
||||
330 | * @param Member $member |
||||
331 | * @param array $context |
||||
332 | * @return bool |
||||
333 | */ |
||||
334 | public function canCreate($member = null, $context = []) |
||||
335 | { |
||||
336 | return false; |
||||
337 | } |
||||
338 | |||||
339 | /** |
||||
340 | * Checks for association with a page, and {@link SiteTree->ProvidePermission} |
||||
341 | * flag being set to true. |
||||
342 | * |
||||
343 | * @param Member $member |
||||
344 | * @return Boolean |
||||
345 | */ |
||||
346 | public function canView($member = null) |
||||
347 | { |
||||
348 | $member = $this->getMember($member); |
||||
349 | |||||
350 | $extended = $this->extendedCan('canView', $member); |
||||
351 | if ($extended !== null) { |
||||
352 | return $extended; |
||||
353 | } |
||||
354 | |||||
355 | if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) { |
||||
356 | return true; |
||||
357 | } |
||||
358 | |||||
359 | if ($parent = $this->Parent()) { |
||||
360 | return $parent->canView($member) |
||||
361 | && $parent->hasExtension(CommentsExtension::class) |
||||
362 | && $parent->CommentsEnabled; |
||||
363 | } |
||||
364 | |||||
365 | return false; |
||||
366 | } |
||||
367 | |||||
368 | /** |
||||
369 | * Checks if the comment can be edited. |
||||
370 | * |
||||
371 | * @param null|int|Member $member |
||||
372 | * @return Boolean |
||||
373 | */ |
||||
374 | public function canEdit($member = null) |
||||
375 | { |
||||
376 | $member = $this->getMember($member); |
||||
377 | |||||
378 | if (!$member) { |
||||
0 ignored issues
–
show
|
|||||
379 | return false; |
||||
380 | } |
||||
381 | |||||
382 | $extended = $this->extendedCan('canEdit', $member); |
||||
383 | if ($extended !== null) { |
||||
384 | return $extended; |
||||
385 | } |
||||
386 | |||||
387 | if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) { |
||||
388 | return true; |
||||
389 | } |
||||
390 | |||||
391 | if ($parent = $this->Parent()) { |
||||
392 | return $parent->canEdit($member); |
||||
393 | } |
||||
394 | |||||
395 | return false; |
||||
396 | } |
||||
397 | |||||
398 | /** |
||||
399 | * Checks if the comment can be deleted. |
||||
400 | * |
||||
401 | * @param null|int|Member $member |
||||
402 | * @return Boolean |
||||
403 | */ |
||||
404 | public function canDelete($member = null) |
||||
405 | { |
||||
406 | $member = $this->getMember($member); |
||||
407 | |||||
408 | if (!$member) { |
||||
0 ignored issues
–
show
|
|||||
409 | return false; |
||||
410 | } |
||||
411 | |||||
412 | $extended = $this->extendedCan('canDelete', $member); |
||||
413 | if ($extended !== null) { |
||||
414 | return $extended; |
||||
415 | } |
||||
416 | |||||
417 | return $this->canEdit($member); |
||||
418 | } |
||||
419 | |||||
420 | /** |
||||
421 | * Resolves Member object. |
||||
422 | * |
||||
423 | * @param Member|int|null $member |
||||
424 | * @return Member|null |
||||
425 | */ |
||||
426 | protected function getMember($member = null) |
||||
427 | { |
||||
428 | if (!$member) { |
||||
429 | $member = Member::currentUser(); |
||||
0 ignored issues
–
show
The function
SilverStripe\Security\Member::currentUser() has been deprecated: 5.0.0 use Security::getCurrentUser()
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.
Loading history...
|
|||||
430 | } |
||||
431 | |||||
432 | if (is_numeric($member)) { |
||||
433 | $member = DataObject::get_by_id(Member::class, $member, true); |
||||
434 | } |
||||
435 | |||||
436 | return $member; |
||||
0 ignored issues
–
show
|
|||||
437 | } |
||||
438 | |||||
439 | /** |
||||
440 | * Return the authors name for the comment |
||||
441 | * |
||||
442 | * @return string |
||||
443 | */ |
||||
444 | public function getAuthorName() |
||||
445 | { |
||||
446 | if ($this->Name) { |
||||
447 | return $this->Name; |
||||
448 | } elseif ($author = $this->Author()) { |
||||
449 | return $author->getName(); |
||||
450 | } |
||||
451 | } |
||||
452 | |||||
453 | /** |
||||
454 | * Return the comment authors email address |
||||
455 | * |
||||
456 | * @return string |
||||
457 | */ |
||||
458 | public function getAuthorEmail() |
||||
459 | { |
||||
460 | if ($this->Email) { |
||||
461 | return $this->Email; |
||||
462 | } elseif ($author = $this->Author()) { |
||||
463 | return $author->Email; |
||||
464 | } |
||||
465 | } |
||||
466 | |||||
467 | /** |
||||
468 | * Generate a secure admin-action link authorised for the specified member |
||||
469 | * |
||||
470 | * @param string $action An action on CommentingController to link to |
||||
471 | * @param Member $member The member authorised to invoke this action |
||||
472 | * |
||||
473 | * @return string |
||||
474 | */ |
||||
475 | protected function actionLink($action, $member = null) |
||||
476 | { |
||||
477 | if (!$member) { |
||||
478 | $member = Member::currentUser(); |
||||
0 ignored issues
–
show
The function
SilverStripe\Security\Member::currentUser() has been deprecated: 5.0.0 use Security::getCurrentUser()
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.
Loading history...
|
|||||
479 | } |
||||
480 | if (!$member) { |
||||
0 ignored issues
–
show
|
|||||
481 | return false; |
||||
482 | } |
||||
483 | |||||
484 | /** |
||||
485 | * @todo: How do we handle "DataObject" instances that don't have a Link to reject/spam/delete?? This may |
||||
486 | * we have to make CMS a hard dependency instead. |
||||
487 | */ |
||||
488 | // if (!$this->Parent()->hasMethod('Link')) { |
||||
489 | // return false; |
||||
490 | // } |
||||
491 | |||||
492 | $url = Controller::join_links( |
||||
493 | Director::baseURL(), |
||||
494 | 'comments', |
||||
495 | $action, |
||||
496 | $this->ID |
||||
497 | ); |
||||
498 | |||||
499 | // Limit access for this user |
||||
500 | $token = $this->getSecurityToken(); |
||||
501 | return $token->addToUrl($url, $member); |
||||
502 | } |
||||
503 | |||||
504 | /** |
||||
505 | * Link to delete this comment |
||||
506 | * |
||||
507 | * @param Member $member |
||||
508 | * |
||||
509 | * @return string |
||||
510 | */ |
||||
511 | public function DeleteLink($member = null) |
||||
512 | { |
||||
513 | if ($this->canDelete($member)) { |
||||
514 | return $this->actionLink('delete', $member); |
||||
515 | } |
||||
516 | } |
||||
517 | |||||
518 | /** |
||||
519 | * Link to mark as spam |
||||
520 | * |
||||
521 | * @param Member $member |
||||
522 | * |
||||
523 | * @return string |
||||
524 | */ |
||||
525 | public function SpamLink($member = null) |
||||
526 | { |
||||
527 | if ($this->canEdit($member) && !$this->IsSpam) { |
||||
528 | return $this->actionLink('spam', $member); |
||||
529 | } |
||||
530 | } |
||||
531 | |||||
532 | /** |
||||
533 | * Link to mark as not-spam (ham) |
||||
534 | * |
||||
535 | * @param Member $member |
||||
536 | * |
||||
537 | * @return string |
||||
538 | */ |
||||
539 | public function HamLink($member = null) |
||||
540 | { |
||||
541 | if ($this->canEdit($member) && $this->IsSpam) { |
||||
542 | return $this->actionLink('ham', $member); |
||||
543 | } |
||||
544 | } |
||||
545 | |||||
546 | /** |
||||
547 | * Link to approve this comment |
||||
548 | * |
||||
549 | * @param Member $member |
||||
550 | * |
||||
551 | * @return string |
||||
552 | */ |
||||
553 | public function ApproveLink($member = null) |
||||
554 | { |
||||
555 | if ($this->canEdit($member) && !$this->Moderated) { |
||||
556 | return $this->actionLink('approve', $member); |
||||
557 | } |
||||
558 | } |
||||
559 | |||||
560 | /** |
||||
561 | * Mark this comment as spam |
||||
562 | */ |
||||
563 | public function markSpam() |
||||
564 | { |
||||
565 | $this->IsSpam = true; |
||||
566 | $this->Moderated = true; |
||||
567 | $this->write(); |
||||
568 | $this->extend('afterMarkSpam'); |
||||
569 | } |
||||
570 | |||||
571 | /** |
||||
572 | * Mark this comment as approved |
||||
573 | */ |
||||
574 | public function markApproved() |
||||
575 | { |
||||
576 | $this->IsSpam = false; |
||||
577 | $this->Moderated = true; |
||||
578 | $this->write(); |
||||
579 | $this->extend('afterMarkApproved'); |
||||
580 | } |
||||
581 | |||||
582 | /** |
||||
583 | * Mark this comment as unapproved |
||||
584 | */ |
||||
585 | public function markUnapproved() |
||||
586 | { |
||||
587 | $this->Moderated = false; |
||||
588 | $this->write(); |
||||
589 | $this->extend('afterMarkUnapproved'); |
||||
590 | } |
||||
591 | |||||
592 | /** |
||||
593 | * @return string |
||||
594 | */ |
||||
595 | public function SpamClass() |
||||
596 | { |
||||
597 | if ($this->IsSpam) { |
||||
598 | return 'spam'; |
||||
599 | } elseif (!$this->Moderated) { |
||||
600 | return 'unmoderated'; |
||||
601 | } else { |
||||
602 | return 'notspam'; |
||||
603 | } |
||||
604 | } |
||||
605 | |||||
606 | /** |
||||
607 | * @return string |
||||
608 | */ |
||||
609 | public function getTitle() |
||||
610 | { |
||||
611 | $title = sprintf(_t(__CLASS__ . '.COMMENTBY', 'Comment by %s', 'Name'), $this->getAuthorName()); |
||||
612 | |||||
613 | if ($parent = $this->Parent()) { |
||||
614 | if ($parent->Title) { |
||||
615 | $title .= sprintf(' %s %s', _t(__CLASS__ . '.ON', 'on'), $parent->Title); |
||||
616 | } |
||||
617 | } |
||||
618 | |||||
619 | return $title; |
||||
620 | } |
||||
621 | |||||
622 | /* |
||||
623 | * Modify the default fields shown to the user |
||||
624 | */ |
||||
625 | public function getCMSFields() |
||||
626 | { |
||||
627 | $commentField = $this->AllowHtml ? HTMLEditorField::class : TextareaField::class; |
||||
628 | $fields = new FieldList( |
||||
629 | $this |
||||
630 | ->obj('Created') |
||||
631 | ->scaffoldFormField($this->fieldLabel('Created')) |
||||
632 | ->performReadonlyTransformation(), |
||||
633 | TextField::create('Name', $this->fieldLabel('Name')), |
||||
634 | $commentField::create('Comment', $this->fieldLabel('Comment')), |
||||
635 | EmailField::create('Email', $this->fieldLabel('Email')), |
||||
636 | TextField::create('URL', $this->fieldLabel('URL')), |
||||
637 | FieldGroup::create(array( |
||||
638 | CheckboxField::create('Moderated', $this->fieldLabel('Moderated')), |
||||
639 | CheckboxField::create('IsSpam', $this->fieldLabel('IsSpam')), |
||||
640 | )) |
||||
641 | ->setTitle(_t(__CLASS__ . '.OPTIONS', 'Options')) |
||||
642 | ->setDescription(_t( |
||||
643 | __CLASS__ . '.OPTION_DESCRIPTION', |
||||
644 | 'Unmoderated and spam comments will not be displayed until approved' |
||||
645 | )) |
||||
646 | ); |
||||
647 | |||||
648 | // Show member name if given |
||||
649 | if (($author = $this->Author()) && $author->exists()) { |
||||
650 | $fields->insertAfter( |
||||
651 | TextField::create('AuthorMember', $this->fieldLabel('Author'), $author->Title) |
||||
652 | ->performReadonlyTransformation(), |
||||
653 | 'Name' |
||||
0 ignored issues
–
show
'Name' of type string is incompatible with the type SilverStripe\Forms\FormField expected by parameter $item of SilverStripe\Forms\FieldList::insertAfter() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
654 | ); |
||||
655 | } |
||||
656 | |||||
657 | // Show parent comment if given |
||||
658 | if (($parent = $this->ParentComment()) && $parent->exists()) { |
||||
659 | $fields->push(new HeaderField( |
||||
660 | 'ParentComment_Title', |
||||
661 | _t(__CLASS__ . '.ParentComment_Title', 'This comment is a reply to the below') |
||||
662 | )); |
||||
663 | // Created date |
||||
664 | // FIXME - the method setName in DatetimeField is not chainable, hence |
||||
665 | // the lack of chaining here |
||||
666 | $createdField = $parent |
||||
667 | ->obj('Created') |
||||
668 | ->scaffoldFormField($parent->fieldLabel('Created')); |
||||
669 | $createdField->setName('ParentComment_Created'); |
||||
670 | $createdField->setValue($parent->Created); |
||||
671 | $createdField->performReadonlyTransformation(); |
||||
672 | $fields->push($createdField); |
||||
673 | |||||
674 | // Name (could be member or string value) |
||||
675 | $fields->push( |
||||
676 | $parent |
||||
677 | ->obj('AuthorName') |
||||
678 | ->scaffoldFormField($parent->fieldLabel('AuthorName')) |
||||
679 | ->setName('ParentComment_AuthorName') |
||||
680 | ->setValue($parent->getAuthorName()) |
||||
681 | ->performReadonlyTransformation() |
||||
682 | ); |
||||
683 | |||||
684 | // Comment body |
||||
685 | $fields->push( |
||||
686 | $parent |
||||
687 | ->obj('EscapedComment') |
||||
688 | ->scaffoldFormField($parent->fieldLabel(self::class)) |
||||
689 | ->setName('ParentComment_EscapedComment') |
||||
690 | ->setValue($parent->Comment) |
||||
691 | ->performReadonlyTransformation() |
||||
692 | ); |
||||
693 | } |
||||
694 | |||||
695 | $this->extend('updateCMSFields', $fields); |
||||
696 | return $fields; |
||||
697 | } |
||||
698 | |||||
699 | /** |
||||
700 | * @param string $dirtyHtml |
||||
701 | * |
||||
702 | * @return string |
||||
703 | */ |
||||
704 | public function purifyHtml($dirtyHtml) |
||||
705 | { |
||||
706 | if ($service = $this->getHtmlPurifierService()) { |
||||
707 | return $service->purify($dirtyHtml); |
||||
708 | } |
||||
709 | |||||
710 | return $dirtyHtml; |
||||
711 | } |
||||
712 | |||||
713 | /** |
||||
714 | * @return HTMLPurifier (or anything with a "purify()" method) |
||||
715 | */ |
||||
716 | public function getHtmlPurifierService() |
||||
717 | { |
||||
718 | if (!class_exists(HTMLPurifier_Config::class)) { |
||||
719 | return null; |
||||
720 | } |
||||
721 | |||||
722 | $config = HTMLPurifier_Config::createDefault(); |
||||
723 | $allowedElements = (array) $this->getOption('html_allowed_elements'); |
||||
724 | if (!empty($allowedElements)) { |
||||
725 | $config->set('HTML.AllowedElements', $allowedElements); |
||||
726 | } |
||||
727 | |||||
728 | // This injector cannot be set unless the 'p' element is allowed |
||||
729 | if (in_array('p', $allowedElements)) { |
||||
730 | $config->set('AutoFormat.AutoParagraph', true); |
||||
731 | } |
||||
732 | |||||
733 | $config->set('AutoFormat.Linkify', true); |
||||
734 | $config->set('URI.DisableExternalResources', true); |
||||
735 | $config->set('Cache.SerializerPath', TempFolder::getTempFolder(BASE_PATH)); |
||||
736 | return new HTMLPurifier($config); |
||||
737 | } |
||||
738 | |||||
739 | /** |
||||
740 | * Calculate the Gravatar link from the email address |
||||
741 | * |
||||
742 | * @return string |
||||
743 | */ |
||||
744 | public function Gravatar() |
||||
745 | { |
||||
746 | $gravatar = ''; |
||||
747 | $use_gravatar = $this->getOption('use_gravatar'); |
||||
748 | |||||
749 | if ($use_gravatar) { |
||||
750 | $gravatar = 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($this->Email))); |
||||
751 | $gravatarsize = $this->getOption('gravatar_size'); |
||||
752 | $gravatardefault = $this->getOption('gravatar_default'); |
||||
753 | $gravatarrating = $this->getOption('gravatar_rating'); |
||||
754 | $gravatar .= '?' . http_build_query(array( |
||||
755 | 's' => $gravatarsize, |
||||
756 | 'd' => $gravatardefault, |
||||
757 | 'r' => $gravatarrating, |
||||
758 | )); |
||||
759 | } |
||||
760 | |||||
761 | return $gravatar; |
||||
762 | } |
||||
763 | |||||
764 | /** |
||||
765 | * Determine if replies are enabled for this instance |
||||
766 | * |
||||
767 | * @return boolean |
||||
768 | */ |
||||
769 | public function getRepliesEnabled() |
||||
770 | { |
||||
771 | // Check reply option |
||||
772 | if (!$this->getOption('nested_comments')) { |
||||
773 | return false; |
||||
774 | } |
||||
775 | |||||
776 | // Check if depth is limited |
||||
777 | $maxLevel = $this->getOption('nested_depth'); |
||||
778 | $notSpam = ($this->SpamClass() == 'notspam'); |
||||
779 | return $notSpam && (!$maxLevel || $this->Depth < $maxLevel); |
||||
780 | } |
||||
781 | |||||
782 | /** |
||||
783 | * Proxy for checking whether the has permission to comment on the comment parent. |
||||
784 | * |
||||
785 | * @param Member $member Member to check |
||||
786 | * |
||||
787 | * @return boolean |
||||
788 | */ |
||||
789 | public function canPostComment($member = null) |
||||
790 | { |
||||
791 | return $this->Parent() |
||||
792 | && $this->Parent()->exists() |
||||
793 | && $this->Parent()->canPostComment($member); |
||||
794 | } |
||||
795 | |||||
796 | /** |
||||
797 | * Returns the list of all replies |
||||
798 | * |
||||
799 | * @return SS_List |
||||
800 | */ |
||||
801 | public function AllReplies() |
||||
802 | { |
||||
803 | // No replies if disabled |
||||
804 | if (!$this->getRepliesEnabled()) { |
||||
805 | return new ArrayList(); |
||||
806 | } |
||||
807 | |||||
808 | // Get all non-spam comments |
||||
809 | $order = $this->getOption('order_replies_by') |
||||
810 | ?: $this->getOption('order_comments_by'); |
||||
811 | $list = $this |
||||
812 | ->ChildComments() |
||||
813 | ->sort($order); |
||||
814 | |||||
815 | $this->extend('updateAllReplies', $list); |
||||
816 | return $list; |
||||
817 | } |
||||
818 | |||||
819 | /** |
||||
820 | * Returns the list of replies, with spam and unmoderated items excluded, for use in the frontend |
||||
821 | * |
||||
822 | * @return SS_List |
||||
823 | */ |
||||
824 | public function Replies() |
||||
825 | { |
||||
826 | // No replies if disabled |
||||
827 | if (!$this->getRepliesEnabled()) { |
||||
828 | return new ArrayList(); |
||||
829 | } |
||||
830 | $list = $this->AllReplies(); |
||||
831 | |||||
832 | // Filter spam comments for non-administrators if configured |
||||
833 | $parent = $this->Parent(); |
||||
834 | $showSpam = $this->getOption('frontend_spam') && $parent && $parent->canModerateComments(); |
||||
835 | if (!$showSpam) { |
||||
836 | $list = $list->filter('IsSpam', 0); |
||||
837 | } |
||||
838 | |||||
839 | // Filter un-moderated comments for non-administrators if moderation is enabled |
||||
840 | $showUnmoderated = $parent && ( |
||||
841 | ($parent->ModerationRequired === 'None') |
||||
842 | || ($this->getOption('frontend_moderation') && $parent->canModerateComments()) |
||||
843 | ); |
||||
844 | if (!$showUnmoderated) { |
||||
845 | $list = $list->filter('Moderated', 1); |
||||
846 | } |
||||
847 | |||||
848 | $this->extend('updateReplies', $list); |
||||
849 | return $list; |
||||
850 | } |
||||
851 | |||||
852 | /** |
||||
853 | * Returns the list of replies paged, with spam and unmoderated items excluded, for use in the frontend |
||||
854 | * |
||||
855 | * @return PaginatedList |
||||
856 | */ |
||||
857 | public function PagedReplies() |
||||
858 | { |
||||
859 | $list = $this->Replies(); |
||||
860 | |||||
861 | // Add pagination |
||||
862 | $list = new PaginatedList($list, Controller::curr()->getRequest()); |
||||
863 | $list->setPaginationGetVar('repliesstart' . $this->ID); |
||||
864 | $list->setPageLength($this->getOption('comments_per_page')); |
||||
865 | |||||
866 | $this->extend('updatePagedReplies', $list); |
||||
867 | return $list; |
||||
868 | } |
||||
869 | |||||
870 | /** |
||||
871 | * Generate a reply form for this comment |
||||
872 | * |
||||
873 | * @return Form |
||||
874 | */ |
||||
875 | public function ReplyForm() |
||||
876 | { |
||||
877 | // Ensure replies are enabled |
||||
878 | if (!$this->getRepliesEnabled()) { |
||||
879 | return null; |
||||
880 | } |
||||
881 | |||||
882 | // Check parent is available |
||||
883 | $parent = $this->Parent(); |
||||
884 | if (!$parent || !$parent->exists()) { |
||||
885 | return null; |
||||
886 | } |
||||
887 | |||||
888 | // Build reply controller |
||||
889 | $controller = CommentingController::create(); |
||||
890 | $controller->setOwnerRecord($parent); |
||||
891 | $controller->setParentClass($parent->ClassName); |
||||
892 | $controller->setOwnerController(Controller::curr()); |
||||
893 | |||||
894 | return $controller->ReplyForm($this); |
||||
895 | } |
||||
896 | |||||
897 | /** |
||||
898 | * @return string |
||||
899 | */ |
||||
900 | public function getDate() |
||||
901 | { |
||||
902 | return $this->Created; |
||||
903 | } |
||||
904 | |||||
905 | /** |
||||
906 | * Refresh of this comment in the hierarchy |
||||
907 | */ |
||||
908 | public function updateDepth() |
||||
909 | { |
||||
910 | $parent = $this->ParentComment(); |
||||
911 | if ($parent && $parent->exists()) { |
||||
912 | $parent->updateDepth(); |
||||
913 | $this->Depth = $parent->Depth + 1; |
||||
914 | } else { |
||||
915 | $this->Depth = 1; |
||||
916 | } |
||||
917 | } |
||||
918 | } |
||||
919 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths