Completed
Pull Request — master (#195)
by Robbie
08:16
created

CommentsTest::testGetOption()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace SilverStripe\Comments\Tests;
4
5
use ReflectionClass;
6
use SilverStripe\Comments\Extensions\CommentsExtension;
7
use SilverStripe\Comments\Model\Comment;
8
use SilverStripe\Comments\Tests\Stubs\CommentableItem;
9
use SilverStripe\Comments\Tests\Stubs\CommentableItemDisabled;
10
use SilverStripe\Comments\Tests\Stubs\CommentableItemEnabled;
11
use SilverStripe\Control\Controller;
12
use SilverStripe\Control\Director;
13
use SilverStripe\Core\Config\Config;
14
use SilverStripe\Core\Email\Email;
15
use SilverStripe\Dev\FunctionalTest;
16
use SilverStripe\Dev\TestOnly;
17
use SilverStripe\i18n\i18n;
18
use SilverStripe\ORM\DataObject;
19
use SilverStripe\Security\Member;
20
use SilverStripe\Security\Permission;
21
22
/**
23
 * @package comments
24
 */
25
class CommentsTest extends FunctionalTest
26
{
27
    /**
28
     * {@inheritDoc}
29
     */
30
    public static $fixture_file = 'comments/tests/CommentsTest.yml';
31
32
    /**
33
     * {@inheritDoc}
34
     */
35
    protected $extraDataObjects = array(
36
        CommentableItem::class,
37
        CommentableItemEnabled::class,
38
        CommentableItemDisabled::class
39
    );
40
41 View Code Duplication
    public function setUp()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
42
    {
43
        parent::setUp();
44
        Config::nest();
45
46
        // Set good default values
47
        Config::inst()->update(CommentsExtension::class, 'comments', array(
48
            'enabled' => true,
49
            'enabled_cms' => false,
50
            'require_login' => false,
51
            'require_login_cms' => false,
52
            'required_permission' => false,
53
            'require_moderation_nonmembers' => false,
54
            'require_moderation' => false,
55
            'require_moderation_cms' => false,
56
            'frontend_moderation' => false,
57
            'frontend_spam' => false,
58
        ));
59
60
        // Configure this dataobject
61
        Config::inst()->update(CommentableItem::class, 'comments', array(
62
            'enabled_cms' => true
63
        ));
64
    }
65
66
    public function tearDown()
67
    {
68
        Config::unnest();
69
        parent::tearDown();
70
    }
71
72
    public function testCommentsList()
73
    {
74
        // comments don't require moderation so unmoderated comments can be
75
        // shown but not spam posts
76
        Config::inst()->update(CommentableItem::class, 'comments', array(
77
            'require_moderation_nonmembers' => false,
78
            'require_moderation' => false,
79
            'require_moderation_cms' => false,
80
        ));
81
82
        $item = $this->objFromFixture(CommentableItem::class, 'spammed');
83
        $this->assertEquals('None', $item->ModerationRequired);
84
85
        $this->assertDOSEquals(array(
86
            array('Name' => 'Comment 1'),
87
            array('Name' => 'Comment 3')
88
        ), $item->Comments(), 'Only 2 non spam posts should be shown');
0 ignored issues
show
Unused Code introduced by
The call to CommentsTest::assertDOSEquals() has too many arguments starting with 'Only 2 non spam posts should be shown'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
89
90
        // when moderated, only moderated, non spam posts should be shown.
91
        Config::inst()->update(CommentableItem::class, 'comments', array('require_moderation_nonmembers' => true));
92
        $this->assertEquals('NonMembersOnly', $item->ModerationRequired);
93
94
        // Check that require_moderation overrides this option
95
        Config::inst()->update(CommentableItem::class, 'comments', array('require_moderation' => true));
96
        $this->assertEquals('Required', $item->ModerationRequired);
97
98
        $this->assertDOSEquals(array(
99
            array('Name' => 'Comment 3')
100
        ), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
0 ignored issues
show
Unused Code introduced by
The call to CommentsTest::assertDOSEquals() has too many arguments starting with 'Only 1 non spam, moderated post should be shown'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
101
        $this->assertEquals(1, $item->Comments()->Count());
102
103
        // require_moderation_nonmembers still filters out unmoderated comments
104
        Config::inst()->update(CommentableItem::class, 'comments', array('require_moderation' => false));
105
        $this->assertEquals(1, $item->Comments()->Count());
106
107
        Config::inst()->update(CommentableItem::class, 'comments', array('require_moderation_nonmembers' => false));
108
        $this->assertEquals(2, $item->Comments()->Count());
109
110
        // With unmoderated comments set to display in frontend
111
        Config::inst()->update(CommentableItem::class, 'comments', array(
112
            'require_moderation' => true,
113
            'frontend_moderation' => true
114
        ));
115
        $this->assertEquals(1, $item->Comments()->Count());
116
117
        $this->logInWithPermission('ADMIN');
118
        $this->assertEquals(2, $item->Comments()->Count());
119
120
        // With spam comments set to display in frontend
121
        Config::inst()->update(CommentableItem::class, 'comments', array(
122
            'require_moderation' => true,
123
            'frontend_moderation' => false,
124
            'frontend_spam' => true,
125
        ));
126
        if ($member = Member::currentUser()) {
127
            $member->logOut();
128
        }
129
        $this->assertEquals(1, $item->Comments()->Count());
130
131
        $this->logInWithPermission('ADMIN');
132
        $this->assertEquals(2, $item->Comments()->Count());
133
134
135
        // With spam and unmoderated comments set to display in frontend
136
        Config::inst()->update(CommentableItem::class, 'comments', array(
137
            'require_moderation' => true,
138
            'frontend_moderation' => true,
139
            'frontend_spam' => true,
140
        ));
141
        if ($member = Member::currentUser()) {
142
            $member->logOut();
143
        }
144
        $this->assertEquals(1, $item->Comments()->Count());
145
146
        $this->logInWithPermission('ADMIN');
147
        $this->assertEquals(4, $item->Comments()->Count());
148
    }
149
150
    /**
151
     * Test moderation options configured via the CMS
152
     */
153
    public function testCommentCMSModerationList()
154
    {
155
        // comments don't require moderation so unmoderated comments can be
156
        // shown but not spam posts
157
        Config::inst()->update(CommentableItem::class, 'comments', array(
158
            'require_moderation' => true,
159
            'require_moderation_cms' => true,
160
        ));
161
162
        $item = $this->objFromFixture(CommentableItem::class, 'spammed');
163
        $this->assertEquals('None', $item->ModerationRequired);
164
165
        $this->assertDOSEquals(array(
166
            array('Name' => 'Comment 1'),
167
            array('Name' => 'Comment 3')
168
        ), $item->Comments(), 'Only 2 non spam posts should be shown');
0 ignored issues
show
Unused Code introduced by
The call to CommentsTest::assertDOSEquals() has too many arguments starting with 'Only 2 non spam posts should be shown'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
169
170
        // when moderated, only moderated, non spam posts should be shown.
171
        $item->ModerationRequired = 'NonMembersOnly';
172
        $item->write();
173
        $this->assertEquals('NonMembersOnly', $item->ModerationRequired);
174
175
        // Check that require_moderation overrides this option
176
        $item->ModerationRequired = 'Required';
177
        $item->write();
178
        $this->assertEquals('Required', $item->ModerationRequired);
179
180
        $this->assertDOSEquals(array(
181
            array('Name' => 'Comment 3')
182
        ), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
0 ignored issues
show
Unused Code introduced by
The call to CommentsTest::assertDOSEquals() has too many arguments starting with 'Only 1 non spam, moderated post should be shown'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
183
        $this->assertEquals(1, $item->Comments()->Count());
184
185
        // require_moderation_nonmembers still filters out unmoderated comments
186
        $item->ModerationRequired = 'NonMembersOnly';
187
        $item->write();
188
        $this->assertEquals(1, $item->Comments()->Count());
189
190
        $item->ModerationRequired = 'None';
191
        $item->write();
192
        $this->assertEquals(2, $item->Comments()->Count());
193
    }
194
195
    public function testCanPostComment()
196
    {
197
        Config::inst()->update(CommentableItem::class, 'comments', array(
198
            'require_login' => false,
199
            'require_login_cms' => false,
200
            'required_permission' => false,
201
        ));
202
        $item = $this->objFromFixture(CommentableItem::class, 'first');
203
        $item2 = $this->objFromFixture(CommentableItem::class, 'second');
204
205
        // Test restriction free commenting
206
        if ($member = Member::currentUser()) {
207
            $member->logOut();
208
        }
209
        $this->assertFalse($item->CommentsRequireLogin);
210
        $this->assertTrue($item->canPostComment());
211
212
        // Test permission required to post
213
        Config::inst()->update(CommentableItem::class, 'comments', array(
214
            'require_login' => true,
215
            'required_permission' => 'POSTING_PERMISSION',
216
        ));
217
        $this->assertTrue($item->CommentsRequireLogin);
218
        $this->assertFalse($item->canPostComment());
219
        $this->logInWithPermission('WRONG_ONE');
220
        $this->assertFalse($item->canPostComment());
221
        $this->logInWithPermission('POSTING_PERMISSION');
222
        $this->assertTrue($item->canPostComment());
223
        $this->logInWithPermission('ADMIN');
224
        $this->assertTrue($item->canPostComment());
225
226
        // Test require login to post, but not any permissions
227
        Config::inst()->update(CommentableItem::class, 'comments', array(
228
            'required_permission' => false,
229
        ));
230
        $this->assertTrue($item->CommentsRequireLogin);
231
        if ($member = Member::currentUser()) {
232
            $member->logOut();
233
        }
234
        $this->assertFalse($item->canPostComment());
235
        $this->logInWithPermission('ANY_PERMISSION');
236
        $this->assertTrue($item->canPostComment());
237
238
        // Test options set via CMS
239
        Config::inst()->update(CommentableItem::class, 'comments', array(
240
            'require_login' => true,
241
            'require_login_cms' => true,
242
        ));
243
        $this->assertFalse($item->CommentsRequireLogin);
244
        $this->assertTrue($item2->CommentsRequireLogin);
245
        if ($member = Member::currentUser()) {
246
            $member->logOut();
247
        }
248
        $this->assertTrue($item->canPostComment());
249
        $this->assertFalse($item2->canPostComment());
250
251
        // Login grants permission to post
252
        $this->logInWithPermission('ANY_PERMISSION');
253
        $this->assertTrue($item->canPostComment());
254
        $this->assertTrue($item2->canPostComment());
255
    }
256
    public function testDeleteComment()
257
    {
258
        // Test anonymous user
259
        if ($member = Member::currentUser()) {
260
            $member->logOut();
261
        }
262
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
263
        $commentID = $comment->ID;
264
        $this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
265
        $delete = $this->get('comments/delete/' . $comment->ID . '?ajax=1');
266
        $this->assertEquals(403, $delete->getStatusCode());
267
        $check = DataObject::get_by_id(Comment::class, $commentID);
268
        $this->assertTrue($check && $check->exists());
269
270
        // Test non-authenticated user
271
        $this->logInAs('visitor');
272
        $this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
273
274
        // Test authenticated user
275
        $this->logInAs('commentadmin');
276
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
277
        $commentID = $comment->ID;
278
        $adminComment1Link = $comment->DeleteLink();
279
        $this->assertContains('comments/delete/' . $commentID . '?t=', $adminComment1Link);
280
281
        // Test that this link can't be shared / XSS exploited
282
        $this->logInAs('commentadmin2');
283
        $delete = $this->get($adminComment1Link);
284
        $this->assertEquals(400, $delete->getStatusCode());
285
        $check = DataObject::get_by_id(Comment::class, $commentID);
286
        $this->assertTrue($check && $check->exists());
287
288
        // Test that this other admin can delete the comment with their own link
289
        $adminComment2Link = $comment->DeleteLink();
290
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
291
        $this->autoFollowRedirection = false;
292
        $delete = $this->get($adminComment2Link);
293
        $this->assertEquals(302, $delete->getStatusCode());
294
        $check = DataObject::get_by_id(Comment::class, $commentID);
295
        $this->assertFalse($check && $check->exists());
296
    }
297
298 View Code Duplication
    public function testSpamComment()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
    {
300
        // Test anonymous user
301
        if ($member = Member::currentUser()) {
302
            $member->logOut();
303
        }
304
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
305
        $commentID = $comment->ID;
306
        $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
307
        $spam = $this->get('comments/spam/'.$comment->ID.'?ajax=1');
308
        $this->assertEquals(403, $spam->getStatusCode());
309
        $check = DataObject::get_by_id(Comment::class, $commentID);
310
        $this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
311
312
        // Test non-authenticated user
313
        $this->logInAs('visitor');
314
        $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
315
316
        // Test authenticated user
317
        $this->logInAs('commentadmin');
318
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
319
        $commentID = $comment->ID;
320
        $adminComment1Link = $comment->SpamLink();
321
        $this->assertContains('comments/spam/' . $commentID . '?t=', $adminComment1Link);
322
323
        // Test that this link can't be shared / XSS exploited
324
        $this->logInAs('commentadmin2');
325
        $spam = $this->get($adminComment1Link);
326
        $this->assertEquals(400, $spam->getStatusCode());
327
        $check = DataObject::get_by_id(Comment::class, $comment->ID);
328
        $this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
329
330
        // Test that this other admin can spam the comment with their own link
331
        $adminComment2Link = $comment->SpamLink();
332
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
333
        $this->autoFollowRedirection = false;
334
        $spam = $this->get($adminComment2Link);
335
        $this->assertEquals(302, $spam->getStatusCode());
336
        $check = DataObject::get_by_id(Comment::class, $commentID);
337
        $this->assertEquals(1, $check->IsSpam);
338
339
        // Cannot re-spam spammed comment
340
        $this->assertNull($check->SpamLink());
341
    }
342
343 View Code Duplication
    public function testHamComment()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
344
    {
345
        // Test anonymous user
346
        if ($member = Member::currentUser()) {
347
            $member->logOut();
348
        }
349
        $comment = $this->objFromFixture(Comment::class, 'secondComC');
350
        $commentID = $comment->ID;
351
        $this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
352
        $ham = $this->get('comments/ham/' . $comment->ID . '?ajax=1');
353
        $this->assertEquals(403, $ham->getStatusCode());
354
        $check = DataObject::get_by_id(Comment::class, $commentID);
355
        $this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
356
357
        // Test non-authenticated user
358
        $this->logInAs('visitor');
359
        $this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
360
361
        // Test authenticated user
362
        $this->logInAs('commentadmin');
363
        $comment = $this->objFromFixture(Comment::class, 'secondComC');
364
        $commentID = $comment->ID;
365
        $adminComment1Link = $comment->HamLink();
366
        $this->assertContains('comments/ham/' . $commentID . '?t=', $adminComment1Link);
367
368
        // Test that this link can't be shared / XSS exploited
369
        $this->logInAs('commentadmin2');
370
        $ham = $this->get($adminComment1Link);
371
        $this->assertEquals(400, $ham->getStatusCode());
372
        $check = DataObject::get_by_id(Comment::class, $comment->ID);
373
        $this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
374
375
        // Test that this other admin can ham the comment with their own link
376
        $adminComment2Link = $comment->HamLink();
377
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
378
        $this->autoFollowRedirection = false;
379
        $ham = $this->get($adminComment2Link);
380
        $this->assertEquals(302, $ham->getStatusCode());
381
        $check = DataObject::get_by_id(Comment::class, $commentID);
382
        $this->assertEquals(0, $check->IsSpam);
383
384
        // Cannot re-ham hammed comment
385
        $this->assertNull($check->HamLink());
386
    }
387
388 View Code Duplication
    public function testApproveComment()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
389
    {
390
        // Test anonymous user
391
        if ($member = Member::currentUser()) {
392
            $member->logOut();
393
        }
394
        $comment = $this->objFromFixture(Comment::class, 'secondComB');
395
        $commentID = $comment->ID;
396
        $this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
397
        $approve = $this->get('comments/approve/' . $comment->ID . '?ajax=1');
398
        $this->assertEquals(403, $approve->getStatusCode());
399
        $check = DataObject::get_by_id(Comment::class, $commentID);
400
        $this->assertEquals(0, $check->Moderated, 'No permission to approve');
401
402
        // Test non-authenticated user
403
        $this->logInAs('visitor');
404
        $this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
405
406
        // Test authenticated user
407
        $this->logInAs('commentadmin');
408
        $comment = $this->objFromFixture(Comment::class, 'secondComB');
409
        $commentID = $comment->ID;
410
        $adminComment1Link = $comment->ApproveLink();
411
        $this->assertContains('comments/approve/' . $commentID . '?t=', $adminComment1Link);
412
413
        // Test that this link can't be shared / XSS exploited
414
        $this->logInAs('commentadmin2');
415
        $approve = $this->get($adminComment1Link);
416
        $this->assertEquals(400, $approve->getStatusCode());
417
        $check = DataObject::get_by_id(Comment::class, $comment->ID);
418
        $this->assertEquals(0, $check->Moderated, 'No permission to approve');
419
420
        // Test that this other admin can approve the comment with their own link
421
        $adminComment2Link = $comment->ApproveLink();
422
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
423
        $this->autoFollowRedirection = false;
424
        $approve = $this->get($adminComment2Link);
425
        $this->assertEquals(302, $approve->getStatusCode());
426
        $check = DataObject::get_by_id(Comment::class, $commentID);
427
        $this->assertEquals(1, $check->Moderated);
428
429
        // Cannot re-approve approved comment
430
        $this->assertNull($check->ApproveLink());
431
    }
432
433
    public function testCommenterURLWrite()
434
    {
435
        $comment = new Comment();
436
        // We only care about the CommenterURL, so only set that
437
        // Check a http and https URL. Add more test urls here as needed.
438
        $protocols = array(
439
            'Http',
440
            'Https',
441
        );
442
        $url = '://example.com';
443
444
        foreach ($protocols as $protocol) {
445
            $comment->CommenterURL = $protocol . $url;
446
            // The protocol should stay as if, assuming it is valid
447
            $comment->write();
448
            $this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol');
449
        }
450
    }
451
452
    public function testSanitizesWithAllowHtml()
453
    {
454
        if (!class_exists('\\HTMLPurifier')) {
455
            $this->markTestSkipped('HTMLPurifier class not found');
456
            return;
457
        }
458
459
        // Add p for paragraph
460
        // NOTE: The config method appears to append to the existing array
461
        Config::inst()->update(CommentableItem::class, 'comments', array(
462
            'html_allowed_elements' => array('p'),
463
        ));
464
465
        // Without HTML allowed
466
        $comment1 = new Comment();
467
        $comment1->AllowHtml = false;
468
        $comment1->BaseClass = CommentableItem::class;
469
        $comment1->Comment = '<p><script>alert("w00t")</script>my comment</p>';
470
        $comment1->write();
471
        $this->assertEquals(
472
            '<p><script>alert("w00t")</script>my comment</p>',
473
            $comment1->Comment,
474
            'Does not remove HTML tags with html_allowed=false, ' .
475
            'which is correct behaviour because the HTML will be escaped'
476
        );
477
478
        // With HTML allowed
479
        $comment2 = new Comment();
480
        $comment2->AllowHtml = true;
481
        $comment2->ParentClass = CommentableItem::class;
482
        $comment2->Comment = '<p><script>alert("w00t")</script>my comment</p>';
483
        $comment2->write();
484
        $this->assertEquals(
485
            '<p>my comment</p>',
486
            $comment2->Comment,
487
            'Removes HTML tags which are not on the whitelist'
488
        );
489
    }
490
491
    public function testDefaultTemplateRendersHtmlWithAllowHtml()
492
    {
493
        if (!class_exists('\\HTMLPurifier')) {
494
            $this->markTestSkipped('HTMLPurifier class not found');
495
        }
496
497
        Config::inst()->update(CommentableItem::class, 'comments', array(
498
            'html_allowed_elements' => array('p'),
499
        ));
500
501
        $item = new CommentableItem();
502
        $item->write();
503
504
        // Without HTML allowed
505
        $comment = new Comment();
506
        $comment->Comment = '<p>my comment</p>';
507
        $comment->AllowHtml = false;
508
        $comment->ParentID = $item->ID;
509
        $comment->BaseClass = CommentableItem::class;
510
        $comment->write();
511
512
        $html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
513
        $this->assertContains(
514
            '&lt;p&gt;my comment&lt;/p&gt;',
515
            $html
516
        );
517
518
        $comment->AllowHtml = true;
519
        $comment->write();
520
        $html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
521
        $this->assertContains(
522
            '<p>my comment</p>',
523
            $html
524
        );
525
    }
526
527
528
    /**
529
     * Tests whether comments are enabled or disabled by default
530
     */
531
    public function testDefaultEnabled()
532
    {
533
        // Ensure values are set via cms (not via config)
534
        Config::inst()->update(CommentableItem::class, 'comments', array(
535
            'enabled_cms' => true,
536
            'require_moderation_cms' => true,
537
            'require_login_cms' => true
538
        ));
539
540
        // With default = true
541
        $obj = new CommentableItem();
542
        $this->assertTrue((bool)$obj->getCommentsOption('enabled'), "Default setting is enabled");
0 ignored issues
show
Documentation Bug introduced by
The method getCommentsOption does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
543
        $this->assertTrue((bool)$obj->ProvideComments);
0 ignored issues
show
Documentation introduced by
The property ProvideComments does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
544
        $this->assertEquals('None', $obj->ModerationRequired);
0 ignored issues
show
Documentation introduced by
The property ModerationRequired does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
545
        $this->assertFalse((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Documentation introduced by
The property CommentsRequireLogin does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
546
547
        $obj = new CommentableItemEnabled();
548
        $this->assertTrue((bool)$obj->ProvideComments);
0 ignored issues
show
Documentation introduced by
The property ProvideComments does not exist on object<SilverStripe\Comm...CommentableItemEnabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
549
        $this->assertEquals('Required', $obj->ModerationRequired);
0 ignored issues
show
Documentation introduced by
The property ModerationRequired does not exist on object<SilverStripe\Comm...CommentableItemEnabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
550
        $this->assertTrue((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Documentation introduced by
The property CommentsRequireLogin does not exist on object<SilverStripe\Comm...CommentableItemEnabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
551
552
        $obj = new CommentableItemDisabled();
553
        $this->assertFalse((bool)$obj->ProvideComments);
0 ignored issues
show
Documentation introduced by
The property ProvideComments does not exist on object<SilverStripe\Comm...ommentableItemDisabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
554
        $this->assertEquals('None', $obj->ModerationRequired);
0 ignored issues
show
Documentation introduced by
The property ModerationRequired does not exist on object<SilverStripe\Comm...ommentableItemDisabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
555
        $this->assertFalse((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Documentation introduced by
The property CommentsRequireLogin does not exist on object<SilverStripe\Comm...ommentableItemDisabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
556
557
        // With default = false
558
        // Because of config rules about falsey values, apply config to object directly
559
        Config::inst()->update(CommentableItem::class, 'comments', array(
560
            'enabled' => false,
561
            'require_login' => true,
562
            'require_moderation' => true
563
        ));
564
        $obj = new CommentableItem();
565
        $this->assertFalse((bool)$obj->getCommentsOption('enabled'), 'Default setting is disabled');
0 ignored issues
show
Documentation Bug introduced by
The method getCommentsOption does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
566
        $this->assertFalse((bool)$obj->ProvideComments);
0 ignored issues
show
Documentation introduced by
The property ProvideComments does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
567
        $this->assertEquals('Required', $obj->ModerationRequired);
0 ignored issues
show
Documentation introduced by
The property ModerationRequired does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
568
        $this->assertTrue((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Documentation introduced by
The property CommentsRequireLogin does not exist on object<SilverStripe\Comm...\Stubs\CommentableItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
569
570
        $obj = new CommentableItemEnabled();
571
        $this->assertTrue((bool)$obj->ProvideComments);
0 ignored issues
show
Documentation introduced by
The property ProvideComments does not exist on object<SilverStripe\Comm...CommentableItemEnabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
572
        $this->assertEquals('Required', $obj->ModerationRequired);
0 ignored issues
show
Documentation introduced by
The property ModerationRequired does not exist on object<SilverStripe\Comm...CommentableItemEnabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
573
        $this->assertTrue((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Documentation introduced by
The property CommentsRequireLogin does not exist on object<SilverStripe\Comm...CommentableItemEnabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
574
575
        $obj = new CommentableItemDisabled();
576
        $this->assertFalse((bool)$obj->ProvideComments);
0 ignored issues
show
Documentation introduced by
The property ProvideComments does not exist on object<SilverStripe\Comm...ommentableItemDisabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
577
        $this->assertEquals('None', $obj->ModerationRequired);
0 ignored issues
show
Documentation introduced by
The property ModerationRequired does not exist on object<SilverStripe\Comm...ommentableItemDisabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
578
        $this->assertFalse((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Documentation introduced by
The property CommentsRequireLogin does not exist on object<SilverStripe\Comm...ommentableItemDisabled>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
579
    }
580
581
    /*
582
    When a parent comment is deleted, remove the children
583
     */
584
    public function testOnBeforeDelete()
585
    {
586
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
587
588
        $child = new Comment();
589
        $child->Name = 'Fred Bloggs';
590
        $child->Comment = 'Child of firstComA';
591
        $child->write();
592
        $comment->ChildComments()->add($child);
593
        $this->assertEquals(4, $comment->ChildComments()->count());
594
595
        $commentID = $comment->ID;
596
        $childCommentID = $child->ID;
597
598
        $comment->delete();
599
600
        // assert that the new child been deleted
601
        $this->assertFalse(DataObject::get_by_id(Comment::class, $commentID));
602
        $this->assertFalse(DataObject::get_by_id(Comment::class, $childCommentID));
603
    }
604
605
    public function testRequireDefaultRecords()
606
    {
607
        $this->markTestSkipped('TODO');
608
    }
609
610
    public function testLink()
611
    {
612
        $comment = $this->objFromFixture(Comment::class, 'thirdComD');
613
        $this->assertEquals(
614
            'CommentableItemController#comment-' . $comment->ID,
615
            $comment->Link()
616
        );
617
        $this->assertEquals($comment->ID, $comment->ID);
618
619
        // An orphan comment has no link
620
        $comment->ParentID = 0;
621
        $comment->ParentClass = null;
622
        $comment->write();
623
        $this->assertEquals('', $comment->Link());
624
    }
625
626
    public function testPermalink()
627
    {
628
        $comment = $this->objFromFixture(Comment::class, 'thirdComD');
629
        $this->assertEquals('comment-' . $comment->ID, $comment->Permalink());
630
    }
631
632
    /**
633
     * Test field labels in 2 languages
634
     */
635
    public function testFieldLabels()
636
    {
637
        $locale = i18n::get_locale();
638
        i18n::set_locale('fr');
639
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
640
        $labels = $comment->FieldLabels();
641
        $expected = array(
642
            'Name' => 'Nom de l\'Auteur',
643
            'Comment' => 'Commentaire',
644
            'Email' => 'Email',
645
            'URL' => 'URL',
646
            'Moderated' => 'Modéré?',
647
            'IsSpam' => 'Spam?',
648
            'AllowHtml' => 'Allow Html',
649
            'SecretToken' => 'Secret Token',
650
            'Depth' => 'Depth',
651
            'Author' => 'Author Member',
652
            'ParentComment' => 'Parent Comment',
653
            'ChildComments' => 'Child Comments',
654
            'ParentTitle' => 'Parent',
655
            'Created' => 'Date de publication',
656
            'Parent' => 'Parent'
657
        );
658
        i18n::set_locale($locale);
659
        $this->assertEquals($expected, $labels);
660
        $labels = $comment->FieldLabels();
661
        $expected = array(
662
            'Name' => 'Author Name',
663
            'Comment' => 'Comment',
664
            'Email' => 'Email',
665
            'URL' => 'URL',
666
            'Moderated' => 'Moderated?',
667
            'IsSpam' => 'Spam?',
668
            'AllowHtml' => 'Allow Html',
669
            'SecretToken' => 'Secret Token',
670
            'Depth' => 'Depth',
671
            'Author' => 'Author Member',
672
            'ParentComment' => 'Parent Comment',
673
            'ChildComments' => 'Child Comments',
674
            'ParentTitle' => 'Parent',
675
            'Created' => 'Date posted',
676
            'Parent' => 'Parent'
677
        );
678
        $this->assertEquals($expected, $labels);
679
    }
680
681
    public function testGetOption()
682
    {
683
        $this->markTestSkipped('TODO');
684
    }
685
686
    public function testGetParent()
687
    {
688
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
689
        $item = $this->objFromFixture(CommentableItem::class, 'first');
690
        $parent = $comment->Parent();
0 ignored issues
show
Bug introduced by
The method Parent() does not exist on SilverStripe\ORM\DataObject. Did you maybe mean parentClass()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
691
        $this->assertSame($item->getClassName(), $parent->getClassName());
692
        $this->assertSame($item->ID, $parent->ID);
693
    }
694
695 View Code Duplication
    public function testGetParentTitle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
696
    {
697
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
698
        $title = $comment->getParentTitle();
699
        $this->assertEquals('First', $title);
700
701
        // Title from a comment with no parent is blank
702
        $comment->ParentID = 0;
703
        $comment->ParentClass = null;
704
        $comment->write();
705
        $this->assertEquals('', $comment->getParentTitle());
706
    }
707
708
    public function testGetParentClassName()
709
    {
710
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
711
        $className = $comment->getParentClassName();
0 ignored issues
show
Bug introduced by
The method getParentClassName() does not exist on SilverStripe\ORM\DataObject. Did you maybe mean parentClass()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
712
        $this->assertEquals(CommentableItem::class, $className);
713
    }
714
715
    public function testCastingHelper()
716
    {
717
        $this->markTestSkipped('TODO');
718
    }
719
720
    public function testGetEscapedComment()
721
    {
722
        $this->markTestSkipped('TODO');
723
    }
724
725
    public function testIsPreview()
726
    {
727
        $comment = new Comment();
728
        $comment->Name = 'Fred Bloggs';
729
        $comment->Comment = 'this is a test comment';
730
        $this->assertTrue($comment->isPreview());
731
        $comment->write();
732
        $this->assertFalse($comment->isPreview());
733
    }
734
735
    public function testCanCreate()
736
    {
737
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
738
739
        // admin can create - this is always false
740
        $this->logInAs('commentadmin');
741
        $this->assertFalse($comment->canCreate());
742
743
        // visitor can view
744
        $this->logInAs('visitor');
745
        $this->assertFalse($comment->canCreate());
746
    }
747
748 View Code Duplication
    public function testCanView()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
749
    {
750
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
751
752
        // admin can view
753
        $this->logInAs('commentadmin');
754
        $this->assertTrue($comment->canView());
755
756
        // visitor can view
757
        $this->logInAs('visitor');
758
        $this->assertTrue($comment->canView());
759
760
        $comment->ParentID = 0;
761
        $comment->write();
762
        $this->assertFalse($comment->canView());
763
    }
764
765 View Code Duplication
    public function testCanEdit()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
766
    {
767
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
768
769
        // admin can edit
770
        $this->logInAs('commentadmin');
771
        $this->assertTrue($comment->canEdit());
772
773
        // visitor cannot
774
        $this->logInAs('visitor');
775
        $this->assertFalse($comment->canEdit());
776
777
        $comment->ParentID = 0;
778
        $comment->write();
779
        $this->assertFalse($comment->canEdit());
780
    }
781
782 View Code Duplication
    public function testCanDelete()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
783
    {
784
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
785
786
        // admin can delete
787
        $this->logInAs('commentadmin');
788
        $this->assertTrue($comment->canDelete());
789
790
        // visitor cannot
791
        $this->logInAs('visitor');
792
        $this->assertFalse($comment->canDelete());
793
794
        $comment->ParentID = 0;
795
        $comment->write();
796
        $this->assertFalse($comment->canDelete());
797
    }
798
799
    public function testGetMember()
800
    {
801
        $this->logInAs('visitor');
802
        $current = Member::currentUser();
803
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
804
        $method = $this->getMethod('getMember');
805
806
        // null case
807
        $member = $method->invokeArgs($comment, array());
808
        $this->assertEquals($current, $member);
809
810
        // numeric ID case
811
        $member = $method->invokeArgs($comment, array($current->ID));
812
        $this->assertEquals($current, $member);
813
814
        // identity case
815
        $member = $method->invokeArgs($comment, array($current));
816
        $this->assertEquals($current, $member);
817
    }
818
819
    public function testGetAuthorName()
820
    {
821
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
822
        $this->assertEquals(
823
            'FA',
824
            $comment->getAuthorName()
825
        );
826
827
        $comment->Name = '';
828
        $this->assertEquals(
829
            '',
830
            $comment->getAuthorName()
831
        );
832
833
        $author = $this->objFromFixture(Member::class, 'visitor');
834
        $comment->AuthorID = $author->ID;
835
        $comment->write();
836
        $this->assertEquals(
837
            'visitor',
838
            $comment->getAuthorName()
839
        );
840
841
        // null the names, expect null back
842
        $comment->Name = null;
843
        $comment->AuthorID = 0;
844
        $this->assertNull($comment->getAuthorName());
845
    }
846
847
848
    public function testLinks()
849
    {
850
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
851
        $this->logInAs('commentadmin');
852
853
        $method = $this->getMethod('ActionLink');
854
855
        // test with starts of strings and tokens and salts change each time
856
        $this->assertStringStartsWith(
857
            '/comments/theaction/' . $comment->ID,
858
            $method->invokeArgs($comment, array('theaction'))
859
        );
860
861
        $this->assertStringStartsWith(
862
            '/comments/delete/' . $comment->ID,
863
            $comment->DeleteLink()
864
        );
865
866
        $this->assertStringStartsWith(
867
            '/comments/spam/' . $comment->ID,
868
            $comment->SpamLink()
869
        );
870
871
        $comment->markSpam();
872
        $this->assertStringStartsWith(
873
            '/comments/ham/' . $comment->ID,
874
            $comment->HamLink()
875
        );
876
877
        //markApproved
878
        $comment->markUnapproved();
879
        $this->assertStringStartsWith(
880
            '/comments/approve/' . $comment->ID,
881
            $comment->ApproveLink()
882
        );
883
    }
884
885
    public function testMarkSpam()
886
    {
887
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
888
        $comment->markSpam();
889
        $this->assertTrue($comment->Moderated);
890
        $this->assertTrue($comment->IsSpam);
891
    }
892
893
    public function testMarkApproved()
894
    {
895
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
896
        $comment->markApproved();
897
        $this->assertTrue($comment->Moderated);
898
        $this->assertFalse($comment->IsSpam);
899
    }
900
901
    public function testMarkUnapproved()
902
    {
903
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
904
        $comment->markApproved();
905
        $this->assertTrue($comment->Moderated);
906
    }
907
908 View Code Duplication
    public function testSpamClass()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
909
    {
910
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
911
        $this->assertEquals('notspam', $comment->spamClass());
912
        $comment->Moderated = false;
913
        $this->assertEquals('unmoderated', $comment->spamClass());
914
        $comment->IsSpam = true;
915
        $this->assertEquals('spam', $comment->spamClass());
916
    }
917
918
    public function testGetTitle()
919
    {
920
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
921
        $this->assertEquals(
922
            'Comment by FA on First',
923
            $comment->getTitle()
924
        );
925
    }
926
927
    public function testGetCMSFields()
928
    {
929
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
930
        $fields = $comment->getCMSFields();
931
        $names = array();
932
        foreach ($fields as $field) {
933
            $names[] = $field->getName();
934
        }
935
        $expected = array(
936
            'Created',
937
            'Name',
938
            'Comment',
939
            'Email',
940
            'URL',
941
            'Options'
942
        );
943
        $this->assertEquals($expected, $names);
944
    }
945
946
    public function testGetCMSFieldsCommentHasAuthor()
947
    {
948
        $member = Member::get()->filter('FirstName', 'visitor')->first();
949
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
950
        $comment->AuthorID = $member->ID;
951
        $comment->write();
952
953
        $fields = $comment->getCMSFields();
954
        $names = array();
955
        foreach ($fields as $field) {
956
            $names[] = $field->getName();
957
        }
958
        $expected = array(
959
            'Created',
960
            'Name',
961
            'AuthorMember',
962
            'Comment',
963
            'Email',
964
            'URL',
965
            'Options'
966
        );
967
        $this->assertEquals($expected, $names);
968
    }
969
970
    public function testGetCMSFieldsWithParentComment()
971
    {
972
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
973
974
        $child = new Comment();
975
        $child->Name = 'John Smith';
976
        $child->Comment = 'This is yet another test commnent';
977
        $child->ParentCommentID = $comment->ID;
978
        $child->write();
979
980
        $fields = $child->getCMSFields();
981
        $names = array();
982
        foreach ($fields as $field) {
983
            $names[] = $field->getName();
984
        }
985
        $expected = array(
986
            'Created',
987
            'Name',
988
            'Comment',
989
            'Email',
990
            'URL',
991
            'Options',
992
            'ParentComment_Title',
993
            'ParentComment_Created',
994
            'ParentComment_AuthorName',
995
            'ParentComment_EscapedComment'
996
        );
997
        $this->assertEquals($expected, $names);
998
    }
999
1000
    public function testPurifyHtml()
1001
    {
1002
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1003
1004
        $dirtyHTML = '<p><script>alert("w00t")</script>my comment</p>';
1005
        $this->assertEquals(
1006
            'my comment',
1007
            $comment->purifyHtml($dirtyHTML)
1008
        );
1009
    }
1010
1011
    public function testGravatar()
1012
    {
1013
        // Turn gravatars on
1014
        Config::inst()->update(CommentableItem::class, 'comments', array(
1015
            'use_gravatar' => true
1016
        ));
1017
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1018
1019
        $this->assertEquals(
1020
            'http://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s'
1021
            . '=80&d=identicon&r=g',
1022
            $comment->gravatar()
1023
        );
1024
1025
        // Turn gravatars off
1026
        Config::inst()->update(CommentableItem::class, 'comments', array(
1027
            'use_gravatar' => false
1028
        ));
1029
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1030
1031
        $this->assertEquals(
1032
            '',
1033
            $comment->gravatar()
1034
        );
1035
    }
1036
1037
    public function testGetRepliesEnabled()
1038
    {
1039
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1040
        Config::inst()->update(CommentableItem::class, 'comments', array(
1041
            'nested_comments' => false
1042
        ));
1043
        $this->assertFalse($comment->getRepliesEnabled());
1044
1045
        Config::inst()->update(CommentableItem::class, 'comments', array(
1046
            'nested_comments' => true,
1047
            'nested_depth' => 4
1048
        ));
1049
        $this->assertTrue($comment->getRepliesEnabled());
1050
1051
        $comment->Depth = 4;
1052
        $this->assertFalse($comment->getRepliesEnabled());
1053
1054
1055
        // 0 indicates no limit for nested_depth
1056
        Config::inst()->update(CommentableItem::class, 'comments', array(
1057
            'nested_comments' => true,
1058
            'nested_depth' => 0
1059
        ));
1060
1061
        $comment->Depth = 234;
1062
        $this->assertTrue($comment->getRepliesEnabled());
1063
        $comment->markUnapproved();
1064
        $this->assertFalse($comment->getRepliesEnabled());
1065
        $comment->markSpam();
1066
        $this->assertFalse($comment->getRepliesEnabled());
1067
1068
        $comment->markApproved();
1069
        $this->assertTrue($comment->getRepliesEnabled());
1070
    }
1071
1072
    public function testAllReplies()
1073
    {
1074
        Config::inst()->update(CommentableItem::class, 'comments', array(
1075
            'nested_comments' => true,
1076
            'nested_depth' => 4
1077
        ));
1078
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1079
        $this->assertEquals(
1080
            3,
1081
            $comment->allReplies()->count()
1082
        );
1083
        $child = new Comment();
1084
        $child->Name = 'Fred Smith';
1085
        $child->Comment = 'This is a child comment';
1086
        $child->ParentCommentID = $comment->ID;
1087
1088
        // spam should be returned by this method
1089
        $child->markSpam();
1090
        $child->write();
1091
        $replies = $comment->allReplies();
0 ignored issues
show
Unused Code introduced by
$replies is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1092
        $this->assertEquals(
1093
            4,
1094
            $comment->allReplies()->count()
1095
        );
1096
1097
        Config::inst()->update(CommentableItem::class, 'comments', array(
1098
            'nested_comments' => false
1099
        ));
1100
1101
        $this->assertEquals(0, $comment->allReplies()->count());
1102
    }
1103
1104
    public function testReplies()
1105
    {
1106
        CommentableItem::add_extension(CommentsExtension::class);
1107
        $this->logInWithPermission('ADMIN');
1108
        Config::inst()->update(CommentableItem::class, 'comments', array(
1109
            'nested_comments' => true,
1110
            'nested_depth' => 4
1111
        ));
1112
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1113
        $this->assertEquals(
1114
            3,
1115
            $comment->Replies()->count()
1116
        );
1117
1118
        // Test that spam comments are not returned
1119
        $childComment = $comment->Replies()->first();
1120
        $childComment->IsSpam = 1;
1121
        $childComment->write();
1122
        $this->assertEquals(
1123
            2,
1124
            $comment->Replies()->count()
1125
        );
1126
1127
        // Test that unmoderated comments are not returned
1128
        //
1129
        $childComment = $comment->Replies()->first();
1130
1131
        // FIXME - moderation settings scenarios need checked here
1132
        $childComment->Moderated = 0;
1133
        $childComment->IsSpam = 0;
1134
        $childComment->write();
1135
        $this->assertEquals(
1136
            2,
1137
            $comment->Replies()->count()
1138
        );
1139
1140
1141
        // Test moderation required on the front end
1142
        $item = $this->objFromFixture(CommentableItem::class, 'first');
1143
        $item->ModerationRequired = 'Required';
1144
        $item->write();
1145
1146
        Config::inst()->update(CommentableItemDisabled::class, 'comments', array(
1147
            'nested_comments' => true,
1148
            'nested_depth' => 4,
1149
            'frontend_moderation' => true
1150
        ));
1151
1152
        $comment = DataObject::get_by_id(Comment::class, $comment->ID);
1153
1154
        $this->assertEquals(
1155
            2,
1156
            $comment->Replies()->count()
1157
        );
1158
1159
        // Turn off nesting, empty array should be returned
1160
        Config::inst()->update(CommentableItem::class, 'comments', array(
1161
            'nested_comments' => false
1162
        ));
1163
1164
        $this->assertEquals(
1165
            0,
1166
            $comment->Replies()->count()
1167
        );
1168
1169
        CommentableItem::remove_extension(CommentsExtension::class);
1170
    }
1171
1172
    public function testPagedReplies()
1173
    {
1174
        Config::inst()->update(CommentableItem::class, 'comments', array(
1175
            'nested_comments' => true,
1176
            'nested_depth' => 4,
1177
            'comments_per_page' => 2 #Force 2nd page for 3 items
1178
        ));
1179
1180
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1181
        $pagedList = $comment->pagedReplies();
1182
        $this->assertEquals(
1183
            2,
1184
            $pagedList->TotalPages()
1185
        );
1186
        $this->assertEquals(
1187
            3,
1188
            $pagedList->getTotalItems()
1189
        );
1190
        //TODO - 2nd page requires controller
1191
        //
1192
        Config::inst()->update(CommentableItem::class, 'comments', array(
1193
            'nested_comments' => false
1194
        ));
1195
1196
        $this->assertEquals(0, $comment->PagedReplies()->count());
1197
    }
1198
1199
    public function testReplyForm()
1200
    {
1201
        Config::inst()->update(CommentableItem::class, 'comments', array(
1202
            'nested_comments' => false,
1203
            'nested_depth' => 4
1204
        ));
1205
1206
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1207
1208
        // No nesting, no reply form
1209
        $form = $comment->replyForm();
1210
        $this->assertNull($form);
1211
1212
        // parent item so show form
1213
        Config::inst()->update(CommentableItem::class, 'comments', array(
1214
            'nested_comments' => true,
1215
            'nested_depth' => 4
1216
        ));
1217
        $form = $comment->replyForm();
1218
1219
        $names = array();
1220
        foreach ($form->Fields() as $field) {
1221
            array_push($names, $field->getName());
1222
        }
1223
1224
        $this->assertEquals(
1225
            array(
1226
                'NameEmailURLComment', // The CompositeField name?
1227
                'ParentID',
1228
                'ParentClassName',
1229
                'ReturnURL',
1230
                'ParentCommentID'
1231
            ),
1232
            $names
1233
        );
1234
1235
        // no parent, no reply form
1236
1237
        $comment->ParentID = 0;
1238
        $comment->ParentClass = null;
1239
        $comment->write();
1240
        $form = $comment->replyForm();
1241
        $this->assertNull($form);
1242
    }
1243
1244
    public function testUpdateDepth()
1245
    {
1246
        Config::inst()->update(CommentableItem::class, 'comments', array(
1247
            'nested_comments' => true,
1248
            'nested_depth' => 4
1249
        ));
1250
1251
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
1252
        $children = $comment->allReplies()->toArray();
1253
        // Make the second child a child of the first
1254
        // Make the third child a child of the second
1255
        $reply1 = $children[0];
1256
        $reply2 = $children[1];
1257
        $reply3 = $children[2];
1258
        $reply2->ParentCommentID = $reply1->ID;
1259
        $reply2->write();
1260
        $this->assertEquals(3, $reply2->Depth);
1261
        $reply3->ParentCommentID = $reply2->ID;
1262
        $reply3->write();
1263
        $this->assertEquals(4, $reply3->Depth);
1264
    }
1265
1266
    public function testGetToken()
1267
    {
1268
        $this->markTestSkipped('TODO');
1269
    }
1270
1271
    public function testMemberSalt()
1272
    {
1273
        $this->markTestSkipped('TODO');
1274
    }
1275
1276
    public function testAddToUrl()
1277
    {
1278
        $this->markTestSkipped('TODO');
1279
    }
1280
1281
    public function testCheckRequest()
1282
    {
1283
        $this->markTestSkipped('TODO');
1284
    }
1285
1286
    public function testGenerate()
1287
    {
1288
        $this->markTestSkipped('TODO');
1289
    }
1290
1291
    protected static function getMethod($name)
1292
    {
1293
        $class = new ReflectionClass(Comment::class);
1294
        $method = $class->getMethod($name);
1295
        $method->setAccessible(true);
1296
        return $method;
1297
    }
1298
}
1299