merge(CommentsExtension::class, 'comments', [ 'enabled' => true, 'comment_permalink_prefix' => 'comment-', ]); } public function testCommentsList() { // comments don't require moderation so unmoderated comments can be // shown but not spam posts Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_moderation_nonmembers' => false, 'require_moderation' => false, 'require_moderation_cms' => false, ]); $item = $this->objFromFixture(CommentableItem::class, 'spammed'); $this->assertListEquals([ ['Name' => 'Comment 1'], ['Name' => 'Comment 3'] ], $item->Comments(), 'Only 2 non spam posts should be shown'); // when moderated, only moderated, non spam posts should be shown. Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation_nonmembers' => true]); // Check that require_moderation overrides this option Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation' => true]); $this->assertListEquals(array( array('Name' => 'Comment 3') ), $item->Comments(), 'Only 1 non spam, moderated post should be shown'); $this->assertEquals(1, $item->Comments()->Count()); // require_moderation_nonmembers still filters out unmoderated comments Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation' => false]); $this->assertEquals(1, $item->Comments()->Count()); Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation_nonmembers' => false]); $this->assertEquals(2, $item->Comments()->Count()); // With unmoderated comments set to display in frontend Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_moderation' => true, 'frontend_moderation' => true, ]); $this->assertEquals(1, $item->Comments()->Count()); $this->logInWithPermission('ADMIN'); $this->assertEquals(2, $item->Comments()->Count()); // With spam comments set to display in frontend Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_moderation' => true, 'frontend_moderation' => false, 'frontend_spam' => true, ]); $this->logOut(); $this->assertEquals(1, $item->Comments()->Count()); $this->logInWithPermission('ADMIN'); $this->assertEquals(2, $item->Comments()->Count()); // With spam and unmoderated comments set to display in frontend Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_moderation' => true, 'frontend_moderation' => true, 'frontend_spam' => true, ]); $this->logOut(); $this->assertEquals(1, $item->Comments()->Count()); $this->logInWithPermission('ADMIN'); $this->assertEquals(4, $item->Comments()->Count()); } /** * Test moderation options configured via the CMS */ public function testCommentCMSModerationList() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_moderation' => true, 'require_moderation_cms' => true, ]); $item = $this->objFromFixture(CommentableItem::class, 'spammed'); $this->assertEquals('None', $item->getModerationRequired()); $this->assertListEquals([ ['Name' => 'Comment 1'], ['Name' => 'Comment 3'] ], $item->Comments(), 'Only 2 non spam posts should be shown'); // when moderated, only moderated, non spam posts should be shown. $item->ModerationRequired = 'NonMembersOnly'; $item->write(); $this->assertEquals('NonMembersOnly', $item->getModerationRequired()); // Check that require_moderation overrides this option $item->ModerationRequired = 'Required'; $item->write(); $this->assertEquals('Required', $item->getModerationRequired()); $this->assertListEquals([ ['Name' => 'Comment 3'] ], $item->Comments(), 'Only 1 non spam, moderated post should be shown'); $this->assertEquals(1, $item->Comments()->Count()); // require_moderation_nonmembers still filters out unmoderated comments $item->ModerationRequired = 'NonMembersOnly'; $item->write(); $this->assertEquals(1, $item->Comments()->Count()); $item->ModerationRequired = 'None'; $item->write(); $this->assertEquals(2, $item->Comments()->Count()); } public function testCanPostComment() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_login' => false, 'require_login_cms' => false, 'required_permission' => false, ]); /** @var CommentableItem&CommentsExtension $item */ $item = $this->objFromFixture(CommentableItem::class, 'first'); /** @var CommentableItem&CommentsExtension $item2 */ $item2 = $this->objFromFixture(CommentableItem::class, 'second'); // Test restriction free commenting $this->logOut(); $this->assertFalse($item->CommentsRequireLogin); $this->assertTrue($item->canPostComment()); // Test permission required to post Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_login' => true, 'required_permission' => 'POSTING_PERMISSION', ]); $this->assertTrue($item->CommentsRequireLogin); $this->assertFalse($item->canPostComment()); $this->logInWithPermission('WRONG_ONE'); $this->assertFalse($item->canPostComment()); $this->logInWithPermission('POSTING_PERMISSION'); $this->assertTrue($item->canPostComment()); $this->logInWithPermission('ADMIN'); $this->assertTrue($item->canPostComment()); // Test require login to post, but not any permissions Config::modify()->merge(CommentableItem::class, 'comments', [ 'required_permission' => false, ]); $this->assertTrue($item->CommentsRequireLogin); $this->logOut(); $this->assertFalse($item->canPostComment()); $this->logInWithPermission('ANY_PERMISSION'); $this->assertTrue($item->canPostComment()); // Test options set via CMS Config::modify()->merge(CommentableItem::class, 'comments', [ 'require_login' => true, 'require_login_cms' => true, ]); $this->assertFalse($item->CommentsRequireLogin); $this->assertTrue($item2->CommentsRequireLogin); $this->logOut(); $this->assertTrue($item->canPostComment()); $this->assertFalse($item2->canPostComment()); // Login grants permission to post $this->logInWithPermission('ANY_PERMISSION'); $this->assertTrue($item->canPostComment()); $this->assertTrue($item2->canPostComment()); } public function testDeleteComment() { // Test anonymous user $this->logOut(); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $commentID = $comment->ID; $this->assertNull($comment->DeleteLink(), 'No permission to see delete link'); $delete = $this->get('comments/delete/' . $comment->ID . '?ajax=1'); $this->assertEquals(403, $delete->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertTrue($check && $check->exists()); // Test non-authenticated user $this->logInAs('visitor'); $this->assertNull($comment->DeleteLink(), 'No permission to see delete link'); // Test authenticated user $this->logInAs('commentadmin'); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $commentID = $comment->ID; $adminComment1Link = $comment->DeleteLink(); $this->assertStringContainsString('comments/delete/' . $commentID . '?t=', $adminComment1Link); // Test that this link can't be shared / XSS exploited $this->logInAs('commentadmin2'); $delete = $this->get($adminComment1Link); $this->assertEquals(400, $delete->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertTrue($check && $check->exists()); // Test that this other admin can delete the comment with their own link $adminComment2Link = $comment->DeleteLink(); $this->assertNotEquals($adminComment2Link, $adminComment1Link); $this->autoFollowRedirection = false; $delete = $this->get($adminComment2Link); $this->assertEquals(302, $delete->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertFalse($check && $check->exists()); } public function testSpamComment() { // Test anonymous user $this->logOut(); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $commentID = $comment->ID; $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link'); $spam = $this->get('comments/spam/' . $comment->ID . '?ajax=1'); $this->assertEquals(403, $spam->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam'); // Test non-authenticated user $this->logInAs('visitor'); $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link'); // Test authenticated user $this->logInAs('commentadmin'); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $commentID = $comment->ID; $adminComment1Link = $comment->SpamLink(); $this->assertStringContainsString('comments/spam/' . $commentID . '?t=', $adminComment1Link); // Test that this link can't be shared / XSS exploited $this->logInAs('commentadmin2'); $spam = $this->get($adminComment1Link); $this->assertEquals(400, $spam->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $comment->ID); $this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam'); // Test that this other admin can spam the comment with their own link $adminComment2Link = $comment->SpamLink(); $this->assertNotEquals($adminComment2Link, $adminComment1Link); $this->autoFollowRedirection = false; $spam = $this->get($adminComment2Link); $this->assertEquals(302, $spam->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertEquals(1, $check->IsSpam); // Cannot re-spam spammed comment $this->assertNull($check->SpamLink()); } public function testHamComment() { // Test anonymous user $this->logOut(); $comment = $this->objFromFixture(Comment::class, 'secondComC'); $commentID = $comment->ID; $this->assertNull($comment->HamLink(), 'No permission to see mark as ham link'); $ham = $this->get('comments/ham/' . $comment->ID . '?ajax=1'); $this->assertEquals(403, $ham->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham'); // Test non-authenticated user $this->logInAs('visitor'); $this->assertNull($comment->HamLink(), 'No permission to see mark as ham link'); // Test authenticated user $this->logInAs('commentadmin'); $comment = $this->objFromFixture(Comment::class, 'secondComC'); $commentID = $comment->ID; $adminComment1Link = $comment->HamLink(); $this->assertStringContainsString('comments/ham/' . $commentID . '?t=', $adminComment1Link); // Test that this link can't be shared / XSS exploited $this->logInAs('commentadmin2'); $ham = $this->get($adminComment1Link); $this->assertEquals(400, $ham->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $comment->ID); $this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham'); // Test that this other admin can ham the comment with their own link $adminComment2Link = $comment->HamLink(); $this->assertNotEquals($adminComment2Link, $adminComment1Link); $this->autoFollowRedirection = false; $ham = $this->get($adminComment2Link); $this->assertEquals(302, $ham->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertEquals(0, $check->IsSpam); // Cannot re-ham hammed comment $this->assertNull($check->HamLink()); } public function testApproveComment() { // Test anonymous user $this->logOut(); $comment = $this->objFromFixture(Comment::class, 'secondComB'); $commentID = $comment->ID; $this->assertNull($comment->ApproveLink(), 'No permission to see approve link'); $approve = $this->get('comments/approve/' . $comment->ID . '?ajax=1'); $this->assertEquals(403, $approve->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertEquals(0, $check->Moderated, 'No permission to approve'); // Test non-authenticated user $this->logInAs('visitor'); $this->assertNull($comment->ApproveLink(), 'No permission to see approve link'); // Test authenticated user $this->logInAs('commentadmin'); $comment = $this->objFromFixture(Comment::class, 'secondComB'); $commentID = $comment->ID; $adminComment1Link = $comment->ApproveLink(); $this->assertStringContainsString('comments/approve/' . $commentID . '?t=', $adminComment1Link); // Test that this link can't be shared / XSS exploited $this->logInAs('commentadmin2'); $approve = $this->get($adminComment1Link); $this->assertEquals(400, $approve->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $comment->ID); $this->assertEquals(0, $check->Moderated, 'No permission to approve'); // Test that this other admin can approve the comment with their own link $adminComment2Link = $comment->ApproveLink(); $this->assertNotEquals($adminComment2Link, $adminComment1Link); $this->autoFollowRedirection = false; $approve = $this->get($adminComment2Link); $this->assertEquals(302, $approve->getStatusCode()); $check = DataObject::get_by_id(Comment::class, $commentID); $this->assertEquals(1, $check->Moderated); // Cannot re-approve approved comment $this->assertNull($check->ApproveLink()); } public function testCommenterURLWrite() { $comment = new Comment(); // We only care about the CommenterURL, so only set that // Check a http and https URL. Add more test urls here as needed. $protocols = [ 'Http', 'Https', ]; $url = '://example.com'; foreach ($protocols as $protocol) { $comment->CommenterURL = $protocol . $url; // The protocol should stay as if, assuming it is valid $comment->write(); $this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol'); } } public function testSanitizesWithAllowHtml() { if (!class_exists('\\HTMLPurifier')) { $this->markTestSkipped('HTMLPurifier class not found'); } // Add p for paragraph // NOTE: The config method appears to append to the existing array Config::modify()->merge(CommentableItem::class, 'comments', [ 'html_allowed_elements' => ['p'], ]); // Without HTML allowed $comment1 = new Comment(); $comment1->AllowHtml = false; $comment1->ParentClass = CommentableItem::class; $comment1->Comment = '

my comment

'; $comment1->write(); $this->assertEquals( '

my comment

', $comment1->Comment, 'Does not remove HTML tags with html_allowed=false, ' . 'which is correct behaviour because the HTML will be escaped' ); // With HTML allowed $comment2 = new Comment(); $comment2->AllowHtml = true; $comment2->ParentClass = CommentableItem::class; $comment2->Comment = '

my comment

'; $comment2->write(); $this->assertEquals( '

my comment

', $comment2->Comment, 'Removes HTML tags which are not on the whitelist' ); } public function testDefaultTemplateRendersHtmlWithAllowHtml() { if (!class_exists('\\HTMLPurifier')) { $this->markTestSkipped('HTMLPurifier class not found'); } Config::modify()->merge(CommentableItem::class, 'comments', [ 'html_allowed_elements' => ['p'], ]); $item = new CommentableItem(); $item->write(); // Without HTML allowed $comment = new Comment(); $comment->Comment = '

my comment

'; $comment->AllowHtml = false; $comment->ParentID = $item->ID; $comment->ParentClass = CommentableItem::class; $comment->write(); $html = $item->customise(['CommentsEnabled' => true])->renderWith('CommentsInterface'); $this->assertStringContainsString( '<p>my comment</p>', $html ); $comment->AllowHtml = true; $comment->write(); $html = $item->customise(['CommentsEnabled' => true])->renderWith('CommentsInterface'); $this->assertStringContainsString( '

my comment

', $html ); } /** * Tests whether comments are enabled or disabled by default */ public function testDefaultEnabled() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'enabled_cms' => true, 'require_moderation_cms' => true, 'require_login_cms' => true, ]); // With default = true $obj = new CommentableItem(); $this->assertTrue((bool)$obj->getCommentsOption('enabled'), "Default setting is enabled"); $this->assertTrue((bool)$obj->ProvideComments); $this->assertEquals('None', $obj->ModerationRequired); $this->assertFalse((bool)$obj->CommentsRequireLogin); $obj = new CommentableItemEnabled(); $this->assertTrue((bool)$obj->ProvideComments); $this->assertEquals('Required', $obj->ModerationRequired); $this->assertTrue((bool)$obj->CommentsRequireLogin); $obj = new CommentableItemDisabled(); $this->assertFalse((bool)$obj->ProvideComments); $this->assertEquals('None', $obj->ModerationRequired); $this->assertFalse((bool)$obj->CommentsRequireLogin); // With default = false // Because of config rules about falsey values, apply config to object directly Config::modify()->merge(CommentableItem::class, 'comments', [ 'enabled' => false, 'require_login' => true, 'require_moderation' => true, ]); $obj = new CommentableItem(); $this->assertFalse((bool)$obj->getCommentsOption('enabled'), 'Default setting is disabled'); $this->assertFalse((bool)$obj->ProvideComments); $this->assertEquals('Required', $obj->ModerationRequired); $this->assertTrue((bool)$obj->CommentsRequireLogin); $obj = new CommentableItemEnabled(); $this->assertTrue((bool)$obj->ProvideComments); $this->assertEquals('Required', $obj->ModerationRequired); $this->assertTrue((bool)$obj->CommentsRequireLogin); $obj = new CommentableItemDisabled(); $this->assertFalse((bool)$obj->ProvideComments); $this->assertEquals('None', $obj->ModerationRequired); $this->assertFalse((bool)$obj->CommentsRequireLogin); } public function testOnBeforeDelete() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $child = new Comment(); $child->Name = 'Fred Bloggs'; $child->Comment = 'Child of firstComA'; $child->write(); $comment->ChildComments()->add($child); $this->assertEquals(4, $comment->ChildComments()->count()); $commentID = $comment->ID; $childCommentID = $child->ID; $comment->delete(); // assert that the new child been deleted $this->assertNull(DataObject::get_by_id(Comment::class, $commentID)); $this->assertNull(DataObject::get_by_id(Comment::class, $childCommentID)); } public function testRequireDefaultRecords() { $this->markTestSkipped('TODO'); } public function testLink() { $comment = $this->objFromFixture(Comment::class, 'thirdComD'); $this->assertEquals( 'CommentableItemController#comment-' . $comment->ID, $comment->Link() ); $this->assertEquals($comment->ID, $comment->ID); // An orphan comment has no link $comment->ParentID = 0; $comment->ParentClass = null; $comment->write(); $this->assertEquals('', $comment->Link()); } public function testPermalink() { $comment = $this->objFromFixture(Comment::class, 'thirdComD'); $this->assertEquals('comment-' . $comment->ID, $comment->Permalink()); } /** * Test field labels are defined */ public function testFieldLabels() { /** @var Comment $comment */ $comment = $this->objFromFixture(Comment::class, 'firstComA'); $labels = $comment->FieldLabels(); $expected = [ 'Name', 'Comment', 'Email', 'URL', 'IsSpam', 'Moderated', 'ParentTitle', 'Created', ]; foreach ($expected as $key) { $this->assertArrayHasKey($key, $labels); } } public function testGetParent() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $item = $this->objFromFixture(CommentableItem::class, 'first'); $parent = $comment->Parent(); $this->assertSame($item->getClassName(), $parent->getClassName()); $this->assertSame($item->ID, $parent->ID); } public function testGetParentTitle() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $title = $comment->getParentTitle(); $this->assertEquals('First', $title); // Title from a comment with no parent is blank $comment->ParentID = 0; $comment->ParentClass = null; $comment->write(); $this->assertEquals('', $comment->getParentTitle()); } public function testGetParentClassName() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $className = $comment->getParentClassName(); $this->assertEquals(CommentableItem::class, $className); } public function testCastingHelper() { $this->markTestSkipped('TODO'); } public function testGetEscapedComment() { $this->markTestSkipped('TODO'); } public function testIsPreview() { $comment = new Comment(); $comment->Name = 'Fred Bloggs'; $comment->Comment = 'this is a test comment'; $this->assertTrue($comment->isPreview()); $comment->write(); $this->assertFalse($comment->isPreview()); } public function testCanCreate() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); // admin can create - this is always false $this->logInAs('commentadmin'); $this->assertFalse($comment->canCreate()); // visitor can view $this->logInAs('visitor'); $this->assertFalse($comment->canCreate()); } public function testCanView() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); // admin can view $this->logInAs('commentadmin'); $this->assertTrue($comment->canView()); // visitor can view $this->logInAs('visitor'); $this->assertTrue($comment->canView()); $comment->ParentID = 0; $comment->write(); $this->assertFalse($comment->canView()); } public function testCanEdit() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); // admin can edit $this->logInAs('commentadmin'); $this->assertTrue($comment->canEdit()); // visitor cannot $this->logInAs('visitor'); $this->assertFalse($comment->canEdit()); $comment->ParentID = 0; $comment->write(); $this->assertFalse($comment->canEdit()); } public function testCanDelete() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); // admin can delete $this->logInAs('commentadmin'); $this->assertTrue($comment->canDelete()); // visitor cannot $this->logInAs('visitor'); $this->assertFalse($comment->canDelete()); $comment->ParentID = 0; $comment->write(); $this->assertFalse($comment->canDelete()); } public function testGetMember() { $this->logInAs('visitor'); $current = Security::getCurrentUser(); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $method = $this->getMethod('getMember'); // null case $member = $method->invokeArgs($comment, array()); $this->assertEquals($current->ID, $member->ID); // numeric ID case $member = $method->invokeArgs($comment, array($current->ID)); $this->assertEquals($current->ID, $member->ID); // identity case $member = $method->invokeArgs($comment, array($current)); $this->assertEquals($current->ID, $member->ID); } public function testGetAuthorName() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertEquals( 'FA', $comment->getAuthorName() ); $comment->Name = ''; $this->assertEquals( '', $comment->getAuthorName() ); $author = $this->objFromFixture(Member::class, 'visitor'); $comment->AuthorID = $author->ID; $comment->write(); $this->assertEquals( 'visitor', $comment->getAuthorName() ); // null the names, expect null back $comment->Name = null; $comment->AuthorID = 0; $this->assertNull($comment->getAuthorName()); } public function testLinks() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->logInAs('commentadmin'); $method = $this->getMethod('ActionLink'); // test with starts of strings and tokens and salts change each time $this->assertStringContainsString( '/comments/theaction/' . $comment->ID, $method->invokeArgs($comment, array('theaction')) ); $this->assertStringContainsString( '/comments/delete/' . $comment->ID, $comment->DeleteLink() ); $this->assertStringContainsString( '/comments/spam/' . $comment->ID, $comment->SpamLink() ); $comment->markSpam(); $this->assertStringContainsString( '/comments/ham/' . $comment->ID, $comment->HamLink() ); //markApproved $comment->markUnapproved(); $this->assertStringContainsString( '/comments/approve/' . $comment->ID, $comment->ApproveLink() ); } public function testMarkSpam() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $comment->markSpam(); $this->assertTrue($comment->Moderated); $this->assertTrue($comment->IsSpam); } public function testMarkApproved() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $comment->markApproved(); $this->assertTrue($comment->Moderated); $this->assertFalse($comment->IsSpam); } public function testMarkUnapproved() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $comment->markApproved(); $this->assertTrue($comment->Moderated); } public function testSpamClass() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertEquals('notspam', $comment->spamClass()); $comment->Moderated = false; $this->assertEquals('unmoderated', $comment->spamClass()); $comment->IsSpam = true; $this->assertEquals('spam', $comment->spamClass()); } public function testGetTitle() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertEquals( 'Comment by FA on First', $comment->getTitle() ); } public function testGetCMSFields() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $fields = $comment->getCMSFields(); $names = []; foreach ($fields as $field) { $names[] = $field->getName(); } $expected = [ 'Created', 'Name', 'Comment', 'Email', 'URL', 'Options', ]; $this->assertEquals($expected, $names); } public function testGetCMSFieldsCommentHasAuthor() { $member = Member::get()->filter('FirstName', 'visitor')->first(); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $comment->AuthorID = $member->ID; $comment->write(); $fields = $comment->getCMSFields(); $names = []; foreach ($fields as $field) { $names[] = $field->getName(); } $expected = [ 'Created', 'Name', 'AuthorMember', 'Comment', 'Email', 'URL', 'Options', ]; $this->assertEquals($expected, $names); } public function testGetCMSFieldsWithParentComment() { $comment = $this->objFromFixture(Comment::class, 'firstComA'); $child = new Comment(); $child->Name = 'John Smith'; $child->Comment = 'This is yet another test commnent'; $child->ParentCommentID = $comment->ID; $child->write(); $fields = $child->getCMSFields(); $names = []; foreach ($fields as $field) { $names[] = $field->getName(); } $expected = [ 'Created', 'Name', 'Comment', 'Email', 'URL', 'Options', 'ParentComment_Title', 'ParentComment_Created', 'ParentComment_AuthorName', 'ParentComment_EscapedComment', ]; $this->assertEquals($expected, $names); } public function testPurifyHtml() { if (!class_exists(HTMLPurifier_Config::class)) { $this->markTestSkipped('HTMLPurifier class not found'); } $comment = $this->objFromFixture(Comment::class, 'firstComA'); $dirtyHTML = '

my comment

'; $this->assertEquals( 'my comment', $comment->purifyHtml($dirtyHTML) ); } public function testGravatar() { // Turn gravatars on Config::modify()->merge(CommentableItem::class, 'comments', [ 'use_gravatar' => true, 'gravatar_size' => 80, 'gravatar_default' => 'identicon', 'gravatar_rating' => 'g', ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertEquals( 'https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s' . '=80&d=identicon&r=g', $comment->Gravatar() ); // Turn gravatars off Config::modify()->merge(CommentableItem::class, 'comments', [ 'use_gravatar' => false, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertEquals( '', $comment->Gravatar() ); } public function testGetRepliesEnabled() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => false, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertFalse($comment->getRepliesEnabled()); Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 4, ]); $this->assertTrue($comment->getRepliesEnabled()); $comment->Depth = 4; $this->assertFalse($comment->getRepliesEnabled()); // 0 indicates no limit for nested_depth Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 0, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $comment->Depth = 234; $comment->markUnapproved(); $this->assertFalse($comment->getRepliesEnabled()); $comment->markSpam(); $this->assertFalse($comment->getRepliesEnabled()); $comment->markApproved(); $this->assertTrue($comment->getRepliesEnabled()); } public function testAllReplies() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 4, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertEquals( 3, $comment->allReplies()->count() ); $child = new Comment(); $child->Name = 'Fred Smith'; $child->Comment = 'This is a child comment'; $child->ParentCommentID = $comment->ID; // spam should be returned by this method $child->markSpam(); $child->write(); $replies = $comment->allReplies(); $this->assertEquals( 4, $comment->allReplies()->count() ); Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => false, ]); $this->assertEquals(0, $comment->allReplies()->count()); } public function testReplies() { CommentableItem::add_extension(CommentsExtension::class); $this->logInWithPermission('ADMIN'); Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 4, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $this->assertEquals( 3, $comment->Replies()->count() ); // Test that spam comments are not returned $childComment = $comment->Replies()->first(); $childComment->IsSpam = 1; $childComment->write(); $this->assertEquals( 2, $comment->Replies()->count() ); // Test that unmoderated comments are not returned // $childComment = $comment->Replies()->first(); // FIXME - moderation settings scenarios need checked here $childComment->Moderated = 0; $childComment->IsSpam = 0; $childComment->write(); $this->assertEquals( 2, $comment->Replies()->count() ); // Test moderation required on the front end $item = $this->objFromFixture(CommentableItem::class, 'first'); $item->ModerationRequired = 'Required'; $item->write(); Config::modify()->merge(CommentableItemDisabled::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 4, 'frontend_moderation' => true, ]); $comment = DataObject::get_by_id(Comment::class, $comment->ID); $this->assertEquals( 2, $comment->Replies()->count() ); // Turn off nesting, empty array should be returned Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => false, ]); $this->assertEquals( 0, $comment->Replies()->count() ); CommentableItem::remove_extension(CommentsExtension::class); } public function testPagedReplies() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 4, 'comments_per_page' => 2, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $pagedList = $comment->pagedReplies(); $this->assertEquals( 2, $pagedList->TotalPages() ); $this->assertEquals( 3, $pagedList->getTotalItems() ); Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => false, ]); $this->assertEquals(0, $comment->PagedReplies()->count()); } public function testReplyForm() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => false, 'nested_depth' => 4, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); // No nesting, no reply form $form = $comment->replyForm(); $this->assertNull($form); // parent item so show form Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 4, ]); $form = $comment->ReplyForm(); $this->assertNotNull($form); $names = []; foreach ($form->Fields() as $field) { array_push($names, $field->getName()); } $this->assertContains('NameEmailURLComment', $names, 'The CompositeField name'); $this->assertContains('ParentID', $names); $this->assertContains('ParentClassName', $names); $this->assertContains('ReturnURL', $names); $this->assertContains('ParentCommentID', $names); // no parent, no reply form $comment->ParentID = 0; $comment->ParentClass = null; $comment->write(); $form = $comment->replyForm(); $this->assertNull($form); } public function testUpdateDepth() { Config::modify()->merge(CommentableItem::class, 'comments', [ 'nested_comments' => true, 'nested_depth' => 4, ]); $comment = $this->objFromFixture(Comment::class, 'firstComA'); $children = $comment->allReplies()->toArray(); // Make the second child a child of the first // Make the third child a child of the second $reply1 = $children[0]; $reply2 = $children[1]; $reply3 = $children[2]; $reply2->ParentCommentID = $reply1->ID; $reply2->write(); $this->assertEquals(3, $reply2->Depth); $reply3->ParentCommentID = $reply2->ID; $reply3->write(); $this->assertEquals(4, $reply3->Depth); } public function testGetToken() { $this->markTestSkipped('TODO'); } public function testMemberSalt() { $this->markTestSkipped('TODO'); } public function testAddToUrl() { $this->markTestSkipped('TODO'); } public function testCheckRequest() { $this->markTestSkipped('TODO'); } public function testGenerate() { $this->markTestSkipped('TODO'); } protected static function getMethod($name) { $class = new ReflectionClass(Comment::class); $method = $class->getMethod($name); $method->setAccessible(true); return $method; } }