Completed
Push — master ( 187618...8c43e0 )
by Will
12s
created

src/Model/Comment.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SilverStripe\Comments\Model;
4
5
use HTMLPurifier_Config;
6
use HTMLPurifier;
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\Email\Email;
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\Core\TempFolder;
15
use SilverStripe\Forms\CheckboxField;
16
use SilverStripe\Forms\EmailField;
17
use SilverStripe\Forms\FieldGroup;
18
use SilverStripe\Forms\FieldList;
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\DB;
26
use SilverStripe\ORM\PaginatedList;
27
use SilverStripe\Security\Member;
28
use SilverStripe\Security\Permission;
29
30
/**
31
 * Represents a single comment object.
32
 *
33
 * @property string  $Name
34
 * @property string  $Comment
35
 * @property string  $Email
36
 * @property string  $URL
37
 * @property string  $BaseClass
38
 * @property boolean $Moderated
39
 * @property boolean $IsSpam      True if the comment is known as spam
40
 * @property integer $ParentID    ID of the parent page / dataobject
41
 * @property boolean $AllowHtml   If true, treat $Comment as HTML instead of plain text
42
 * @property string  $SecretToken Secret admin token required to provide moderation links between sessions
43
 * @property integer $Depth       Depth of this comment in the nested chain
44
 *
45
 * @method HasManyList ChildComments() List of child comments
46
 * @method Member Author() Member object who created this comment
47
 * @method Comment ParentComment() Parent comment this is a reply to
48
 * @package comments
49
 */
50
class Comment extends DataObject
51
{
52
    /**
53
     * {@inheritDoc}
54
     */
55
    private static $db = array(
56
        'Name' => 'Varchar(200)',
57
        'Comment' => 'Text',
58
        'Email' => 'Varchar(200)',
59
        'URL' => 'Varchar(255)',
60
        'Moderated' => 'Boolean(0)',
61
        'IsSpam' => 'Boolean(0)',
62
        'AllowHtml' => 'Boolean',
63
        'SecretToken' => 'Varchar(255)',
64
        'Depth' => 'Int'
65
    );
66
67
    /**
68
     * {@inheritDoc}
69
     */
70
    private static $has_one = array(
71
        'Author' => Member::class,
72
        'ParentComment' => self::class,
73
        'Parent' => DataObject::class
74
    );
75
76
    /**
77
     * {@inheritDoc}
78
     */
79
    private static $has_many = array(
80
        'ChildComments' => self::class
81
    );
82
83
    /**
84
     * {@inheritDoc}
85
     */
86
    private static $default_sort = '"Created" DESC';
87
88
    /**
89
     * {@inheritDoc}
90
     */
91
    private static $defaults = array(
92
        'Moderated' => 0,
93
        'IsSpam' => 0,
94
    );
95
96
    /**
97
     * {@inheritDoc}
98
     */
99
    private static $casting = array(
100
        'Title' => 'Varchar',
101
        'ParentTitle' => 'Varchar',
102
        'ParentClassName' => 'Varchar',
103
        'AuthorName' => 'Varchar',
104
        'RSSName' => 'Varchar',
105
        'DeleteLink' => 'Varchar',
106
        'SpamLink' => 'Varchar',
107
        'HamLink' => 'Varchar',
108
        'ApproveLink' => 'Varchar',
109
        'Permalink' => 'Varchar'
110
    );
111
112
    /**
113
     * {@inheritDoc}
114
     */
115
    private static $searchable_fields = array(
116
        'Name',
117
        'Email',
118
        'Comment',
119
        'Created'
120
    );
121
122
    /**
123
     * {@inheritDoc}
124
     */
125
    private static $summary_fields = array(
126
        'Name' => 'Submitted By',
127
        'Email' => 'Email',
128
        'Comment.LimitWordCount' => 'Comment',
129
        'Created' => 'Date Posted',
130
        'Parent.Title' => 'Post',
131
        'IsSpam' => 'Is Spam'
132
    );
133
134
    /**
135
     * {@inheritDoc}
136
     */
137
    private static $field_labels = array(
138
        'Author' => 'Author Member'
139
    );
140
141
    /**
142
     * {@inheritDoc}
143
     */
144
    private static $table_name = 'Comment';
145
146
    /**
147
     * {@inheritDoc}
148
     */
149
    public function onBeforeWrite()
150
    {
151
        parent::onBeforeWrite();
152
153
        // Sanitize HTML, because its expected to be passed to the template unescaped later
154
        if ($this->AllowHtml) {
155
            $this->Comment = $this->purifyHtml($this->Comment);
156
        }
157
158
        // Check comment depth
159
        $this->updateDepth();
160
    }
161
162
    /**
163
     * {@inheritDoc}
164
     */
165
    public function onBeforeDelete()
166
    {
167
        parent::onBeforeDelete();
168
169
        // Delete all children
170
        foreach ($this->ChildComments() as $comment) {
171
            $comment->delete();
172
        }
173
    }
174
175
    /**
176
     * @return Comment_SecurityToken
177
     */
178
    public function getSecurityToken()
179
    {
180
        return Injector::inst()->createWithArgs(SecurityToken::class, array($this));
181
    }
182
183
    /**
184
     * Migrates the old {@link PageComment} objects to {@link Comment}
185
     */
186
    public function requireDefaultRecords()
187
    {
188
        parent::requireDefaultRecords();
189
190
        if (DB::get_schema()->hasTable('PageComment')) {
191
            $comments = DB::query('SELECT * FROM "PageComment"');
192
193
            if ($comments) {
194
                while ($pageComment = $comments->next()) {
195
                    // create a new comment from the older page comment
196
                    $comment = new Comment();
197
                    $comment->update($pageComment);
198
199
                    // set the variables which have changed
200
                    $comment->BaseClass = SiteTree::class;
201
                    $comment->URL = (isset($pageComment['CommenterURL'])) ? $pageComment['CommenterURL'] : '';
202
                    if ((int) $pageComment['NeedsModeration'] == 0) {
203
                        $comment->Moderated = true;
204
                    }
205
206
                    $comment->write();
207
                }
208
            }
209
210
            DB::alteration_message('Migrated PageComment to Comment', 'changed');
211
            DB::get_schema()->dontRequireTable('PageComment');
212
        }
213
    }
214
215
    /**
216
     * Return a link to this comment
217
     *
218
     * @param string $action
219
     *
220
     * @return string link to this comment.
221
     */
222
    public function Link($action = '')
223
    {
224
        if ($parent = $this->Parent()) {
225
            return $parent->Link($action) . '#' . $this->Permalink();
226
        }
227
    }
228
229
    /**
230
     * Returns the permalink for this {@link Comment}. Inserted into
231
     * the ID tag of the comment
232
     *
233
     * @return string
234
     */
235
    public function Permalink()
236
    {
237
        $prefix = $this->getOption('comment_permalink_prefix');
238
        return $prefix . $this->ID;
239
    }
240
241
    /**
242
     * Translate the form field labels for the CMS administration
243
     *
244
     * @param boolean $includerelations
245
     *
246
     * @return array
247
     */
248
    public function fieldLabels($includerelations = true)
249
    {
250
        $labels = parent::fieldLabels($includerelations);
251
252
        $labels['Name'] = _t('SilverStripe\\Comments\\Model\\Comment.NAME', 'Author Name');
253
        $labels['Comment'] = _t('SilverStripe\\Comments\\Model\\Comment.COMMENT', 'Comment');
254
        $labels['Email'] = _t('SilverStripe\\Comments\\Model\\Comment.EMAIL', 'Email');
255
        $labels['URL'] = _t('SilverStripe\\Comments\\Model\\Comment.URL', 'URL');
256
        $labels['IsSpam'] = _t('SilverStripe\\Comments\\Model\\Comment.ISSPAM', 'Spam?');
257
        $labels['Moderated'] = _t('SilverStripe\\Comments\\Model\\Comment.MODERATED', 'Moderated?');
258
        $labels['ParentTitle'] = _t('SilverStripe\\Comments\\Model\\Comment.PARENTTITLE', 'Parent');
259
        $labels['Created'] = _t('SilverStripe\\Comments\\Model\\Comment.CREATED', 'Date posted');
260
261
        return $labels;
262
    }
263
264
    /**
265
     * Get the commenting option
266
     *
267
     * @param string $key
268
     *
269
     * @return mixed Result if the setting is available, or null otherwise
270
     */
271
    public function getOption($key)
272
    {
273
        // If possible use the current record
274
        $record = $this->Parent();
275
276
        if (!$record && $this->Parent()) {
277
            // Otherwise a singleton of that record
278
            $record = singleton($this->Parent()->dataClass());
279
        } elseif (!$record) {
280
            // Otherwise just use the default options
281
            $record = singleton(CommentsExtension::class);
282
        }
283
284
        return ($record instanceof CommentsExtension || $record->hasExtension(CommentsExtension::class))
285
            ? $record->getCommentsOption($key)
286
            : null;
287
    }
288
289
    /**
290
     * Returns the parent {@link DataObject} this comment is attached too
291
     *
292
     * @deprecated 4.0.0 Use $this->Parent() instead
293
     * @return DataObject
294
     */
295
    public function getParent()
296
    {
297
        return $this->BaseClass && $this->ParentID
298
            ? DataObject::get_by_id($this->BaseClass, $this->ParentID, true)
299
            : null;
300
    }
301
302
303
    /**
304
     * Returns a string to help identify the parent of the comment
305
     *
306
     * @return string
307
     */
308
    public function getParentTitle()
309
    {
310
        if ($parent = $this->Parent()) {
311
            return $parent->Title ?: ($parent->ClassName . ' #' . $parent->ID);
312
        }
313
    }
314
315
    /**
316
     * Comment-parent classnames obviously vary, return the parent classname
317
     *
318
     * @return string
319
     */
320
    public function getParentClassName()
321
    {
322
        return $this->Parent()->getClassName();
323
    }
324
325
    /**
326
     * {@inheritDoc}
327
     */
328
    public function castingHelper($field)
329
    {
330
        // Safely escape the comment
331
        if (in_array($field, ['EscapedComment', 'Comment'], true)) {
332
            return $this->AllowHtml ? 'HTMLText' : 'Text';
333
        }
334
        return parent::castingHelper($field);
335
    }
336
337
    /**
338
     * Content to be safely escaped on the frontend
339
     *
340
     * @return string
341
     */
342
    public function getEscapedComment()
343
    {
344
        return $this->Comment;
345
    }
346
347
    /**
348
     * Return whether this comment is a preview (has not been written to the db)
349
     *
350
     * @return boolean
351
     */
352
    public function isPreview()
353
    {
354
        return !$this->exists();
355
    }
356
357
    /**
358
     * @todo needs to compare to the new {@link Commenting} configuration API
359
     *
360
     * @param Member $member
361
     * @param array  $context
362
     * @return bool
363
     */
364
    public function canCreate($member = null, $context = [])
365
    {
366
        return false;
367
    }
368
369
    /**
370
     * Checks for association with a page, and {@link SiteTree->ProvidePermission}
371
     * flag being set to true.
372
     *
373
     * @param Member $member
374
     * @return Boolean
375
     */
376
    public function canView($member = null)
377
    {
378
        $member = $this->getMember($member);
379
380
        $extended = $this->extendedCan('canView', $member);
381
        if ($extended !== null) {
382
            return $extended;
383
        }
384
385
        if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
386
            return true;
387
        }
388
389
        if ($parent = $this->Parent()) {
390
            return $parent->canView($member)
391
                && $parent->hasExtension(CommentsExtension::class)
392
                && $parent->CommentsEnabled;
393
        }
394
395
        return false;
396
    }
397
398
    /**
399
     * Checks if the comment can be edited.
400
     *
401
     * @param null|int|Member $member
402
     * @return Boolean
403
     */
404
    public function canEdit($member = null)
405
    {
406
        $member = $this->getMember($member);
407
408
        if (!$member) {
409
            return false;
410
        }
411
412
        $extended = $this->extendedCan('canEdit', $member);
413
        if ($extended !== null) {
414
            return $extended;
415
        }
416
417
        if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
418
            return true;
419
        }
420
421
        if ($parent = $this->Parent()) {
422
            return $parent->canEdit($member);
423
        }
424
425
        return false;
426
    }
427
428
    /**
429
     * Checks if the comment can be deleted.
430
     *
431
     * @param null|int|Member $member
432
     * @return Boolean
433
     */
434
    public function canDelete($member = null)
435
    {
436
        $member = $this->getMember($member);
437
438
        if (!$member) {
439
            return false;
440
        }
441
442
        $extended = $this->extendedCan('canDelete', $member);
443
        if ($extended !== null) {
444
            return $extended;
445
        }
446
447
        return $this->canEdit($member);
448
    }
449
450
    /**
451
     * Resolves Member object.
452
     *
453
     * @param Member|int|null $member
454
     * @return Member|null
455
     */
456
    protected function getMember($member = null)
457
    {
458
        if (!$member) {
459
            $member = Member::currentUser();
460
        }
461
462
        if (is_numeric($member)) {
463
            $member = DataObject::get_by_id(Member::class, $member, true);
464
        }
465
466
        return $member;
467
    }
468
469
    /**
470
     * Return the authors name for the comment
471
     *
472
     * @return string
473
     */
474
    public function getAuthorName()
475
    {
476
        if ($this->Name) {
477
            return $this->Name;
478
        } elseif ($author = $this->Author()) {
479
            return $author->getName();
480
        }
481
    }
482
483
    /**
484
     * Generate a secure admin-action link authorised for the specified member
485
     *
486
     * @param string $action An action on CommentingController to link to
487
     * @param Member $member The member authorised to invoke this action
488
     *
489
     * @return string
490
     */
491
    protected function actionLink($action, $member = null)
492
    {
493
        if (!$member) {
494
            $member = Member::currentUser();
495
        }
496
        if (!$member) {
497
            return false;
498
        }
499
500
        /**
501
         * @todo: How do we handle "DataObject" instances that don't have a Link to reject/spam/delete?? This may
502
         * we have to make CMS a hard dependency instead.
503
         */
504
        // if (!$this->Parent()->hasMethod('Link')) {
505
        //     return false;
506
        // }
507
508
        $url = Controller::join_links(
509
            Director::baseURL(),
510
            'comments',
511
            $action,
512
            $this->ID
513
        );
514
515
        // Limit access for this user
516
        $token = $this->getSecurityToken();
517
        return $token->addToUrl($url, $member);
518
    }
519
520
    /**
521
     * Link to delete this comment
522
     *
523
     * @param Member $member
524
     *
525
     * @return string
526
     */
527
    public function DeleteLink($member = null)
528
    {
529
        if ($this->canDelete($member)) {
530
            return $this->actionLink('delete', $member);
531
        }
532
    }
533
534
    /**
535
     * Link to mark as spam
536
     *
537
     * @param Member $member
538
     *
539
     * @return string
540
     */
541
    public function SpamLink($member = null)
542
    {
543
        if ($this->canEdit($member) && !$this->IsSpam) {
544
            return $this->actionLink('spam', $member);
545
        }
546
    }
547
548
    /**
549
     * Link to mark as not-spam (ham)
550
     *
551
     * @param Member $member
552
     *
553
     * @return string
554
     */
555
    public function HamLink($member = null)
556
    {
557
        if ($this->canEdit($member) && $this->IsSpam) {
558
            return $this->actionLink('ham', $member);
559
        }
560
    }
561
562
    /**
563
     * Link to approve this comment
564
     *
565
     * @param Member $member
566
     *
567
     * @return string
568
     */
569
    public function ApproveLink($member = null)
570
    {
571
        if ($this->canEdit($member) && !$this->Moderated) {
572
            return $this->actionLink('approve', $member);
573
        }
574
    }
575
576
    /**
577
     * Mark this comment as spam
578
     */
579
    public function markSpam()
580
    {
581
        $this->IsSpam = true;
582
        $this->Moderated = true;
583
        $this->write();
584
        $this->extend('afterMarkSpam');
585
    }
586
587
    /**
588
     * Mark this comment as approved
589
     */
590
    public function markApproved()
591
    {
592
        $this->IsSpam = false;
593
        $this->Moderated = true;
594
        $this->write();
595
        $this->extend('afterMarkApproved');
596
    }
597
598
    /**
599
     * Mark this comment as unapproved
600
     */
601
    public function markUnapproved()
602
    {
603
        $this->Moderated = false;
604
        $this->write();
605
        $this->extend('afterMarkUnapproved');
606
    }
607
608
    /**
609
     * @return string
610
     */
611
    public function SpamClass()
612
    {
613
        if ($this->IsSpam) {
614
            return 'spam';
615
        } elseif (!$this->Moderated) {
616
            return 'unmoderated';
617
        } else {
618
            return 'notspam';
619
        }
620
    }
621
622
    /**
623
     * @return string
624
     */
625
    public function getTitle()
626
    {
627
        $title = sprintf(_t('SilverStripe\\Comments\\Model\\Comment.COMMENTBY', 'Comment by %s', 'Name'), $this->getAuthorName());
628
629
        if ($parent = $this->Parent()) {
630
            if ($parent->Title) {
631
                $title .= sprintf(' %s %s', _t('SilverStripe\\Comments\\Model\\Comment.ON', 'on'), $parent->Title);
632
            }
633
        }
634
635
        return $title;
636
    }
637
638
    /*
639
     * Modify the default fields shown to the user
640
     */
641
    public function getCMSFields()
642
    {
643
        $commentField = $this->AllowHtml ? HTMLEditorField::class : TextareaField::class;
644
        $fields = new FieldList(
645
            $this
646
                ->obj('Created')
647
                ->scaffoldFormField($this->fieldLabel('Created'))
648
                ->performReadonlyTransformation(),
649
            TextField::create('Name', $this->fieldLabel('Name')),
650
            $commentField::create('Comment', $this->fieldLabel('Comment')),
651
            EmailField::create('Email', $this->fieldLabel('Email')),
652
            TextField::create('URL', $this->fieldLabel('URL')),
653
            FieldGroup::create(array(
654
                CheckboxField::create('Moderated', $this->fieldLabel('Moderated')),
655
                CheckboxField::create('IsSpam', $this->fieldLabel('IsSpam')),
656
            ))
657
                ->setTitle(_t('SilverStripe\\Comments\\Model\\Comment.OPTIONS', 'Options'))
658
                ->setDescription(_t(
659
                    'SilverStripe\\Comments\\Model\\Comment.OPTION_DESCRIPTION',
660
                    'Unmoderated and spam comments will not be displayed until approved'
661
                ))
662
        );
663
664
        // Show member name if given
665
        if (($author = $this->Author()) && $author->exists()) {
666
            $fields->insertAfter(
667
                TextField::create('AuthorMember', $this->fieldLabel('Author'), $author->Title)
668
                    ->performReadonlyTransformation(),
669
                'Name'
670
            );
671
        }
672
673
        // Show parent comment if given
674
        if (($parent = $this->ParentComment()) && $parent->exists()) {
675
            $fields->push(new HeaderField(
676
                'ParentComment_Title',
677
                _t('SilverStripe\\Comments\\Model\\Comment.ParentComment_Title', 'This comment is a reply to the below')
678
            ));
679
            // Created date
680
            // FIXME - the method setName in DatetimeField is not chainable, hence
681
            // the lack of chaining here
682
            $createdField = $parent
683
                ->obj('Created')
684
                ->scaffoldFormField($parent->fieldLabel('Created'));
685
            $createdField->setName('ParentComment_Created');
686
            $createdField->setValue($parent->Created);
687
            $createdField->performReadonlyTransformation();
688
            $fields->push($createdField);
689
690
            // Name (could be member or string value)
691
            $fields->push(
692
                $parent
693
                    ->obj('AuthorName')
694
                    ->scaffoldFormField($parent->fieldLabel('AuthorName'))
695
                    ->setName('ParentComment_AuthorName')
696
                    ->setValue($parent->getAuthorName())
697
                    ->performReadonlyTransformation()
698
            );
699
700
            // Comment body
701
            $fields->push(
702
                $parent
703
                    ->obj('EscapedComment')
704
                    ->scaffoldFormField($parent->fieldLabel(self::class))
705
                    ->setName('ParentComment_EscapedComment')
706
                    ->setValue($parent->Comment)
707
                    ->performReadonlyTransformation()
708
            );
709
        }
710
711
        $this->extend('updateCMSFields', $fields);
712
        return $fields;
713
    }
714
715
    /**
716
     * @param  string $dirtyHtml
717
     *
718
     * @return string
719
     */
720
    public function purifyHtml($dirtyHtml)
721
    {
722
        if ($service = $this->getHtmlPurifierService()) {
723
            return $service->purify($dirtyHtml);
724
        }
725
726
        return $dirtyHtml;
727
    }
728
729
    /**
730
     * @return HTMLPurifier (or anything with a "purify()" method)
0 ignored issues
show
Should the return type not be HTMLPurifier|null?

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.

Loading history...
731
     */
732
    public function getHtmlPurifierService()
733
    {
734
        if (!class_exists(HTMLPurifier_Config::class)) {
735
            return null;
736
        }
737
738
        $config = HTMLPurifier_Config::createDefault();
739
        $allowedElements = (array) $this->getOption('html_allowed_elements');
740
        if (!empty($allowedElements)) {
741
            $config->set('HTML.AllowedElements', $allowedElements);
742
        }
743
744
        // This injector cannot be set unless the 'p' element is allowed
745
        if (in_array('p', $allowedElements)) {
746
            $config->set('AutoFormat.AutoParagraph', true);
747
        }
748
749
        $config->set('AutoFormat.Linkify', true);
750
        $config->set('URI.DisableExternalResources', true);
751
        $config->set('Cache.SerializerPath', TempFolder::getTempFolder(BASE_PATH));
752
        return new HTMLPurifier($config);
753
    }
754
755
    /**
756
     * Calculate the Gravatar link from the email address
757
     *
758
     * @return string
759
     */
760
    public function Gravatar()
761
    {
762
        $gravatar = '';
763
        $use_gravatar = $this->getOption('use_gravatar');
764
765
        if ($use_gravatar) {
766
            $gravatar = 'http://www.gravatar.com/avatar/' . md5(strtolower(trim($this->Email)));
767
            $gravatarsize = $this->getOption('gravatar_size');
768
            $gravatardefault = $this->getOption('gravatar_default');
769
            $gravatarrating = $this->getOption('gravatar_rating');
770
            $gravatar .= '?s=' . $gravatarsize . '&d=' . $gravatardefault . '&r=' . $gravatarrating;
771
        }
772
773
        return $gravatar;
774
    }
775
776
    /**
777
     * Determine if replies are enabled for this instance
778
     *
779
     * @return boolean
780
     */
781
    public function getRepliesEnabled()
782
    {
783
        // Check reply option
784
        if (!$this->getOption('nested_comments')) {
785
            return false;
786
        }
787
788
        // Check if depth is limited
789
        $maxLevel = $this->getOption('nested_depth');
790
        $notSpam = ($this->SpamClass() == 'notspam');
791
        return $notSpam && (!$maxLevel || $this->Depth < $maxLevel);
792
    }
793
794
    /**
795
     * Returns the list of all replies
796
     *
797
     * @return SS_List
798
     */
799
    public function AllReplies()
800
    {
801
        // No replies if disabled
802
        if (!$this->getRepliesEnabled()) {
803
            return new ArrayList();
804
        }
805
806
        // Get all non-spam comments
807
        $order = $this->getOption('order_replies_by')
808
            ?: $this->getOption('order_comments_by');
809
        $list = $this
810
            ->ChildComments()
811
            ->sort($order);
812
813
        $this->extend('updateAllReplies', $list);
814
        return $list;
815
    }
816
817
    /**
818
     * Returns the list of replies, with spam and unmoderated items excluded, for use in the frontend
819
     *
820
     * @return SS_List
821
     */
822
    public function Replies()
823
    {
824
        // No replies if disabled
825
        if (!$this->getRepliesEnabled()) {
826
            return new ArrayList();
827
        }
828
        $list = $this->AllReplies();
829
830
        // Filter spam comments for non-administrators if configured
831
        $parent = $this->Parent();
832
        $showSpam = $this->getOption('frontend_spam') && $parent && $parent->canModerateComments();
833
        if (!$showSpam) {
834
            $list = $list->filter('IsSpam', 0);
835
        }
836
837
        // Filter un-moderated comments for non-administrators if moderation is enabled
838
        $showUnmoderated = $parent && (
839
            ($parent->ModerationRequired === 'None')
840
            || ($this->getOption('frontend_moderation') && $parent->canModerateComments())
841
        );
842
        if (!$showUnmoderated) {
843
            $list = $list->filter('Moderated', 1);
844
        }
845
846
        $this->extend('updateReplies', $list);
847
        return $list;
848
    }
849
850
    /**
851
     * Returns the list of replies paged, with spam and unmoderated items excluded, for use in the frontend
852
     *
853
     * @return PaginatedList
854
     */
855 View Code Duplication
    public function PagedReplies()
856
    {
857
        $list = $this->Replies();
858
859
        // Add pagination
860
        $list = new PaginatedList($list, Controller::curr()->getRequest());
861
        $list->setPaginationGetVar('repliesstart' . $this->ID);
862
        $list->setPageLength($this->getOption('comments_per_page'));
863
864
        $this->extend('updatePagedReplies', $list);
865
        return $list;
866
    }
867
868
    /**
869
     * Generate a reply form for this comment
870
     *
871
     * @return Form
0 ignored issues
show
Should the return type not be null|\SilverStripe\Comments\Controllers\Form?

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.

Loading history...
872
     */
873
    public function ReplyForm()
874
    {
875
        // Ensure replies are enabled
876
        if (!$this->getRepliesEnabled()) {
877
            return null;
878
        }
879
880
        // Check parent is available
881
        $parent = $this->Parent();
882
        if (!$parent || !$parent->exists()) {
883
            return null;
884
        }
885
886
        // Build reply controller
887
        $controller = CommentingController::create();
888
        $controller->setOwnerRecord($parent);
889
        $controller->setParentClass($parent->ClassName);
890
        $controller->setOwnerController(Controller::curr());
891
892
        return $controller->ReplyForm($this);
893
    }
894
895
    /**
896
     * Refresh of this comment in the hierarchy
897
     */
898
    public function updateDepth()
899
    {
900
        $parent = $this->ParentComment();
901
        if ($parent && $parent->exists()) {
902
            $parent->updateDepth();
903
            $this->Depth = $parent->Depth + 1;
904
        } else {
905
            $this->Depth = 1;
906
        }
907
    }
908
}
909