CommentsExtension::getCommentHolderID()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Comments\Extensions;
4
5
use SilverStripe\CMS\Model\SiteTree;
0 ignored issues
show
Bug introduced by
The type SilverStripe\CMS\Model\SiteTree 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use SilverStripe\Comments\Admin\CommentsGridField;
7
use SilverStripe\Comments\Admin\CommentsGridFieldConfig;
8
use SilverStripe\Comments\Controllers\CommentingController;
9
use SilverStripe\Comments\Model\Comment;
10
use SilverStripe\Control\Controller;
11
use SilverStripe\Control\Director;
12
use SilverStripe\Core\Config\Config;
13
use SilverStripe\Forms\CheckboxField;
14
use SilverStripe\Forms\DropdownField;
15
use SilverStripe\Forms\FieldGroup;
16
use SilverStripe\Forms\FieldList;
17
use SilverStripe\Forms\Tab;
18
use SilverStripe\Forms\TabSet;
19
use SilverStripe\ORM\DataExtension;
20
use SilverStripe\ORM\DataList;
21
use SilverStripe\ORM\PaginatedList;
22
use SilverStripe\Security\Member;
23
use SilverStripe\Security\Permission;
24
use SilverStripe\Security\Security;
25
use SilverStripe\View\Requirements;
26
27
/**
28
 * Extension to {@link DataObject} to enable tracking comments.
29
 *
30
 * @package comments
31
 */
32
class CommentsExtension extends DataExtension
33
{
34
    /**
35
     * Default configuration values
36
     *
37
     * enabled:                     Allows commenting to be disabled even if the extension is present
38
     * enabled_cms:                 Allows commenting to be enabled or disabled via the CMS
39
     * require_login:               Boolean, whether a user needs to login (required for required_permission)
40
     * require_login_cms:           Allows require_login to be set via the CMS
41
     * required_permission:         Permission (or array of permissions) required to comment
42
     * include_js:                  Enhance operation by ajax behaviour on moderation links (required for use_preview)
43
     * use_gravatar:                Set to true to show gravatar icons
44
     * gravatar_default:            Theme for 'not found' gravatar {@see http://gravatar.com/site/implement/images}
45
     * gravatar_rating:             Gravatar rating (same as the standard default)
46
     * show_comments_when_disabled: Show older comments when commenting has been disabled.
47
     * order_comments_by:           Default sort order.
48
     * order_replies_by:            Sort order for replies.
49
     * comments_holder_id:          ID for the comments holder
50
     * comment_permalink_prefix:    ID prefix for each comment
51
     * require_moderation:          Require moderation for all comments
52
     * require_moderation_cms:      Ignore other comment moderation config settings and set via CMS
53
     * frontend_moderation:         Display unmoderated comments in the frontend, if the user can moderate them.
54
     * frontend_spam:               Display spam comments in the frontend, if the user can moderate them.
55
     * html_allowed:                Allow for sanitized HTML in comments
56
     * use_preview:                 Preview formatted comment (when allowing HTML)
57
     * nested_comments:             Enable nested comments
58
     * nested_depth:                Max depth of nested comments in levels (where root is 1 depth) 0 means no limit.
59
     *
60
     * @var array
61
     *
62
     * @config
63
     */
64
    private static $comments = [
0 ignored issues
show
introduced by
The private property $comments is not used, and could be removed.
Loading history...
65
        'enabled' => true,
66
        'enabled_cms' => false,
67
        'require_login' => false,
68
        'require_login_cms' => false,
69
        'required_permission' => false,
70
        'include_js' => true,
71
        'use_gravatar' => false,
72
        'gravatar_size' => 80,
73
        'gravatar_default' => 'identicon',
74
        'gravatar_rating' => 'g',
75
        'show_comments_when_disabled' => false,
76
        'order_comments_by' => '"Created" DESC',
77
        'order_replies_by' => false,
78
        'comments_per_page' => 10,
79
        'comments_holder_id' => 'comments-holder',
80
        'comment_permalink_prefix' => 'comment-',
81
        'require_moderation' => false,
82
        'require_moderation_nonmembers' => false,
83
        'require_moderation_cms' => false,
84
        'frontend_moderation' => false,
85
        'frontend_spam' => false,
86
        'html_allowed' => false,
87
        'html_allowed_elements' => ['a', 'img', 'i', 'b'],
88
        'use_preview' => false,
89
        'nested_comments' => false,
90
        'nested_depth' => 2,
91
    ];
92
93
    /**
94
     * @var array
95
     */
96
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
97
        'ProvideComments' => 'Boolean',
98
        'ModerationRequired' => 'Enum(\'None,Required,NonMembersOnly\',\'None\')',
99
        'CommentsRequireLogin' => 'Boolean',
100
    ];
101
102
    /**
103
     * {@inheritDoc}
104
     */
105
    private static $has_many = [
0 ignored issues
show
introduced by
The private property $has_many is not used, and could be removed.
Loading history...
106
        'Commments' => Comment::class . '.Parent'
107
    ];
108
109
    /**
110
     * CMS configurable options should default to the config values, but respect
111
     * default values specified by the object
112
     */
113
    public function populateDefaults()
114
    {
115
        $defaults = $this->owner->config()->get('defaults');
116
117
        // Set if comments should be enabled by default
118
        if (isset($defaults['ProvideComments'])) {
119
            $this->owner->ProvideComments = $defaults['ProvideComments'];
120
        } else {
121
            $this->owner->ProvideComments = $this->owner->getCommentsOption('enabled') ? 1 : 0;
122
        }
123
124
        // If moderation options should be configurable via the CMS then
125
        if (isset($defaults['ModerationRequired'])) {
126
            $this->owner->ModerationRequired = $defaults['ModerationRequired'];
127
        } elseif ($this->owner->getCommentsOption('require_moderation')) {
128
            $this->owner->ModerationRequired = 'Required';
129
        } elseif ($this->owner->getCommentsOption('require_moderation_nonmembers')) {
130
            $this->owner->ModerationRequired = 'NonMembersOnly';
131
        } else {
132
            $this->owner->ModerationRequired = 'None';
133
        }
134
135
        // Set login required
136
        if (isset($defaults['CommentsRequireLogin'])) {
137
            $this->owner->CommentsRequireLogin = $defaults['CommentsRequireLogin'];
138
        } else {
139
            $this->owner->CommentsRequireLogin = $this->owner->getCommentsOption('require_login') ? 1 : 0;
140
        }
141
    }
142
143
144
    /**
145
     * If this extension is applied to a {@link SiteTree} record then
146
     * append a Provide Comments checkbox to allow authors to trigger
147
     * whether or not to display comments
148
     *
149
     * @todo Allow customization of other {@link Commenting} configuration
150
     *
151
     * @param FieldList $fields
152
     */
153
    public function updateSettingsFields(FieldList $fields)
154
    {
155
        $options = FieldGroup::create()->setTitle(_t(__CLASS__ . '.COMMENTOPTIONS', 'Comments'));
156
157
        // Check if enabled setting should be cms configurable
158
        if ($this->owner->getCommentsOption('enabled_cms')) {
159
            $options->push(CheckboxField::create('ProvideComments', _t(
160
                'SilverStripe\\Comments\\Model\\Comment.ALLOWCOMMENTS',
161
                'Allow comments'
162
            )));
163
        }
164
165
        // Check if we should require users to login to comment
166
        if ($this->owner->getCommentsOption('require_login_cms')) {
167
            $options->push(
168
                CheckboxField::create(
169
                    'CommentsRequireLogin',
170
                    _t('Comments.COMMENTSREQUIRELOGIN', 'Require login to comment')
171
                )
172
            );
173
        }
174
175
        if ($options->FieldList()->count()) {
176
            if ($fields->hasTabSet()) {
177
                $fields->addFieldsToTab('Root.Settings', $options);
178
            } else {
179
                $fields->push($options);
180
            }
181
        }
182
183
        // Check if moderation should be enabled via cms configurable
184
        if ($this->owner->getCommentsOption('require_moderation_cms')) {
185
            $moderationField = DropdownField::create(
186
                'ModerationRequired',
187
                _t(
188
                    __CLASS__ . '.COMMENTMODERATION',
189
                    'Comment Moderation'
190
                ),
191
                [
192
                    'None' => _t(__CLASS__ . '.MODERATIONREQUIRED_NONE', 'No moderation required'),
193
                    'Required' => _t(__CLASS__ . '.MODERATIONREQUIRED_REQUIRED', 'Moderate all comments'),
194
                    'NonMembersOnly' => _t(
195
                        __CLASS__ . '.MODERATIONREQUIRED_NONMEMBERSONLY',
196
                        'Only moderate non-members'
197
                    ),
198
                ]
199
            );
200
            if ($fields->hasTabSet()) {
201
                $fields->addFieldToTab('Root.Settings', $moderationField);
202
            } else {
203
                $fields->push($moderationField);
204
            }
205
        }
206
    }
207
208
    /**
209
     * Get comment moderation rules for this parent
210
     *
211
     * None:           No moderation required
212
     * Required:       All comments
213
     * NonMembersOnly: Only anonymous users
214
     *
215
     * @return string
216
     */
217
    public function getModerationRequired()
218
    {
219
        if ($this->owner->getCommentsOption('require_moderation_cms')) {
220
            return $this->owner->getField('ModerationRequired');
221
        }
222
223
        if ($this->owner->getCommentsOption('require_moderation')) {
224
            return 'Required';
225
        }
226
227
        if ($this->owner->getCommentsOption('require_moderation_nonmembers')) {
228
            return 'NonMembersOnly';
229
        }
230
231
        return 'None';
232
    }
233
234
    /**
235
     * Determine if users must be logged in to post comments
236
     *
237
     * @return boolean
238
     */
239
    public function getCommentsRequireLogin()
240
    {
241
        if ($this->owner->getCommentsOption('require_login_cms')) {
242
            return (bool) $this->owner->getField('CommentsRequireLogin');
243
        }
244
        return (bool) $this->owner->getCommentsOption('require_login');
245
    }
246
247
    /**
248
     * Returns the RelationList of all comments against this object. Can be used as a data source
249
     * for a gridfield with write access.
250
     *
251
     * @return DataList
252
     */
253
    public function AllComments()
254
    {
255
        $order = $this->owner->getCommentsOption('order_comments_by');
256
        $comments = Comment::get()
257
            ->filter([
258
                'ParentID' => $this->owner->ID,
259
                'ParentClass' => $this->owner->ClassName,
260
            ])
261
            ->sort($order);
262
        $this->owner->extend('updateAllComments', $comments);
263
        return $comments;
264
    }
265
266
    /**
267
     * Returns all comments against this object, with with spam and unmoderated items excluded, for use in the frontend
268
     *
269
     * @return DataList
270
     */
271
    public function AllVisibleComments()
272
    {
273
        $list = $this->AllComments();
274
275
        // Filter spam comments for non-administrators if configured
276
        $showSpam = $this->owner->getCommentsOption('frontend_spam') && $this->owner->canModerateComments();
277
278
        if (!$showSpam) {
279
            $list = $list->filter('IsSpam', 0);
280
        }
281
282
        // Filter un-moderated comments for non-administrators if moderation is enabled
283
        $showUnmoderated = ($this->owner->ModerationRequired === 'None')
284
            || ($this->owner->getCommentsOption('frontend_moderation') && $this->owner->canModerateComments());
285
        if (!$showUnmoderated) {
286
            $list = $list->filter('Moderated', 1);
287
        }
288
289
        $this->owner->extend('updateAllVisibleComments', $list);
290
        return $list;
291
    }
292
293
    /**
294
     * Returns the root level comments, with spam and unmoderated items excluded, for use in the frontend
295
     *
296
     * @return DataList
297
     */
298
    public function Comments()
299
    {
300
        $list = $this->AllVisibleComments();
301
302
        // If nesting comments, only show root level
303
        if ($this->owner->getCommentsOption('nested_comments')) {
304
            $list = $list->filter('ParentCommentID', 0);
305
        }
306
307
        $this->owner->extend('updateComments', $list);
308
        return $list;
309
    }
310
311
    /**
312
     * Returns a paged list of the root level comments, with spam and unmoderated items excluded,
313
     * for use in the frontend
314
     *
315
     * @return PaginatedList
316
     */
317
    public function PagedComments()
318
    {
319
        $list = $this->Comments();
320
321
        // Add pagination
322
        $list = PaginatedList::create($list, Controller::curr()->getRequest());
323
        $list->setPaginationGetVar('commentsstart' . $this->owner->ID);
324
        $list->setPageLength($this->owner->getCommentsOption('comments_per_page'));
325
326
        $this->owner->extend('updatePagedComments', $list);
327
        return $list;
328
    }
329
330
    /**
331
     * Determine if comments are enabled for this instance
332
     *
333
     * @return boolean
334
     */
335
    public function getCommentsEnabled()
336
    {
337
        // Don't display comments form for pseudo-pages (such as the login form)
338
        if (!$this->owner->exists()) {
339
            return false;
340
        }
341
342
        // Determine which flag should be used to determine if this is enabled
343
        if ($this->owner->getCommentsOption('enabled_cms')) {
344
            return (bool) $this->owner->ProvideComments;
345
        }
346
347
        return (bool) $this->owner->getCommentsOption('enabled');
348
    }
349
350
    /**
351
     * Get the HTML ID for the comment holder in the template
352
     *
353
     * @return string
354
     */
355
    public function getCommentHolderID()
356
    {
357
        return $this->owner->getCommentsOption('comments_holder_id');
358
    }
359
360
    /**
361
     * Permission codes required in order to post (or empty if none required)
362
     *
363
     * @return string|array Permission or list of permissions, if required
364
     */
365
    public function getPostingRequiredPermission()
366
    {
367
        return $this->owner->getCommentsOption('required_permission');
368
    }
369
370
    /**
371
     * Determine if a user can post comments on this item
372
     *
373
     * @param Member $member Member to check
374
     *
375
     * @return boolean
376
     */
377
    public function canPostComment($member = null)
378
    {
379
        // Deny if not enabled for this object
380
        if (!$this->owner->CommentsEnabled) {
381
            return false;
382
        }
383
384
        if (!$this->owner->canView($member)) {
385
            // deny if current user cannot view the underlying record.
386
            return false;
387
        }
388
389
        // Check if member is required
390
        $requireLogin = $this->owner->CommentsRequireLogin;
391
        if (!$requireLogin) {
392
            return true;
393
        }
394
395
        // Check member is logged in
396
        $member = $member ?: Security::getCurrentUser();
397
        if (!$member) {
0 ignored issues
show
introduced by
$member is of type SilverStripe\Security\Member, thus it always evaluated to true.
Loading history...
398
            return false;
399
        }
400
401
        // If member required check permissions
402
        $requiredPermission = $this->owner->PostingRequiredPermission;
403
        if ($requiredPermission && !Permission::checkMember($member, $requiredPermission)) {
404
            return false;
405
        }
406
407
        return true;
408
    }
409
410
    /**
411
     * Determine if this member can moderate comments in the CMS
412
     *
413
     * @param Member $member
414
     *
415
     * @return boolean
416
     */
417
    public function canModerateComments($member = null)
418
    {
419
        // Deny if not enabled for this object
420
        if (!$this->owner->CommentsEnabled) {
421
            return false;
422
        }
423
424
        // Fallback to can-edit
425
        return $this->owner->canEdit($member);
426
    }
427
428
    /**
429
     * Gets the RSS link to all comments
430
     *
431
     * @return string
432
     */
433
    public function getCommentRSSLink()
434
    {
435
        return Director::absoluteURL('comments/rss');
436
    }
437
438
    /**
439
     * Get the RSS link to all comments on this page
440
     *
441
     * @return string
442
     */
443
    public function getCommentRSSLinkPage()
444
    {
445
        return Controller::join_links(
446
            $this->getCommentRSSLink(),
447
            str_replace('\\', '-', get_class($this->owner)),
448
            $this->owner->ID
449
        );
450
    }
451
452
    /**
453
     * Comments interface for the front end. Includes the CommentAddForm and the composition
454
     * of the comments display.
455
     *
456
     * To customize the html see templates/CommentInterface.ss or extend this function with
457
     * your own extension.
458
     *
459
     * @todo Cleanup the passing of all this configuration based functionality
460
     *
461
     * @see  docs/en/Extending
462
     */
463
    public function CommentsForm()
464
    {
465
        // Check if enabled
466
        $enabled = $this->getCommentsEnabled();
467
        if ($enabled && $this->owner->getCommentsOption('include_js')) {
468
            Requirements::javascript('//code.jquery.com/jquery-3.3.1.min.js');
469
            Requirements::javascript('silverstripe/comments:thirdparty/jquery-validate/jquery.validate.min.js');
470
            Requirements::javascript('silverstripe/admin:client/dist/js/i18n.js');
471
            Requirements::add_i18n_javascript('silverstripe/comments:javascript/lang');
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\View\Requir...::add_i18n_javascript() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

471
            /** @scrutinizer ignore-deprecated */ Requirements::add_i18n_javascript('silverstripe/comments:javascript/lang');
Loading history...
472
            Requirements::javascript('silverstripe/comments:javascript/CommentsInterface.js');
473
        }
474
475
        $controller = CommentingController::create();
476
        $controller->setOwnerRecord($this->owner);
477
        $controller->setParentClass($this->owner->getClassName());
478
        $controller->setOwnerController(Controller::curr());
479
480
        $session = Controller::curr()->getRequest()->getSession();
481
        $moderatedSubmitted = $session->get('CommentsModerated');
482
        $session->clear('CommentsModerated');
483
484
        $form = ($enabled) ? $controller->CommentsForm() : false;
485
486
        // a little bit all over the show but to ensure a slightly easier upgrade for users
487
        // return back the same variables as previously done in comments
488
        return $this
489
            ->owner
490
            ->customise([
491
                'AddCommentForm' => $form,
492
                'ModeratedSubmitted' => $moderatedSubmitted,
493
            ])
494
            ->renderWith('CommentsInterface');
495
    }
496
497
    /**
498
     * Returns whether this extension instance is attached to a {@link SiteTree} object
499
     *
500
     * @return bool
501
     */
502
    public function attachedToSiteTree()
503
    {
504
        $class = $this->owner->baseClass();
505
506
        return (is_subclass_of($class, SiteTree::class)) || ($class == SiteTree::class);
507
    }
508
509
    /**
510
     * Get the commenting option for this object.
511
     *
512
     * This can be overridden in any instance or extension to customise the
513
     * option available.
514
     *
515
     * @param string $key
516
     *
517
     * @return mixed Result if the setting is available, or null otherwise
518
     */
519
    public function getCommentsOption($key)
520
    {
521
        $settings = $this->getCommentsOptions();
522
        $value = null;
523
524
        if (isset($settings[$key])) {
525
            $value = $settings[$key];
526
        }
527
528
        // To allow other extensions to customise this option
529
        if ($this->owner) {
530
            $this->owner->extend('updateCommentsOption', $key, $value);
531
        }
532
533
        return $value;
534
    }
535
536
    /**
537
     * @return array
538
     */
539
    public function getCommentsOptions()
540
    {
541
        if ($this->owner) {
542
            $settings = $this->owner->config()->get('comments');
543
        } else {
544
            $settings = Config::inst()->get(__CLASS__, 'comments');
545
        }
546
547
        return $settings;
548
    }
549
550
    /**
551
     * Add moderation functions to the current fieldlist
552
     *
553
     * @param FieldList $fields
554
     */
555
    protected function updateModerationFields(FieldList $fields)
556
    {
557
        Requirements::css('silverstripe/comments:css/cms.css');
558
559
        $newComments = $this->owner->AllComments()->filter('Moderated', 0);
560
561
        $newGrid = CommentsGridField::create(
562
            'NewComments',
563
            _t('CommentsAdmin.NewComments', 'New'),
564
            $newComments,
565
            CommentsGridFieldConfig::create()
566
        );
567
568
        $approvedComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 0);
569
570
        $approvedGrid = new CommentsGridField(
571
            'ApprovedComments',
572
            _t('CommentsAdmin.Comments', 'Approved'),
573
            $approvedComments,
574
            CommentsGridFieldConfig::create()
575
        );
576
577
        $spamComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 1);
578
579
        $spamGrid = CommentsGridField::create(
580
            'SpamComments',
581
            _t('CommentsAdmin.SpamComments', 'Spam'),
582
            $spamComments,
583
            CommentsGridFieldConfig::create()
584
        );
585
586
        $newCount = '(' . count($newComments) . ')';
587
        $approvedCount = '(' . count($approvedComments) . ')';
588
        $spamCount = '(' . count($spamComments) . ')';
589
590
        if ($fields->hasTabSet()) {
591
            $tabs = TabSet::create(
592
                'Comments',
593
                Tab::create(
594
                    'CommentsNewCommentsTab',
595
                    _t('SilverStripe\\Comments\\Admin\\CommentAdmin.NewComments', 'New') . ' ' . $newCount,
596
                    $newGrid
597
                ),
598
                Tab::create(
599
                    'CommentsCommentsTab',
600
                    _t('SilverStripe\\Comments\\Admin\\CommentAdmin.Comments', 'Approved') . ' ' . $approvedCount,
601
                    $approvedGrid
602
                ),
603
                Tab::create(
604
                    'CommentsSpamCommentsTab',
605
                    _t('SilverStripe\\Comments\\Admin\\CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount,
606
                    $spamGrid
607
                )
608
            );
609
            $tabs->setTitle(_t(__CLASS__ . '.COMMENTSTABSET', 'Comments'));
610
611
            $fields->addFieldToTab('Root', $tabs);
612
        } else {
613
            $fields->push($newGrid);
614
            $fields->push($approvedGrid);
615
            $fields->push($spamGrid);
616
        }
617
    }
618
619
    public function updateCMSFields(FieldList $fields)
620
    {
621
        // Disable moderation if not permitted
622
        if ($this->owner->canModerateComments()) {
623
            $this->updateModerationFields($fields);
624
        }
625
626
        // If this isn't a page we should merge the settings into the CMS fields
627
        if (!$this->attachedToSiteTree()) {
628
            $this->updateSettingsFields($fields);
629
        }
630
    }
631
}
632