mirror of
https://github.com/silverstripe/silverstripe-comments
synced 2024-10-22 11:05:49 +02:00
API Move configuration of comments to the config API and enable per-instance configuration
Enable comment moderation per-page
This commit is contained in:
parent
93414c5231
commit
1900ab14b5
30
_config.php
30
_config.php
@ -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');
|
||||
|
@ -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
91
code/CommentList.php
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
);
|
33
code/admin/CommentsGridFieldConfig.php
Normal file
33
code/admin/CommentsGridFieldConfig.php
Normal 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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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">« <% _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') %> »</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 %>
|
||||
|
||||
|
@ -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>
|
||||
|
21
templates/Includes/CommentPagination.ss
Normal file
21
templates/Includes/CommentPagination.ss
Normal file
@ -0,0 +1,21 @@
|
||||
<% if $MoreThanOnePage %>
|
||||
<div class="comments-pagination">
|
||||
<p>
|
||||
<% if $PrevLink %>
|
||||
<a href="$PrevLink.ATT" class="previous">« <% _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') %> »</a>
|
||||
<% end_if %>
|
||||
</p>
|
||||
</div>
|
||||
<% end_if %>
|
@ -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";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user