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

src/Model/Comment.php (1 issue)

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