API Allow commenting options to be set per-page in CMS

This commit is contained in:
Damian Mooyman 2015-04-08 17:55:19 +12:00
parent f9c93ef9aa
commit 6ebbd9e1ac
6 changed files with 316 additions and 74 deletions

9
_config/comments.yml Normal file
View File

@ -0,0 +1,9 @@
---
# SiteTree specific config for comments
Name: commentssitetree
only:
moduleexists: 'cms'
---
SiteTree:
comments:
enabled_cms: true

View File

@ -360,22 +360,21 @@ class CommentingController extends Controller {
// create the comment form
$form = new Form($this, 'CommentsForm', $fields, $actions, $required);
// Load member data
$requireLogin = $this->getOption('require_login');
$permission = $this->getOption('required_permission');
$member = Member::currentUser();
if(($requireLogin || $permission) && $member) {
$fields = $form->Fields();
$fields->removeByName('Name');
$fields->removeByName('Email');
$fields->insertBefore(new ReadonlyField("NameView", _t('CommentInterface.YOURNAME', 'Your name'), $member->getName()), 'URL');
$fields->push(new HiddenField("Name", "", $member->getName()));
$fields->push(new HiddenField("Email", "", $member->Email));
}
// if the record exists load the extra required data
if($record = $this->getOwnerRecord()) {
// Load member data
$member = Member::currentUser();
if(($record->CommentsRequireLogin || $record->PostingRequiredPermission) && $member) {
$fields = $form->Fields();
$fields->removeByName('Name');
$fields->removeByName('Email');
$fields->insertBefore(new ReadonlyField("NameView", _t('CommentInterface.YOURNAME', 'Your name'), $member->getName()), 'URL');
$fields->push(new HiddenField("Name", "", $member->getName()));
$fields->push(new HiddenField("Email", "", $member->Email));
}
// we do not want to read a new URL when the form has already been submitted
// which in here, it hasn't been.
$form->loadDataFrom(array(
@ -453,11 +452,18 @@ class CommentingController extends Controller {
$form->Fields()->push(new HiddenField("AuthorID", "Author ID", $member->ID));
}
// is moderation turned on
$requireModeration = $this->getOption('require_moderation');
if(!$requireModeration) {
$requireModerationNonmembers = $this->getOption('require_moderation_nonmembers');
$requireModeration = $requireModerationNonmembers ? !Member::currentUser() : false;
// What kind of moderation is required?
switch($this->getOwnerRecord()->ModerationRequired) {
case 'Required':
$requireModeration = true;
break;
case 'NonMembersOnly':
$requireModeration = empty($member);
break;
case 'None':
default:
$requireModeration = false;
break;
}
// we want to show a notification if comments are moderated

View File

@ -16,34 +16,56 @@ class CommentsExtension extends DataExtension {
*/
private static $comments = array(
'enabled' => true, // Allows commenting to be disabled even if the extension is present
'enabled_cms' => false, // Allows commenting to be enabled or disabled via the CMS
'require_login' => false, // boolean, whether a user needs to login
'required_permission' => false, // required permission to comment (or array of permissions)
'require_login_cms' => false, // Allows require_login to be set via the CMS
// required permission to comment (or array of permissions). require_login must be set for this to work
'required_permission' => false,
'include_js' => true, // Enhance operation by ajax behaviour on moderation links
'use_gravatar' => false, // set to true to show gravatar icons,
'gravatar_size' => 80, // size of gravatar in pixels. This is the same as the standard default
'gravatar_default' => 'identicon', // theme for 'not found' gravatar (see http://gravatar.com/site/implement/images/)
// theme for 'not found' gravatar (see http://gravatar.com/site/implement/images/)
'gravatar_default' => 'identicon',
'gravatar_rating' => 'g', // gravatar rating. This is the same as the standard default
'show_comments_when_disabled' => false, // when comments are disabled should we show older comments (if available)
// when comments are disabled should we show older comments (if available)
'show_comments_when_disabled' => false,
'order_comments_by' => "\"Created\" DESC",
'comments_per_page' => 10,
'comments_holder_id' => "comments-holder", // id for the comments holder
'comment_permalink_prefix' => "comment-", // id prefix for each comment. If needed make this different
'require_moderation' => false,
'require_moderation_nonmembers' => false, // requires moderation for comments posted by non-members. 'require_moderation' overrides this if set.
'require_moderation' => false, // Require moderation for all comments
// requires moderation for comments posted by non-members. 'require_moderation' overrides this if set.
'require_moderation_nonmembers' => false,
// If true, ignore above values and configure moderation requirements via the CMS only
'require_moderation_cms' => false,
'html_allowed' => false, // allow for sanitized HTML in comments
'html_allowed_elements' => array('a', 'img', 'i', 'b'),
'use_preview' => false, // preview formatted comment (when allowing HTML). Requires include_js=true
);
public static function get_extra_config($class, $extension, $args = null) {
$config = array();
// if it is attached to the SiteTree then we need to add ProvideComments
if(is_subclass_of($class, 'SiteTree') || $class == 'SiteTree') {
$config['db'] = array('ProvideComments' => 'Boolean');
private static $db = array(
'ProvideComments' => 'Boolean',
'ModerationRequired' => "Enum('None,Required,NonMembersOnly','None')",
'CommentsRequireLogin' => 'Boolean',
);
/**
* CMS configurable options should default to the config values
*/
public function populateDefaults() {
// Set if comments should be enabled by default
$this->owner->ProvideComments = $this->owner->getCommentsOption('enabled') ? 1 : 0;
// If moderations options should be configurable via the CMS then
if($this->owner->getCommentsOption('require_moderation')) {
$this->owner->ModerationRequired = 'Required';
} elseif($this->owner->getCommentsOption('require_moderation_nonmembers')) {
$this->owner->ModerationRequired = 'NonMembersOnly';
} else {
$this->owner->ModerationRequired = 'None';
}
return $config;
$this->owner->CommentsRequireLogin = $this->owner->getCommentsOption('require_login') ? 1 : 0;
}
@ -57,11 +79,48 @@ class CommentsExtension extends DataExtension {
* @param FieldSet
*/
public function updateSettingsFields(FieldList $fields) {
if($this->attachedToSiteTree()) {
$fields->addFieldToTab('Root.Settings',
new CheckboxField('ProvideComments', _t('Comment.ALLOWCOMMENTS', 'Allow Comments'))
$options = FieldGroup::create()->setTitle(_t('CommentsExtension.COMMENTOPTIONS', 'Comments'));
// Check if enabled setting should be cms configurable
if($this->owner->getCommentsOption('enabled_cms')) {
$options->push(new CheckboxField('ProvideComments', _t('Comment.ALLOWCOMMENTS', 'Allow Comments')));
}
// Check if we should require users to login to comment
if($this->owner->getCommentsOption('require_login_cms')) {
$options->push(
new CheckboxField(
'CommentsRequireLogin',
_t('Comments.COMMENTSREQUIRELOGIN', 'Require login to comment')
)
);
}
if($options->FieldList()->count()) {
if($fields->hasTabSet()) {
$fields->addFieldsToTab('Root.Settings', $options);
} else {
$fields->push($options);
}
}
// Check if moderation should be enabled via cms configurable
if($this->owner->getCommentsOption('require_moderation_cms')) {
$moderationField = new DropdownField('ModerationRequired', 'Comment Moderation', array(
'None' => _t('CommentsExtension.MODERATIONREQUIRED_NONE', 'No moderation required'),
'Required' => _t('CommentsExtension.MODERATIONREQUIRED_REQUIRED', 'Moderate all comments'),
'NonMembersOnly' => _t(
'CommentsExtension.MODERATIONREQUIRED_NONMEMBERSONLY',
'Only moderate non-members'
),
));
if($fields->hasTabSet()) {
$fields->addFieldsToTab('Root.Settings', $moderationField);
} else {
$fields->push($moderationField);
}
}
}
/**
@ -80,7 +139,38 @@ class CommentsExtension extends DataExtension {
Deprecation::notice('2.0', 'Use PagedComments to get paged coments');
return $this->PagedComments();
}
/**
* Get comment moderation rules for this parent
*
* @return string A value of either 'None' (no moderation required), 'Required' (all comments),
* or 'NonMembersOnly' (only not logged in users)
*/
public function getModerationRequired() {
if($this->owner->getCommentsOption('require_moderation_cms')) {
return $this->owner->getField('ModerationRequired');
} elseif($this->owner->getCommentsOption('require_moderation')) {
return 'Required';
} elseif($this->owner->getCommentsOption('require_moderation_nonmembers')) {
return 'NonMembersOnly';
} else {
return 'None';
}
}
/**
* Determine if users must be logged in to post comments
*
* @return boolean
*/
public function getCommentsRequireLogin() {
if($this->owner->getCommentsOption('require_login_cms')) {
return (bool)$this->owner->getField('CommentsRequireLogin');
} else {
return (bool)$this->owner->getCommentsOption('require_login');
}
}
/**
* Returns the root level comments, with spam and unmoderated items excluded, for use in the frontend
*
@ -95,9 +185,7 @@ class CommentsExtension extends DataExtension {
->filter('IsSpam', 0);
// Filter unmoderated comments for non-administrators if moderation is enabled
if ($this->owner->getCommentsOption('require_moderation')
|| $this->owner->getCommentsOption('require_moderation_nonmembers')
) {
if ($this->owner->ModerationRequired !== 'None') {
$list = $list->filter('Moderated', 1);
}
@ -142,10 +230,12 @@ class CommentsExtension extends DataExtension {
* @return boolean
*/
public function getCommentsEnabled() {
if(!$this->owner->getCommentsOption('enabled')) return false;
// Non-page objects always have comments enabled
return !$this->attachedToSiteTree() || $this->owner->ProvideComments;
// Determine which flag should be used to determine if this is enabled
if($this->owner->getCommentsOption('enabled_cms')) {
return $this->owner->ProvideComments;
} else {
return $this->owner->getCommentsOption('enabled');
}
}
/**
@ -186,8 +276,11 @@ class CommentsExtension extends DataExtension {
* @return boolean
*/
public function canPostComment($member = null) {
// Deny if not enabled for this object
if(!$this->owner->CommentsEnabled) return false;
// Check if member is required
$requireLogin = $this->owner->getCommentsOption('require_login');
$requireLogin = $this->owner->CommentsRequireLogin;
if(!$requireLogin) return true;
// Check member is logged in
@ -195,7 +288,7 @@ class CommentsExtension extends DataExtension {
if(!$member) return false;
// If member required check permissions
$requiredPermission = $this->getPostingRequiredPermission();
$requiredPermission = $this->owner->PostingRequiredPermission;
if($requiredPermission && !Permission::checkMember($member, $requiredPermission)) return false;
return true;
@ -323,10 +416,12 @@ class CommentsExtension extends DataExtension {
return $value;
}
public function updateCMSFields(\FieldList $fields) {
// Disable moderation if not permitted
if(!$this->owner->canModerateComments()) return;
/**
* Add moderation functions to the current fieldlist
*
* @param FieldList $fields
*/
protected function updateModerationFields(FieldList $fields) {
// Create gridfield config
$commentsConfig = CommentsGridFieldConfig::create();
@ -360,4 +455,16 @@ class CommentsExtension extends DataExtension {
$fields->push($moderated);
}
}
public function updateCMSFields(\FieldList $fields) {
// Disable moderation if not permitted
if($this->owner->canModerateComments()) {
$this->updateModerationFields($fields);
}
// If this isn't a page we should merge the settings into the CMS fields
if(!$this->attachedToSiteTree()) {
$this->updateSettingsFields($fields);
}
}
}

View File

@ -24,26 +24,35 @@ SiteTree:
extensions:
- CommentsExtension
comments:
require_login: false # boolean, whether a user needs to login
required_permission: false # required permission to comment (or array of permissions)
include_js: true # Enhance operation by ajax behaviour on moderation links
show_comments_when_disabled: false # when comments are disabled should we show older comments (if available)
order_comments_by: '"Created" DESC'
comments_per_page: 10
comments_holder_id: 'comments-holder' # id for the comments holder
comment_permalink_prefix: 'comment-' # id prefix for each comment. If needed make this different
require_moderation: false
html_allowed: false # allow for sanitized HTML in comments
html_allowed_elements:
- a
- img
- i
- b
use_preview: false # preview formatted comment (when allowing HTML). Requires include_js=true
use_gravatar: false
gravatar_size: 80
enabled: true # Enables commenting to be disabled for a specific class (or subclass of a parent with commenting enabled)
enabled_cms: false # The 'enabled' option will be set via the CMS instead of config
require_login: false # boolean, whether a user needs to login
require_login_cms: false # The 'require_login' option will be set via the CMS instead of config
required_permission: false # required permission to comment (or array of permissions)
include_js: true # Enhance operation by ajax behaviour on moderation links
use_gravatar; false # set to true to show gravatar icons,
gravatar_size: 80 # size of gravatar in pixels. This is the same as the standard default
gravatar_default: 'identicon' # theme for 'not found' gravatar (see http://gravatar.com/site/implement/images/)
gravatar_rating: 'g' # gravatar rating. This is the same as the standard default
show_comments_when_disabled: false # when comments are disabled should we show older comments (if available)
order_comments_by: '"Created" DESC'
comments_per_page: 10
comments_holder_id: 'comments-holder' # id for the comments holder
comment_permalink_prefix: 'comment-' # id prefix for each comment. If needed make this different
require_moderation: false
require_moderation_nonmembers: false # requires moderation for comments posted by non-members. 'require_moderation' overrides this if set.
require_moderation_cms: false # If true, ignore above values and configure moderation requirements via the CMS only
html_allowed: false # allow for sanitized HTML in comments
html_allowed_elements:
- a
- img
- i
- b
use_preview: false # preview formatted comment (when allowing HTML). Requires include_js=true
```
Enabling any of the *_cms options will instead allow these options to be configured under the settings tab
of each page in the CMS.
If you want to customize any of the configuration options after you have added
the extension (or on the built-in SiteTree commenting) use `set_config_value`

View File

@ -22,11 +22,16 @@ class CommentsTest extends FunctionalTest {
}
public function testCommentsList() {
// comments don't require moderation so unmoderated comments can be
// comments don't require moderation so unmoderated comments can be
// shown but not spam posts
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => false));
Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation_nonmembers' => false,
'require_moderation' => false,
'require_moderation_cms' => false,
));
$item = $this->objFromFixture('CommentableItem', 'spammed');
$this->assertEquals('None', $item->ModerationRequired);
$this->assertDOSEquals(array(
array('Name' => 'Comment 1'),
@ -34,23 +39,126 @@ class CommentsTest extends FunctionalTest {
), $item->Comments(), 'Only 2 non spam posts should be shown');
// when moderated, only moderated, non spam posts should be shown.
Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => true));
$this->assertEquals('NonMembersOnly', $item->ModerationRequired);
// Check that require_moderation overrides this option
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => true));
$this->assertEquals('Required', $item->ModerationRequired);
$this->assertDOSEquals(array(
array('Name' => 'Comment 3')
), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
// As of 2.0, logging in with admin no longer grants special privileges to view frontend comments and should
// be done via the CMS
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => true));
$this->assertEquals(1, $item->Comments()->Count());
// require_moderation_nonmembers still filters out unmoderated comments
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => false));
$this->assertEquals(1, $item->Comments()->Count());
Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => false));
$this->assertEquals(2, $item->Comments()->Count());
}
/**
* Test moderation options configured via the CMS
*/
public function testCommentCMSModerationList() {
// comments don't require moderation so unmoderated comments can be
// shown but not spam posts
Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation' => true,
'require_moderation_cms' => true,
));
$item = $this->objFromFixture('CommentableItem', 'spammed');
$this->assertEquals('None', $item->ModerationRequired);
$this->assertDOSEquals(array(
array('Name' => 'Comment 1'),
array('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->ModerationRequired);
// Check that require_moderation overrides this option
$item->ModerationRequired = 'Required';
$item->write();
$this->assertEquals('Required', $item->ModerationRequired);
$this->assertDOSEquals(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
$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::inst()->update('CommentableItem', 'comments', array(
'require_login' => false,
'require_login_cms' => false,
'required_permission' => false,
));
$item = $this->objFromFixture('CommentableItem', 'first');
$item2 = $this->objFromFixture('CommentableItem', 'second');
// Test restriction free commenting
if($member = Member::currentUser()) $member->logOut();
$this->assertFalse($item->CommentsRequireLogin);
$this->assertTrue($item->canPostComment());
// Test permission required to post
Config::inst()->update('CommentableItem', 'comments', array(
'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::inst()->update('CommentableItem', 'comments', array(
'required_permission' => false,
));
$this->assertTrue($item->CommentsRequireLogin);
if($member = Member::currentUser()) $member->logOut();
$this->assertFalse($item->canPostComment());
$this->logInWithPermission('ANY_PERMISSION');
$this->assertTrue($item->canPostComment());
// Test options set via CMS
Config::inst()->update('CommentableItem', 'comments', array(
'require_login' => true,
'require_login_cms' => true,
));
$this->assertFalse($item->CommentsRequireLogin);
$this->assertTrue($item2->CommentsRequireLogin);
if($member = Member::currentUser()) $member->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 testCanView() {
$visitor = $this->objFromFixture('Member', 'visitor');
$admin = $this->objFromFixture('Member', 'commentadmin');

View File

@ -20,9 +20,11 @@ CommentableItem:
first:
Title: First
ProvideComments: 1
CommentsRequireLogin: 0
second:
Title: Second
ProvideComments: 1
CommentsRequireLogin: 1
third:
Title: Third
ProvideComments: 1
@ -32,6 +34,7 @@ CommentableItem:
spammed:
ProvideComments: 1
Title: spammed
ModerationRequired: 'None'
Comment:
firstComA: