API Move configuration of comments to the config API and enable per-instance configuration

Enable comment moderation per-page
This commit is contained in:
Damian Mooyman 2015-04-07 15:56:00 +12:00
parent 93414c5231
commit 1900ab14b5
16 changed files with 798 additions and 440 deletions

View File

@ -1,31 +1,3 @@
<?php
/**
* Comments Default Configuration
*
* To enable comments on your own {@link DataObject}'s you need to
* call Commenting::add_comments($object_name, $settings);
*
* Where $object_name is the name of the subclass of DataObject you want
* to add the comments to and $settings is a map of configuration options
* and values
*
* Example: mysite/_config.php
*
* <code>
* // uses the default values
* Commenting::add('SiteTree');
*
* // set configuration
* Commenting::add('SiteTree', array(
* 'require_login' => true
* ));
* </code>
*
* To see all the configuration options read docs/en/Configuration.md or
* consult the Commenting class.
*/
if(Config::inst()->get('Commenting', 'sitetree_comments') && class_exists('SiteTree') && !Commenting::has_commenting('SiteTree')) {
Commenting::add('SiteTree');
}
Deprecation::notification_version('2.0', 'comments');

View File

@ -7,4 +7,4 @@ Director:
# handle old 2.4 style urls
'CommentingController//$Action/$ID/$OtherID': 'CommentingController'
'PageComments/$Action/$ID/$OtherID': 'CommentingController'
'PageComments_Controller/$Action/$ID/$OtherID': 'CommentingController'
'PageComments_Controller/$Action/$ID/$OtherID': 'CommentingController'

91
code/CommentList.php Normal file
View File

@ -0,0 +1,91 @@
<?php
/**
* Handles polymorphic relation for commentlist
*
* Uses elements of PolymorphicHasManyList in 3.2
*
* @author dmooyman
*/
class CommentList extends HasManyList {
/**
* Retrieve the name of the class this relation is filtered by
*
* @return string
*/
public function getForeignClass() {
return $this->dataQuery->getQueryParam('Foreign.Class');
}
public function __construct($parentClassName) {
parent::__construct('Comment', 'ParentID');
// Ensure underlying DataQuery globally references the class filter
$this->dataQuery->setQueryParam('Foreign.Class', $parentClassName);
// For queries with multiple foreign IDs (such as that generated by
// DataList::relation) the filter must be generalised to filter by subclasses
$classNames = Convert::raw2sql(ClassInfo::subclassesFor($parentClassName));
$this->dataQuery->where(sprintf(
"\"BaseClass\" IN ('%s')", implode("', '", $classNames)
));
}
/**
* Adds the item to this relation.
*
* @param Comment $item The comment to be added
*/
public function add($item) {
// Check item given
if(is_numeric($item)) {
$item = Comment::get()->byID($item);
}
if(!($item instanceof Comment)) {
throw new InvalidArgumentException("CommentList::add() expecting a Comment object, or ID value");
}
// Validate foreignID
$foreignID = $this->getForeignID();
if(!$foreignID || is_array($foreignID)) {
throw new InvalidArgumentException("CommentList::add() can't be called until a single foreign ID is set");
}
$item->ParentID = $foreignID;
$item->BaseClass = $this->getForeignClass();
$item->write();
}
/**
* Remove a Comment from this relation by clearing the foreign key. Does not actually delete the comment.
*
* @param Comment $item The Comment to be removed
*/
public function remove($item) {
// Check item given
if(is_numeric($item)) {
$item = Comment::get()->byID($item);
}
if(!($item instanceof Comment)) {
throw new InvalidArgumentException("CommentList::remove() expecting a Comment object, or ID",
E_USER_ERROR);
}
// Don't remove item with unrelated class key
$foreignClass = $this->getForeignClass();
$classNames = ClassInfo::subclassesFor($foreignClass);
if(!in_array($item->BaseClass, $classNames)) return;
// Don't remove item which doesn't belong to this list
$foreignID = $this->getForeignID();
if( empty($foreignID)
|| (is_array($foreignID) && in_array($item->ParentID, $foreignID))
|| $foreignID == $item->ParentID
) {
$item->ParentID = null;
$item->BaseClass = null;
$item->write();
}
}
}

View File

@ -9,180 +9,138 @@
*
* For documentation on how to use this class see docs/en/Configuration.md
*
* @deprecated since version 2.0
*
* @package comments
*/
class Commenting {
/**
* @var array map of enabled {@link DataObject} and related configuration
*/
private static $enabled_classes = array();
/**
* @config
* @var bool Whether to enable commenting on SiteTree objects by default
*/
private static $sitetree_comments = true;
/**
* @var array default configuration values
*/
private static $default_config = array(
'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
'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.
'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
);
/**
* Adds commenting to a {@link DataObject}
*
* @deprecated since version 2.0
*
* @param string classname to add commenting to
* @param array $setting Settings. See {@link self::$default_config} for
* @param array $settings Settings. See {@link self::$default_config} for
* available settings
*
* @throws InvalidArgumentException
*/
public static function add($class, $settings = false) {
if($settings && !is_array($settings)) {
throw new InvalidArgumentException('$settings needs to be an array or null', E_USER_ERROR);
}
self::$enabled_classes[$class] = $settings;
Deprecation::notice('2.0', 'Using Commenting::add is deprecated. Please use the config API instead');
Config::inst()->update($class, 'extensions', array('CommentsExtension'));
$class::add_extension('CommentsExtension');
// Check if settings must be customised
if($settings === false) return;
if(!is_array($settings)) {
throw new InvalidArgumentException('$settings needs to be an array or null');
}
Config::inst()->update($class, 'comments', $settings);
}
/**
* Removes commenting from a {@link DataObject}. Does not remove existing comments
* but does remove the extension.
*
* @deprecated since version 2.0
*
* @param string $class Class to remove {@link CommentsExtension} from
*/
public static function remove($class) {
if(isset(self::$enabled_classes[$class])) {
unset(self::$enabled_classes[$class]);
}
Deprecation::notice('2.0', 'Using Commenting::remove is deprecated. Please use the config API instead');
$class::remove_extension('CommentsExtension');
}
/**
* Returns whether a given class name has commenting enabled
*
* @deprecated since version 2.0
*
* @return bool
*/
public static function has_commenting($class) {
return (isset(self::$enabled_classes[$class]));
Deprecation::notice('2.0', 'Using Commenting::has_commenting is deprecated. Please use the config API instead');
return $class::has_extension('CommentsExtension');
}
/**
* Sets a value for a class of a given config setting. Passing 'all' as the class
* sets it for everything
*
* @deprecated since version 2.0
*
* @param string $class Class to set the value on. Passing 'all' will set it to all
* active mappings
* @param string $key setting to change
* @param mixed $value value of the setting
*/
public static function set_config_value($class, $key, $value = false) {
if($class == "all") {
if($enabledClasses = self::$enabled_classes) {
foreach($enabledClasses as $enabled) {
if(!is_array($enabled)) $enabled = array();
$enabled[$key] = $value;
}
}
}
else if(isset(self::$enabled_classes[$class])) {
if(!is_array(self::$enabled_classes[$class])) self::$enabled_classes[$class] = array();
self::$enabled_classes[$class][$key] = $value;
}
else {
throw new Exception("$class does not have commenting enabled", E_USER_ERROR);
}
Deprecation::notice('2.0', 'Commenting::set_config_value is deprecated. Use the config api instead');
if($class === "all") $class = 'CommentsExtension';
Config::inst()->update($class, 'comments', array($key => $value));
}
/**
* Returns a given config value for a commenting class
*
* @deprecated since version 2.0
*
* @param string $class
* @param string $key config value to return
*
* @throws Exception
* @throws Exception
* @return mixed
*/
public static function get_config_value($class = null, $key) {
if(!$class || isset(self::$enabled_classes[$class])) {
// custom configuration
if(isset(self::$enabled_classes[$class][$key])) return self::$enabled_classes[$class][$key];
// default configuration
if(isset(self::$default_config[$key])) return self::$default_config[$key];
// config value doesn't exist
throw new Exception("Config ($key) is not a valid configuration value", E_USER_WARNING);
}
else {
throw new Exception("$class does not have commenting enabled", E_USER_ERROR);
public static function get_config_value($class, $key) {
Deprecation::notice(
'2.0',
'Using Commenting::get_config_value is deprecated. Please use $parent->getCommentsOption() or '
. 'CommentingController::getOption() instead'
);
// Get settings
if(!$class) {
$class = 'CommentsExtension';
} elseif(!$class::has_extension('CommentsExtension')) {
throw new InvalidArgumentException("$class does not have commenting enabled");
}
return singleton($class)->getCommentsOption($key);
}
/**
* Determines whether a config value on the commenting extension
* matches a given value.
*
* @deprecated since version 2.0
*
* @param string $class
* @param string $key
* @param string $value Expected value
*
* @return bool
* @return boolean
*/
public static function config_value_equals($class, $key, $value) {
try {
$check = self::get_config_value($class, $key);
if($check && ($check == $value)) return true;
}
catch(Exception $e) {}
return false;
$check = self::get_config_value($class, $key);
if($check && ($check == $value)) return true;
}
/**
* Return whether a user can post on a given commenting instance
*
* @deprecated since version 2.0
*
* @param string $class
* @return boolean true
*/
public static function can_member_post($class) {
Deprecation::notice('2.0', 'Use $instance->canPostComment() directly instead');
$member = Member::currentUser();
try {
$login = self::get_config_value($class, 'require_login');
$permission = self::get_config_value($class, 'required_permission');
if($permission && !Permission::check($permission)) return false;
if($login && !$member) return false;
}
catch(Exception $e) {}
return true;
// Check permission
$permission = self::get_config_value($class, 'required_permission');
if($permission && !Permission::check($permission)) return false;
// Check login required
$requireLogin = self::get_config_value($class, 'require_login');
return !$requireLogin || $member;
}
}

View File

@ -46,37 +46,7 @@ class CommentAdmin extends LeftAndMain implements PermissionProvider {
return Security::permissionFailure($this);
}
$commentsConfig = GridFieldConfig::create()->addComponents(
new GridFieldFilterHeader(),
$columns = new GridFieldDataColumns(),
new GridFieldSortableHeader(),
new GridFieldPaginator(25),
new GridFieldDeleteAction(),
new GridFieldDetailForm(),
new GridFieldExportButton(),
new GridFieldEditButton(),
new GridFieldDetailForm(),
$manager = new GridFieldBulkManager()
);
$manager->addBulkAction(
'markAsSpam', 'Mark as spam', 'CommentsGridFieldBulkAction_MarkAsSpam',
array(
'isAjax' => true,
'icon' => 'delete',
'isDestructive' => true
)
);
$columns->setFieldFormatting(array(
'ParentTitle' => function($value, &$item) {
return sprintf(
'<a href="%s" class="cms-panel-link external-link action" target="_blank">%s</a>',
Convert::raw2xml($item->Link()),
Convert::raw2xml($value)
);
}
));
$commentsConfig = CommentsGridFieldConfig::create();
$needs = new GridField(
'Comments',
@ -87,7 +57,7 @@ class CommentAdmin extends LeftAndMain implements PermissionProvider {
$moderated = new GridField(
'CommentsModerated',
_t('CommentsAdmin.CommentsModerated'),
_t('CommentsAdmin.Moderated', 'Moderated'),
Comment::get()->filter('Moderated',1),
$commentsConfig
);

View File

@ -0,0 +1,33 @@
<?php
class CommentsGridFieldConfig extends GridFieldConfig_RecordEditor {
public function __construct($itemsPerPage = 25) {
parent::__construct($itemsPerPage);
$this->addComponent(new GridFieldExportButton());
// Format column
$columns = $this->getComponentByType('GridFieldDataColumns');
$columns->setFieldFormatting(array(
'ParentTitle' => function($value, &$item) {
return sprintf(
'<a href="%s" class="cms-panel-link external-link action" target="_blank">%s</a>',
Convert::raw2xml($item->Link()),
Convert::raw2xml($value)
);
}
));
// Add bulk option
$manager = new GridFieldBulkManager();
$manager->addBulkAction(
'markAsSpam', 'Mark as spam', 'CommentsGridFieldBulkAction_MarkAsSpam',
array(
'isAjax' => true,
'icon' => 'delete',
'isDestructive' => true
)
);
$this->addComponent($manager);
}
}

View File

@ -17,48 +17,122 @@ class CommentingController extends Controller {
'doPreviewComment'
);
/**
* Base class this commenting form is for
*
* @var string
*/
private $baseClass = "";
private $ownerRecord = "";
private $ownerController = "";
/**
* The record this commenting form is for
*
* @var DataObject
*/
private $ownerRecord = null;
/**
* Parent controller record
*
* @var Controller
*/
private $ownerController = null;
/**
* Backup url to return to
*
* @var string
*/
protected $fallbackReturnURL = null;
/**
* Set the base class to use
*
* @param string $class
*/
public function setBaseClass($class) {
$this->baseClass = $class;
}
/**
* Get the base class used
*
* @return string
*/
public function getBaseClass() {
return $this->baseClass;
}
/**
* Set the record this controller is working on
*
* @param DataObject $record
*/
public function setOwnerRecord($record) {
$this->ownerRecord = $record;
}
/**
* Get the record
*
* @return DataObject
*/
public function getOwnerRecord() {
return $this->ownerRecord;
}
/**
* Set the parent controller
*
* @param Controller $controller
*/
public function setOwnerController($controller) {
$this->ownerController = $controller;
}
/**
* Get the parent controller
*
* @return Controller
*/
public function getOwnerController() {
return $this->ownerController;
}
/**
* Get the commenting option for the current state
*
* @param string $key
* @return mixed Result if the setting is available, or null otherwise
*/
public function getOption($key) {
// If possible use the current record
if($record = $this->getOwnerRecord()) {
return $record->getCommentsOption($key);
}
// Otherwise a singleton of that record
if($class = $this->getBaseClass()) {
return singleton($class)->getCommentsOption($key);
}
// Otherwise just use the default options
return singleton('CommentsExtension')->getCommentsOption($key);
}
/**
* Workaround for generating the link to this controller
*
* @return string
*/
public function Link($action = "", $id = '', $other = '') {
return Controller::join_links(__CLASS__ , $action, $id, $other);
public function Link($action = '', $id = '', $other = '') {
return Controller::join_links(Director::baseURL(), __CLASS__ , $action, $id, $other);
}
/**
* Outputs the RSS feed of comments
*
* @return XML
* @return HTMLText
*/
public function rss() {
return $this->getFeed($this->request)->outputToBrowser();
@ -81,42 +155,37 @@ class CommentingController extends Controller {
$class = $request->param('ID');
$id = $request->param('OtherID');
// Support old pageid param
if(!$id && !$class && ($id = $request->getVar('pageid'))) {
$class = 'SiteTree';
}
$comments = Comment::get()->filter(array(
'Moderated' => 1,
'IsSpam' => 0,
));
if($request->getVar('pageid')) {
$comments = $comments->filter(array(
'BaseClass' => 'SiteTree',
'ParentID' => $request->getVar('pageid'),
));
$link = $this->Link('rss', 'SiteTree', $id);
} elseif($class && $id) {
if(Commenting::has_commenting($class)) {
$comments = $comments->filter(array(
'BaseClass' => $class,
'ParentID' => $id,
));
$link = $this->Link('rss', Convert::raw2xml($class), (int) $id);
} else {
// Check if class filter
if($class) {
if(!is_subclass_of($class, 'DataObject') || !$class::has_extension('CommentsExtension')) {
return $this->httpError(404);
}
} elseif($class) {
if(Commenting::has_commenting($class)) {
$comments = $comments->filter('BaseClass', $class);
} else {
return $this->httpError(404);
$this->setBaseClass($class);
$comments = $comments->filter('BaseClass', $class);
$link = Controller::join_links($link, $class);
// Check if id filter
if($id) {
$comments = $comments->filter('ParentID', $id);
$link = Controller::join_links($link, $id);
$this->setOwnerRecord(DataObject::get_by_id($class, $id));
}
}
$title = _t('CommentingController.RSSTITLE', "Comments RSS Feed");
$comments = new PaginatedList($comments, $request);
$comments->setPageLength(Commenting::get_config_value(null, 'comments_per_page'));
$comments->setPageLength($this->getOption('comments_per_page'));
return new RSSFeed(
$comments,
@ -232,8 +301,7 @@ class CommentingController extends Controller {
* @return Form
*/
public function CommentsForm() {
$usePreview = Commenting::get_config_value($this->getBaseClass(), 'use_preview');
$member = Member::currentUser();
$usePreview = $this->getOption('use_preview');
$fields = new FieldList(
$dataFields = new CompositeField(
@ -292,34 +360,30 @@ 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()) {
$require_login = Commenting::get_config_value($this->getBaseClass(), 'require_login');
$permission = Commenting::get_config_value($this->getBaseClass(), 'required_permission');
if(($require_login || $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));
$form->setFields($fields);
}
// we do not want to read a new URL when the form has already been submitted
// which in here, it hasn't been.
$url = (isset($_SERVER['REQUEST_URI'])) ? Director::protocolAndHost() . '' . $_SERVER['REQUEST_URI'] : false;
$form->loadDataFrom(array(
'ParentID' => $record->ID,
'ReturnURL' => $url,
'ReturnURL' => $this->request->getURL(),
'BaseClass' => $this->getBaseClass()
));
}
// Set it so the user gets redirected back down to the form upon form fail
$form->setRedirectToFormOnValidationError(true);
@ -336,7 +400,7 @@ class CommentingController extends Controller {
// allow previous value to fill if comment not stored in cookie (i.e. validation error)
$prevComment = Cookie::get('CommentsForm_Comment');
if($prevComment && $prevComment != ''){
$form->loadDataFrom(array("Comment" => $prevComment));
$form->loadDataFrom(array("Comment" => $prevComment));
}
}
@ -357,13 +421,14 @@ class CommentingController extends Controller {
* @param Form $form
*/
public function doPostComment($data, $form) {
$class = (isset($data['BaseClass'])) ? $data['BaseClass'] : $this->getBaseClass();
$usePreview = Commenting::get_config_value($class, 'use_preview');
$isPreview = ($usePreview && isset($data['IsPreview']) && $data['IsPreview']);
// if no class then we cannot work out what controller or model they
// are on so throw an error
if(!$class) user_error("No OwnerClass set on CommentingController.", E_USER_ERROR);
// Load class and parent from data
if(isset($data['BaseClass'])) {
$this->setBaseClass($data['BaseClass']);
}
if(isset($data['ParentID']) && ($class = $this->getBaseClass())) {
$this->setOwnerRecord($class::get()->byID($data['ParentID']));
}
if(!$this->getOwnerRecord()) return $this->httpError(404);
// cache users data
Cookie::set("CommentsForm_UserData", Convert::raw2json($data));
@ -373,7 +438,7 @@ class CommentingController extends Controller {
$this->extend('onBeforePostComment', $form);
// If commenting can only be done by logged in users, make sure the user is logged in
if(!Commenting::can_member_post($class)) {
if(!$this->getOwnerRecord()->canPostComment()) {
return Security::permissionFailure(
$this,
_t(
@ -389,9 +454,9 @@ class CommentingController extends Controller {
}
// is moderation turned on
$requireModeration = Commenting::get_config_value($class, 'require_moderation');
if(!$requireModeration){
$requireModerationNonmembers = Commenting::get_config_value($class, 'require_moderation_nonmembers');
$requireModeration = $this->getOption('require_moderation');
if(!$requireModeration) {
$requireModerationNonmembers = $this->getOption('require_moderation_nonmembers');
$requireModeration = $requireModerationNonmembers ? !Member::currentUser() : false;
}
@ -400,18 +465,19 @@ class CommentingController extends Controller {
Session::set('CommentsModerated', 1);
}
$comment = new Comment();
$form->saveInto($comment);
$comment->AllowHtml = Commenting::get_config_value($class, 'html_allowed');
$comment->AllowHtml = $this->getOption('html_allowed');
$comment->Moderated = !$requireModeration;
// Save into DB, or call pre-save hooks to give accurate preview
$usePreview = $this->getOption('use_preview');
$isPreview = $usePreview && !empty($data['IsPreview']);
if($isPreview) {
$comment->extend('onBeforeWrite', $dummy);
$comment->extend('onBeforeWrite');
} else {
$comment->write();
$comment->write();
// extend hook to allow extensions. Also see onBeforePostComment
$this->extend('onAfterPostComment', $comment);
@ -432,7 +498,7 @@ class CommentingController extends Controller {
// Given a redirect page exists, attempt to link to the correct anchor
if(!$comment->Moderated) {
// Display the "awaiting moderation" text
$holder = Commenting::get_config_value($comment->BaseClass, 'comments_holder_id');
$holder = $this->getOption('comments_holder_id');
$hash = "{$holder}_PostCommentForm_error";
} elseif($comment->IsSpam) {
// Link to the form with the error message contained

View File

@ -13,40 +13,40 @@
* @property integer $ParentID ID of the parent page / dataobject
* @property boolean $AllowHtml If true, treat $Comment as HTML instead of plain text
* @property string $SecretToken Secret admin token required to provide moderation links between sessions
* @method Member Author()
* @method HasManyList ChildComments() List of child comments
* @method Member Author() Member object who created this comment
* @package comments
*/
class Comment extends DataObject {
private static $db = array(
"Name" => "Varchar(200)",
"Comment" => "Text",
"Email" => "Varchar(200)",
"URL" => "Varchar(255)",
"BaseClass" => "Varchar(200)",
"Moderated" => "Boolean",
"IsSpam" => "Boolean",
"ParentID" => "Int",
'AllowHtml' => "Boolean",
"SecretToken" => "Varchar(255)",
"Name" => "Varchar(200)",
"Comment" => "Text",
"Email" => "Varchar(200)",
"URL" => "Varchar(255)",
"BaseClass" => "Varchar(200)",
"Moderated" => "Boolean(1)",
"IsSpam" => "Boolean(0)",
"ParentID" => "Int",
'AllowHtml' => "Boolean",
"SecretToken" => "Varchar(255)",
);
private static $has_one = array(
"Author" => "Member"
"Author" => "Member",
);
private static $default_sort = '"Created" DESC';
private static $has_many = array();
private static $many_many = array();
private static $defaults = array(
"Moderated" => 1,
"IsSpam" => 0
"IsSpam" => 0,
);
private static $casting = array(
'Title' => 'Varchar',
'ParentTitle' => 'Varchar',
'ParentClassName' => 'Varchar',
'AuthorName' => 'Varchar',
'RSSName' => 'Varchar',
'DeleteLink' => 'Varchar',
@ -70,7 +70,7 @@ class Comment extends DataObject {
'Comment' => 'Comment',
'Created' => 'Date Posted',
'ParentTitle' => 'Parent',
'IsSpam' => 'Is Spam'
'IsSpam' => 'Is Spam',
);
public function onBeforeWrite() {
@ -136,8 +136,7 @@ class Comment extends DataObject {
* @return string
*/
public function Permalink() {
$prefix = Commenting::get_config_value($this->BaseClass, 'comment_permalink_prefix');
$prefix = $this->getOption('comment_permalink_prefix');
return $prefix . $this->ID;
}
@ -159,6 +158,26 @@ class Comment extends DataObject {
return $labels;
}
/**
* Get the commenting option
*
* @param string $key
* @return mixed Result if the setting is available, or null otherwise
*/
public function getOption($key) {
// If possible use the current record
$record = $this->getParent();
if(!$record && $this->BaseClass) {
// Otherwise a singleton of that record
$record = singleton($this->BaseClass);
} elseif(!$record) {
// Otherwise just use the default options
$record = singleton('CommentsExtension');
}
return $record->getCommentsOption($key);
}
/**
* Returns the parent {@link DataObject} this comment is attached too
@ -166,11 +185,9 @@ class Comment extends DataObject {
* @return DataObject
*/
public function getParent() {
if(!$this->BaseClass) {
$this->BaseClass = "SiteTree";
}
return ($this->ParentID) ? DataObject::get_by_id($this->BaseClass, $this->ParentID) : null;
return $this->BaseClass && $this->ParentID
? DataObject::get_by_id($this->BaseClass, $this->ParentID, true)
: null;
}
@ -180,43 +197,35 @@ class Comment extends DataObject {
* @return string
*/
public function getParentTitle() {
if($parent = $this->getParent()){
return ($parent && $parent->Title) ? $parent->Title : $parent->ClassName . " #" . $parent->ID;
}
if($parent = $this->getParent()) {
return $parent->Title ?: ($parent->ClassName . " #" . $parent->ID);
}
}
/**
* Comment-parent classnames obviousely vary, return the parent classname
* Comment-parent classnames obviously vary, return the parent classname
*
* @return string
*/
public function getParentClassName() {
$default = 'SiteTree';
if(!$this->BaseClass) {
return $default;
}
return $this->BaseClass;
}
public function castingHelper($field) {
// Safely escape the comment
if($field === 'EscapedComment') {
return $this->AllowHtml ? 'HTMLText' : 'Varchar';
}
return parent::castingHelper($field);
}
/**
* Return the content for this comment escaped depending on the Html state.
* Content to be safely escaped on the frontend
*
* @return HTMLText
* @return string
*/
public function getEscapedComment() {
$comment = $this->dbObject('Comment');
if ($comment->exists()) {
if ($this->AllowHtml) {
return DBField::create_field('HTMLText', nl2br($comment->RAW()));
} else {
return DBField::create_field('HTMLText', sprintf("<p>%s</p>", nl2br($comment->XML())));
}
}
return $comment;
return $this->Comment;
}
/**
@ -225,7 +234,7 @@ class Comment extends DataObject {
* @return boolean
*/
public function isPreview() {
return ($this->ID < 1);
return !$this->exists();
}
/**
@ -250,11 +259,13 @@ class Comment extends DataObject {
// Standard mechanism for accepting permission changes from decorators
$extended = $this->extendedCan('canView', $member);
if($extended !== null) return $extended;
$page = $this->getParent();
$admin = (bool) Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin');
return (($page && $page->ProvideComments && $page->canView($member)) || $admin);
// Allow admin
if(Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) return true;
// Check if parent has comments and can be viewed
$parent = $this->getParent();
return $parent && $parent->ProvideComments && $parent->canView($member);
}
/**
@ -301,8 +312,8 @@ class Comment extends DataObject {
public function getAuthorName() {
if($this->Name) {
return $this->Name;
} else if($this->Author()) {
return $this->Author()->getName();
} else if($author = $this->Author()) {
return $author->getName();
}
}
@ -434,9 +445,7 @@ class Comment extends DataObject {
*/
public function getHtmlPurifierService() {
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.AllowedElements',
Commenting::get_config_value($this->BaseClass, 'html_allowed_elements')
);
$config->set('HTML.AllowedElements', $this->getOption('html_allowed_elements'));
$config->set('AutoFormat.AutoParagraph', true);
$config->set('AutoFormat.Linkify', true);
$config->set('URI.DisableExternalResources', true);
@ -444,17 +453,19 @@ class Comment extends DataObject {
return new HTMLPurifier($config);
}
/*
Calcualate the gravatar link from the email address
*/
/**
* Calcualate the gravatar link from the email address
*
* @return string
*/
public function Gravatar() {
$gravatar = '';
$use_gravatar = Commenting::get_config_value($this->BaseClass, 'use_gravatar');
$use_gravatar = $this->getOption('use_gravatar');
if ($use_gravatar) {
$gravatar = "http://www.gravatar.com/avatar/" . md5( strtolower(trim($this->Email)));
$gravatarsize = Commenting::get_config_value($this->BaseClass, 'gravatar_size');
$gravatardefault = Commenting::get_config_value($this->BaseClass, 'gravatar_default');
$gravatarrating = Commenting::get_config_value($this->BaseClass, 'gravatar_rating');
$gravatarsize = $this->getOption('gravatar_size');
$gravatardefault = $this->getOption('gravatar_default');
$gravatarrating = $this->getOption('gravatar_rating');
$gravatar.= "?s=".$gravatarsize."&d=".$gravatardefault."&r=".$gravatarrating;
}

View File

@ -7,10 +7,37 @@
*/
class CommentsExtension extends DataExtension {
/**
* Default configuration values
*
* @var array
* @config
*/
private static $comments = array(
'enabled' => true, // Allows commenting to be disabled even if the extension is present
'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
'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.
'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');
@ -31,53 +58,188 @@ class CommentsExtension extends DataExtension {
*/
public function updateSettingsFields(FieldList $fields) {
if($this->attachedToSiteTree()) {
$fields->addFieldToTab('Root.Settings',
$fields->addFieldToTab('Root.Settings',
new CheckboxField('ProvideComments', _t('Comment.ALLOWCOMMENTS', 'Allow Comments'))
);
}
}
/**
* Returns the RelationList of all comments against this object. Can be used as a data source
* for a gridfield with write access.
*
* @return CommentList
*/
public function AllComments() {
$comments = CommentList::create($this->ownerBaseClass)->forForeignID($this->owner->ID);
$this->owner->extend('updateAllComments', $comments);
return $comments;
}
public function getComments() {
Deprecation::notice('2.0', 'Use PagedComments to get paged coments');
return $this->PagedComments();
}
/**
* Returns a list of all the comments attached to this record.
* Returns the root level comments, with spam and unmoderated items excluded, for use in the frontend
*
* @return CommentList
*/
public function Comments() {
// Get all non-spam comments
$order = $this->owner->getCommentsOption('order_comments_by');
$list = $this
->AllComments()
->sort($order)
->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')
) {
$list = $list->filter('Moderated', 1);
}
$this->owner->extend('updateComments', $list);
return $list;
}
/**
* Returns a paged list of the root level comments, with spam and unmoderated items excluded,
* for use in the frontend
*
* @return PaginatedList
*/
public function getComments() {
$order = Commenting::get_config_value($this->ownerBaseClass, 'order_comments_by');
$list = Comment::get()->filter(array(
'ParentID' => $this->owner->ID,
'BaseClass' => $this->ownerBaseClass
))->sort($order);
// Filter content for unauthorised users
if (!($member = Member::currentUser()) || !Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
// Filter unmoderated comments for non-administrators if moderation is enabled
if (Commenting::get_config_value($this->ownerBaseClass, 'require_moderation') || Commenting::get_config_value($this->ownerBaseClass, 'require_moderation_nonmembers')) {
$list = $list->filter(array(
'Moderated' => 1,
'IsSpam' => 0
));
} else {
// Filter spam comments for non-administrators if auto-moderated
$list = $list->filter('IsSpam', 0);
}
}
$list = new PaginatedList($list);
$list->setPageLength(Commenting::get_config_value(
$this->ownerBaseClass, 'comments_per_page'
));
$controller = Controller::curr();
$list->setPageStart($controller->request->getVar("commentsstart". $this->owner->ID));
$list->setPaginationGetVar("commentsstart". $this->owner->ID);
public function PagedComments() {
$list = $this->Comments();
// Add pagination
$list = new PaginatedList($list, Controller::curr()->getRequest());
$list->setPaginationGetVar('commentsstart'.$this->owner->ID);
$list->setPageLength($this->owner->getCommentsOption('comments_per_page'));
$this->owner->extend('updatePagedComments', $list);
return $list;
}
/**
* Check if comments are configured for this page even if they are currently disabled.
* Do not include the comments on pages which don't have id's such as security pages
*
* @deprecated since version 2.0
*
* @return boolean
*/
public function getCommentsConfigured() {
Deprecation::notice('2.0', 'getCommentsConfigured is deprecated. Use getCommentsEnabled instead');
return true; // by virtue of all classes with this extension being 'configured'
}
/**
* Determine if comments are enabled for this instance
*
* @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;
}
/**
* Get the HTML ID for the comment holder in the template
*
* @return string
*/
public function getCommentHolderID() {
return $this->owner->getCommentsOption('comments_holder_id');
}
/**
* @deprecated since version 2.0
*/
public function getPostingRequiresPermission() {
Deprecation::notice('2.0', 'Use getPostingRequiredPermission instead');
return $this->getPostingRequiredPermission();
}
/**
* Permission codes required in order to post (or empty if none required)
*
* @return string|array Permission or list of permissions, if required
*/
public function getPostingRequiredPermission() {
return $this->owner->getCommentsOption('required_permission');
}
public function canPost() {
Deprecation::notice('2.0', 'Use canPostComment instead');
return $this->canPostComment();
}
/**
* Determine if a user can post comments on this item
*
* @param Member $member Member to check
* @return boolean
*/
public function canPostComment($member = null) {
// Check if member is required
$requireLogin = $this->owner->getCommentsOption('require_login');
if(!$requireLogin) return true;
// Check member is logged in
$member = $member ?: Member::currentUser();
if(!$member) return false;
// If member required check permissions
$requiredPermission = $this->getPostingRequiredPermission();
if($requiredPermission && !Permission::checkMember($member, $requiredPermission)) return false;
return true;
}
/**
* Determine if this member can moderate comments in the CMS
*
* @param Member $member
* @return boolean
*/
public function canModerateComments($member = null) {
return $this->owner->canEdit($member);
}
public function getRssLink() {
Deprecation::notice('2.0', 'Use getCommentRSSLink instead');
return $this->getCommentRSSLink();
}
/**
* Gets the RSS link to all comments
*
* @return string
*/
public function getCommentRSSLink() {
return Controller::join_links(Director::baseURL(), "CommentingController/rss");
}
public function getRssLinkPage() {
Deprecation::notice('2.0', 'Use getCommentRSSLinkPage instead');
return $this->getCommentRSSLinkPage();
}
/**
* Get the RSS link to all comments on this page
*
* @return string
*/
public function getCommentRSSLinkPage() {
return Controller::join_links(
$this->getCommentRSSLink(), $this->ownerBaseClass, $this->owner->ID
);
}
/**
* Comments interface for the front end. Includes the CommentAddForm and the composition
@ -91,23 +253,15 @@ class CommentsExtension extends DataExtension {
* @see docs/en/Extending
*/
public function CommentsForm() {
if(Commenting::has_commenting($this->ownerBaseClass) && Commenting::get_config_value($this->ownerBaseClass, 'include_js')) {
// Check if enabled
$enabled = $this->getCommentsEnabled();
if($enabled && $this->owner->getCommentsOption('include_js')) {
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/lib/jquery.form.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/jquery.validate.pack.js');
Requirements::javascript('comments/javascript/CommentsInterface.js');
}
$interface = new SSViewer('CommentsInterface');
// detect whether we comments are enabled. By default if $CommentsForm is included
// on a {@link DataObject} then it is enabled, however {@link SiteTree} objects can
// trigger comments on / off via ProvideComments
$enabled = (!$this->attachedToSiteTree() || $this->owner->ProvideComments) ? true : false;
// do not include the comments on pages which don't have id's such as security pages
if($this->owner->ID < 0) return false;
$controller = CommentingController::create();
$controller->setOwnerRecord($this->owner);
$controller->setBaseClass($this->ownerBaseClass);
@ -120,18 +274,13 @@ class CommentsExtension extends DataExtension {
// a little bit all over the show but to ensure a slightly easier upgrade for users
// return back the same variables as previously done in comments
return $interface->process(new ArrayData(array(
'CommentHolderID' => Commenting::get_config_value($this->ownerBaseClass, 'comments_holder_id'),
'PostingRequiresPermission' => Commenting::get_config_value($this->ownerBaseClass, 'required_permission'),
'CanPost' => Commenting::can_member_post($this->ownerBaseClass),
'RssLink' => "CommentingController/rss",
'RssLinkPage' => "CommentingController/rss/". $this->ownerBaseClass . '/'.$this->owner->ID,
'CommentsEnabled' => $enabled,
'Parent' => $this->owner,
'AddCommentForm' => $form,
'ModeratedSubmitted' => $moderatedSubmitted,
'Comments' => $this->getComments()
)));
return $this
->owner
->customise(array(
'AddCommentForm' => $form,
'ModeratedSubmitted' => $moderatedSubmitted,
))
->renderWith('CommentsInterface');
}
/**
@ -144,14 +293,71 @@ class CommentsExtension extends DataExtension {
return (is_subclass_of($class, 'SiteTree')) || ($class == 'SiteTree');
}
/**
* @deprecated 1.0 Please use {@link CommentsExtension->CommentsForm()}
*/
public function PageComments() {
// This method is very commonly used, don't throw a warning just yet
//user_error('$PageComments is deprecated. Please use $CommentsForm', E_USER_WARNING);
Deprecation::notice('1.0', '$PageComments is deprecated. Please use $CommentsForm');
return $this->CommentsForm();
}
/**
* Get the commenting option for this object
*
* This can be overridden in any instance or extension to customise the option available
*
* @param string $key
* @return mixed Result if the setting is available, or null otherwise
*/
public function getCommentsOption($key) {
$settings = $this->owner // In case singleton is called on the extension directly
? $this->owner->config()->comments
: Config::inst()->get(__CLASS__, 'comments');
$value = null;
if(isset($settings[$key])) $value = $settings[$key];
// To allow other extensions to customise this option
if($this->owner) $this->owner->extend('updateCommentsOption', $key, $value);
return $value;
}
public function updateCMSFields(\FieldList $fields) {
// Disable moderation if not permitted
if(!$this->owner->canModerateComments()) return;
// Create gridfield config
$commentsConfig = CommentsGridFieldConfig::create();
$needs = new GridField(
'CommentsNeedsModeration',
_t('CommentsAdmin.NeedsModeration', 'Needs Moderation'),
$this->owner->AllComments()->filter('Moderated', 0),
$commentsConfig
);
$moderated = new GridField(
'CommentsModerated',
_t('CommentsAdmin.Moderated', 'Moderated'),
$this->owner->AllComments()->filter('Moderated', 1),
$commentsConfig
);
if($fields->hasTabSet()) {
$tabset = new TabSet(
'Comments',
new Tab('CommentsNeedsModerationTab', _t('CommentAdmin.NeedsModeration', 'Needs Moderation'),
$needs
),
new Tab('CommentsModeratedTab', _t('CommentAdmin.Moderated', 'Moderated'),
$moderated
)
);
$fields->addFieldToTab('Root', $tabset);
} else {
$fields->push($needs);
$fields->push($moderated);
}
}
}

View File

@ -19,7 +19,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
"dev-master": "2.0.x-dev"
}
}
}

View File

@ -5,33 +5,62 @@
The module provides a number of built in configuration settings below are the
default settings
// mysite/_config.php
Commenting::add('Foo', array(
'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' => array('a', 'img', 'i', 'b'),
'use_preview' => false, // preview formatted comment (when allowing HTML). Requires include_js=true
'use_gravatar' => false,
'gravatar_size' => 80
));
In order to add commenting to your site, the minimum amount of work necessary is to add the `CommentsExtension` to
the base class for the object which holds comments.
```yaml
SiteTree:
extensions:
- CommentsExtension
```
## Configuration
In order to configure options for any class you should assign the specific option a value under the 'comments'
config of the specified class.
```yaml
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
```
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`
// mysite/_config.php - Sets require_login to true for all pages
Commenting::set_config_value('SiteTree', 'require_login', true);
// mysite/_config.php - Returns the setting
Commenting::get_config_value('SiteTree', 'require_login');
```yaml
# Set the default option for pages to require login
SiteTree:
comments:
require_login: true
```
```php
// Get the setting
$loginRequired = singleton('SiteTree')->getCommentsOption('require_login');
```
## HTML Comments
@ -56,12 +85,20 @@ properly sanitized. Don't allow tags like `<script>` or arbitrary attributes.
Gravatars can be turned on by adding this to your mysite/_config.php file
Commenting::set_config_value('SiteTree', 'use_gravatar', true);
```yaml
SiteTree:
comments:
use_gravitar: true
````
The default size is 80 pixels, as per the gravatar site if the 's' parameter is
omitted. To change this add the following (again to mysite/_config.php):
Commenting::set_config_value('SiteTree', 'gravatar_size', 40);
```yaml
SiteTree:
comments:
gravatar_size: 40
```
If the email address used to comment does not have a gravatar, it is possible
to configure the default image shown. Valid values can be found at
@ -79,14 +116,22 @@ return an HTTP 404 (File Not Found) response.
* blank: a transparent PNG image (border added to HTML below for demonstration
purposes)
To change the default image style, add the following to mysite/_config.php
To change the default image style, add the following to mysite/_config/config.yml
Commenting::set_config_value('SiteTree', 'gravatar_default', 'retro');
```yaml
SiteTree:
comments:
gravatar_default: 'retro'
```
The rating of the image can be changed by adding a line similar to this to
mysite/_config.php
mysite/_config/config.yml
Commenting::set_config_value('SiteTree', 'gravatar_rating', 'r');
```yaml
SiteTree:
comments:
gravatar_rating: 'r'
```
Vald values for rating are as follows:

View File

@ -3,13 +3,13 @@
<h4><% _t('CommentsInterface_ss.POSTCOM','Post your comment') %></h4>
<% if $AddCommentForm %>
<% if $CanPost %>
<% if $canPostComment %>
<% if $ModeratedSubmitted %>
<p id="{$CommentHolderID}_PostCommentForm_error" class="message good"><% _t('CommentsInterface_ss.AWAITINGMODERATION', 'Your comment has been submitted and is now awaiting moderation.') %></p>
<% end_if %>
$AddCommentForm
<% else %>
<p><% _t('CommentsInterface_ss.COMMENTLOGINERROR', 'You cannot post comments until you have logged in') %><% if PostingRequiresPermission %>,<% _t('CommentsInterface_ss.COMMENTPERMISSIONERROR', 'and that you have an appropriate permission level') %><% end_if %>.
<p><% _t('CommentsInterface_ss.COMMENTLOGINERROR', 'You cannot post comments until you have logged in') %><% if $PostingRequiredPermission %>,<% _t('CommentsInterface_ss.COMMENTPERMISSIONERROR', 'and that you have an appropriate permission level') %><% end_if %>.
<a href="Security/login?BackURL={$Parent.Link}" title="<% _t('CommentsInterface_ss.LOGINTOPOSTCOMMENT', 'Login to post a comment') %>"><% _t('CommentsInterface_ss.COMMENTPOSTLOGIN', 'Login Here') %></a>.
</p>
<% end_if %>
@ -28,30 +28,9 @@
</li>
<% end_loop %>
</ul>
<% if $Comments.MoreThanOnePage %>
<div class="comments-pagination">
<p>
<% if $Comments.PrevLink %>
<a href="$Comments.PrevLink" class="previous">&laquo; <% _t('CommentsInterface_ss.PREV','previous') %></a>
<% end_if %>
<% if $Comments.Pages %>
<% loop $Comments.Pages %>
<% if $CurrentBool %>
<strong>$PageNum</strong>
<% else %>
<a href="$Link">$PageNum</a>
<% end_if %>
<% end_loop %>
<% end_if %>
<% if $Comments.NextLink %>
<a href="$Comments.NextLink" class="next"><% _t('CommentsInterface_ss.NEXT','next') %> &raquo;</a>
<% end_if %>
</p>
</div>
<% end_if %>
<% with $Comments %>
<% include CommentPagination %>
<% end_with %>
<% end_if %>
<p class="no-comments-yet"<% if $Comments.Count %> style='display: none' <% end_if %> ><% _t('CommentsInterface_ss.NOCOMMENTSYET','No one has commented on this page yet.') %></p>
@ -65,9 +44,8 @@
<% end_if %>
<p class="commenting-rss-feed">
<a href="$RssLinkPage"><% _t('CommentsInterface_ss.RSSFEEDCOMMENTS', 'RSS feed for comments on this page') %></a> |
<a href="$RssLink"><% _t('CommentsInterface_ss.RSSFEEDALLCOMMENTS', 'RSS feed for all comments') %></a>
<a href="$CommentRSSLinkPage"><% _t('CommentsInterface_ss.RSSFEEDCOMMENTS', 'RSS feed for comments on this page') %></a> |
<a href="$CommentRSSLink"><% _t('CommentsInterface_ss.RSSFEEDALLCOMMENTS', 'RSS feed for all comments') %></a>
</p>
</div>
<% end_if %>

View File

@ -1,3 +1,3 @@
<div class="pending-comment">
<p><% _t('CommentsInterface_pendingcomment_ss.AWAITINGMODERATION', 'Your comment has been submitted and is now awaiting moderation.') %></p>
</div>
</div>

View File

@ -0,0 +1,21 @@
<% if $MoreThanOnePage %>
<div class="comments-pagination">
<p>
<% if $PrevLink %>
<a href="$PrevLink.ATT" class="previous">&laquo; <% _t('CommentsInterface_ss.PREV','previous') %></a>
<% end_if %>
<% if $Pages %><% loop $Pages %>
<% if $CurrentBool %>
<strong>$PageNum</strong>
<% else %>
<a href="$Link.ATT">$PageNum</a>
<% end_if %>
<% end_loop %><% end_if %>
<% if $NextLink %>
<a href="$NextLink.ATT" class="next"><% _t('CommentsInterface_ss.NEXT','next') %> &raquo;</a>
<% end_if %>
</p>
</div>
<% end_if %>

View File

@ -13,39 +13,42 @@ class CommentsTest extends FunctionalTest {
public function setUp() {
parent::setUp();
Commenting::add('CommentableItem');
Config::nest();
}
public function tearDown() {
Config::unnest();
parent::tearDown();
}
public function testCommentsList() {
// comments don't require moderation so unmoderated comments can be
// shown but not spam posts
Commenting::set_config_value('CommentableItem','require_moderation', false);
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => false));
$item = $this->objFromFixture('CommentableItem', 'spammed');
$this->assertDOSEquals(array(
array('Name' => 'Comment 1'),
array('Name' => 'Comment 3')
), $item->getComments(), 'Only 2 non spam posts should be shown');
), $item->Comments(), 'Only 2 non spam posts should be shown');
// when moderated, only moderated, non spam posts should be shown.
Commenting::set_config_value('CommentableItem','require_moderation', true);
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => true));
$this->assertDOSEquals(array(
array('Name' => 'Comment 3')
), $item->getComments(), 'Only 1 non spam, moderated post should be shown');
), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
// when logged in as an user with CMS_ACCESS_CommentAdmin rights they
// should see all the comments whether we have moderation on or not
// 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');
Commenting::set_config_value('CommentableItem','require_moderation', true);
$this->assertEquals(4, $item->getComments()->Count());
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => true));
$this->assertEquals(1, $item->Comments()->Count());
Commenting::set_config_value('CommentableItem','require_moderation', false);
$this->assertEquals(4, $item->getComments()->Count());
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => false));
$this->assertEquals(2, $item->Comments()->Count());
}
public function testCanView() {
@ -353,6 +356,10 @@ class CommentableItem extends DataObject implements TestOnly {
'Title' => 'Varchar'
);
private static $extensions = array(
'CommentsExtension'
);
public function RelativeLink() {
return "CommentableItem_Controller";
}