mirror of
https://github.com/silverstripe/silverstripe-comments
synced 2024-10-22 11:05:49 +02:00
Clean up
This commit is contained in:
parent
832fc8685a
commit
725f150c82
@ -1,58 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Helper Class for storing the configuration options. Retains the mapping between
|
||||
* objects which have comments attached and the related configuration options.
|
||||
* Helper Class for storing the configuration options. Retains the mapping between objects which
|
||||
* have comments attached and the related configuration options.
|
||||
*
|
||||
* Also handles adding the Commenting extension to the {@link DataObject} on behalf
|
||||
* of the user.
|
||||
*
|
||||
* For documentation on how to use this class see docs/en/Configuration.md
|
||||
* Also handles adding the Commenting extension to the {@link DataObject} on behalf of the user.
|
||||
*
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
* @package comments
|
||||
*/
|
||||
class Commenting {
|
||||
|
||||
/**
|
||||
* Adds commenting to a {@link DataObject}
|
||||
* Adds commenting to a {@link DataObject}.
|
||||
*
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
* @param string classname to add commenting to
|
||||
* @param array $settings Settings. See {@link self::$default_config} for
|
||||
* available settings
|
||||
* @param string $class
|
||||
* @param bool|array $settings
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function add($class, $settings = false) {
|
||||
Deprecation::notice('2.0', 'Using Commenting::add is deprecated. Please use the config API instead');
|
||||
|
||||
Config::inst()->update($class, 'extensions', array('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');
|
||||
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.
|
||||
* 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
|
||||
* @param string $class
|
||||
*/
|
||||
public static function remove($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
|
||||
* Returns whether a given class name has commenting enabled.
|
||||
*
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
@ -60,87 +62,99 @@ class Commenting {
|
||||
*/
|
||||
public static function has_commenting($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
|
||||
* 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
|
||||
* @param string $class
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function set_config_value($class, $key, $value = false) {
|
||||
Deprecation::notice('2.0', 'Commenting::set_config_value is deprecated. Use the config api instead');
|
||||
if($class === "all") $class = 'CommentsExtension';
|
||||
|
||||
if($class === "all") {
|
||||
$class = 'CommentsExtension';
|
||||
}
|
||||
|
||||
Config::inst()->update($class, 'comments', array($key => $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a given config value for a commenting class
|
||||
* 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
|
||||
* @return mixed
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
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'
|
||||
);
|
||||
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");
|
||||
throw new InvalidArgumentException(
|
||||
sprintf('%s does not have commenting enabled', $class)
|
||||
);
|
||||
}
|
||||
|
||||
return singleton($class)->getCommentsOption($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a config value on the commenting extension
|
||||
* matches a given value.
|
||||
* 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 boolean
|
||||
* @param string $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function config_value_equals($class, $key, $value) {
|
||||
$check = self::get_config_value($class, $key);
|
||||
if($check && ($check == $value)) return true;
|
||||
|
||||
if($check && ($check == $value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether a user can post on a given commenting instance
|
||||
* Return whether a user can post on a given commenting instance.
|
||||
*
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
* @param string $class
|
||||
* @return boolean true
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_member_post($class) {
|
||||
Deprecation::notice('2.0', 'Use $instance->canPostComment() directly instead');
|
||||
|
||||
$member = Member::currentUser();
|
||||
|
||||
// Check permission
|
||||
$permission = self::get_config_value($class, 'required_permission');
|
||||
if($permission && !Permission::check($permission)) return false;
|
||||
|
||||
// Check login required
|
||||
if($permission && !Permission::check($permission)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$requireLogin = self::get_config_value($class, 'require_login');
|
||||
|
||||
return !$requireLogin || $member;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Comment administration system within the CMS
|
||||
* Comment administration system within the CMS.
|
||||
*
|
||||
* @package comments
|
||||
*/
|
||||
class CommentAdmin extends LeftAndMain implements PermissionProvider {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $url_segment = 'comments';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $url_rule = '/$Action';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $menu_title = 'Comments';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'approvedmarked',
|
||||
'deleteall',
|
||||
@ -21,25 +32,29 @@ class CommentAdmin extends LeftAndMain implements PermissionProvider {
|
||||
'showtable',
|
||||
'spammarked',
|
||||
'EditForm',
|
||||
'unmoderated'
|
||||
'unmoderated',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providePermissions() {
|
||||
return array(
|
||||
"CMS_ACCESS_CommentAdmin" => array(
|
||||
'name' => _t('CommentAdmin.ADMIN_PERMISSION', "Access to 'Comments' section"),
|
||||
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
|
||||
)
|
||||
'CMS_ACCESS_CommentAdmin' => array(
|
||||
'name' => _t('CommentAdmin.ADMIN_PERMISSION', 'Access to \'Comments\' section'),
|
||||
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Form
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEditForm($id = null, $fields = null) {
|
||||
if(!$id) $id = $this->currentPageID();
|
||||
if(!$id) {
|
||||
$id = $this->currentPageID();
|
||||
}
|
||||
|
||||
$form = parent::getEditForm($id);
|
||||
$record = $this->getRecord($id);
|
||||
|
||||
if($record && !$record->canView()) {
|
||||
@ -48,61 +63,82 @@ class CommentAdmin extends LeftAndMain implements PermissionProvider {
|
||||
|
||||
$commentsConfig = CommentsGridFieldConfig::create();
|
||||
|
||||
$newComments = Comment::get()->filter('Moderated', 0);
|
||||
$newComments = Comment::get()
|
||||
->filter('Moderated', 0);
|
||||
|
||||
$newGrid = new CommentsGridField(
|
||||
$newCommentsGrid = new CommentsGridField(
|
||||
'NewComments',
|
||||
_t('CommentsAdmin.NewComments', 'New'),
|
||||
$newComments,
|
||||
$commentsConfig
|
||||
);
|
||||
|
||||
$approvedComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 0);
|
||||
$newCommentsCountLabel = sprintf('(%s)', count($newComments));
|
||||
|
||||
$approvedGrid = new CommentsGridField(
|
||||
$approvedComments = Comment::get()
|
||||
->filter('Moderated', 1)
|
||||
->filter('IsSpam', 0);
|
||||
|
||||
$approvedCommentsGrid = new CommentsGridField(
|
||||
'ApprovedComments',
|
||||
_t('CommentsAdmin.ApprovedComments', 'Approved'),
|
||||
$approvedComments,
|
||||
$commentsConfig
|
||||
);
|
||||
|
||||
$spamComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 1);
|
||||
$approvedCommentsCountLabel = sprintf('(%s)', count($approvedComments));
|
||||
|
||||
$spamGrid = new CommentsGridField(
|
||||
$spamComments = Comment::get()
|
||||
->filter('Moderated', 1)
|
||||
->filter('IsSpam', 1);
|
||||
|
||||
$spamCommentsGrid = new CommentsGridField(
|
||||
'SpamComments',
|
||||
_t('CommentsAdmin.SpamComments', 'Spam'),
|
||||
$spamComments,
|
||||
$commentsConfig
|
||||
);
|
||||
|
||||
$newCount = '(' . count($newComments) . ')';
|
||||
$approvedCount = '(' . count($approvedComments) . ')';
|
||||
$spamCount = '(' . count($spamComments) . ')';
|
||||
$spamCommentsCountLabel = sprintf('(%s)', count($spamComments));
|
||||
|
||||
$fields = new FieldList(
|
||||
$root = new TabSet(
|
||||
$tabSet = new TabSet(
|
||||
'Root',
|
||||
new Tab('NewComments', _t('CommentAdmin.NewComments', 'New') . ' ' . $newCount,
|
||||
$newGrid
|
||||
new Tab(
|
||||
'NewComments',
|
||||
sprintf(
|
||||
'%s %s',
|
||||
_t('CommentAdmin.NewComments', 'New'),
|
||||
$newCommentsCountLabel
|
||||
),
|
||||
new Tab('ApprovedComments', _t('CommentAdmin.ApprovedComments', 'Approved') . ' ' . $approvedCount,
|
||||
$approvedGrid
|
||||
$newCommentsGrid
|
||||
),
|
||||
new Tab('SpamComments', _t('CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount,
|
||||
$spamGrid
|
||||
)
|
||||
new Tab(
|
||||
'ApprovedComments',
|
||||
sprintf(
|
||||
'%s %s',
|
||||
_t('CommentAdmin.ApprovedComments', 'Approved'),
|
||||
$approvedCommentsCountLabel
|
||||
),
|
||||
$approvedCommentsGrid
|
||||
),
|
||||
new Tab(
|
||||
'SpamComments',
|
||||
sprintf(
|
||||
'%s %s',
|
||||
_t('CommentAdmin.SpamComments', 'Spam'),
|
||||
$spamCommentsCountLabel
|
||||
),
|
||||
$spamCommentsGrid
|
||||
)
|
||||
);
|
||||
|
||||
$root->setTemplate('CMSTabSet');
|
||||
|
||||
$actions = new FieldList();
|
||||
$tabSet->setTemplate('CMSTabSet');
|
||||
|
||||
$form = new Form(
|
||||
$this,
|
||||
'EditForm',
|
||||
$fields,
|
||||
$actions
|
||||
new FieldList($tabSet),
|
||||
new FieldList()
|
||||
);
|
||||
|
||||
$form->addExtraClass('cms-edit-form');
|
||||
|
@ -3,6 +3,8 @@
|
||||
class CommentsGridField extends GridField {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param Comment $record
|
||||
*/
|
||||
protected function newRow($total, $index, $record, $attributes, $content) {
|
||||
if(!isset($attributes['class'])) {
|
||||
|
@ -14,7 +14,9 @@ class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_Act
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getColumnAttributes($gridField, $record, $columnName) {
|
||||
return array('class' => 'col-buttons');
|
||||
return array(
|
||||
'class' => 'col-buttons',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -22,24 +24,34 @@ class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_Act
|
||||
*/
|
||||
public function getColumnMetadata($gridField, $columnName) {
|
||||
if($columnName == 'Actions') {
|
||||
return array('title' => '');
|
||||
return array(
|
||||
'title' => '',
|
||||
);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getColumnsHandled($gridField) {
|
||||
return array('Actions');
|
||||
return array(
|
||||
'Actions',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param Comment $record
|
||||
*/
|
||||
public function getColumnContent($gridField, $record, $columnName) {
|
||||
if(!$record->canEdit()) return;
|
||||
if(!$record->canEdit()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$field = "";
|
||||
$field = '';
|
||||
|
||||
if(!$record->IsSpam || !$record->Moderated) {
|
||||
$field .= GridField_FormAction::create(
|
||||
@ -47,7 +59,9 @@ class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_Act
|
||||
'CustomAction' . $record->ID,
|
||||
'Spam',
|
||||
'spam',
|
||||
array('RecordID' => $record->ID)
|
||||
array(
|
||||
'RecordID' => $record->ID,
|
||||
)
|
||||
)->Field();
|
||||
}
|
||||
|
||||
@ -57,7 +71,9 @@ class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_Act
|
||||
'CustomAction' . $record->ID,
|
||||
'Approve',
|
||||
'approve',
|
||||
array('RecordID' => $record->ID)
|
||||
array(
|
||||
'RecordID' => $record->ID,
|
||||
)
|
||||
)->Field();
|
||||
}
|
||||
|
||||
@ -68,7 +84,10 @@ class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_Act
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getActions($gridField) {
|
||||
return array('spam', 'approve');
|
||||
return array(
|
||||
'spam',
|
||||
'approve',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,22 +95,34 @@ class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_Act
|
||||
*/
|
||||
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
|
||||
if($actionName == 'spam') {
|
||||
$comment = Comment::get()->byID($arguments["RecordID"]);
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = Comment::get()
|
||||
->byID($arguments["RecordID"]);
|
||||
|
||||
$comment->markSpam();
|
||||
|
||||
// output a success message to the user
|
||||
Controller::curr()->getResponse()->setStatusCode(
|
||||
Controller::curr()
|
||||
->getResponse()
|
||||
->setStatusCode(
|
||||
200,
|
||||
'Comment marked as spam.'
|
||||
);
|
||||
}
|
||||
|
||||
if($actionName == 'approve') {
|
||||
$comment = Comment::get()->byID($arguments["RecordID"]);
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = Comment::get()
|
||||
->byID($arguments["RecordID"]);
|
||||
|
||||
$comment->markApproved();
|
||||
|
||||
// output a success message to the user
|
||||
Controller::curr()->getResponse()->setStatusCode(
|
||||
Controller::curr()
|
||||
->getResponse()
|
||||
->setStatusCode(
|
||||
200,
|
||||
'Comment approved.'
|
||||
);
|
||||
|
@ -8,54 +8,80 @@ class CommentsGridFieldBulkAction extends GridFieldBulkActionHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link GridFieldBulkActionHandler} for bulk marking comments as spam
|
||||
* A {@link GridFieldBulkActionHandler} for bulk marking comments as spam.
|
||||
*
|
||||
* @package comments
|
||||
*/
|
||||
class CommentsGridFieldBulkAction_Handlers extends CommentsGridFieldBulkAction {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'spam',
|
||||
'approve',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $url_handlers = array(
|
||||
'spam' => 'spam',
|
||||
'approve' => 'approve',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function spam(SS_HTTPRequest $request) {
|
||||
$ids = array();
|
||||
|
||||
foreach($this->getRecords() as $record) {
|
||||
array_push($ids, $record->ID);
|
||||
/**
|
||||
* @var Comment $record
|
||||
*/
|
||||
$record->markSpam();
|
||||
|
||||
array_push($ids, $record->ID);
|
||||
}
|
||||
|
||||
$response = new SS_HTTPResponse(Convert::raw2json(array(
|
||||
$response = new SS_HTTPResponse(
|
||||
Convert::raw2json(array(
|
||||
'done' => true,
|
||||
'records' => $ids
|
||||
)));
|
||||
'records' => $ids,
|
||||
))
|
||||
);
|
||||
|
||||
$response->addHeader('Content-Type', 'text/json');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function approve(SS_HTTPRequest $request) {
|
||||
$ids = array();
|
||||
|
||||
foreach($this->getRecords() as $record) {
|
||||
array_push($ids, $record->ID);
|
||||
/**
|
||||
* @var Comment $record
|
||||
*/
|
||||
$record->markApproved();
|
||||
|
||||
array_push($ids, $record->ID);
|
||||
}
|
||||
|
||||
$response = new SS_HTTPResponse(Convert::raw2json(array(
|
||||
$response = new SS_HTTPResponse(
|
||||
Convert::raw2json(array(
|
||||
'done' => true,
|
||||
'records' => $ids
|
||||
)));
|
||||
'records' => $ids,
|
||||
))
|
||||
);
|
||||
|
||||
$response->addHeader('Content-Type', 'text/json');
|
||||
|
||||
|
@ -1,15 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @method static CommentsGridFieldConfig create()
|
||||
*/
|
||||
class CommentsGridFieldConfig extends GridFieldConfig_RecordEditor {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($itemsPerPage = 25) {
|
||||
parent::__construct($itemsPerPage);
|
||||
|
||||
// $this->addComponent(new GridFieldExportButton());
|
||||
|
||||
$this->addComponent(new CommentsGridFieldAction());
|
||||
|
||||
// Format column
|
||||
/**
|
||||
* @var GridFieldDataColumns $columns
|
||||
*/
|
||||
$columns = $this->getComponentByType('GridFieldDataColumns');
|
||||
|
||||
$columns->setFieldFormatting(array(
|
||||
'ParentTitle' => function ($value, &$item) {
|
||||
return sprintf(
|
||||
@ -20,24 +27,27 @@ class CommentsGridFieldConfig extends GridFieldConfig_RecordEditor {
|
||||
}
|
||||
));
|
||||
|
||||
// Add bulk option
|
||||
$manager = new GridFieldBulkManager();
|
||||
|
||||
$manager->addBulkAction(
|
||||
'spam', 'Spam', 'CommentsGridFieldBulkAction_Handlers',
|
||||
'spam',
|
||||
'Spam',
|
||||
'CommentsGridFieldBulkAction_Handlers',
|
||||
array(
|
||||
'isAjax' => true,
|
||||
'icon' => 'cross',
|
||||
'isDestructive' => false
|
||||
'isDestructive' => false,
|
||||
)
|
||||
);
|
||||
|
||||
$manager->addBulkAction(
|
||||
'approve', 'Approve', 'CommentsGridFieldBulkAction_Handlers',
|
||||
'approve',
|
||||
'Approve',
|
||||
'CommentsGridFieldBulkAction_Handlers',
|
||||
array(
|
||||
'isAjax' => true,
|
||||
'icon' => 'cross',
|
||||
'isDestructive' => false
|
||||
'isDestructive' => false,
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -3,9 +3,10 @@
|
||||
/**
|
||||
* @package comments
|
||||
*/
|
||||
|
||||
class CommentingController extends Controller {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'delete',
|
||||
'spam',
|
||||
@ -15,55 +16,59 @@ class CommentingController extends Controller {
|
||||
'CommentsForm',
|
||||
'reply',
|
||||
'doPostComment',
|
||||
'doPreviewComment'
|
||||
'doPreviewComment',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $url_handlers = array(
|
||||
'reply/$ParentCommentID//$ID/$OtherID' => 'reply',
|
||||
);
|
||||
|
||||
/**
|
||||
* Fields required for this form
|
||||
* Fields required for this form.
|
||||
*
|
||||
* @config
|
||||
*
|
||||
* @var array
|
||||
* @config
|
||||
*/
|
||||
private static $required_fields = array(
|
||||
'Name',
|
||||
'Email',
|
||||
'Comment'
|
||||
'Comment',
|
||||
);
|
||||
|
||||
/**
|
||||
* Base class this commenting form is for
|
||||
* Base class this commenting form is for.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $baseClass = "";
|
||||
private $baseClass = '';
|
||||
|
||||
/**
|
||||
* The record this commenting form is for
|
||||
* The record this commenting form is for.
|
||||
*
|
||||
* @var DataObject
|
||||
* @var null|DataObject
|
||||
*/
|
||||
private $ownerRecord = null;
|
||||
|
||||
/**
|
||||
* Parent controller record
|
||||
* Parent controller record.
|
||||
*
|
||||
* @var Controller
|
||||
* @var null|Controller
|
||||
*/
|
||||
private $ownerController = null;
|
||||
|
||||
/**
|
||||
* Backup url to return to
|
||||
* Backup url to return to.
|
||||
*
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $fallbackReturnURL = null;
|
||||
|
||||
/**
|
||||
* Set the base class to use
|
||||
* Set the base class to use.
|
||||
*
|
||||
* @param string $class
|
||||
*/
|
||||
@ -72,7 +77,7 @@ class CommentingController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base class used
|
||||
* Get the base class used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -81,7 +86,7 @@ class CommentingController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the record this controller is working on
|
||||
* Set the record this controller is working on.
|
||||
*
|
||||
* @param DataObject $record
|
||||
*/
|
||||
@ -90,16 +95,16 @@ class CommentingController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the record
|
||||
* Get the record.
|
||||
*
|
||||
* @return DataObject
|
||||
* @return null|DataObject
|
||||
*/
|
||||
public function getOwnerRecord() {
|
||||
return $this->ownerRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent controller
|
||||
* Set the parent controller.
|
||||
*
|
||||
* @param Controller $controller
|
||||
*/
|
||||
@ -108,46 +113,42 @@ class CommentingController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent controller
|
||||
* Get the parent controller.
|
||||
*
|
||||
* @return Controller
|
||||
* @return null|Controller
|
||||
*/
|
||||
public function getOwnerController() {
|
||||
return $this->ownerController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the commenting option for the current state
|
||||
* Get the commenting option for the current state.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed Result if the setting is available, or null otherwise
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
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
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function Link($action = '', $id = '', $other = '') {
|
||||
return Controller::join_links(Director::baseURL(), __CLASS__, $action, $id, $other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the RSS feed of comments
|
||||
* Outputs the RSS feed of comments.
|
||||
*
|
||||
* @return HTMLText
|
||||
*/
|
||||
@ -156,12 +157,11 @@ class CommentingController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an RSSFeed of comments for a given set of comments or all
|
||||
* comments on the website.
|
||||
* Return an RSSFeed of comments for a given set of comments or all comments on the website.
|
||||
*
|
||||
* To maintain backwards compatibility with 2.4 this supports mapping
|
||||
* of PageComment/rss?pageid= as well as the new RSS format for comments
|
||||
* of CommentingController/rss/{classname}/{id}
|
||||
* To maintain backwards compatibility with 2.4 this supports mapping of
|
||||
* PageComment/rss?pageid= as well as the new RSS format for comments of
|
||||
* CommentingController/rss/{classname}/{id}
|
||||
*
|
||||
* @param SS_HTTPRequest
|
||||
*
|
||||
@ -172,26 +172,26 @@ 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(
|
||||
$comments = Comment::get()
|
||||
->filter(array(
|
||||
'Moderated' => 1,
|
||||
'IsSpam' => 0,
|
||||
));
|
||||
|
||||
// Check if class filter
|
||||
if($class) {
|
||||
if(!is_subclass_of($class, 'DataObject') || !$class::has_extension('CommentsExtension')) {
|
||||
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);
|
||||
@ -199,17 +199,22 @@ class CommentingController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
$title = _t('CommentingController.RSSTITLE', "Comments RSS Feed");
|
||||
$title = _t('CommentingController.RSSTITLE', 'Comments RSS Feed');
|
||||
|
||||
$comments = new PaginatedList($comments, $request);
|
||||
$comments->setPageLength($this->getOption('comments_per_page'));
|
||||
|
||||
$comments->setPageLength(
|
||||
$this->getOption('comments_per_page')
|
||||
);
|
||||
|
||||
return new RSSFeed(
|
||||
$comments,
|
||||
$link,
|
||||
$title,
|
||||
$link,
|
||||
'Title', 'EscapedComment', 'AuthorName'
|
||||
'Title',
|
||||
'EscapedComment',
|
||||
'AuthorName'
|
||||
);
|
||||
}
|
||||
|
||||
@ -218,35 +223,53 @@ class CommentingController extends Controller {
|
||||
*/
|
||||
public function delete() {
|
||||
$comment = $this->getComment();
|
||||
if(!$comment) return $this->httpError(404);
|
||||
|
||||
if(!$comment) {
|
||||
$this->httpError(404);
|
||||
}
|
||||
|
||||
if(!$comment->canDelete()) {
|
||||
return Security::permissionFailure($this, 'You do not have permission to delete this comment');
|
||||
}
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400);
|
||||
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) {
|
||||
$this->httpError(400);
|
||||
}
|
||||
|
||||
$comment->delete();
|
||||
|
||||
return $this->request->isAjax()
|
||||
? true
|
||||
: $this->redirectBack();
|
||||
if($this->request->isAjax()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a given {@link Comment} as spam. Removes the comment from display
|
||||
* Marks a given {@link Comment} as spam. Removes the comment from display.
|
||||
*/
|
||||
public function spam() {
|
||||
$comment = $this->getComment();
|
||||
if(!$comment) return $this->httpError(404);
|
||||
|
||||
if(!$comment) {
|
||||
$this->httpError(404);
|
||||
}
|
||||
|
||||
if(!$comment->canEdit()) {
|
||||
return Security::permissionFailure($this, 'You do not have permission to edit this comment');
|
||||
}
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400);
|
||||
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) {
|
||||
$this->httpError(400);
|
||||
}
|
||||
|
||||
$comment->markSpam();
|
||||
|
||||
return $this->request->isAjax()
|
||||
? $comment->renderWith('CommentsInterface_singlecomment')
|
||||
: $this->redirectBack();
|
||||
if($this->request->isAjax()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,17 +277,26 @@ class CommentingController extends Controller {
|
||||
*/
|
||||
public function ham() {
|
||||
$comment = $this->getComment();
|
||||
if(!$comment) return $this->httpError(404);
|
||||
|
||||
if(!$comment) {
|
||||
$this->httpError(404);
|
||||
}
|
||||
|
||||
if(!$comment->canEdit()) {
|
||||
return Security::permissionFailure($this, 'You do not have permission to edit this comment');
|
||||
}
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400);
|
||||
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) {
|
||||
$this->httpError(400);
|
||||
}
|
||||
|
||||
$comment->markApproved();
|
||||
|
||||
return $this->request->isAjax()
|
||||
? $comment->renderWith('CommentsInterface_singlecomment')
|
||||
: $this->redirectBack();
|
||||
if($this->request->isAjax()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,33 +304,42 @@ class CommentingController extends Controller {
|
||||
*/
|
||||
public function approve() {
|
||||
$comment = $this->getComment();
|
||||
if(!$comment) return $this->httpError(404);
|
||||
|
||||
if(!$comment) {
|
||||
$this->httpError(404);
|
||||
}
|
||||
|
||||
if(!$comment->canEdit()) {
|
||||
return Security::permissionFailure($this, 'You do not have permission to approve this comment');
|
||||
}
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400);
|
||||
|
||||
if(!$comment->getSecurityToken()->checkRequest($this->request)) {
|
||||
$this->httpError(400);
|
||||
}
|
||||
|
||||
$comment->markApproved();
|
||||
|
||||
return $this->request->isAjax()
|
||||
? $comment->renderWith('CommentsInterface_singlecomment')
|
||||
: $this->redirectBack();
|
||||
if($this->request->isAjax()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment referenced in the URL (by ID). Permission checking
|
||||
* should be done in the callee.
|
||||
* Returns the comment referenced in the URL. Permission checking should be done in the callee.
|
||||
*
|
||||
* @return Comment|false
|
||||
* @return bool|Comment
|
||||
*/
|
||||
public function getComment() {
|
||||
$id = isset($this->urlParams['ID']) ? $this->urlParams['ID'] : false;
|
||||
if(isset($this->urlParams['ID'])) {
|
||||
$id = $this->urlParams['ID'];
|
||||
|
||||
if($id) {
|
||||
$comment = DataObject::get_by_id('Comment', $id);
|
||||
|
||||
if($comment) {
|
||||
$this->fallbackReturnURL = $comment->Link();
|
||||
|
||||
return $comment;
|
||||
}
|
||||
}
|
||||
@ -307,140 +348,121 @@ class CommentingController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reply form for a specified comment
|
||||
* Create a reply form for a specified comment.
|
||||
*
|
||||
* @param Comment $comment
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function ReplyForm($comment) {
|
||||
// Enables multiple forms with different names to use the same handler
|
||||
$form = $this->CommentsForm();
|
||||
$form->setName('ReplyForm_' . $comment->ID);
|
||||
$form->addExtraClass('reply-form');
|
||||
|
||||
// Load parent into reply form
|
||||
$form->loadDataFrom(array(
|
||||
'ParentCommentID' => $comment->ID
|
||||
'ParentCommentID' => $comment->ID,
|
||||
));
|
||||
|
||||
// Customise action
|
||||
$form->setFormAction($this->Link('reply', $comment->ID));
|
||||
$form->setFormAction(
|
||||
$this->Link('reply', $comment->ID)
|
||||
);
|
||||
|
||||
$this->extend('updateReplyForm', $form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request handler for reply form.
|
||||
* This method will disambiguate multiple reply forms in the same method
|
||||
*
|
||||
* This method will disambiguate multiple reply forms in the same method.
|
||||
*
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return null|Form
|
||||
*/
|
||||
public function reply(SS_HTTPRequest $request) {
|
||||
// Extract parent comment from reply and build this way
|
||||
if($parentID = $request->param('ParentCommentID')) {
|
||||
/**
|
||||
* @var null|Comment $comment
|
||||
*/
|
||||
$comment = DataObject::get_by_id('Comment', $parentID, true);
|
||||
|
||||
if($comment) {
|
||||
return $this->ReplyForm($comment);
|
||||
}
|
||||
}
|
||||
return $this->httpError(404);
|
||||
|
||||
$this->httpError(404);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post a comment form
|
||||
* Post a comment form.
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function CommentsForm() {
|
||||
$usePreview = $this->getOption('use_preview');
|
||||
|
||||
$nameRequired = _t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name');
|
||||
$emailRequired = _t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address');
|
||||
$emailInvalid = _t('CommentInterface.EMAILADDRESS_MESSAGE_EMAIL', 'Please enter a valid email address');
|
||||
$urlInvalid = _t('CommentInterface.COMMENT_MESSAGE_URL', 'Please enter a valid URL');
|
||||
$commentRequired = _t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment');
|
||||
|
||||
$fields = new FieldList(
|
||||
$dataFields = new CompositeField(
|
||||
// Name
|
||||
TextField::create("Name", _t('CommentInterface.YOURNAME', 'Your name'))
|
||||
->setCustomValidationMessage($nameRequired)
|
||||
->setAttribute('data-msg-required', $nameRequired),
|
||||
|
||||
// Email
|
||||
EmailField::create(
|
||||
"Email",
|
||||
_t('CommentingController.EMAILADDRESS', "Your email address (will not be published)")
|
||||
)
|
||||
->setCustomValidationMessage($emailRequired)
|
||||
->setAttribute('data-msg-required', $emailRequired)
|
||||
->setAttribute('data-msg-email', $emailInvalid)
|
||||
->setAttribute('data-rule-email', true),
|
||||
|
||||
// Url
|
||||
TextField::create("URL", _t('CommentingController.WEBSITEURL', "Your website URL"))
|
||||
->setAttribute('data-msg-url', $urlInvalid)
|
||||
->setAttribute('data-rule-url', true),
|
||||
|
||||
// Comment
|
||||
TextareaField::create("Comment", _t('CommentingController.COMMENTS', "Comments"))
|
||||
->setCustomValidationMessage($commentRequired)
|
||||
->setAttribute('data-msg-required', $commentRequired)
|
||||
$this->getNameField(),
|
||||
$this->getEmailField(),
|
||||
$this->getURLField(),
|
||||
$this->getCommentField()
|
||||
),
|
||||
HiddenField::create("ParentID"),
|
||||
HiddenField::create("ReturnURL"),
|
||||
HiddenField::create("ParentCommentID"),
|
||||
HiddenField::create("BaseClass")
|
||||
HiddenField::create('ParentID'),
|
||||
HiddenField::create('ReturnURL'),
|
||||
HiddenField::create('ParentCommentID'),
|
||||
HiddenField::create('BaseClass')
|
||||
);
|
||||
|
||||
// Preview formatted comment. Makes most sense when shortcodes or
|
||||
// limited HTML is allowed. Populated by JS/Ajax.
|
||||
if($usePreview) {
|
||||
$fields->insertAfter(
|
||||
ReadonlyField::create('PreviewComment', _t('CommentInterface.PREVIEWLABEL', 'Preview'))
|
||||
->setAttribute('style', 'display: none'), // enable through JS
|
||||
$this->getPreviewCommentField(),
|
||||
'Comment'
|
||||
);
|
||||
}
|
||||
|
||||
$dataFields->addExtraClass('data-fields');
|
||||
|
||||
// save actions
|
||||
$actions = new FieldList(
|
||||
new FormAction("doPostComment", _t('CommentInterface.POST', 'Post'))
|
||||
new FormAction(
|
||||
'doPostComment',
|
||||
_t('CommentInterface.POST', 'Post')
|
||||
)
|
||||
);
|
||||
|
||||
if($usePreview) {
|
||||
$actions->push(
|
||||
FormAction::create('doPreviewComment', _t('CommentInterface.PREVIEW', 'Preview'))
|
||||
->addExtraClass('action-minor')
|
||||
->setAttribute('style', 'display: none') // enable through JS
|
||||
$this->getPreviewCommentAction()
|
||||
);
|
||||
}
|
||||
|
||||
// required fields for server side
|
||||
$required = new RequiredFields($this->config()->required_fields);
|
||||
|
||||
// create the comment form
|
||||
$form = new Form($this, 'CommentsForm', $fields, $actions, $required);
|
||||
$form = new Form(
|
||||
$this,
|
||||
'CommentsForm',
|
||||
$fields,
|
||||
$actions,
|
||||
$required
|
||||
);
|
||||
|
||||
// if the record exists load the extra required data
|
||||
if($record = $this->getOwnerRecord()) {
|
||||
|
||||
// Load member data
|
||||
$member = Member::currentUser();
|
||||
if(($record->CommentsRequireLogin || $record->PostingRequiredPermission) && $member) {
|
||||
$fields = $form->Fields();
|
||||
|
||||
$fields->removeByName('Name');
|
||||
$fields->removeByName('Email');
|
||||
$fields->insertBefore(new ReadonlyField("NameView", _t('CommentInterface.YOURNAME', 'Your name'), $member->getName()), 'URL');
|
||||
$fields->push(new HiddenField("Name", "", $member->getName()));
|
||||
$fields->push(new HiddenField("Email", "", $member->Email));
|
||||
$fields->insertBefore(new ReadonlyField('NameView', _t('CommentInterface.YOURNAME', 'Your name'), $member->getName()), 'URL');
|
||||
$fields->push(new HiddenField('Name', '', $member->getName()));
|
||||
$fields->push(new HiddenField('Email', '', $member->Email));
|
||||
}
|
||||
|
||||
// we do not want to read a new URL when the form has already been submitted
|
||||
// which in here, it hasn't been.
|
||||
$form->loadDataFrom(array(
|
||||
'ParentID' => $record->ID,
|
||||
'ReturnURL' => $this->request->getURL(),
|
||||
@ -448,22 +470,25 @@ class CommentingController extends Controller {
|
||||
));
|
||||
}
|
||||
|
||||
// Set it so the user gets redirected back down to the form upon form fail
|
||||
$form->setRedirectToFormOnValidationError(true);
|
||||
|
||||
// load any data from the cookies
|
||||
if($data = Cookie::get('CommentsForm_UserData')) {
|
||||
$data = Convert::json2array($data);
|
||||
|
||||
$form->loadDataFrom(array(
|
||||
"Name" => isset($data['Name']) ? $data['Name'] : '',
|
||||
"URL" => isset($data['URL']) ? $data['URL'] : '',
|
||||
"Email" => isset($data['Email']) ? $data['Email'] : ''
|
||||
));
|
||||
// allow previous value to fill if comment not stored in cookie (i.e. validation error)
|
||||
$data += array(
|
||||
'Name' => '',
|
||||
'URL' => '',
|
||||
'Email' => '',
|
||||
);
|
||||
|
||||
$form->loadDataFrom($data);
|
||||
|
||||
$prevComment = Cookie::get('CommentsForm_Comment');
|
||||
|
||||
if($prevComment && $prevComment != '') {
|
||||
$form->loadDataFrom(array("Comment" => $prevComment));
|
||||
$form->loadDataFrom(array(
|
||||
'Comment' => $prevComment,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -471,7 +496,6 @@ class CommentingController extends Controller {
|
||||
$form->loadDataFrom($member);
|
||||
}
|
||||
|
||||
// hook to allow further extensions to alter the comments form
|
||||
$this->extend('alterCommentForm', $form);
|
||||
|
||||
return $form;
|
||||
@ -482,41 +506,43 @@ class CommentingController extends Controller {
|
||||
*
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
*
|
||||
* @return bool|SS_HTTPResponse
|
||||
*/
|
||||
public function doPostComment($data, $form) {
|
||||
// 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));
|
||||
Cookie::set("CommentsForm_Comment", $data['Comment']);
|
||||
if(!$this->getOwnerRecord()) {
|
||||
return $this->httpError(404);
|
||||
}
|
||||
|
||||
Cookie::set('CommentsForm_UserData', Convert::raw2json($data));
|
||||
Cookie::set('CommentsForm_Comment', $data['Comment']);
|
||||
|
||||
// extend hook to allow extensions. Also see onAfterPostComment
|
||||
$this->extend('onBeforePostComment', $form);
|
||||
|
||||
// If commenting can only be done by logged in users, make sure the user is logged in
|
||||
if(!$this->getOwnerRecord()->canPostComment()) {
|
||||
return Security::permissionFailure(
|
||||
$this,
|
||||
_t(
|
||||
'CommentingController.PERMISSIONFAILURE',
|
||||
"You're not able to post comments to this page. Please ensure you are logged in and have an "
|
||||
. "appropriate permission level."
|
||||
'You\'re not able to post comments to this page. Please ensure you are logged in and have an appropriate permission level.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if($member = Member::currentUser()) {
|
||||
$form->Fields()->push(new HiddenField("AuthorID", "Author ID", $member->ID));
|
||||
$form->Fields()->push(
|
||||
new HiddenField('AuthorID', 'Author ID', $member->ID)
|
||||
);
|
||||
}
|
||||
|
||||
// What kind of moderation is required?
|
||||
switch($this->getOwnerRecord()->ModerationRequired) {
|
||||
case 'Required':
|
||||
$requireModeration = true;
|
||||
@ -524,7 +550,6 @@ class CommentingController extends Controller {
|
||||
case 'NonMembersOnly':
|
||||
$requireModeration = empty($member);
|
||||
break;
|
||||
case 'None':
|
||||
default:
|
||||
$requireModeration = false;
|
||||
break;
|
||||
@ -536,27 +561,20 @@ class CommentingController extends Controller {
|
||||
$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) {
|
||||
if($this->getOption('use_preview') && !empty($data['IsPreview'])) {
|
||||
$comment->extend('onBeforeWrite');
|
||||
} else {
|
||||
$comment->write();
|
||||
|
||||
// extend hook to allow extensions. Also see onBeforePostComment
|
||||
$this->extend('onAfterPostComment', $comment);
|
||||
}
|
||||
|
||||
// we want to show a notification if comments are moderated
|
||||
if($requireModeration && !$comment->IsSpam) {
|
||||
Session::set('CommentsModerated', 1);
|
||||
}
|
||||
|
||||
// clear the users comment since it passed validation
|
||||
Cookie::set('CommentsForm_Comment', false);
|
||||
|
||||
// Find parent link
|
||||
if(!empty($data['ReturnURL'])) {
|
||||
$url = $data['ReturnURL'];
|
||||
} elseif($parent = $comment->getParent()) {
|
||||
@ -565,37 +583,44 @@ class CommentingController extends Controller {
|
||||
return $this->redirectBack();
|
||||
}
|
||||
|
||||
// Given a redirect page exists, attempt to link to the correct anchor
|
||||
if(!$comment->Moderated) {
|
||||
// Display the "awaiting moderation" text
|
||||
$holder = $this->getOption('comments_holder_id');
|
||||
$hash = "{$holder}_PostCommentForm_error";
|
||||
$hash = sprintf(
|
||||
'%s_PostCommentForm_error',
|
||||
$this->getOption('comments_holder_id')
|
||||
);
|
||||
} elseif($comment->IsSpam) {
|
||||
// Link to the form with the error message contained
|
||||
$hash = $form->FormName();
|
||||
} else {
|
||||
// Link to the moderated, non-spam comment
|
||||
$hash = $comment->Permalink();
|
||||
}
|
||||
|
||||
return $this->redirect(Controller::join_links($url, "#{$hash}"));
|
||||
return $this->redirect(Controller::join_links($url, '#' . $hash));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
*
|
||||
* @return bool|SS_HTTPResponse
|
||||
*/
|
||||
public function doPreviewComment($data, $form) {
|
||||
$data['IsPreview'] = 1;
|
||||
|
||||
return $this->doPostComment($data, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* In edge-cases, this will be called outside of a handleRequest() context; in that case,
|
||||
* redirect to the homepage. Don't break into the global state at this stage because we'll
|
||||
* be calling from a test context or something else where the global state is inappropriate.
|
||||
*
|
||||
* @return bool|SS_HTTPResponse
|
||||
*/
|
||||
public function redirectBack() {
|
||||
// Don't cache the redirect back ever
|
||||
HTTP::set_cache_age(0);
|
||||
|
||||
$url = null;
|
||||
|
||||
// In edge-cases, this will be called outside of a handleRequest() context; in that case,
|
||||
// redirect to the homepage - don't break into the global state at this stage because we'll
|
||||
// be calling from a test context or something else where the global state is inappropraite
|
||||
if($this->request) {
|
||||
if($this->request->requestVar('BackURL')) {
|
||||
$url = $this->request->requestVar('BackURL');
|
||||
@ -606,15 +631,150 @@ class CommentingController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
if(!$url) $url = $this->fallbackReturnURL;
|
||||
if(!$url) $url = Director::baseURL();
|
||||
if(!$url) {
|
||||
$url = $this->fallbackReturnURL;
|
||||
}
|
||||
|
||||
if(!$url) {
|
||||
$url = Director::baseURL();
|
||||
}
|
||||
|
||||
// absolute redirection URLs not located on this site may cause phishing
|
||||
if(Director::is_site_url($url)) {
|
||||
return $this->redirect($url);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TextField
|
||||
*/
|
||||
protected function getNameField() {
|
||||
$nameFieldLabel = _t(
|
||||
'CommentInterface.YOURNAME',
|
||||
'Your name'
|
||||
);
|
||||
|
||||
$nameRequiredLabel = _t(
|
||||
'CommentInterface.YOURNAME_MESSAGE_REQUIRED',
|
||||
'Please enter your name'
|
||||
);
|
||||
|
||||
$nameField = TextField::create('Name', $nameFieldLabel);
|
||||
|
||||
$nameField->setCustomValidationMessage($nameRequiredLabel);
|
||||
$nameField->setAttribute('data-msg-required', $nameRequiredLabel);
|
||||
|
||||
return $nameField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EmailField
|
||||
*/
|
||||
protected function getEmailField() {
|
||||
$emailFieldLabel = _t(
|
||||
'CommentingController.EMAILADDRESS',
|
||||
'Your email address (will not be published)'
|
||||
);
|
||||
|
||||
$emailFieldRequiredLabel = _t(
|
||||
'CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED',
|
||||
'Please enter your email address'
|
||||
);
|
||||
|
||||
$emailFieldInvalidLabel = _t(
|
||||
'CommentInterface.EMAILADDRESS_MESSAGE_EMAIL',
|
||||
'Please enter a valid email address'
|
||||
);
|
||||
|
||||
$emailField = EmailField::create('Email', $emailFieldLabel);
|
||||
|
||||
$emailField->setCustomValidationMessage($emailFieldRequiredLabel);
|
||||
$emailField->setAttribute('data-msg-required', $emailFieldRequiredLabel);
|
||||
$emailField->setAttribute('data-msg-email', $emailFieldInvalidLabel);
|
||||
$emailField->setAttribute('data-rule-email', true);
|
||||
|
||||
return $emailField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TextField
|
||||
*/
|
||||
protected function getURLField() {
|
||||
$urlFieldLabel = _t(
|
||||
'CommentingController.WEBSITEURL',
|
||||
'Your website URL'
|
||||
);
|
||||
|
||||
$urlInvalidLabel = _t(
|
||||
'CommentInterface.COMMENT_MESSAGE_URL',
|
||||
'Please enter a valid URL'
|
||||
);
|
||||
|
||||
$urlField = TextField::create('URL', $urlFieldLabel);
|
||||
|
||||
$urlField->setAttribute('data-msg-url', $urlInvalidLabel);
|
||||
$urlField->setAttribute('data-rule-url', true);
|
||||
|
||||
return $urlField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TextareaField
|
||||
*/
|
||||
protected function getCommentField() {
|
||||
$commentFieldLabel = _t(
|
||||
'CommentingController.COMMENTS',
|
||||
'Comments'
|
||||
);
|
||||
|
||||
$commentRequiredLabel = _t(
|
||||
'CommentInterface.COMMENT_MESSAGE_REQUIRED',
|
||||
'Please enter your comment'
|
||||
);
|
||||
|
||||
$commentField = TextareaField::create('Comment', $commentFieldLabel);
|
||||
|
||||
$commentField->setCustomValidationMessage($commentRequiredLabel);
|
||||
$commentField->setAttribute('data-msg-required', $commentRequiredLabel);
|
||||
|
||||
return $commentField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReadonlyField
|
||||
*/
|
||||
protected function getPreviewCommentField() {
|
||||
$previewCommentFieldLabel = _t(
|
||||
'CommentInterface.PREVIEWLABEL',
|
||||
'Preview'
|
||||
);
|
||||
|
||||
$previewCommentField = ReadonlyField::create(
|
||||
'PreviewComment',
|
||||
$previewCommentFieldLabel
|
||||
);
|
||||
|
||||
$previewCommentField->setAttribute('style', 'display: none');
|
||||
|
||||
return $previewCommentField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FormAction
|
||||
*/
|
||||
protected function getPreviewCommentAction() {
|
||||
$previewCommentActionLabel = _t(
|
||||
'CommentInterface.PREVIEW',
|
||||
'Preview'
|
||||
);
|
||||
|
||||
$previewCommentAction = FormAction::create(
|
||||
'doPreviewComment',
|
||||
$previewCommentActionLabel
|
||||
);
|
||||
|
||||
$previewCommentAction->addExtraClass('action-minor');
|
||||
$previewCommentAction->setAttribute('style', 'display: none');
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,10 @@
|
||||
/**
|
||||
* Extension to {@link DataObject} to enable tracking comments.
|
||||
*
|
||||
* @property bool $ProvideComments
|
||||
* @property string $ModerationRequired
|
||||
* @property bool $CommentsRequireLogin
|
||||
*
|
||||
* @package comments
|
||||
*/
|
||||
class CommentsExtension extends DataExtension {
|
||||
@ -32,9 +36,9 @@ class CommentsExtension extends DataExtension {
|
||||
* nested_comments: Enable nested comments
|
||||
* nested_depth: Max depth of nested comments in levels (where root is 1 depth) 0 means no limit.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @config
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $comments = array(
|
||||
'enabled' => true,
|
||||
@ -78,41 +82,50 @@ class CommentsExtension extends DataExtension {
|
||||
* CMS configurable options should default to the config values
|
||||
*/
|
||||
public function populateDefaults() {
|
||||
// Set if comments should be enabled by default
|
||||
$this->owner->ProvideComments = $this->owner->getCommentsOption('enabled') ? 1 : 0;
|
||||
$this->owner->ProvideComments = 0;
|
||||
|
||||
if($this->owner->getCommentsOption('enabled')) {
|
||||
$this->owner->ProvideComments = 1;
|
||||
}
|
||||
|
||||
$this->owner->ModerationRequired = 'None';
|
||||
|
||||
// If moderation options should be configurable via the CMS then
|
||||
if($this->owner->getCommentsOption('require_moderation')) {
|
||||
$this->owner->ModerationRequired = 'Required';
|
||||
} elseif($this->owner->getCommentsOption('require_moderation_nonmembers')) {
|
||||
}
|
||||
|
||||
if($this->owner->getCommentsOption('require_moderation_nonmembers')) {
|
||||
$this->owner->ModerationRequired = 'NonMembersOnly';
|
||||
} else {
|
||||
$this->owner->ModerationRequired = 'None';
|
||||
}
|
||||
|
||||
$this->owner->CommentsRequireLogin = $this->owner->getCommentsOption('require_login') ? 1 : 0;
|
||||
}
|
||||
$this->owner->CommentsRequireLogin = 0;
|
||||
|
||||
if($this->owner->getCommentsOption('require_login')) {
|
||||
$this->owner->CommentsRequireLogin = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this extension is applied to a {@link SiteTree} record then
|
||||
* append a Provide Comments checkbox to allow authors to trigger
|
||||
* whether or not to display comments
|
||||
* If this extension is applied to a {@link SiteTree} record then append a Provide Comments
|
||||
* checkbox to allow authors to trigger whether or not to display comments.
|
||||
*
|
||||
* @todo Allow customization of other {@link Commenting} configuration
|
||||
*
|
||||
* @param FieldList $fields
|
||||
*/
|
||||
public function updateSettingsFields(FieldList $fields) {
|
||||
$options = FieldGroup::create();
|
||||
$options->setTitle(_t('CommentsExtension.COMMENTOPTIONS', 'Comments'));
|
||||
|
||||
$options = FieldGroup::create()->setTitle(_t('CommentsExtension.COMMENTOPTIONS', 'Comments'));
|
||||
|
||||
// Check if enabled setting should be cms configurable
|
||||
if($this->owner->getCommentsOption('enabled_cms')) {
|
||||
$options->push(new CheckboxField('ProvideComments', _t('Comment.ALLOWCOMMENTS', 'Allow Comments')));
|
||||
$options->push(
|
||||
new CheckboxField(
|
||||
'ProvideComments',
|
||||
_t('Comment.ALLOWCOMMENTS', 'Allow Comments')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if we should require users to login to comment
|
||||
if($this->owner->getCommentsOption('require_login_cms')) {
|
||||
$options->push(
|
||||
new CheckboxField(
|
||||
@ -130,16 +143,26 @@ class CommentsExtension extends DataExtension {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if moderation should be enabled via cms configurable
|
||||
if($this->owner->getCommentsOption('require_moderation_cms')) {
|
||||
$moderationField = new DropdownField('ModerationRequired', 'Comment Moderation', array(
|
||||
'None' => _t('CommentsExtension.MODERATIONREQUIRED_NONE', 'No moderation required'),
|
||||
'Required' => _t('CommentsExtension.MODERATIONREQUIRED_REQUIRED', 'Moderate all comments'),
|
||||
$moderationField = new DropdownField(
|
||||
'ModerationRequired',
|
||||
'Comment Moderation',
|
||||
array(
|
||||
'None' => _t(
|
||||
'CommentsExtension.MODERATIONREQUIRED_NONE',
|
||||
'No moderation required'
|
||||
),
|
||||
'Required' => _t(
|
||||
'CommentsExtension.MODERATIONREQUIRED_REQUIRED',
|
||||
'Moderate all comments'
|
||||
),
|
||||
'NonMembersOnly' => _t(
|
||||
'CommentsExtension.MODERATIONREQUIRED_NONMEMBERSONLY',
|
||||
'Only moderate non-members'
|
||||
),
|
||||
));
|
||||
)
|
||||
);
|
||||
|
||||
if($fields->hasTabSet()) {
|
||||
$fields->addFieldsToTab('Root.Settings', $moderationField);
|
||||
} else {
|
||||
@ -149,7 +172,7 @@ class CommentsExtension extends DataExtension {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comment moderation rules for this parent
|
||||
* Get comment moderation rules for this parent.
|
||||
*
|
||||
* None: No moderation required
|
||||
* Required: All comments
|
||||
@ -160,26 +183,30 @@ class CommentsExtension extends DataExtension {
|
||||
public function getModerationRequired() {
|
||||
if($this->owner->getCommentsOption('require_moderation_cms')) {
|
||||
return $this->owner->getField('ModerationRequired');
|
||||
} elseif($this->owner->getCommentsOption('require_moderation')) {
|
||||
return 'Required';
|
||||
} elseif($this->owner->getCommentsOption('require_moderation_nonmembers')) {
|
||||
return 'NonMembersOnly';
|
||||
} else {
|
||||
return 'None';
|
||||
}
|
||||
|
||||
if($this->owner->getCommentsOption('require_moderation')) {
|
||||
return 'Required';
|
||||
}
|
||||
|
||||
if($this->owner->getCommentsOption('require_moderation_nonmembers')) {
|
||||
return 'NonMembersOnly';
|
||||
}
|
||||
|
||||
return 'None';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if users must be logged in to post comments
|
||||
* Determine if users must be logged in to post comments.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function getCommentsRequireLogin() {
|
||||
if($this->owner->getCommentsOption('require_login_cms')) {
|
||||
return (bool) $this->owner->getField('CommentsRequireLogin');
|
||||
} else {
|
||||
return (bool) $this->owner->getCommentsOption('require_login');
|
||||
}
|
||||
|
||||
return (bool) $this->owner->getCommentsOption('require_login');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,105 +217,109 @@ class CommentsExtension extends DataExtension {
|
||||
*/
|
||||
public function AllComments() {
|
||||
$order = $this->owner->getCommentsOption('order_comments_by');
|
||||
$comments = CommentList::create($this->ownerBaseClass)
|
||||
->forForeignID($this->owner->ID)
|
||||
->sort($order);
|
||||
|
||||
$comments = CommentList::create($this->ownerBaseClass);
|
||||
$comments->forForeignID($this->owner->ID);
|
||||
$comments->sort($order);
|
||||
|
||||
$this->owner->extend('updateAllComments', $comments);
|
||||
|
||||
return $comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all comments against this object, with with spam and unmoderated items excluded, for use in the frontend
|
||||
* Returns all comments against this object, with with spam and un-moderated items excluded.
|
||||
*
|
||||
* @return CommentList
|
||||
*/
|
||||
public function AllVisibleComments() {
|
||||
$list = $this->AllComments();
|
||||
|
||||
// Filter spam comments for non-administrators if configured
|
||||
$showSpam = $this->owner->getCommentsOption('frontend_spam') && $this->owner->canModerateComments();
|
||||
|
||||
if(!$showSpam) {
|
||||
$list = $list->filter('IsSpam', 0);
|
||||
}
|
||||
|
||||
// Filter un-moderated comments for non-administrators if moderation is enabled
|
||||
$showUnmoderated = ($this->owner->ModerationRequired === 'None')
|
||||
|| ($this->owner->getCommentsOption('frontend_moderation') && $this->owner->canModerateComments());
|
||||
if(!$showUnmoderated) {
|
||||
$showUnModerated = ($this->owner->ModerationRequired === 'None') || ($this->owner->getCommentsOption('frontend_moderation') && $this->owner->canModerateComments());
|
||||
|
||||
if(!$showUnModerated) {
|
||||
$list = $list->filter('Moderated', 1);
|
||||
}
|
||||
|
||||
$this->owner->extend('updateAllVisibleComments', $list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root level comments, with spam and unmoderated items excluded, for use in the frontend
|
||||
* Returns the root level comments, with spam and un-moderated items excluded.
|
||||
*
|
||||
* @return CommentList
|
||||
*/
|
||||
public function Comments() {
|
||||
$list = $this->AllVisibleComments();
|
||||
|
||||
// If nesting comments, only show root level
|
||||
if($this->owner->getCommentsOption('nested_comments')) {
|
||||
$list = $list->filter('ParentCommentID', 0);
|
||||
}
|
||||
|
||||
$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
|
||||
* Returns a paged list of the root level comments, with spam and un-moderated items excluded.
|
||||
*
|
||||
* @return PaginatedList
|
||||
*/
|
||||
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
|
||||
*
|
||||
* Do not include the comments on pages which don't have id's such as security pages.
|
||||
*
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
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'
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if comments are enabled for this instance
|
||||
* Determine if comments are enabled for this instance.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function getCommentsEnabled() {
|
||||
// Don't display comments form for pseudo-pages (such as the login form)
|
||||
if(!$this->owner->exists()) return false;
|
||||
if(!$this->owner->exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine which flag should be used to determine if this is enabled
|
||||
if($this->owner->getCommentsOption('enabled_cms')) {
|
||||
return $this->owner->ProvideComments;
|
||||
} else {
|
||||
return $this->owner->getCommentsOption('enabled');
|
||||
}
|
||||
|
||||
return $this->owner->getCommentsOption('enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML ID for the comment holder in the template
|
||||
* Get the HTML ID for the comment holder in the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -301,71 +332,86 @@ class CommentsExtension extends DataExtension {
|
||||
*/
|
||||
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)
|
||||
* Permission codes required in order to post (or empty if none required).
|
||||
*
|
||||
* @return string|array Permission or list of permissions, if required
|
||||
* @return string|array
|
||||
*/
|
||||
public function getPostingRequiredPermission() {
|
||||
return $this->owner->getCommentsOption('required_permission');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function canPost() {
|
||||
Deprecation::notice('2.0', 'Use canPostComment instead');
|
||||
|
||||
return $this->canPostComment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a user can post comments on this item
|
||||
* Determine if a user can post comments on this item.
|
||||
*
|
||||
* @param Member $member Member to check
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function canPostComment($member = null) {
|
||||
// Deny if not enabled for this object
|
||||
if(!$this->owner->CommentsEnabled) return false;
|
||||
if(!$this->owner->CommentsEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if member is required
|
||||
$requireLogin = $this->owner->CommentsRequireLogin;
|
||||
if(!$requireLogin) return true;
|
||||
|
||||
// Check member is logged in
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(!$member) return false;
|
||||
if(!$requireLogin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!$member) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
if(!$member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If member required check permissions
|
||||
$requiredPermission = $this->owner->PostingRequiredPermission;
|
||||
if($requiredPermission && !Permission::checkMember($member, $requiredPermission)) return false;
|
||||
|
||||
if($requiredPermission && !Permission::checkMember($member, $requiredPermission)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this member can moderate comments in the CMS
|
||||
* Determine if this member can moderate comments in the CMS.
|
||||
*
|
||||
* @param Member $member
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function canModerateComments($member = null) {
|
||||
// Deny if not enabled for this object
|
||||
if(!$this->owner->CommentsEnabled) return false;
|
||||
if(!$this->owner->CommentsEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fallback to can-edit
|
||||
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
|
||||
* Gets the RSS link to all comments.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -373,42 +419,44 @@ class CommentsExtension extends DataExtension {
|
||||
return Controller::join_links(Director::baseURL(), 'CommentingController/rss');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRssLinkPage() {
|
||||
Deprecation::notice('2.0', 'Use getCommentRSSLinkPage instead');
|
||||
|
||||
return $this->getCommentRSSLinkPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RSS link to all comments on this page
|
||||
* 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
|
||||
$this->getCommentRSSLink(),
|
||||
$this->ownerBaseClass,
|
||||
$this->owner->ID
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comments interface for the front end. Includes the CommentAddForm and the composition
|
||||
* of the comments display.
|
||||
* Comments interface for the front end. Includes the CommentAddForm and the composition of the
|
||||
* comments display.
|
||||
*
|
||||
* To customize the html see templates/CommentInterface.ss or extend this function with
|
||||
* your own extension.
|
||||
*
|
||||
* @todo Cleanup the passing of all this configuration based functionality
|
||||
*
|
||||
* @see docs/en/Extending
|
||||
* To customize the html see templates/CommentInterface.ss or extend this function with your
|
||||
* own extension.
|
||||
*/
|
||||
public function CommentsForm() {
|
||||
// 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-entwine/dist/jquery.entwine-dist.js');
|
||||
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/lib/jquery.form.js');
|
||||
Requirements::javascript(COMMENTS_THIRDPARTY . '/jquery-validate/jquery.validate.min.js');
|
||||
Requirements::javascript('comments/javascript/CommentsInterface.js');
|
||||
Requirements::javascript(COMMENTS_DIR . '/javascript/CommentsInterface.js');
|
||||
}
|
||||
|
||||
$controller = CommentingController::create();
|
||||
@ -417,12 +465,15 @@ class CommentsExtension extends DataExtension {
|
||||
$controller->setOwnerController(Controller::curr());
|
||||
|
||||
$moderatedSubmitted = Session::get('CommentsModerated');
|
||||
|
||||
Session::clear('CommentsModerated');
|
||||
|
||||
$form = ($enabled) ? $controller->CommentsForm() : false;
|
||||
$form = false;
|
||||
|
||||
if($enabled) {
|
||||
$form = $controller->CommentsForm();
|
||||
}
|
||||
|
||||
// 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 $this
|
||||
->owner
|
||||
->customise(array(
|
||||
@ -433,48 +484,58 @@ class CommentsExtension extends DataExtension {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this extension instance is attached to a {@link SiteTree} object
|
||||
* Returns whether this extension instance is attached to a {@link SiteTree} object.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function attachedToSiteTree() {
|
||||
$class = $this->ownerBaseClass;
|
||||
|
||||
return (is_subclass_of($class, 'SiteTree')) || ($class == 'SiteTree');
|
||||
return $class === 'SiteTree' || is_subclass_of($class, 'SiteTree');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 1.0 Please use {@link CommentsExtension->CommentsForm()}
|
||||
* Please use {@link CommentsExtension->CommentsForm()}.
|
||||
*
|
||||
* @deprecated 1.0
|
||||
*/
|
||||
public function PageComments() {
|
||||
// This method is very commonly used, don't throw a warning just yet
|
||||
Deprecation::notice('1.0', '$PageComments is deprecated. Please use $CommentsForm');
|
||||
|
||||
return $this->CommentsForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the commenting option for this object
|
||||
* Get the commenting option for this object.
|
||||
*
|
||||
* This can be overridden in any instance or extension to customise the option available
|
||||
* 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
|
||||
* @return mixed
|
||||
*/
|
||||
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];
|
||||
if($this->owner) {
|
||||
$settings = $this->owner->config()->comments;
|
||||
} else {
|
||||
$settings = Config::inst()->get(__CLASS__, 'comments');
|
||||
}
|
||||
|
||||
$value = null;
|
||||
|
||||
if(isset($settings[$key])) {
|
||||
$value = $settings[$key];
|
||||
}
|
||||
|
||||
if($this->owner) {
|
||||
$this->owner->extend('updateCommentsOption', $key, $value);
|
||||
}
|
||||
|
||||
// To allow other extensions to customise this option
|
||||
if($this->owner) $this->owner->extend('updateCommentsOption', $key, $value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add moderation functions to the current fieldlist
|
||||
* Add moderation functions to the current field list.
|
||||
*
|
||||
* @param FieldList $fields
|
||||
*/
|
||||
@ -483,65 +544,91 @@ class CommentsExtension extends DataExtension {
|
||||
|
||||
$commentsConfig = CommentsGridFieldConfig::create();
|
||||
|
||||
$newComments = $this->owner->AllComments()->filter('Moderated', 0);
|
||||
$newComments = Comment::get()
|
||||
->filter('Moderated', 0);
|
||||
|
||||
$newGrid = new CommentsGridField(
|
||||
$newCommentsGrid = new CommentsGridField(
|
||||
'NewComments',
|
||||
_t('CommentsAdmin.NewComments', 'New'),
|
||||
$newComments,
|
||||
$commentsConfig
|
||||
);
|
||||
|
||||
$approvedComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 0);
|
||||
$newCommentsCountLabel = sprintf('(%s)', count($newComments));
|
||||
|
||||
$approvedGrid = new CommentsGridField(
|
||||
$approvedComments = Comment::get()
|
||||
->filter('Moderated', 1)
|
||||
->filter('IsSpam', 0);
|
||||
|
||||
$approvedCommentsGrid = new CommentsGridField(
|
||||
'ApprovedComments',
|
||||
_t('CommentsAdmin.Comments', 'Approved'),
|
||||
_t('CommentsAdmin.ApprovedComments', 'Approved'),
|
||||
$approvedComments,
|
||||
$commentsConfig
|
||||
);
|
||||
|
||||
$spamComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 1);
|
||||
$approvedCommentsCountLabel = sprintf('(%s)', count($approvedComments));
|
||||
|
||||
$spamGrid = new CommentsGridField(
|
||||
$spamComments = Comment::get()
|
||||
->filter('Moderated', 1)
|
||||
->filter('IsSpam', 1);
|
||||
|
||||
$spamCommentsGrid = new CommentsGridField(
|
||||
'SpamComments',
|
||||
_t('CommentsAdmin.SpamComments', 'Spam'),
|
||||
$spamComments,
|
||||
$commentsConfig
|
||||
);
|
||||
|
||||
$newCount = '(' . count($newComments) . ')';
|
||||
$approvedCount = '(' . count($approvedComments) . ')';
|
||||
$spamCount = '(' . count($spamComments) . ')';
|
||||
$spamCommentsCountLabel = sprintf('(%s)', count($spamComments));
|
||||
|
||||
if($fields->hasTabSet()) {
|
||||
$tabs = new TabSet(
|
||||
'Comments',
|
||||
new Tab('CommentsNewCommentsTab', _t('CommentAdmin.NewComments', 'New') . ' ' . $newCount,
|
||||
$newGrid
|
||||
new Tab(
|
||||
'NewComments',
|
||||
sprintf(
|
||||
'%s %s',
|
||||
_t('CommentAdmin.NewComments', 'New'),
|
||||
$newCommentsCountLabel
|
||||
),
|
||||
new Tab('CommentsCommentsTab', _t('CommentAdmin.Comments', 'Approved') . ' ' . $approvedCount,
|
||||
$approvedGrid
|
||||
$newCommentsGrid
|
||||
),
|
||||
new Tab('CommentsSpamCommentsTab', _t('CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount,
|
||||
$spamGrid
|
||||
new Tab(
|
||||
'ApprovedComments',
|
||||
sprintf(
|
||||
'%s %s',
|
||||
_t('CommentAdmin.ApprovedComments', 'Approved'),
|
||||
$approvedCommentsCountLabel
|
||||
),
|
||||
$approvedCommentsGrid
|
||||
),
|
||||
new Tab(
|
||||
'SpamComments',
|
||||
sprintf(
|
||||
'%s %s',
|
||||
_t('CommentAdmin.SpamComments', 'Spam'),
|
||||
$spamCommentsCountLabel
|
||||
),
|
||||
$spamCommentsGrid
|
||||
)
|
||||
);
|
||||
$fields->addFieldToTab('Root', $tabs);
|
||||
} else {
|
||||
$fields->push($newGrid);
|
||||
$fields->push($approvedGrid);
|
||||
$fields->push($spamGrid);
|
||||
$fields->push($newCommentsGrid);
|
||||
$fields->push($approvedCommentsGrid);
|
||||
$fields->push($spamCommentsGrid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateCMSFields(FieldList $fields) {
|
||||
// Disable moderation if not permitted
|
||||
if($this->owner->canModerateComments()) {
|
||||
$this->updateModerationFields($fields);
|
||||
}
|
||||
|
||||
// If this isn't a page we should merge the settings into the CMS fields
|
||||
if(!$this->attachedToSiteTree()) {
|
||||
$this->updateSettingsFields($fields);
|
||||
}
|
||||
|
@ -8,20 +8,20 @@
|
||||
* @property string $Email
|
||||
* @property string $URL
|
||||
* @property string $BaseClass
|
||||
* @property boolean $Moderated
|
||||
* @property boolean $IsSpam True if the comment is known as spam
|
||||
* @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
|
||||
* @property integer $Depth Depth of this comment in the nested chain
|
||||
* @property bool $Moderated
|
||||
* @property bool $IsSpam
|
||||
* @property int $ParentID
|
||||
* @property bool $AllowHtml
|
||||
* @property string $SecretToken
|
||||
* @property int $Depth
|
||||
*
|
||||
* @method HasManyList ChildComments()
|
||||
* @method Member Author()
|
||||
* @method Comment ParentComment()
|
||||
*
|
||||
* @method HasManyList ChildComments() List of child comments
|
||||
* @method Member Author() Member object who created this comment
|
||||
* @method Comment ParentComment() Parent comment this is a reply to
|
||||
* @package comments
|
||||
*/
|
||||
class Comment extends DataObject {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -39,22 +39,37 @@ class Comment extends DataObject {
|
||||
'Depth' => 'Int',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
"Author" => "Member",
|
||||
"ParentComment" => "Comment",
|
||||
'Author' => 'Member',
|
||||
'ParentComment' => 'Comment',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_many = array(
|
||||
"ChildComments" => "Comment"
|
||||
'ChildComments' => 'Comment'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $default_sort = '"Created" DESC';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = array(
|
||||
'Moderated' => 0,
|
||||
'IsSpam' => 0,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $casting = array(
|
||||
'Title' => 'Varchar',
|
||||
'ParentTitle' => 'Varchar',
|
||||
@ -68,6 +83,9 @@ class Comment extends DataObject {
|
||||
'Permalink' => 'Varchar',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $searchable_fields = array(
|
||||
'Name',
|
||||
'Email',
|
||||
@ -76,6 +94,9 @@ class Comment extends DataObject {
|
||||
'BaseClass',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $summary_fields = array(
|
||||
'Name' => 'Submitted By',
|
||||
'Email' => 'Email',
|
||||
@ -85,26 +106,32 @@ class Comment extends DataObject {
|
||||
'IsSpam' => 'Is Spam',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $field_labels = array(
|
||||
'Author' => 'Author Member',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc
|
||||
*/
|
||||
public function onBeforeWrite() {
|
||||
parent::onBeforeWrite();
|
||||
|
||||
// Sanitize HTML, because its expected to be passed to the template unescaped later
|
||||
if($this->AllowHtml) {
|
||||
$this->Comment = $this->purifyHtml($this->Comment);
|
||||
}
|
||||
|
||||
// Check comment depth
|
||||
$this->updateDepth();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc
|
||||
*/
|
||||
public function onBeforeDelete() {
|
||||
parent::onBeforeDelete();
|
||||
|
||||
// Delete all children
|
||||
foreach($this->ChildComments() as $comment) {
|
||||
$comment->delete();
|
||||
}
|
||||
@ -118,24 +145,31 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the old {@link PageComment} objects to {@link Comment}
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Migrates the old {@link PageComment} objects to {@link Comment}.
|
||||
*/
|
||||
public function requireDefaultRecords() {
|
||||
parent::requireDefaultRecords();
|
||||
|
||||
if(DB::getConn()->hasTable('PageComment')) {
|
||||
$comments = DB::query('SELECT * FROM "PageComment"');
|
||||
$comments = DB::query('SELECT * FROM PageComment');
|
||||
|
||||
if($comments) {
|
||||
while($pageComment = $comments->nextRecord()) {
|
||||
// create a new comment from the older page comment
|
||||
$comment = new Comment();
|
||||
$comment->update($pageComment);
|
||||
|
||||
// set the variables which have changed
|
||||
$comment->BaseClass = 'SiteTree';
|
||||
$comment->URL = (isset($pageComment['CommenterURL'])) ? $pageComment['CommenterURL'] : '';
|
||||
if((int) $pageComment['NeedsModeration'] == 0) $comment->Moderated = true;
|
||||
$comment->URL = '';
|
||||
|
||||
if(isset($pageComment['CommenterURL'])) {
|
||||
$comment->URL = $pageComment['CommenterURL'];
|
||||
}
|
||||
|
||||
if($pageComment['NeedsModeration'] == false) {
|
||||
$comment->Moderated = true;
|
||||
}
|
||||
|
||||
$comment->write();
|
||||
}
|
||||
@ -147,38 +181,36 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a link to this comment
|
||||
* Return a link to this comment.
|
||||
*
|
||||
* @param string $action
|
||||
*
|
||||
* @return string link to this comment.
|
||||
* @return string
|
||||
*/
|
||||
public function Link($action = '') {
|
||||
if($parent = $this->getParent()) {
|
||||
return $parent->Link($action) . '#' . $this->Permalink();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permalink for this {@link Comment}. Inserted into
|
||||
* the ID tag of the comment
|
||||
* Returns the permalink for this {@link Comment}. Inserted into the ID tag of the comment.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Permalink() {
|
||||
$prefix = $this->getOption('comment_permalink_prefix');
|
||||
return $prefix . $this->ID;
|
||||
return $this->getOption('comment_permalink_prefix') . $this->ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the form field labels for the CMS administration
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param boolean $includerelations
|
||||
*
|
||||
* @return array
|
||||
* @param bool $includeRelations
|
||||
*/
|
||||
public function fieldLabels($includerelations = true) {
|
||||
$labels = parent::fieldLabels($includerelations);
|
||||
public function fieldLabels($includeRelations = true) {
|
||||
$labels = parent::fieldLabels($includeRelations);
|
||||
|
||||
$labels['Name'] = _t('Comment.NAME', 'Author Name');
|
||||
$labels['Comment'] = _t('Comment.COMMENT', 'Comment');
|
||||
@ -193,20 +225,18 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the commenting option
|
||||
* Get the commenting option.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed Result if the setting is available, or null otherwise
|
||||
* @return mixed
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
@ -214,30 +244,35 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent {@link DataObject} this comment is attached too
|
||||
* Returns the parent {@link DataObject} this comment is attached too.
|
||||
*
|
||||
* @return DataObject
|
||||
* @return null|DataObject
|
||||
*/
|
||||
public function getParent() {
|
||||
return $this->BaseClass && $this->ParentID
|
||||
? DataObject::get_by_id($this->BaseClass, $this->ParentID, true)
|
||||
: null;
|
||||
if($this->BaseClass && $this->ParentID) {
|
||||
return DataObject::get_by_id($this->BaseClass, $this->ParentID, true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string to help identify the parent of the comment
|
||||
* Returns a string to help identify the parent of the comment.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getParentTitle() {
|
||||
if($parent = $this->getParent()) {
|
||||
return $parent->Title ?: ($parent->ClassName . ' #' . $parent->ID);
|
||||
$parent = $this->getParent();
|
||||
|
||||
if($parent && $parent->Title) {
|
||||
return $parent->Title;
|
||||
}
|
||||
|
||||
return $parent->ClassName . ' #' . $parent->ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comment-parent classnames obviously vary, return the parent classname
|
||||
* Comment-parent class names may vary, return the parent class name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -245,16 +280,19 @@ class Comment extends DataObject {
|
||||
return $this->BaseClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function castingHelper($field) {
|
||||
// Safely escape the comment
|
||||
if($field === 'EscapedComment') {
|
||||
return $this->AllowHtml ? 'HTMLText' : 'Text';
|
||||
}
|
||||
|
||||
return parent::castingHelper($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content to be safely escaped on the frontend
|
||||
* @todo escape this comment? (DOH!)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -263,9 +301,9 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this comment is a preview (has not been written to the db)
|
||||
* Return whether this comment is a preview (has not been written to the db).
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isPreview() {
|
||||
return !$this->exists();
|
||||
@ -274,7 +312,7 @@ class Comment extends DataObject {
|
||||
/**
|
||||
* @todo needs to compare to the new {@link Commenting} configuration API
|
||||
*
|
||||
* @param Member $member
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@ -283,17 +321,18 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for association with a page, and {@link SiteTree->ProvidePermission}
|
||||
* flag being set to true.
|
||||
* Checks for association with a page, and {@link SiteTree->ProvidePermission} flag being set
|
||||
* to true.
|
||||
*
|
||||
* @param Member $member
|
||||
* @param null|int|Member $member
|
||||
*
|
||||
* @return Boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function canView($member = null) {
|
||||
$member = $this->getMember($member);
|
||||
|
||||
$extended = $this->extendedCan('canView', $member);
|
||||
|
||||
if($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
@ -303,9 +342,7 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
if($parent = $this->getParent()) {
|
||||
return $parent->canView($member)
|
||||
&& $parent->has_extension('CommentsExtension')
|
||||
&& $parent->CommentsEnabled;
|
||||
return $parent->canView($member) && $parent->has_extension('CommentsExtension') && $parent->CommentsEnabled;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -316,7 +353,7 @@ class Comment extends DataObject {
|
||||
*
|
||||
* @param null|int|Member $member
|
||||
*
|
||||
* @return Boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function canEdit($member = null) {
|
||||
$member = $this->getMember($member);
|
||||
@ -326,6 +363,7 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
$extended = $this->extendedCan('canEdit', $member);
|
||||
|
||||
if($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
@ -346,7 +384,7 @@ class Comment extends DataObject {
|
||||
*
|
||||
* @param null|int|Member $member
|
||||
*
|
||||
* @return Boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function canDelete($member = null) {
|
||||
$member = $this->getMember($member);
|
||||
@ -356,6 +394,7 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
$extended = $this->extendedCan('canDelete', $member);
|
||||
|
||||
if($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
@ -366,8 +405,9 @@ class Comment extends DataObject {
|
||||
/**
|
||||
* Resolves Member object.
|
||||
*
|
||||
* @param Member|int|null $member
|
||||
* @return Member|null
|
||||
* @param null|int|Member $member
|
||||
*
|
||||
* @return null|Member
|
||||
*/
|
||||
protected function getMember($member = null) {
|
||||
if(!$member) {
|
||||
@ -382,7 +422,7 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authors name for the comment
|
||||
* Return the authors name for the comment.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -392,19 +432,26 @@ class Comment extends DataObject {
|
||||
} else if($author = $this->Author()) {
|
||||
return $author->getName();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secure admin-action link authorised for the specified member
|
||||
* Generate a secure admin-action link authorised for the specified member.
|
||||
*
|
||||
* @param string $action An action on CommentingController to link to
|
||||
* @param Member $member The member authorised to invoke this action
|
||||
* @param string $action
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function actionLink($action, $member = null) {
|
||||
if(!$member) $member = Member::currentUser();
|
||||
if(!$member) return false;
|
||||
if(!$member) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
if(!$member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$url = Controller::join_links(
|
||||
Director::baseURL(),
|
||||
@ -413,89 +460,100 @@ class Comment extends DataObject {
|
||||
$this->ID
|
||||
);
|
||||
|
||||
// Limit access for this user
|
||||
$token = $this->getSecurityToken();
|
||||
|
||||
return $token->addToUrl($url, $member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to delete this comment
|
||||
* Link to delete this comment.
|
||||
*
|
||||
* @param Member $member
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function DeleteLink($member = null) {
|
||||
if($this->canDelete($member)) {
|
||||
return $this->actionLink('delete', $member);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to mark as spam
|
||||
* Link to mark as spam.
|
||||
*
|
||||
* @param Member $member
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function SpamLink($member = null) {
|
||||
if($this->canEdit($member) && !$this->IsSpam) {
|
||||
return $this->actionLink('spam', $member);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to mark as not-spam (ham)
|
||||
* Link to mark as not-spam.
|
||||
*
|
||||
* @param Member $member
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function HamLink($member = null) {
|
||||
if($this->canEdit($member) && $this->IsSpam) {
|
||||
return $this->actionLink('ham', $member);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to approve this comment
|
||||
* Link to approve this comment.
|
||||
*
|
||||
* @param Member $member
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return string
|
||||
* @return null|string
|
||||
*/
|
||||
public function ApproveLink($member = null) {
|
||||
if($this->canEdit($member) && !$this->Moderated) {
|
||||
return $this->actionLink('approve', $member);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this comment as spam
|
||||
* Mark this comment as spam.
|
||||
*/
|
||||
public function markSpam() {
|
||||
$this->IsSpam = true;
|
||||
$this->Moderated = true;
|
||||
$this->write();
|
||||
|
||||
$this->extend('afterMarkSpam');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this comment as approved
|
||||
* Mark this comment as approved.
|
||||
*/
|
||||
public function markApproved() {
|
||||
$this->IsSpam = false;
|
||||
$this->Moderated = true;
|
||||
$this->write();
|
||||
|
||||
$this->extend('afterMarkApproved');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this comment as unapproved
|
||||
* Mark this comment as unapproved.
|
||||
*/
|
||||
public function markUnapproved() {
|
||||
$this->Moderated = false;
|
||||
$this->write();
|
||||
|
||||
$this->extend('afterMarkUnapproved');
|
||||
}
|
||||
|
||||
@ -507,90 +565,120 @@ class Comment extends DataObject {
|
||||
return 'spam';
|
||||
} else if(!$this->Moderated) {
|
||||
return 'unmoderated';
|
||||
} else {
|
||||
return 'notspam';
|
||||
}
|
||||
|
||||
return 'notspam';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle() {
|
||||
$title = sprintf(_t('Comment.COMMENTBY', 'Comment by %s', 'Name'), $this->getAuthorName());
|
||||
$title = sprintf(
|
||||
_t('Comment.COMMENTBY', 'Comment by %s', 'Name'),
|
||||
$this->getAuthorName()
|
||||
);
|
||||
|
||||
if($parent = $this->getParent()) {
|
||||
if($parent->Title) {
|
||||
$title .= sprintf(' %s %s', _t('Comment.ON', 'on'), $parent->Title);
|
||||
}
|
||||
$parent = $this->getParent();
|
||||
|
||||
if($parent && $parent->Title) {
|
||||
$title .= sprintf(
|
||||
' %s %s',
|
||||
_t('Comment.ON', 'on'),
|
||||
$parent->Title
|
||||
);
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify the default fields shown to the user
|
||||
/**
|
||||
* Modify the default fields shown to the user.
|
||||
*/
|
||||
public function getCMSFields() {
|
||||
$commentField = $this->AllowHtml ? 'HtmlEditorField' : 'TextareaField';
|
||||
$fields = new FieldList(
|
||||
$this
|
||||
->obj('Created')
|
||||
|
||||
$commentFieldType = 'TextareaField';
|
||||
|
||||
if($this->AllowHtml) {
|
||||
$commentFieldType = 'HtmlEditorField';
|
||||
}
|
||||
|
||||
$createdField = $this->obj('Created')
|
||||
->scaffoldFormField($this->fieldLabel('Created'))
|
||||
->performReadonlyTransformation(),
|
||||
TextField::create('Name', $this->fieldLabel('AuthorName')),
|
||||
$commentField::create('Comment', $this->fieldLabel('Comment')),
|
||||
EmailField::create('Email', $this->fieldLabel('Email')),
|
||||
TextField::create('URL', $this->fieldLabel('URL')),
|
||||
FieldGroup::create(array(
|
||||
CheckboxField::create('Moderated', $this->fieldLabel('Moderated')),
|
||||
CheckboxField::create('IsSpam', $this->fieldLabel('IsSpam')),
|
||||
))
|
||||
->setTitle('Options')
|
||||
->setDescription(_t(
|
||||
->performReadonlyTransformation();
|
||||
|
||||
$nameField = TextField::create('Name', $this->fieldLabel('AuthorName'));
|
||||
|
||||
$commentField = $commentFieldType::create('Comment', $this->fieldLabel('Comment'));
|
||||
|
||||
$emailField = EmailField::create('Email', $this->fieldLabel('Email'));
|
||||
|
||||
$urlField = TextField::create('URL', $this->fieldLabel('URL'));
|
||||
|
||||
$moderatedField = CheckboxField::create('Moderated', $this->fieldLabel('Moderated'));
|
||||
|
||||
$spamField = CheckboxField::create('IsSpam', $this->fieldLabel('IsSpam'));
|
||||
|
||||
$fieldGroup = FieldGroup::create(array(
|
||||
$moderatedField,
|
||||
$spamField,
|
||||
));
|
||||
|
||||
$fieldGroup->setTitle('Options');
|
||||
$fieldGroup->setDescription(_t(
|
||||
'Comment.OPTION_DESCRIPTION',
|
||||
'Unmoderated and spam comments will not be displayed until approved'
|
||||
))
|
||||
));
|
||||
|
||||
$fields = new FieldList(
|
||||
$createdField,
|
||||
$nameField,
|
||||
$commentField,
|
||||
$emailField,
|
||||
$urlField,
|
||||
$fieldGroup
|
||||
);
|
||||
|
||||
// Show member name if given
|
||||
if(($author = $this->Author()) && $author->exists()) {
|
||||
$author = $this->Author();
|
||||
|
||||
if($author && $author->exists()) {
|
||||
$authorMemberField = TextField::create('AuthorMember', $this->fieldLabel('Author'), $author->Title);
|
||||
$authorMemberField->performReadonlyTransformation();
|
||||
|
||||
$fields->insertAfter(
|
||||
TextField::create('AuthorMember', $this->fieldLabel('Author'), $author->Title)
|
||||
->performReadonlyTransformation(),
|
||||
$authorMemberField,
|
||||
'Name'
|
||||
);
|
||||
}
|
||||
|
||||
// Show parent comment if given
|
||||
if(($parent = $this->ParentComment()) && $parent->exists()) {
|
||||
$fields->push(new HeaderField(
|
||||
$parent = $this->ParentComment();
|
||||
|
||||
if($parent && $parent->exists()) {
|
||||
$fields->push(
|
||||
new HeaderField(
|
||||
'ParentComment_Title',
|
||||
_t('Comment.ParentComment_Title', 'This comment is a reply to the below')
|
||||
));
|
||||
// Created date
|
||||
)
|
||||
);
|
||||
|
||||
$fields->push(
|
||||
$parent
|
||||
->obj('Created')
|
||||
$parent->obj('Created')
|
||||
->scaffoldFormField($parent->fieldLabel('Created'))
|
||||
->setName('ParentComment_Created')
|
||||
->setValue($parent->Created)
|
||||
->performReadonlyTransformation()
|
||||
);
|
||||
|
||||
// Name (could be member or string value)
|
||||
$fields->push(
|
||||
$parent
|
||||
->obj('AuthorName')
|
||||
$parent->obj('AuthorName')
|
||||
->scaffoldFormField($parent->fieldLabel('AuthorName'))
|
||||
->setName('ParentComment_AuthorName')
|
||||
->setValue($parent->getAuthorName())
|
||||
->performReadonlyTransformation()
|
||||
);
|
||||
|
||||
// Comment body
|
||||
$fields->push(
|
||||
$parent
|
||||
->obj('EscapedComment')
|
||||
$parent->obj('EscapedComment')
|
||||
->scaffoldFormField($parent->fieldLabel('Comment'))
|
||||
->setName('ParentComment_EscapedComment')
|
||||
->setValue($parent->Comment)
|
||||
@ -599,157 +687,165 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $dirtyHtml
|
||||
* @param string $dirtyHtml
|
||||
*
|
||||
* @return String
|
||||
* @return string
|
||||
*/
|
||||
public function purifyHtml($dirtyHtml) {
|
||||
$purifier = $this->getHtmlPurifierService();
|
||||
return $purifier->purify($dirtyHtml);
|
||||
return $this->getHtmlPurifierService()
|
||||
->purify($dirtyHtml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HTMLPurifier (or anything with a "purify()" method)
|
||||
* @return HTMLPurifier
|
||||
*/
|
||||
public function getHtmlPurifierService() {
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
|
||||
$config->set('HTML.AllowedElements', $this->getOption('html_allowed_elements'));
|
||||
$config->set('AutoFormat.AutoParagraph', true);
|
||||
$config->set('AutoFormat.Linkify', true);
|
||||
$config->set('URI.DisableExternalResources', true);
|
||||
$config->set('Cache.SerializerPath', getTempFolder());
|
||||
|
||||
return new HTMLPurifier($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the Gravatar link from the email address
|
||||
* Calculate the Gravatar link from the email address.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Gravatar() {
|
||||
$gravatar = '';
|
||||
$use_gravatar = $this->getOption('use_gravatar');
|
||||
if($use_gravatar) {
|
||||
$gravatar = 'http://www.gravatar.com/avatar/' . md5(strtolower(trim($this->Email)));
|
||||
$gravatarsize = $this->getOption('gravatar_size');
|
||||
$gravatardefault = $this->getOption('gravatar_default');
|
||||
$gravatarrating = $this->getOption('gravatar_rating');
|
||||
$gravatar .= '?s=' . $gravatarsize . '&d=' . $gravatardefault . '&r=' . $gravatarrating;
|
||||
if($this->getOption('use_gravatar')) {
|
||||
return sprintf(
|
||||
'http://www.gravatar.com/avatar/%s?s=%s&d=%s&r=%s',
|
||||
md5(strtolower(trim($this->Email))),
|
||||
$this->getOption('gravatar_size'),
|
||||
$this->getOption('gravatar_default'),
|
||||
$this->getOption('gravatar_rating')
|
||||
);
|
||||
}
|
||||
|
||||
return $gravatar;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if replies are enabled for this instance
|
||||
* Determine if replies are enabled for this instance.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function getRepliesEnabled() {
|
||||
// Check reply option
|
||||
if(!$this->getOption('nested_comments')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if depth is limited
|
||||
$maxLevel = $this->getOption('nested_depth');
|
||||
return !$maxLevel || $this->Depth < $maxLevel;
|
||||
|
||||
return !$maxLevel || $this->Depth < (int) $maxLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all replies
|
||||
* Returns the list of all replies.
|
||||
*
|
||||
* @return SS_List
|
||||
*/
|
||||
public function AllReplies() {
|
||||
// No replies if disabled
|
||||
if(!$this->getRepliesEnabled()) {
|
||||
return new ArrayList();
|
||||
}
|
||||
|
||||
// Get all non-spam comments
|
||||
$order = $this->getOption('order_replies_by')
|
||||
?: $this->getOption('order_comments_by');
|
||||
$order = $this->getOption('order_replies_by');
|
||||
|
||||
if(!$order) {
|
||||
$order = $this->getOption('order_comments_by');
|
||||
}
|
||||
|
||||
$list = $this
|
||||
->ChildComments()
|
||||
->sort($order);
|
||||
|
||||
$this->extend('updateAllReplies', $list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of replies, with spam and unmoderated items excluded, for use in the frontend
|
||||
* Returns the list of replies, with spam and un-moderated items excluded, for use in the
|
||||
* frontend.
|
||||
*
|
||||
* @return SS_List
|
||||
*/
|
||||
public function Replies() {
|
||||
// No replies if disabled
|
||||
if(!$this->getRepliesEnabled()) {
|
||||
return new ArrayList();
|
||||
}
|
||||
|
||||
$list = $this->AllReplies();
|
||||
|
||||
// Filter spam comments for non-administrators if configured
|
||||
$parent = $this->getParent();
|
||||
$showSpam = $this->getOption('frontend_spam') && $parent && $parent->canModerateComments();
|
||||
|
||||
$showSpam = $parent && $parent->canModerateComments() && $this->getOption('frontend_spam');
|
||||
|
||||
if(!$showSpam) {
|
||||
$list = $list->filter('IsSpam', 0);
|
||||
}
|
||||
|
||||
// Filter un-moderated comments for non-administrators if moderation is enabled
|
||||
$showUnmoderated = $parent && (
|
||||
($parent->ModerationRequired === 'None')
|
||||
|| ($this->getOption('frontend_moderation') && $parent->canModerateComments())
|
||||
);
|
||||
if (!$showUnmoderated) {
|
||||
$noModerationRequired = $parent && $parent->ModerationRequired === 'None';
|
||||
|
||||
$showUnModerated = $noModerationRequired || $showSpam;
|
||||
|
||||
if(!$showUnModerated) {
|
||||
$list = $list->filter('Moderated', 1);
|
||||
}
|
||||
|
||||
$this->extend('updateReplies', $list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of replies paged, with spam and unmoderated items excluded, for use in the frontend
|
||||
* Returns the list of replies paged, with spam and un-moderated items excluded, for use in the
|
||||
* frontend.
|
||||
*
|
||||
* @return PaginatedList
|
||||
*/
|
||||
public function PagedReplies() {
|
||||
$list = $this->Replies();
|
||||
|
||||
// Add pagination
|
||||
$list = new PaginatedList($list, Controller::curr()->getRequest());
|
||||
|
||||
$list->setPaginationGetVar('repliesstart' . $this->ID);
|
||||
$list->setPageLength($this->getOption('comments_per_page'));
|
||||
|
||||
$this->extend('updatePagedReplies', $list);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a reply form for this comment
|
||||
* Generate a reply form for this comment.
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function ReplyForm() {
|
||||
// Ensure replies are enabled
|
||||
if(!$this->getRepliesEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check parent is available
|
||||
$parent = $this->getParent();
|
||||
|
||||
if(!$parent || !$parent->exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Build reply controller
|
||||
$controller = CommentingController::create();
|
||||
|
||||
$controller->setOwnerRecord($parent);
|
||||
$controller->setBaseClass($parent->ClassName);
|
||||
$controller->setOwnerController(Controller::curr());
|
||||
@ -758,12 +854,14 @@ class Comment extends DataObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh of this comment in the hierarchy
|
||||
* Refresh of this comment in the hierarchy.
|
||||
*/
|
||||
public function updateDepth() {
|
||||
$parent = $this->ParentComment();
|
||||
|
||||
if($parent && $parent->exists()) {
|
||||
$parent->updateDepth();
|
||||
|
||||
$this->Depth = $parent->Depth + 1;
|
||||
} else {
|
||||
$this->Depth = 1;
|
||||
@ -771,27 +869,29 @@ class Comment extends DataObject {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides the ability to generate cryptographically secure tokens for comment moderation
|
||||
* Provides the ability to generate cryptographically secure tokens for comment moderation.
|
||||
*/
|
||||
class Comment_SecurityToken {
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $secret = null;
|
||||
|
||||
/**
|
||||
* @param Comment $comment Comment to generate this token for
|
||||
* @param Comment $comment Comment to generate this token for.
|
||||
*/
|
||||
public function __construct($comment) {
|
||||
if(!$comment->SecretToken) {
|
||||
$comment->SecretToken = $this->generate();
|
||||
$comment->write();
|
||||
}
|
||||
|
||||
$this->secret = $comment->SecretToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the token for the given salt and current secret
|
||||
* Generate the token for the given salt and current secret.
|
||||
*
|
||||
* @param string $salt
|
||||
*
|
||||
@ -808,27 +908,31 @@ class Comment_SecurityToken {
|
||||
* query string, requiring the same user to be present at both the link generation and the
|
||||
* controller action.
|
||||
*
|
||||
* @param string $salt Single use salt
|
||||
* @param Member $member Member object
|
||||
* @param string $salt
|
||||
* @param Member $member
|
||||
*
|
||||
* @return string Generated salt specific to this member
|
||||
* @return string
|
||||
*/
|
||||
protected function memberSalt($salt, $member) {
|
||||
// Fallback to salting with ID in case the member has not one set
|
||||
return $salt . ($member->Salt ?: $member->ID);
|
||||
$pepper = $member->Salt;
|
||||
|
||||
if(!$pepper) {
|
||||
$pepper = $member->ID;
|
||||
}
|
||||
|
||||
return $salt . $pepper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url Comment action URL
|
||||
* @param Member $member Member to restrict access to this action to
|
||||
* @param string $url
|
||||
* @param Member $member
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function addToUrl($url, $member) {
|
||||
$salt = $this->generate(15); // New random salt; Will be passed into url
|
||||
// Generate salt specific to this member
|
||||
$memberSalt = $this->memberSalt($salt, $member);
|
||||
$token = $this->getToken($memberSalt);
|
||||
$salt = $this->generate(15);
|
||||
$token = $this->getToken($this->memberSalt($salt, $member));
|
||||
|
||||
return Controller::join_links(
|
||||
$url,
|
||||
sprintf(
|
||||
@ -842,32 +946,37 @@ class Comment_SecurityToken {
|
||||
/**
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function checkRequest($request) {
|
||||
$member = Member::currentUser();
|
||||
if(!$member) return false;
|
||||
|
||||
if(!$member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$salt = $request->getVar('s');
|
||||
$memberSalt = $this->memberSalt($salt, $member);
|
||||
$token = $this->getToken($memberSalt);
|
||||
$token = $this->getToken($this->memberSalt($salt, $member));
|
||||
|
||||
// Ensure tokens match
|
||||
return $token === $request->getVar('t');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates new random key
|
||||
* Generates new random key.
|
||||
*
|
||||
* @param integer $length
|
||||
* @param null|int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate($length = null) {
|
||||
$generator = new RandomGenerator();
|
||||
|
||||
$result = $generator->randomToken('sha256');
|
||||
if($length !== null) return substr($result, 0, $length);
|
||||
|
||||
if($length !== null) {
|
||||
return substr($result, 0, $length);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Handles polymorphic relation for commentlist
|
||||
* Handles polymorphic relation for comment list.
|
||||
*
|
||||
* Uses elements of PolymorphicHasManyList in 3.2
|
||||
*
|
||||
* @author dmooyman
|
||||
*/
|
||||
class CommentList extends HasManyList {
|
||||
|
||||
/**
|
||||
* Retrieve the name of the class this relation is filtered by
|
||||
* Retrieve the name of the class this relation is filtered by.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -18,74 +15,83 @@ class CommentList extends HasManyList {
|
||||
return $this->dataQuery->getQueryParam('Foreign.Class');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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)
|
||||
'BaseClass IN (\'%s\')',
|
||||
implode('\', \'', $classNames)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the item to this relation.
|
||||
*
|
||||
* @param Comment $item The comment to be added
|
||||
* @param Comment $comment
|
||||
*/
|
||||
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");
|
||||
public function add($comment) {
|
||||
if(is_numeric($comment)) {
|
||||
$comment = Comment::get()->byID($comment);
|
||||
}
|
||||
|
||||
if(!$comment 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");
|
||||
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();
|
||||
$comment->ParentID = $foreignID;
|
||||
$comment->BaseClass = $this->getForeignClass();
|
||||
$comment->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Comment from this relation by clearing the foreign key. Does not actually delete the comment.
|
||||
* 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
|
||||
* @param Comment $comment
|
||||
*/
|
||||
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);
|
||||
public function remove($comment) {
|
||||
if(is_numeric($comment)) {
|
||||
$comment = Comment::get()->byID($comment);
|
||||
}
|
||||
|
||||
if(!$comment 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
|
||||
$subclasses = ClassInfo::subclassesFor($foreignClass);
|
||||
|
||||
if(!in_array($comment->BaseClass, $subclasses)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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();
|
||||
|
||||
if(empty($foreignID) || $foreignID == $comment->ParentID || (is_array($foreignID) && in_array($comment->ParentID, $foreignID))) {
|
||||
$comment->ParentID = null;
|
||||
$comment->BaseClass = null;
|
||||
$comment->write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,91 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*
|
||||
* @package comments
|
||||
* @subpackage tests
|
||||
*/
|
||||
class CommentingControllerTest extends FunctionalTest {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'CommentsTest.yml';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $extraDataObjects = array(
|
||||
'CommentableItem'
|
||||
'HasComments',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $securityEnabled;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tearDown() {
|
||||
if($this->securityEnabled) {
|
||||
SecurityToken::enable();
|
||||
} else {
|
||||
SecurityToken::disable();
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->securityEnabled = SecurityToken::is_enabled();
|
||||
}
|
||||
|
||||
public function testRSS() {
|
||||
$item = $this->objFromFixture('CommentableItem', 'first');
|
||||
$item = $this->objFromFixture('HasComments', 'first');
|
||||
|
||||
// comments sitewide
|
||||
$response = $this->get('CommentingController/rss');
|
||||
$this->assertEquals(10, substr_count($response->getBody(), "<item>"), "10 approved, non spam comments on page 1");
|
||||
|
||||
$this->assertEquals(10, substr_count($response->getBody(), '<item>'), '10 approved, non spam comments on page 1');
|
||||
|
||||
$response = $this->get('CommentingController/rss?start=10');
|
||||
$this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
|
||||
|
||||
// all comments on a type
|
||||
$response = $this->get('CommentingController/rss/CommentableItem');
|
||||
$this->assertEquals(10, substr_count($response->getBody(), "<item>"));
|
||||
$this->assertEquals(4, substr_count($response->getBody(), '<item>'), '3 approved, non spam comments on page 2');
|
||||
|
||||
$response = $this->get('CommentingController/rss/CommentableItem?start=10');
|
||||
$this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
|
||||
$response = $this->get('CommentingController/rss/HasComments');
|
||||
|
||||
// specific page
|
||||
$response = $this->get('CommentingController/rss/CommentableItem/'.$item->ID);
|
||||
$this->assertEquals(1, substr_count($response->getBody(), "<item>"));
|
||||
$this->assertEquals(10, substr_count($response->getBody(), '<item>'));
|
||||
|
||||
$response = $this->get('CommentingController/rss/HasComments?start=10');
|
||||
|
||||
$this->assertEquals(4, substr_count($response->getBody(), '<item>'), '3 approved, non spam comments on page 2');
|
||||
|
||||
$response = $this->get('CommentingController/rss/HasComments/' . $item->ID);
|
||||
|
||||
$this->assertEquals(1, substr_count($response->getBody(), '<item>'));
|
||||
$this->assertContains('<dc:creator>FA</dc:creator>', $response->getBody());
|
||||
|
||||
// test accessing comments on a type that doesn't exist
|
||||
$response = $this->get('CommentingController/rss/Fake');
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testCommentsForm() {
|
||||
SecurityToken::disable();
|
||||
$this->autoFollowRedirection = false;
|
||||
$parent = $this->objFromFixture('CommentableItem', 'first');
|
||||
|
||||
// Test posting to base comment
|
||||
$this->autoFollowRedirection = false;
|
||||
|
||||
$parent = $this->objFromFixture('HasComments', 'first');
|
||||
|
||||
$response = $this->post('CommentingController/CommentsForm',
|
||||
array(
|
||||
'Name' => 'Poster',
|
||||
'Email' => 'guy@test.com',
|
||||
'Comment' => 'My Comment',
|
||||
'ParentID' => $parent->ID,
|
||||
'BaseClass' => 'CommentableItem',
|
||||
'BaseClass' => 'HasComments',
|
||||
'action_doPostComment' => 'Post'
|
||||
)
|
||||
);
|
||||
$this->assertEquals(302, $response->getStatusCode());
|
||||
$this->assertStringStartsWith('CommentableItem_Controller#comment-', $response->getHeader('Location'));
|
||||
$this->assertStringStartsWith('HasComments_Controller#comment-', $response->getHeader('Location'));
|
||||
$this->assertDOSEquals(
|
||||
array(array(
|
||||
array(
|
||||
array(
|
||||
'Name' => 'Poster',
|
||||
'Email' => 'guy@test.com',
|
||||
'Comment' => 'My Comment',
|
||||
'ParentID' => $parent->ID,
|
||||
'BaseClass' => 'CommentableItem',
|
||||
)),
|
||||
'BaseClass' => 'HasComments',
|
||||
)
|
||||
),
|
||||
Comment::get()->filter('Email', 'guy@test.com')
|
||||
);
|
||||
|
||||
// Test posting to parent comment
|
||||
/**
|
||||
* @var Comment $parentComment
|
||||
*/
|
||||
$parentComment = $this->objFromFixture('Comment', 'firstComA');
|
||||
|
||||
$this->assertEquals(0, $parentComment->ChildComments()->count());
|
||||
|
||||
$response = $this->post(
|
||||
@ -95,20 +120,23 @@ class CommentingControllerTest extends FunctionalTest {
|
||||
'Email' => 'test@test.com',
|
||||
'Comment' => 'Making a reply to firstComA',
|
||||
'ParentID' => $parent->ID,
|
||||
'BaseClass' => 'CommentableItem',
|
||||
'BaseClass' => 'HasComments',
|
||||
'ParentCommentID' => $parentComment->ID,
|
||||
'action_doPostComment' => 'Post'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals(302, $response->getStatusCode());
|
||||
$this->assertStringStartsWith('CommentableItem_Controller#comment-', $response->getHeader('Location'));
|
||||
$this->assertDOSEquals(array(array(
|
||||
$this->assertStringStartsWith('HasComments_Controller#comment-', $response->getHeader('Location'));
|
||||
$this->assertDOSEquals(array(
|
||||
array(
|
||||
'Name' => 'Test Author',
|
||||
'Email' => 'test@test.com',
|
||||
'Comment' => 'Making a reply to firstComA',
|
||||
'ParentID' => $parent->ID,
|
||||
'BaseClass' => 'CommentableItem',
|
||||
'BaseClass' => 'HasComments',
|
||||
'ParentCommentID' => $parentComment->ID
|
||||
)), $parentComment->ChildComments());
|
||||
)
|
||||
), $parentComment->ChildComments());
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*
|
||||
* @package comments
|
||||
*/
|
||||
class CommentsTest extends FunctionalTest {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'comments/tests/CommentsTest.yml';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $extraDataObjects = array(
|
||||
'CommentableItem'
|
||||
'HasComments',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
Config::nest();
|
||||
|
||||
// Set good default values
|
||||
Config::inst()->update('CommentsExtension', 'comments', array(
|
||||
'enabled' => true,
|
||||
'enabled_cms' => false,
|
||||
@ -29,185 +38,241 @@ class CommentsTest extends FunctionalTest {
|
||||
'frontend_spam' => false,
|
||||
));
|
||||
|
||||
// Configure this dataobject
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
'enabled_cms' => true
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'enabled_cms' => true,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_moderation_nonmembers' => false,
|
||||
'require_moderation' => false,
|
||||
'require_moderation_cms' => false,
|
||||
));
|
||||
|
||||
$item = $this->objFromFixture('CommentableItem', 'spammed');
|
||||
$this->assertEquals('None', $item->ModerationRequired);
|
||||
/**
|
||||
* @var HasComments $item
|
||||
*/
|
||||
$item = $this->objFromFixture('HasComments', 'spammed');
|
||||
|
||||
$this->assertDOSEquals(array(
|
||||
$this->assertEquals('None', $item->ModerationRequired);
|
||||
$this->assertDOSEquals(
|
||||
array(
|
||||
array('Name' => 'Comment 1'),
|
||||
array('Name' => 'Comment 3')
|
||||
), $item->Comments(), 'Only 2 non spam posts should be shown');
|
||||
),
|
||||
$item->Comments(),
|
||||
'Only 2 non spam posts should be shown'
|
||||
);
|
||||
|
||||
Config::inst()->update('HasComments', 'comments', array('require_moderation_nonmembers' => true));
|
||||
|
||||
// when moderated, only moderated, non spam posts should be shown.
|
||||
Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => true));
|
||||
$this->assertEquals('NonMembersOnly', $item->ModerationRequired);
|
||||
|
||||
// Check that require_moderation overrides this option
|
||||
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => true));
|
||||
Config::inst()->update('HasComments', 'comments', array('require_moderation' => true));
|
||||
|
||||
$this->assertEquals('Required', $item->ModerationRequired);
|
||||
|
||||
$this->assertDOSEquals(array(
|
||||
$this->assertDOSEquals(
|
||||
array(
|
||||
array('Name' => 'Comment 3')
|
||||
), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
|
||||
),
|
||||
$item->Comments(),
|
||||
'Only 1 non spam, moderated post should be shown'
|
||||
);
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
// require_moderation_nonmembers still filters out unmoderated comments
|
||||
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => false));
|
||||
Config::inst()->update('HasComments', 'comments', array('require_moderation' => false));
|
||||
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => false));
|
||||
Config::inst()->update('HasComments', 'comments', array('require_moderation_nonmembers' => false));
|
||||
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
|
||||
// With unmoderated comments set to display in frontend
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_moderation' => true,
|
||||
'frontend_moderation' => true
|
||||
'frontend_moderation' => true,
|
||||
));
|
||||
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
|
||||
// With spam comments set to display in frontend
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_moderation' => true,
|
||||
'frontend_moderation' => false,
|
||||
'frontend_spam' => true,
|
||||
));
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
|
||||
|
||||
// With spam and unmoderated comments set to display in frontend
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_moderation' => true,
|
||||
'frontend_moderation' => true,
|
||||
'frontend_spam' => true,
|
||||
));
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
$this->assertEquals(4, $item->Comments()->Count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test moderation options configured via the CMS
|
||||
* Test moderation options configured via the CMS.
|
||||
*/
|
||||
public function testCommentCMSModerationList() {
|
||||
// comments don't require moderation so unmoderated comments can be
|
||||
// shown but not spam posts
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_moderation' => true,
|
||||
'require_moderation_cms' => true,
|
||||
));
|
||||
|
||||
$item = $this->objFromFixture('CommentableItem', 'spammed');
|
||||
$this->assertEquals('None', $item->ModerationRequired);
|
||||
/**
|
||||
* @var HasComments $item
|
||||
*/
|
||||
$item = $this->objFromFixture('HasComments', 'spammed');
|
||||
|
||||
$this->assertDOSEquals(array(
|
||||
$this->assertEquals('None', $item->ModerationRequired);
|
||||
$this->assertDOSEquals(
|
||||
array(
|
||||
array('Name' => 'Comment 1'),
|
||||
array('Name' => 'Comment 3')
|
||||
), $item->Comments(), 'Only 2 non spam posts should be shown');
|
||||
),
|
||||
$item->Comments(),
|
||||
'Only 2 non spam posts should be shown'
|
||||
);
|
||||
|
||||
// when moderated, only moderated, non spam posts should be shown.
|
||||
$item->ModerationRequired = 'NonMembersOnly';
|
||||
$item->write();
|
||||
|
||||
$this->assertEquals('NonMembersOnly', $item->ModerationRequired);
|
||||
|
||||
// Check that require_moderation overrides this option
|
||||
$item->ModerationRequired = 'Required';
|
||||
$item->write();
|
||||
|
||||
$this->assertEquals('Required', $item->ModerationRequired);
|
||||
|
||||
$this->assertDOSEquals(array(
|
||||
$this->assertDOSEquals(
|
||||
array(
|
||||
array('Name' => 'Comment 3')
|
||||
), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
),
|
||||
$item->Comments(),
|
||||
'Only 1 non spam, moderated post should be shown'
|
||||
);
|
||||
|
||||
// require_moderation_nonmembers still filters out unmoderated comments
|
||||
$item->ModerationRequired = 'NonMembersOnly';
|
||||
$item->write();
|
||||
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
$item->ModerationRequired = 'None';
|
||||
$item->write();
|
||||
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
}
|
||||
|
||||
public function testCanPostComment() {
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_login' => false,
|
||||
'require_login_cms' => false,
|
||||
'required_permission' => false,
|
||||
));
|
||||
$item = $this->objFromFixture('CommentableItem', 'first');
|
||||
$item2 = $this->objFromFixture('CommentableItem', 'second');
|
||||
|
||||
// Test restriction free commenting
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
/**
|
||||
* @var HasComments $item
|
||||
*/
|
||||
$item = $this->objFromFixture('HasComments', 'first');
|
||||
|
||||
/**
|
||||
* @var HasComments $item2
|
||||
*/
|
||||
$item2 = $this->objFromFixture('HasComments', 'second');
|
||||
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
$this->assertFalse($item->CommentsRequireLogin);
|
||||
$this->assertTrue($item->canPostComment());
|
||||
|
||||
// Test permission required to post
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_login' => true,
|
||||
'required_permission' => 'POSTING_PERMISSION',
|
||||
));
|
||||
|
||||
$this->assertTrue($item->CommentsRequireLogin);
|
||||
$this->assertFalse($item->canPostComment());
|
||||
|
||||
$this->logInWithPermission('WRONG_ONE');
|
||||
|
||||
$this->assertFalse($item->canPostComment());
|
||||
|
||||
$this->logInWithPermission('POSTING_PERMISSION');
|
||||
$this->assertTrue($item->canPostComment());
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
$this->assertTrue($item->canPostComment());
|
||||
|
||||
// Test require login to post, but not any permissions
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
$this->assertTrue($item->canPostComment());
|
||||
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'required_permission' => false,
|
||||
));
|
||||
|
||||
$this->assertTrue($item->CommentsRequireLogin);
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
$this->assertFalse($item->canPostComment());
|
||||
|
||||
$this->logInWithPermission('ANY_PERMISSION');
|
||||
|
||||
$this->assertTrue($item->canPostComment());
|
||||
|
||||
// Test options set via CMS
|
||||
Config::inst()->update('CommentableItem', 'comments', array(
|
||||
Config::inst()->update('HasComments', 'comments', array(
|
||||
'require_login' => true,
|
||||
'require_login_cms' => true,
|
||||
));
|
||||
|
||||
$this->assertFalse($item->CommentsRequireLogin);
|
||||
$this->assertTrue($item2->CommentsRequireLogin);
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
$this->assertTrue($item->canPostComment());
|
||||
$this->assertFalse($item2->canPostComment());
|
||||
|
||||
// Login grants permission to post
|
||||
$this->logInWithPermission('ANY_PERMISSION');
|
||||
|
||||
$this->assertTrue($item->canPostComment());
|
||||
$this->assertTrue($item2->canPostComment());
|
||||
|
||||
@ -255,201 +320,258 @@ class CommentsTest extends FunctionalTest {
|
||||
}
|
||||
|
||||
public function testDeleteComment() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = $this->objFromFixture('Comment', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
|
||||
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
|
||||
$delete = $this->get('CommentingController/delete/'.$comment->ID.'?ajax=1');
|
||||
|
||||
$delete = $this->get(sprintf(
|
||||
'CommentingController/delete/%s?ajax=1',
|
||||
$comment->ID
|
||||
));
|
||||
|
||||
$this->assertEquals(403, $delete->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertTrue($check && $check->exists());
|
||||
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
|
||||
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
|
||||
|
||||
// Test authenticated user
|
||||
$this->logInAs('commentadmin');
|
||||
$comment = $this->objFromFixture('Comment', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->DeleteLink();
|
||||
$this->assertContains('CommentingController/delete/'.$commentID.'?t=', $adminComment1Link);
|
||||
|
||||
// Test that this link can't be shared / XSS exploited
|
||||
$commentAdminLink = $comment->DeleteLink();
|
||||
|
||||
$this->assertContains(
|
||||
sprintf(
|
||||
'CommentingController/delete/%s?t=',
|
||||
$comment->ID
|
||||
),
|
||||
$commentAdminLink
|
||||
);
|
||||
|
||||
$this->logInAs('commentadmin2');
|
||||
$delete = $this->get($adminComment1Link);
|
||||
$this->assertEquals(400, $delete->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertTrue($check && $check->exists());
|
||||
|
||||
// Test that this other admin can delete the comment with their own link
|
||||
$adminComment2Link = $comment->DeleteLink();
|
||||
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
|
||||
$delete = $this->get($commentAdminLink);
|
||||
|
||||
$this->assertEquals(400, $delete->getStatusCode());
|
||||
$this->assertNotEquals($comment->DeleteLink(), $commentAdminLink);
|
||||
|
||||
$this->autoFollowRedirection = false;
|
||||
$delete = $this->get($adminComment2Link);
|
||||
|
||||
$delete = $this->get($comment->DeleteLink());
|
||||
|
||||
$this->assertEquals(302, $delete->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertFalse($check && $check->exists());
|
||||
$this->assertFalse(DataObject::get_by_id('Comment', $comment->ID));
|
||||
}
|
||||
|
||||
public function testSpamComment() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = $this->objFromFixture('Comment', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
|
||||
$this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
|
||||
$spam = $this->get('CommentingController/spam/'.$comment->ID.'?ajax=1');
|
||||
|
||||
$spam = $this->get(sprintf(
|
||||
'CommentingController/spam/%s?ajax=1',
|
||||
$comment->ID
|
||||
));
|
||||
|
||||
$this->assertEquals(403, $spam->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
|
||||
$this->assertEquals(0, $comment->IsSpam, 'No permission to mark as spam');
|
||||
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
|
||||
$this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
|
||||
|
||||
// Test authenticated user
|
||||
$this->logInAs('commentadmin');
|
||||
$comment = $this->objFromFixture('Comment', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->SpamLink();
|
||||
$this->assertContains('CommentingController/spam/'.$commentID.'?t=', $adminComment1Link);
|
||||
|
||||
// Test that this link can't be shared / XSS exploited
|
||||
$commentAdminLink = $comment->SpamLink();
|
||||
|
||||
$this->assertContains(
|
||||
sprintf(
|
||||
'CommentingController/spam/%s?t=',
|
||||
$comment->ID
|
||||
),
|
||||
$commentAdminLink
|
||||
);
|
||||
|
||||
$this->logInAs('commentadmin2');
|
||||
$spam = $this->get($adminComment1Link);
|
||||
|
||||
$spam = $this->get($commentAdminLink);
|
||||
|
||||
$this->assertEquals(400, $spam->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $comment->ID);
|
||||
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
|
||||
$this->assertNotEquals($comment->SpamLink(), $commentAdminLink);
|
||||
|
||||
// Test that this other admin can spam the comment with their own link
|
||||
$adminComment2Link = $comment->SpamLink();
|
||||
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
|
||||
$this->autoFollowRedirection = false;
|
||||
$spam = $this->get($adminComment2Link);
|
||||
$this->assertEquals(302, $spam->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(1, $check->IsSpam);
|
||||
|
||||
// Cannot re-spam spammed comment
|
||||
$this->assertNull($check->SpamLink());
|
||||
$spam = $this->get($comment->SpamLink());
|
||||
|
||||
$this->assertEquals(302, $spam->getStatusCode());
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = DataObject::get_by_id('Comment', $comment->ID);
|
||||
|
||||
$this->assertEquals(1, $comment->IsSpam);
|
||||
$this->assertNull($comment->SpamLink());
|
||||
}
|
||||
|
||||
public function testHamComment() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = $this->objFromFixture('Comment', 'secondComC');
|
||||
$commentID = $comment->ID;
|
||||
|
||||
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
|
||||
$ham = $this->get('CommentingController/ham/'.$comment->ID.'?ajax=1');
|
||||
|
||||
$ham = $this->get(sprintf(
|
||||
'CommentingController/ham/%s?ajax=1',
|
||||
$comment->ID
|
||||
));
|
||||
|
||||
$this->assertEquals(403, $ham->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
|
||||
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
|
||||
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
|
||||
|
||||
// Test authenticated user
|
||||
$this->logInAs('commentadmin');
|
||||
$comment = $this->objFromFixture('Comment', 'secondComC');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->HamLink();
|
||||
$this->assertContains('CommentingController/ham/'.$commentID.'?t=', $adminComment1Link);
|
||||
|
||||
// Test that this link can't be shared / XSS exploited
|
||||
$adminCommentLink = $comment->HamLink();
|
||||
|
||||
$this->assertContains(
|
||||
sprintf(
|
||||
'CommentingController/ham/%s?t=',
|
||||
$comment->ID
|
||||
),
|
||||
$adminCommentLink
|
||||
);
|
||||
|
||||
$this->logInAs('commentadmin2');
|
||||
$ham = $this->get($adminComment1Link);
|
||||
|
||||
$ham = $this->get($adminCommentLink);
|
||||
|
||||
$this->assertEquals(400, $ham->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $comment->ID);
|
||||
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
|
||||
$this->assertNotEquals($comment->HamLink(), $adminCommentLink);
|
||||
|
||||
// Test that this other admin can ham the comment with their own link
|
||||
$adminComment2Link = $comment->HamLink();
|
||||
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
|
||||
$this->autoFollowRedirection = false;
|
||||
$ham = $this->get($adminComment2Link);
|
||||
$this->assertEquals(302, $ham->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(0, $check->IsSpam);
|
||||
|
||||
// Cannot re-ham hammed comment
|
||||
$this->assertNull($check->HamLink());
|
||||
$ham = $this->get($comment->HamLink());
|
||||
|
||||
$this->assertEquals(302, $ham->getStatusCode());
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = DataObject::get_by_id('Comment', $comment->ID);
|
||||
|
||||
$this->assertEquals(0, $comment->IsSpam);
|
||||
$this->assertNull($comment->HamLink());
|
||||
}
|
||||
|
||||
public function testApproveComment() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
if($member = Member::currentUser()) {
|
||||
$member->logOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = $this->objFromFixture('Comment', 'secondComB');
|
||||
$commentID = $comment->ID;
|
||||
|
||||
$this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
|
||||
$approve = $this->get('CommentingController/approve/'.$comment->ID.'?ajax=1');
|
||||
|
||||
$approve = $this->get(sprintf(
|
||||
'CommentingController/approve/%s?ajax=1',
|
||||
$comment->ID
|
||||
));
|
||||
|
||||
$this->assertEquals(403, $approve->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(0, $check->Moderated, 'No permission to approve');
|
||||
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
|
||||
$this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
|
||||
|
||||
// Test authenticated user
|
||||
$this->logInAs('commentadmin');
|
||||
$comment = $this->objFromFixture('Comment', 'secondComB');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->ApproveLink();
|
||||
$this->assertContains('CommentingController/approve/'.$commentID.'?t=', $adminComment1Link);
|
||||
|
||||
// Test that this link can't be shared / XSS exploited
|
||||
$adminCommentLink = $comment->ApproveLink();
|
||||
|
||||
$this->assertContains(
|
||||
sprintf(
|
||||
'CommentingController/approve/%s?t=',
|
||||
$comment->ID
|
||||
),
|
||||
$adminCommentLink
|
||||
);
|
||||
|
||||
$this->logInAs('commentadmin2');
|
||||
$approve = $this->get($adminComment1Link);
|
||||
|
||||
$approve = $this->get($adminCommentLink);
|
||||
|
||||
$this->assertEquals(400, $approve->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $comment->ID);
|
||||
$this->assertEquals(0, $check->Moderated, 'No permission to approve');
|
||||
|
||||
// Test that this other admin can approve the comment with their own link
|
||||
$adminComment2Link = $comment->ApproveLink();
|
||||
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
|
||||
$this->assertNotEquals($comment->ApproveLink(), $adminCommentLink);
|
||||
|
||||
$this->autoFollowRedirection = false;
|
||||
$approve = $this->get($adminComment2Link);
|
||||
$this->assertEquals(302, $approve->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(1, $check->Moderated);
|
||||
|
||||
// Cannot re-approve approved comment
|
||||
$this->assertNull($check->ApproveLink());
|
||||
$approve = $this->get($comment->ApproveLink());
|
||||
|
||||
$this->assertEquals(302, $approve->getStatusCode());
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
$comment = DataObject::get_by_id('Comment', $comment->ID);
|
||||
|
||||
$this->assertEquals(1, $comment->Moderated);
|
||||
$this->assertNull($comment->ApproveLink());
|
||||
}
|
||||
|
||||
public function testCommenterURLWrite() {
|
||||
$comment = new Comment();
|
||||
// We only care about the CommenterURL, so only set that
|
||||
// Check a http and https URL. Add more test urls here as needed.
|
||||
|
||||
$protocols = array(
|
||||
'Http',
|
||||
'Https',
|
||||
'HTTP',
|
||||
'HTTPS',
|
||||
);
|
||||
|
||||
$url = '://example.com';
|
||||
|
||||
foreach($protocols as $protocol) {
|
||||
$comment->CommenterURL = $protocol . $url;
|
||||
// The protocol should stay as if, assuming it is valid
|
||||
$comment->URL = $protocol . $url;
|
||||
$comment->write();
|
||||
$this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol');
|
||||
|
||||
$this->assertEquals($comment->URL, $protocol . $url, $protocol . ':// is a valid protocol');
|
||||
}
|
||||
}
|
||||
|
||||
public function testSanitizesWithAllowHtml() {
|
||||
if(!class_exists('HTMLPurifier')) {
|
||||
$this->markTestSkipped('HTMLPurifier class not found');
|
||||
return;
|
||||
}
|
||||
|
||||
$origAllowed = Commenting::get_config_value('CommentableItem','html_allowed');
|
||||
$originalHtmlAllowed = Commenting::get_config_value('HasComments', 'html_allowed');
|
||||
|
||||
// Without HTML allowed
|
||||
$comment1 = new Comment();
|
||||
$comment1->BaseClass = 'CommentableItem';
|
||||
$comment1->BaseClass = 'HasComments';
|
||||
$comment1->Comment = '<p><script>alert("w00t")</script>my comment</p>';
|
||||
$comment1->write();
|
||||
|
||||
$this->assertEquals(
|
||||
'<p><script>alert("w00t")</script>my comment</p>',
|
||||
$comment1->Comment,
|
||||
@ -457,19 +579,20 @@ class CommentsTest extends FunctionalTest {
|
||||
'which is correct behaviour because the HTML will be escaped'
|
||||
);
|
||||
|
||||
// With HTML allowed
|
||||
Commenting::set_config_value('CommentableItem','html_allowed', true);
|
||||
Commenting::set_config_value('HasComments', 'html_allowed', true);
|
||||
|
||||
$comment2 = new Comment();
|
||||
$comment2->BaseClass = 'CommentableItem';
|
||||
$comment2->BaseClass = 'HasComments';
|
||||
$comment2->Comment = '<p><script>alert("w00t")</script>my comment</p>';
|
||||
$comment2->write();
|
||||
|
||||
$this->assertEquals(
|
||||
'<p>my comment</p>',
|
||||
$comment2->Comment,
|
||||
'Removes HTML tags which are not on the whitelist'
|
||||
);
|
||||
|
||||
Commenting::set_config_value('CommentableItem','html_allowed', $origAllowed);
|
||||
Commenting::set_config_value('HasComments', 'html_allowed', $originalHtmlAllowed);
|
||||
}
|
||||
|
||||
public function testDefaultTemplateRendersHtmlWithAllowHtml() {
|
||||
@ -477,63 +600,92 @@ class CommentsTest extends FunctionalTest {
|
||||
$this->markTestSkipped('HTMLPurifier class not found');
|
||||
}
|
||||
|
||||
$origAllowed = Commenting::get_config_value('CommentableItem', 'html_allowed');
|
||||
$item = new CommentableItem();
|
||||
$originalHtmlAllowed = Commenting::get_config_value('HasComments', 'html_allowed');
|
||||
|
||||
$item = new HasComments();
|
||||
$item->write();
|
||||
|
||||
// Without HTML allowed
|
||||
$comment = new Comment();
|
||||
$comment->Comment = '<p>my comment</p>';
|
||||
$comment->ParentID = $item->ID;
|
||||
$comment->BaseClass = 'CommentableItem';
|
||||
$comment->BaseClass = 'HasComments';
|
||||
$comment->write();
|
||||
|
||||
$html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
|
||||
$html = $item
|
||||
->customise(array(
|
||||
'CommentsEnabled' => true,
|
||||
))
|
||||
->renderWith('CommentsInterface');
|
||||
|
||||
$this->assertContains(
|
||||
'<p>my comment</p>',
|
||||
$html
|
||||
);
|
||||
|
||||
Commenting::set_config_value('CommentableItem','html_allowed', true);
|
||||
$html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
|
||||
Commenting::set_config_value('HasComments', 'html_allowed', true);
|
||||
|
||||
$html = $item
|
||||
->customise(array(
|
||||
'CommentsEnabled' => true,
|
||||
))
|
||||
->renderWith('CommentsInterface');
|
||||
|
||||
$this->assertContains(
|
||||
'<p>my comment</p>',
|
||||
$html
|
||||
);
|
||||
|
||||
Commenting::set_config_value('CommentableItem','html_allowed', $origAllowed);
|
||||
Commenting::set_config_value('HasComments', 'html_allowed', $originalHtmlAllowed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @mixin CommentsExtension
|
||||
*
|
||||
* @package comments
|
||||
* @subpackage tests
|
||||
*/
|
||||
class CommentableItem extends DataObject implements TestOnly {
|
||||
|
||||
class HasComments extends DataObject implements TestOnly {
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'ProvideComments' => 'Boolean',
|
||||
'Title' => 'Varchar'
|
||||
'Title' => 'Varchar',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $extensions = array(
|
||||
'CommentsExtension'
|
||||
'CommentsExtension',
|
||||
);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function RelativeLink() {
|
||||
return "CommentableItem_Controller";
|
||||
return 'HasComments_Controller';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canView($member = null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Link() {
|
||||
return $this->RelativeLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function AbsoluteLink() {
|
||||
return Director::absoluteURL($this->RelativeLink());
|
||||
}
|
||||
@ -543,9 +695,11 @@ class CommentableItem extends DataObject implements TestOnly {
|
||||
* @package comments
|
||||
* @subpackage tests
|
||||
*/
|
||||
class CommentableItem_Controller extends Controller implements TestOnly {
|
||||
|
||||
class HasComments_Controller extends Controller implements TestOnly {
|
||||
/**
|
||||
* @return Form
|
||||
*/
|
||||
public function index() {
|
||||
return CommentableItem::get()->first()->CommentsForm();
|
||||
return HasComments::get()->first()->CommentsForm();
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ Permission:
|
||||
Code: CMS_ACCESS_CommentAdmin
|
||||
Group: =>Group.commentadmins
|
||||
|
||||
CommentableItem:
|
||||
HasComments:
|
||||
first:
|
||||
Title: First
|
||||
ProvideComments: 1
|
||||
@ -38,135 +38,135 @@ CommentableItem:
|
||||
|
||||
Comment:
|
||||
firstComA:
|
||||
ParentID: =>CommentableItem.first
|
||||
ParentID: =>HasComments.first
|
||||
Name: FA
|
||||
Comment: textFA
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
secondComA:
|
||||
ParentID: =>CommentableItem.second
|
||||
ParentID: =>HasComments.second
|
||||
Name: SA
|
||||
Comment: textSA
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
secondComB:
|
||||
ParentID: =>CommentableItem.second
|
||||
ParentID: =>HasComments.second
|
||||
Name: SB
|
||||
Comment: textSB
|
||||
Moderated: 0
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
secondComC:
|
||||
ParentID: =>CommentableItem.second
|
||||
ParentID: =>HasComments.second
|
||||
Name: SB
|
||||
Comment: textSB
|
||||
Moderated: 1
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComA:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TA
|
||||
Comment: textTA
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComB:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TB
|
||||
Comment: textTB
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComC:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComD:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComE:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComF:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComG:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComH:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComI:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComJ:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
thirdComK:
|
||||
ParentID: =>CommentableItem.third
|
||||
ParentID: =>HasComments.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
disabledCom:
|
||||
ParentID: =>CommentableItem.nocomments
|
||||
ParentID: =>HasComments.nocomments
|
||||
Name: Disabled
|
||||
Moderated: 0
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
testCommentList1:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
ParentID: =>HasComments.spammed
|
||||
Name: Comment 1
|
||||
Moderated: 0
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
testCommentList2:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
ParentID: =>HasComments.spammed
|
||||
Name: Comment 2
|
||||
Moderated: 1
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
testCommentList3:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
ParentID: =>HasComments.spammed
|
||||
Name: Comment 3
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
||||
testCommentList4:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
ParentID: =>HasComments.spammed
|
||||
Name: Comment 4
|
||||
Moderated: 0
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
BaseClass: HasComments
|
Loading…
Reference in New Issue
Block a user