Reformat as PSR-2

This commit is contained in:
Damian Mooyman 2016-02-19 13:48:25 +13:00
parent c2fee6bd33
commit 4b31713128
21 changed files with 3368 additions and 3041 deletions

View File

@ -13,134 +13,150 @@
* *
* @package comments * @package comments
*/ */
class Commenting { class Commenting
{
/** /**
* Adds commenting to a {@link DataObject} * Adds commenting to a {@link DataObject}
* *
* @deprecated since version 2.0 * @deprecated since version 2.0
* *
* @param string classname to add commenting to * @param string classname to add commenting to
* @param array $settings Settings. See {@link self::$default_config} for * @param array $settings Settings. See {@link self::$default_config} for
* available settings * available settings
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public static function add($class, $settings = false) { 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')); 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 // Check if settings must be customised
if($settings === false) return; if ($settings === false) {
if(!is_array($settings)) { return;
throw new InvalidArgumentException('$settings needs to be an array or null'); }
} if (!is_array($settings)) {
Config::inst()->update($class, 'comments', $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 * Removes commenting from a {@link DataObject}. Does not remove existing comments
* but does remove the extension. * but does remove the extension.
* *
* @deprecated since version 2.0 * @deprecated since version 2.0
* *
* @param string $class Class to remove {@link CommentsExtension} from * @param string $class Class to remove {@link CommentsExtension} from
*/ */
public static function remove($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'); 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 * @deprecated since version 2.0
* *
* @return bool * @return bool
*/ */
public static function has_commenting($class) { 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'); 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 a value for a class of a given config setting. Passing 'all' as the class
* sets it for everything * sets it for everything
* *
* @deprecated since version 2.0 * @deprecated since version 2.0
* *
* @param string $class Class to set the value on. Passing 'all' will set it to all * @param string $class Class to set the value on. Passing 'all' will set it to all
* active mappings * active mappings
* @param string $key setting to change * @param string $key setting to change
* @param mixed $value value of the setting * @param mixed $value value of the setting
*/ */
public static function set_config_value($class, $key, $value = false) { 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'; Deprecation::notice('2.0', 'Commenting::set_config_value is deprecated. Use the config api instead');
Config::inst()->update($class, 'comments', array($key => $value)); 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 * @deprecated since version 2.0
* *
* @param string $class * @param string $class
* @param string $key config value to return * @param string $key config value to return
* *
* @throws Exception * @throws Exception
* @return mixed * @return mixed
*/ */
public static function get_config_value($class, $key) { public static function get_config_value($class, $key)
Deprecation::notice( {
'2.0', Deprecation::notice(
'Using Commenting::get_config_value is deprecated. Please use $parent->getCommentsOption() or ' '2.0',
. 'CommentingController::getOption() instead' 'Using Commenting::get_config_value is deprecated. Please use $parent->getCommentsOption() or '
); . 'CommentingController::getOption() instead'
);
// Get settings // Get settings
if(!$class) { if (!$class) {
$class = 'CommentsExtension'; $class = 'CommentsExtension';
} elseif(!$class::has_extension('CommentsExtension')) { } elseif (!$class::has_extension('CommentsExtension')) {
throw new InvalidArgumentException("$class does not have commenting enabled"); throw new InvalidArgumentException("$class does not have commenting enabled");
} }
return singleton($class)->getCommentsOption($key); return singleton($class)->getCommentsOption($key);
} }
/** /**
* Determines whether a config value on the commenting extension * Determines whether a config value on the commenting extension
* matches a given value. * matches a given value.
* *
* @deprecated since version 2.0 * @deprecated since version 2.0
* *
* @param string $class * @param string $class
* @param string $key * @param string $key
* @param string $value Expected value * @param string $value Expected value
* @return boolean * @return boolean
*/ */
public static function config_value_equals($class, $key, $value) { public static function config_value_equals($class, $key, $value)
$check = self::get_config_value($class, $key); {
if($check && ($check == $value)) return true; $check = self::get_config_value($class, $key);
} if ($check && ($check == $value)) {
return true;
}
}
/** /**
* 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 * @deprecated since version 2.0
* *
* @param string $class * @param string $class
* @return boolean true * @return boolean true
*/ */
public static function can_member_post($class) { public static function can_member_post($class)
Deprecation::notice('2.0', 'Use $instance->canPostComment() directly instead'); {
$member = Member::currentUser(); Deprecation::notice('2.0', 'Use $instance->canPostComment() directly instead');
$member = Member::currentUser();
// Check permission // Check permission
$permission = self::get_config_value($class, 'required_permission'); $permission = self::get_config_value($class, 'required_permission');
if($permission && !Permission::check($permission)) return false; if ($permission && !Permission::check($permission)) {
return false;
}
// Check login required // Check login required
$requireLogin = self::get_config_value($class, 'require_login'); $requireLogin = self::get_config_value($class, 'require_login');
return !$requireLogin || $member; return !$requireLogin || $member;
} }
} }

View File

@ -5,114 +5,119 @@
* *
* @package comments * @package comments
*/ */
class CommentAdmin extends LeftAndMain implements PermissionProvider { class CommentAdmin extends LeftAndMain implements PermissionProvider
{
private static $url_segment = 'comments'; private static $url_segment = 'comments';
private static $url_rule = '/$Action'; private static $url_rule = '/$Action';
private static $menu_title = 'Comments'; private static $menu_title = 'Comments';
private static $allowed_actions = array( private static $allowed_actions = array(
'approvedmarked', 'approvedmarked',
'deleteall', 'deleteall',
'deletemarked', 'deletemarked',
'hammarked', 'hammarked',
'showtable', 'showtable',
'spammarked', 'spammarked',
'EditForm', 'EditForm',
'unmoderated' 'unmoderated'
); );
public function providePermissions() { public function providePermissions()
return array( {
"CMS_ACCESS_CommentAdmin" => array( return array(
'name' => _t('CommentAdmin.ADMIN_PERMISSION', "Access to 'Comments' section"), "CMS_ACCESS_CommentAdmin" => array(
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access') 'name' => _t('CommentAdmin.ADMIN_PERMISSION', "Access to 'Comments' section"),
) 'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
); )
} );
}
/** /**
* @return Form * @return Form
*/ */
public function getEditForm($id = null, $fields = null) { public function getEditForm($id = null, $fields = null)
if(!$id) $id = $this->currentPageID(); {
if (!$id) {
$id = $this->currentPageID();
}
$form = parent::getEditForm($id); $form = parent::getEditForm($id);
$record = $this->getRecord($id); $record = $this->getRecord($id);
if($record && !$record->canView()) { if ($record && !$record->canView()) {
return Security::permissionFailure($this); return Security::permissionFailure($this);
} }
$newComments = Comment::get()->filter('Moderated', 0); $newComments = Comment::get()->filter('Moderated', 0);
$newGrid = new CommentsGridField( $newGrid = new CommentsGridField(
'NewComments', 'NewComments',
_t('CommentsAdmin.NewComments', 'New'), _t('CommentsAdmin.NewComments', 'New'),
$newComments, $newComments,
CommentsGridFieldConfig::create() CommentsGridFieldConfig::create()
); );
$approvedComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 0); $approvedComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 0);
$approvedGrid = new CommentsGridField( $approvedGrid = new CommentsGridField(
'ApprovedComments', 'ApprovedComments',
_t('CommentsAdmin.ApprovedComments', 'Approved'), _t('CommentsAdmin.ApprovedComments', 'Approved'),
$approvedComments, $approvedComments,
CommentsGridFieldConfig::create() CommentsGridFieldConfig::create()
); );
$spamComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 1); $spamComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 1);
$spamGrid = new CommentsGridField( $spamGrid = new CommentsGridField(
'SpamComments', 'SpamComments',
_t('CommentsAdmin.SpamComments', 'Spam'), _t('CommentsAdmin.SpamComments', 'Spam'),
$spamComments, $spamComments,
CommentsGridFieldConfig::create() CommentsGridFieldConfig::create()
); );
$newCount = '(' . count($newComments) . ')'; $newCount = '(' . count($newComments) . ')';
$approvedCount = '(' . count($approvedComments) . ')'; $approvedCount = '(' . count($approvedComments) . ')';
$spamCount = '(' . count($spamComments) . ')'; $spamCount = '(' . count($spamComments) . ')';
$fields = new FieldList( $fields = new FieldList(
$root = new TabSet( $root = new TabSet(
'Root', 'Root',
new Tab('NewComments', _t('CommentAdmin.NewComments', 'New') . ' ' . $newCount, new Tab('NewComments', _t('CommentAdmin.NewComments', 'New') . ' ' . $newCount,
$newGrid $newGrid
), ),
new Tab('ApprovedComments', _t('CommentAdmin.ApprovedComments', 'Approved') . ' ' . $approvedCount, new Tab('ApprovedComments', _t('CommentAdmin.ApprovedComments', 'Approved') . ' ' . $approvedCount,
$approvedGrid $approvedGrid
), ),
new Tab('SpamComments', _t('CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount, new Tab('SpamComments', _t('CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount,
$spamGrid $spamGrid
) )
) )
); );
$root->setTemplate('CMSTabSet'); $root->setTemplate('CMSTabSet');
$actions = new FieldList(); $actions = new FieldList();
$form = new Form( $form = new Form(
$this, $this,
'EditForm', 'EditForm',
$fields, $fields,
$actions $actions
); );
$form->addExtraClass('cms-edit-form'); $form->addExtraClass('cms-edit-form');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); $form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
if($form->Fields()->hasTabset()) { if ($form->Fields()->hasTabset()) {
$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet'); $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
$form->addExtraClass('center ss-tabset cms-tabset ' . $this->BaseCSSClasses()); $form->addExtraClass('center ss-tabset cms-tabset ' . $this->BaseCSSClasses());
} }
$this->extend('updateEditForm', $form); $this->extend('updateEditForm', $form);
return $form; return $form;
} }
} }

View File

@ -1,22 +1,24 @@
<?php <?php
class CommentsGridField extends GridField { class CommentsGridField extends GridField
/** {
* {@inheritdoc} /**
*/ * {@inheritdoc}
protected function newRow($total, $index, $record, $attributes, $content) { */
if(!isset($attributes['class'])) { protected function newRow($total, $index, $record, $attributes, $content)
$attributes['class'] = ''; {
} if (!isset($attributes['class'])) {
$attributes['class'] = '';
}
if($record->IsSpam) { if ($record->IsSpam) {
$attributes['class'] .= ' spam'; $attributes['class'] .= ' spam';
} }
return FormField::create_tag( return FormField::create_tag(
'tr', 'tr',
$attributes, $attributes,
$content $content
); );
} }
} }

View File

@ -1,100 +1,110 @@
<?php <?php
class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_ActionProvider { class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_ActionProvider
/** {
* {@inheritdoc} /**
*/ * {@inheritdoc}
public function augmentColumns($gridField, &$columns) { */
if(!in_array('Actions', $columns)) { public function augmentColumns($gridField, &$columns)
$columns[] = 'Actions'; {
} if (!in_array('Actions', $columns)) {
} $columns[] = 'Actions';
}
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getColumnAttributes($gridField, $record, $columnName) { public function getColumnAttributes($gridField, $record, $columnName)
return array('class' => 'col-buttons'); {
} return array('class' => 'col-buttons');
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getColumnMetadata($gridField, $columnName) { public function getColumnMetadata($gridField, $columnName)
if($columnName == 'Actions') { {
return array('title' => ''); if ($columnName == 'Actions') {
} return array('title' => '');
} }
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getColumnsHandled($gridField) { public function getColumnsHandled($gridField)
return array('Actions'); {
} return array('Actions');
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName)
if(!$record->canEdit()) return; {
if (!$record->canEdit()) {
return;
}
$field = ""; $field = "";
if(!$record->IsSpam || !$record->Moderated) { if (!$record->IsSpam || !$record->Moderated) {
$field .= GridField_FormAction::create( $field .= GridField_FormAction::create(
$gridField, $gridField,
'CustomAction' . $record->ID . 'Spam', 'CustomAction' . $record->ID . 'Spam',
'Spam', 'Spam',
'spam', 'spam',
array('RecordID' => $record->ID) array('RecordID' => $record->ID)
)->Field(); )->Field();
} }
if($record->IsSpam || !$record->Moderated) { if ($record->IsSpam || !$record->Moderated) {
$field .= GridField_FormAction::create( $field .= GridField_FormAction::create(
$gridField, $gridField,
'CustomAction' . $record->ID . 'Approve', 'CustomAction' . $record->ID . 'Approve',
'Approve', 'Approve',
'approve', 'approve',
array('RecordID' => $record->ID) array('RecordID' => $record->ID)
)->Field(); )->Field();
} }
return $field; return $field;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getActions($gridField) { public function getActions($gridField)
return array('spam', 'approve'); {
} return array('spam', 'approve');
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handleAction(GridField $gridField, $actionName, $arguments, $data) { public function handleAction(GridField $gridField, $actionName, $arguments, $data)
if($actionName == 'spam') { {
$comment = Comment::get()->byID($arguments["RecordID"]); if ($actionName == 'spam') {
$comment->markSpam(); $comment = Comment::get()->byID($arguments["RecordID"]);
$comment->markSpam();
// output a success message to the user // output a success message to the user
Controller::curr()->getResponse()->setStatusCode( Controller::curr()->getResponse()->setStatusCode(
200, 200,
'Comment marked as spam.' 'Comment marked as spam.'
); );
} }
if($actionName == 'approve') { if ($actionName == 'approve') {
$comment = Comment::get()->byID($arguments["RecordID"]); $comment = Comment::get()->byID($arguments["RecordID"]);
$comment->markApproved(); $comment->markApproved();
// output a success message to the user // output a success message to the user
Controller::curr()->getResponse()->setStatusCode( Controller::curr()->getResponse()->setStatusCode(
200, 200,
'Comment approved.' 'Comment approved.'
); );
} }
} }
} }

View File

@ -3,8 +3,8 @@
/** /**
* @package comments * @package comments
*/ */
class CommentsGridFieldBulkAction extends GridFieldBulkActionHandler { class CommentsGridFieldBulkAction extends GridFieldBulkActionHandler
{
} }
/** /**
@ -12,53 +12,56 @@ class CommentsGridFieldBulkAction extends GridFieldBulkActionHandler {
* *
* @package comments * @package comments
*/ */
class CommentsGridFieldBulkAction_Handlers extends CommentsGridFieldBulkAction { class CommentsGridFieldBulkAction_Handlers extends CommentsGridFieldBulkAction
{
private static $allowed_actions = array( private static $allowed_actions = array(
'spam', 'spam',
'approve', 'approve',
); );
private static $url_handlers = array( private static $url_handlers = array(
'spam' => 'spam', 'spam' => 'spam',
'approve' => 'approve', 'approve' => 'approve',
); );
public function spam(SS_HTTPRequest $request) { public function spam(SS_HTTPRequest $request)
$ids = array(); {
$ids = array();
foreach($this->getRecords() as $record) { foreach ($this->getRecords() as $record) {
array_push($ids, $record->ID); array_push($ids, $record->ID);
$record->markSpam(); $record->markSpam();
} }
$response = new SS_HTTPResponse(Convert::raw2json(array( $response = new SS_HTTPResponse(Convert::raw2json(array(
'done' => true, 'done' => true,
'records' => $ids 'records' => $ids
))); )));
$response->addHeader('Content-Type', 'text/json'); $response->addHeader('Content-Type', 'text/json');
return $response; return $response;
} }
public function approve(SS_HTTPRequest $request) { public function approve(SS_HTTPRequest $request)
$ids = array(); {
$ids = array();
foreach($this->getRecords() as $record) { foreach ($this->getRecords() as $record) {
array_push($ids, $record->ID); array_push($ids, $record->ID);
$record->markApproved(); $record->markApproved();
} }
$response = new SS_HTTPResponse(Convert::raw2json(array( $response = new SS_HTTPResponse(Convert::raw2json(array(
'done' => true, 'done' => true,
'records' => $ids 'records' => $ids
))); )));
$response->addHeader('Content-Type', 'text/json'); $response->addHeader('Content-Type', 'text/json');
return $response; return $response;
} }
} }

View File

@ -1,49 +1,51 @@
<?php <?php
class CommentsGridFieldConfig extends GridFieldConfig_RecordEditor { class CommentsGridFieldConfig extends GridFieldConfig_RecordEditor
public function __construct($itemsPerPage = 25) { {
parent::__construct($itemsPerPage); public function __construct($itemsPerPage = 25)
{
parent::__construct($itemsPerPage);
// $this->addComponent(new GridFieldExportButton()); // $this->addComponent(new GridFieldExportButton());
$this->addComponent(new CommentsGridFieldAction()); $this->addComponent(new CommentsGridFieldAction());
// Format column // Format column
$columns = $this->getComponentByType('GridFieldDataColumns'); $columns = $this->getComponentByType('GridFieldDataColumns');
$columns->setFieldFormatting(array( $columns->setFieldFormatting(array(
'ParentTitle' => function($value, &$item) { 'ParentTitle' => function ($value, &$item) {
return sprintf( return sprintf(
'<a href="%s" class="cms-panel-link external-link action" target="_blank">%s</a>', '<a href="%s" class="cms-panel-link external-link action" target="_blank">%s</a>',
Convert::raw2att($item->Link()), Convert::raw2att($item->Link()),
$item->obj('ParentTitle')->forTemplate() $item->obj('ParentTitle')->forTemplate()
); );
} }
)); ));
// Add bulk option // Add bulk option
$manager = new GridFieldBulkManager(); $manager = new GridFieldBulkManager();
$manager->addBulkAction( $manager->addBulkAction(
'spam', 'Spam', 'CommentsGridFieldBulkAction_Handlers', 'spam', 'Spam', 'CommentsGridFieldBulkAction_Handlers',
array( array(
'isAjax' => true, 'isAjax' => true,
'icon' => 'cross', 'icon' => 'cross',
'isDestructive' => false 'isDestructive' => false
) )
); );
$manager->addBulkAction( $manager->addBulkAction(
'approve', 'Approve', 'CommentsGridFieldBulkAction_Handlers', 'approve', 'Approve', 'CommentsGridFieldBulkAction_Handlers',
array( array(
'isAjax' => true, 'isAjax' => true,
'icon' => 'cross', 'icon' => 'cross',
'isDestructive' => false 'isDestructive' => false
) )
); );
$manager->removeBulkAction('bulkEdit'); $manager->removeBulkAction('bulkEdit');
$manager->removeBulkAction('unLink'); $manager->removeBulkAction('unLink');
$this->addComponent($manager); $this->addComponent($manager);
} }
} }

View File

@ -4,284 +4,316 @@
* @package comments * @package comments
*/ */
class CommentingController extends Controller { class CommentingController extends Controller
{
private static $allowed_actions = array( private static $allowed_actions = array(
'delete', 'delete',
'spam', 'spam',
'ham', 'ham',
'approve', 'approve',
'rss', 'rss',
'CommentsForm', 'CommentsForm',
'reply', 'reply',
'doPostComment', 'doPostComment',
'doPreviewComment' 'doPreviewComment'
); );
private static $url_handlers = array( private static $url_handlers = array(
'reply/$ParentCommentID//$ID/$OtherID' => 'reply', 'reply/$ParentCommentID//$ID/$OtherID' => 'reply',
); );
/** /**
* Fields required for this form * Fields required for this form
* *
* @var array * @var array
* @config * @config
*/ */
private static $required_fields = array( private static $required_fields = array(
'Name', 'Name',
'Email', 'Email',
'Comment' 'Comment'
); );
/** /**
* Base class this commenting form is for * Base class this commenting form is for
* *
* @var string * @var string
*/ */
private $baseClass = ""; private $baseClass = "";
/** /**
* The record this commenting form is for * The record this commenting form is for
* *
* @var DataObject * @var DataObject
*/ */
private $ownerRecord = null; private $ownerRecord = null;
/** /**
* Parent controller record * Parent controller record
* *
* @var Controller * @var Controller
*/ */
private $ownerController = null; private $ownerController = null;
/** /**
* Backup url to return to * Backup url to return to
* *
* @var string * @var string
*/ */
protected $fallbackReturnURL = null; protected $fallbackReturnURL = null;
/** /**
* Set the base class to use * Set the base class to use
* *
* @param string $class * @param string $class
*/ */
public function setBaseClass($class) { public function setBaseClass($class)
$this->baseClass = $class; {
} $this->baseClass = $class;
}
/** /**
* Get the base class used * Get the base class used
* *
* @return string * @return string
*/ */
public function getBaseClass() { public function getBaseClass()
return $this->baseClass; {
} return $this->baseClass;
}
/** /**
* Set the record this controller is working on * Set the record this controller is working on
* *
* @param DataObject $record * @param DataObject $record
*/ */
public function setOwnerRecord($record) { public function setOwnerRecord($record)
$this->ownerRecord = $record; {
} $this->ownerRecord = $record;
}
/** /**
* Get the record * Get the record
* *
* @return DataObject * @return DataObject
*/ */
public function getOwnerRecord() { public function getOwnerRecord()
return $this->ownerRecord; {
} return $this->ownerRecord;
}
/** /**
* Set the parent controller * Set the parent controller
* *
* @param Controller $controller * @param Controller $controller
*/ */
public function setOwnerController($controller) { public function setOwnerController($controller)
$this->ownerController = $controller; {
} $this->ownerController = $controller;
}
/** /**
* Get the parent controller * Get the parent controller
* *
* @return Controller * @return Controller
*/ */
public function getOwnerController() { public function getOwnerController()
return $this->ownerController; {
} return $this->ownerController;
}
/** /**
* Get the commenting option for the current state * Get the commenting option for the current state
* *
* @param string $key * @param string $key
* @return mixed Result if the setting is available, or null otherwise * @return mixed Result if the setting is available, or null otherwise
*/ */
public function getOption($key) { public function getOption($key)
// If possible use the current record {
if($record = $this->getOwnerRecord()) { // If possible use the current record
return $record->getCommentsOption($key); if ($record = $this->getOwnerRecord()) {
} return $record->getCommentsOption($key);
}
// Otherwise a singleton of that record // Otherwise a singleton of that record
if($class = $this->getBaseClass()) { if ($class = $this->getBaseClass()) {
return singleton($class)->getCommentsOption($key); return singleton($class)->getCommentsOption($key);
} }
// Otherwise just use the default options // Otherwise just use the default options
return singleton('CommentsExtension')->getCommentsOption($key); return singleton('CommentsExtension')->getCommentsOption($key);
} }
/** /**
* Workaround for generating the link to this controller * Workaround for generating the link to this controller
* *
* @return string * @return string
*/ */
public function Link($action = '', $id = '', $other = '') { public function Link($action = '', $id = '', $other = '')
return Controller::join_links(Director::baseURL(), __CLASS__ , $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 * @return HTMLText
*/ */
public function rss() { public function rss()
return $this->getFeed($this->request)->outputToBrowser(); {
} return $this->getFeed($this->request)->outputToBrowser();
}
/** /**
* Return an RSSFeed of comments for a given set of comments or all * Return an RSSFeed of comments for a given set of comments or all
* comments on the website. * comments on the website.
* *
* To maintain backwards compatibility with 2.4 this supports mapping * To maintain backwards compatibility with 2.4 this supports mapping
* of PageComment/rss?pageid= as well as the new RSS format for comments * of PageComment/rss?pageid= as well as the new RSS format for comments
* of CommentingController/rss/{classname}/{id} * of CommentingController/rss/{classname}/{id}
* *
* @param SS_HTTPRequest * @param SS_HTTPRequest
* *
* @return RSSFeed * @return RSSFeed
*/ */
public function getFeed(SS_HTTPRequest $request) { public function getFeed(SS_HTTPRequest $request)
$link = $this->Link('rss'); {
$class = $request->param('ID'); $link = $this->Link('rss');
$id = $request->param('OtherID'); $class = $request->param('ID');
$id = $request->param('OtherID');
// Support old pageid param // Support old pageid param
if(!$id && !$class && ($id = $request->getVar('pageid'))) { if (!$id && !$class && ($id = $request->getVar('pageid'))) {
$class = 'SiteTree'; $class = 'SiteTree';
} }
$comments = Comment::get()->filter(array( $comments = Comment::get()->filter(array(
'Moderated' => 1, 'Moderated' => 1,
'IsSpam' => 0, 'IsSpam' => 0,
)); ));
// Check if class filter // Check if class filter
if($class) { if ($class) {
if(!is_subclass_of($class, 'DataObject') || !$class::has_extension('CommentsExtension')) { if (!is_subclass_of($class, 'DataObject') || !$class::has_extension('CommentsExtension')) {
return $this->httpError(404); return $this->httpError(404);
} }
$this->setBaseClass($class); $this->setBaseClass($class);
$comments = $comments->filter('BaseClass', $class); $comments = $comments->filter('BaseClass', $class);
$link = Controller::join_links($link, $class); $link = Controller::join_links($link, $class);
// Check if id filter // Check if id filter
if($id) { if ($id) {
$comments = $comments->filter('ParentID', $id); $comments = $comments->filter('ParentID', $id);
$link = Controller::join_links($link, $id); $link = Controller::join_links($link, $id);
$this->setOwnerRecord(DataObject::get_by_id($class, $id)); $this->setOwnerRecord(DataObject::get_by_id($class, $id));
} }
} }
$title = _t('CommentingController.RSSTITLE', "Comments RSS Feed"); $title = _t('CommentingController.RSSTITLE', "Comments RSS Feed");
$comments = new PaginatedList($comments, $request); $comments = new PaginatedList($comments, $request);
$comments->setPageLength($this->getOption('comments_per_page')); $comments->setPageLength($this->getOption('comments_per_page'));
return new RSSFeed( return new RSSFeed(
$comments, $comments,
$link, $link,
$title, $title,
$link, $link,
'Title', 'EscapedComment', 'AuthorName' 'Title', 'EscapedComment', 'AuthorName'
); );
} }
/** /**
* Deletes a given {@link Comment} via the URL. * Deletes a given {@link Comment} via the URL.
*/ */
public function delete() { public function delete()
$comment = $this->getComment(); {
if(!$comment) return $this->httpError(404); $comment = $this->getComment();
if(!$comment->canDelete()) { if (!$comment) {
return Security::permissionFailure($this, 'You do not have permission to delete this comment'); return $this->httpError(404);
} }
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400); 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);
}
$comment->delete(); $comment->delete();
return $this->request->isAjax() return $this->request->isAjax()
? true ? true
: $this->redirectBack(); : $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() { public function spam()
$comment = $this->getComment(); {
if(!$comment) return $this->httpError(404); $comment = $this->getComment();
if(!$comment->canEdit()) { if (!$comment) {
return Security::permissionFailure($this, 'You do not have permission to edit this comment'); return $this->httpError(404);
} }
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400); 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);
}
$comment->markSpam(); $comment->markSpam();
return $this->renderChangedCommentState($comment); return $this->renderChangedCommentState($comment);
} }
/** /**
* Marks a given {@link Comment} as ham (not spam). * Marks a given {@link Comment} as ham (not spam).
*/ */
public function ham() { public function ham()
$comment = $this->getComment(); {
if(!$comment) return $this->httpError(404); $comment = $this->getComment();
if(!$comment->canEdit()) { if (!$comment) {
return Security::permissionFailure($this, 'You do not have permission to edit this comment'); return $this->httpError(404);
} }
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400); 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);
}
$comment->markApproved(); $comment->markApproved();
return $this->renderChangedCommentState($comment); return $this->renderChangedCommentState($comment);
} }
/** /**
* Marks a given {@link Comment} as approved. * Marks a given {@link Comment} as approved.
*/ */
public function approve() { public function approve()
$comment = $this->getComment(); {
if(!$comment) return $this->httpError(404); $comment = $this->getComment();
if(!$comment->canEdit()) { if (!$comment) {
return Security::permissionFailure($this, 'You do not have permission to approve this comment'); return $this->httpError(404);
} }
if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400); 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);
}
$comment->markApproved(); $comment->markApproved();
return $this->renderChangedCommentState($comment); return $this->renderChangedCommentState($comment);
} }
/** /**
* Redirect back to referer if available, ensuring that only site URLs * Redirect back to referer if available, ensuring that only site URLs
* are allowed to avoid phishing. If it's an AJAX request render the * are allowed to avoid phishing. If it's an AJAX request render the
* comment in it's new state * comment in it's new state
*/ */
private function renderChangedCommentState($comment) { private function renderChangedCommentState($comment)
{
$referer = $this->request->getHeader('Referer'); $referer = $this->request->getHeader('Referer');
// Render comment using AJAX // Render comment using AJAX
@ -295,7 +327,7 @@ class CommentingController extends Controller {
// Redirect to the comment, but check for phishing // Redirect to the comment, but check for phishing
$url = $referer . '#comment-' . $comment->ID; $url = $referer . '#comment-' . $comment->ID;
// absolute redirection URLs not located on this site may cause phishing // absolute redirection URLs not located on this site may cause phishing
if(Director::is_site_url($url)) { if (Director::is_site_url($url)) {
return $this->redirect($url); return $this->redirect($url);
} else { } else {
return false; return false;
@ -304,336 +336,348 @@ class CommentingController extends Controller {
} }
} }
/** /**
* Returns the comment referenced in the URL (by ID). Permission checking * Returns the comment referenced in the URL (by ID). Permission checking
* should be done in the callee. * should be done in the callee.
* *
* @return Comment|false * @return Comment|false
*/ */
public function getComment() { public function getComment()
$id = isset($this->urlParams['ID']) ? $this->urlParams['ID'] : false; {
$id = isset($this->urlParams['ID']) ? $this->urlParams['ID'] : false;
if($id) { if ($id) {
$comment = DataObject::get_by_id('Comment', $id); $comment = DataObject::get_by_id('Comment', $id);
if($comment) { if ($comment) {
$this->fallbackReturnURL = $comment->Link(); $this->fallbackReturnURL = $comment->Link();
return $comment; return $comment;
} }
} }
return false; return false;
} }
/** /**
* Create a reply form for a specified comment * Create a reply form for a specified comment
* *
* @param Comment $comment * @param Comment $comment
*/ */
public function ReplyForm($comment) { public function ReplyForm($comment)
// Enables multiple forms with different names to use the same handler {
$form = $this->CommentsForm(); // Enables multiple forms with different names to use the same handler
$form->setName('ReplyForm_'.$comment->ID); $form = $this->CommentsForm();
$form->addExtraClass('reply-form'); $form->setName('ReplyForm_'.$comment->ID);
$form->addExtraClass('reply-form');
// Load parent into reply form // Load parent into reply form
$form->loadDataFrom(array( $form->loadDataFrom(array(
'ParentCommentID' => $comment->ID 'ParentCommentID' => $comment->ID
)); ));
// Customise action // Customise action
$form->setFormAction($this->Link('reply', $comment->ID)); $form->setFormAction($this->Link('reply', $comment->ID));
$this->extend('updateReplyForm', $form); $this->extend('updateReplyForm', $form);
return $form; return $form;
} }
/** /**
* Request handler for reply 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 * @param SS_HTTPRequest $request
*/ */
public function reply(SS_HTTPRequest $request) { public function reply(SS_HTTPRequest $request)
// Extract parent comment from reply and build this way {
if($parentID = $request->param('ParentCommentID')) { // Extract parent comment from reply and build this way
$comment = DataObject::get_by_id('Comment', $parentID, true); if ($parentID = $request->param('ParentCommentID')) {
if($comment) { $comment = DataObject::get_by_id('Comment', $parentID, true);
return $this->ReplyForm($comment); if ($comment) {
} return $this->ReplyForm($comment);
} }
return $this->httpError(404); }
} return $this->httpError(404);
}
/** /**
* Post a comment form * Post a comment form
* *
* @return Form * @return Form
*/ */
public function CommentsForm() { public function CommentsForm()
$usePreview = $this->getOption('use_preview'); {
$usePreview = $this->getOption('use_preview');
$nameRequired = _t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name'); $nameRequired = _t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name');
$emailRequired = _t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address'); $emailRequired = _t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address');
$emailInvalid = _t('CommentInterface.EMAILADDRESS_MESSAGE_EMAIL', 'Please enter a valid 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'); $urlInvalid = _t('CommentInterface.COMMENT_MESSAGE_URL', 'Please enter a valid URL');
$commentRequired = _t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment'); $commentRequired = _t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment');
$fields = new FieldList( $fields = new FieldList(
$dataFields = new CompositeField( $dataFields = new CompositeField(
// Name // Name
TextField::create("Name", _t('CommentInterface.YOURNAME', 'Your name')) TextField::create("Name", _t('CommentInterface.YOURNAME', 'Your name'))
->setCustomValidationMessage($nameRequired) ->setCustomValidationMessage($nameRequired)
->setAttribute('data-msg-required', $nameRequired), ->setAttribute('data-msg-required', $nameRequired),
// Email // Email
EmailField::create( EmailField::create(
"Email", "Email",
_t('CommentingController.EMAILADDRESS', "Your email address (will not be published)") _t('CommentingController.EMAILADDRESS', "Your email address (will not be published)")
) )
->setCustomValidationMessage($emailRequired) ->setCustomValidationMessage($emailRequired)
->setAttribute('data-msg-required', $emailRequired) ->setAttribute('data-msg-required', $emailRequired)
->setAttribute('data-msg-email', $emailInvalid) ->setAttribute('data-msg-email', $emailInvalid)
->setAttribute('data-rule-email', true), ->setAttribute('data-rule-email', true),
// Url // Url
TextField::create("URL", _t('CommentingController.WEBSITEURL', "Your website URL")) TextField::create("URL", _t('CommentingController.WEBSITEURL', "Your website URL"))
->setAttribute('data-msg-url', $urlInvalid) ->setAttribute('data-msg-url', $urlInvalid)
->setAttribute('data-rule-url', true), ->setAttribute('data-rule-url', true),
// Comment // Comment
TextareaField::create("Comment", _t('CommentingController.COMMENTS', "Comments")) TextareaField::create("Comment", _t('CommentingController.COMMENTS', "Comments"))
->setCustomValidationMessage($commentRequired) ->setCustomValidationMessage($commentRequired)
->setAttribute('data-msg-required', $commentRequired) ->setAttribute('data-msg-required', $commentRequired)
), ),
HiddenField::create("ParentID"), HiddenField::create("ParentID"),
HiddenField::create("ReturnURL"), HiddenField::create("ReturnURL"),
HiddenField::create("ParentCommentID"), HiddenField::create("ParentCommentID"),
HiddenField::create("BaseClass") HiddenField::create("BaseClass")
); );
// Preview formatted comment. Makes most sense when shortcodes or // Preview formatted comment. Makes most sense when shortcodes or
// limited HTML is allowed. Populated by JS/Ajax. // limited HTML is allowed. Populated by JS/Ajax.
if($usePreview) { if ($usePreview) {
$fields->insertAfter( $fields->insertAfter(
ReadonlyField::create('PreviewComment', _t('CommentInterface.PREVIEWLABEL', 'Preview')) ReadonlyField::create('PreviewComment', _t('CommentInterface.PREVIEWLABEL', 'Preview'))
->setAttribute('style', 'display: none'), // enable through JS ->setAttribute('style', 'display: none'), // enable through JS
'Comment' 'Comment'
); );
} }
$dataFields->addExtraClass('data-fields'); $dataFields->addExtraClass('data-fields');
// save actions // save actions
$actions = new FieldList( $actions = new FieldList(
new FormAction("doPostComment", _t('CommentInterface.POST', 'Post')) new FormAction("doPostComment", _t('CommentInterface.POST', 'Post'))
); );
if($usePreview) { if ($usePreview) {
$actions->push( $actions->push(
FormAction::create('doPreviewComment', _t('CommentInterface.PREVIEW', 'Preview')) FormAction::create('doPreviewComment', _t('CommentInterface.PREVIEW', 'Preview'))
->addExtraClass('action-minor') ->addExtraClass('action-minor')
->setAttribute('style', 'display: none') // enable through JS ->setAttribute('style', 'display: none') // enable through JS
); );
} }
// required fields for server side // required fields for server side
$required = new RequiredFields($this->config()->required_fields); $required = new RequiredFields($this->config()->required_fields);
// create the comment form // 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 the record exists load the extra required data
if($record = $this->getOwnerRecord()) { if ($record = $this->getOwnerRecord()) {
// Load member data // Load member data
$member = Member::currentUser(); $member = Member::currentUser();
if(($record->CommentsRequireLogin || $record->PostingRequiredPermission) && $member) { if (($record->CommentsRequireLogin || $record->PostingRequiredPermission) && $member) {
$fields = $form->Fields(); $fields = $form->Fields();
$fields->removeByName('Name'); $fields->removeByName('Name');
$fields->removeByName('Email'); $fields->removeByName('Email');
$fields->insertBefore(new ReadonlyField("NameView", _t('CommentInterface.YOURNAME', 'Your name'), $member->getName()), 'URL'); $fields->insertBefore(new ReadonlyField("NameView", _t('CommentInterface.YOURNAME', 'Your name'), $member->getName()), 'URL');
$fields->push(new HiddenField("Name", "", $member->getName())); $fields->push(new HiddenField("Name", "", $member->getName()));
$fields->push(new HiddenField("Email", "", $member->Email)); $fields->push(new HiddenField("Email", "", $member->Email));
} }
// we do not want to read a new URL when the form has already been submitted // we do not want to read a new URL when the form has already been submitted
// which in here, it hasn't been. // which in here, it hasn't been.
$form->loadDataFrom(array( $form->loadDataFrom(array(
'ParentID' => $record->ID, 'ParentID' => $record->ID,
'ReturnURL' => $this->request->getURL(), 'ReturnURL' => $this->request->getURL(),
'BaseClass' => $this->getBaseClass() 'BaseClass' => $this->getBaseClass()
)); ));
} }
// Set it so the user gets redirected back down to the form upon form fail // Set it so the user gets redirected back down to the form upon form fail
$form->setRedirectToFormOnValidationError(true); $form->setRedirectToFormOnValidationError(true);
// load any data from the cookies // load any data from the cookies
if($data = Cookie::get('CommentsForm_UserData')) { if ($data = Cookie::get('CommentsForm_UserData')) {
$data = Convert::json2array($data); $data = Convert::json2array($data);
$form->loadDataFrom(array( $form->loadDataFrom(array(
"Name" => isset($data['Name']) ? $data['Name'] : '', "Name" => isset($data['Name']) ? $data['Name'] : '',
"URL" => isset($data['URL']) ? $data['URL'] : '', "URL" => isset($data['URL']) ? $data['URL'] : '',
"Email" => isset($data['Email']) ? $data['Email'] : '' "Email" => isset($data['Email']) ? $data['Email'] : ''
)); ));
// allow previous value to fill if comment not stored in cookie (i.e. validation error) // allow previous value to fill if comment not stored in cookie (i.e. validation error)
$prevComment = Cookie::get('CommentsForm_Comment'); $prevComment = Cookie::get('CommentsForm_Comment');
if($prevComment && $prevComment != ''){ if ($prevComment && $prevComment != '') {
$form->loadDataFrom(array("Comment" => $prevComment)); $form->loadDataFrom(array("Comment" => $prevComment));
} }
} }
if(!empty($member)) { if (!empty($member)) {
$form->loadDataFrom($member); $form->loadDataFrom($member);
} }
// hook to allow further extensions to alter the comments form // hook to allow further extensions to alter the comments form
$this->extend('alterCommentForm', $form); $this->extend('alterCommentForm', $form);
return $form; return $form;
} }
/** /**
* Process which creates a {@link Comment} once a user submits a comment from this form. * Process which creates a {@link Comment} once a user submits a comment from this form.
* *
* @param array $data * @param array $data
* @param Form $form * @param Form $form
*/ */
public function doPostComment($data, $form) { public function doPostComment($data, $form)
// Load class and parent from data {
if(isset($data['BaseClass'])) { // Load class and parent from data
$this->setBaseClass($data['BaseClass']); if (isset($data['BaseClass'])) {
} $this->setBaseClass($data['BaseClass']);
if(isset($data['ParentID']) && ($class = $this->getBaseClass())) { }
$this->setOwnerRecord($class::get()->byID($data['ParentID'])); if (isset($data['ParentID']) && ($class = $this->getBaseClass())) {
} $this->setOwnerRecord($class::get()->byID($data['ParentID']));
if(!$this->getOwnerRecord()) return $this->httpError(404); }
if (!$this->getOwnerRecord()) {
return $this->httpError(404);
}
// cache users data // cache users data
Cookie::set("CommentsForm_UserData", Convert::raw2json($data)); Cookie::set("CommentsForm_UserData", Convert::raw2json($data));
Cookie::set("CommentsForm_Comment", $data['Comment']); Cookie::set("CommentsForm_Comment", $data['Comment']);
// extend hook to allow extensions. Also see onAfterPostComment // extend hook to allow extensions. Also see onAfterPostComment
$this->extend('onBeforePostComment', $form); $this->extend('onBeforePostComment', $form);
// If commenting can only be done by logged in users, make sure the user is logged in // If commenting can only be done by logged in users, make sure the user is logged in
if(!$this->getOwnerRecord()->canPostComment()) { if (!$this->getOwnerRecord()->canPostComment()) {
return Security::permissionFailure( return Security::permissionFailure(
$this, $this,
_t( _t(
'CommentingController.PERMISSIONFAILURE', 'CommentingController.PERMISSIONFAILURE',
"You're not able to post comments to this page. Please ensure you are logged in and have an " "You're not able to post comments to this page. Please ensure you are logged in and have an "
. "appropriate permission level." . "appropriate permission level."
) )
); );
} }
if($member = Member::currentUser()) { 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? // What kind of moderation is required?
switch($this->getOwnerRecord()->ModerationRequired) { switch ($this->getOwnerRecord()->ModerationRequired) {
case 'Required': case 'Required':
$requireModeration = true; $requireModeration = true;
break; break;
case 'NonMembersOnly': case 'NonMembersOnly':
$requireModeration = empty($member); $requireModeration = empty($member);
break; break;
case 'None': case 'None':
default: default:
$requireModeration = false; $requireModeration = false;
break; break;
} }
$comment = new Comment(); $comment = new Comment();
$form->saveInto($comment); $form->saveInto($comment);
$comment->AllowHtml = $this->getOption('html_allowed'); $comment->AllowHtml = $this->getOption('html_allowed');
$comment->Moderated = !$requireModeration; $comment->Moderated = !$requireModeration;
// Save into DB, or call pre-save hooks to give accurate preview // Save into DB, or call pre-save hooks to give accurate preview
$usePreview = $this->getOption('use_preview'); $usePreview = $this->getOption('use_preview');
$isPreview = $usePreview && !empty($data['IsPreview']); $isPreview = $usePreview && !empty($data['IsPreview']);
if($isPreview) { if ($isPreview) {
$comment->extend('onBeforeWrite'); $comment->extend('onBeforeWrite');
} else { } else {
$comment->write(); $comment->write();
// extend hook to allow extensions. Also see onBeforePostComment // extend hook to allow extensions. Also see onBeforePostComment
$this->extend('onAfterPostComment', $comment); $this->extend('onAfterPostComment', $comment);
} }
// we want to show a notification if comments are moderated // we want to show a notification if comments are moderated
if ($requireModeration && !$comment->IsSpam) { if ($requireModeration && !$comment->IsSpam) {
Session::set('CommentsModerated', 1); Session::set('CommentsModerated', 1);
} }
// clear the users comment since it passed validation // clear the users comment since it passed validation
Cookie::set('CommentsForm_Comment', false); Cookie::set('CommentsForm_Comment', false);
// Find parent link // Find parent link
if(!empty($data['ReturnURL'])) { if (!empty($data['ReturnURL'])) {
$url = $data['ReturnURL']; $url = $data['ReturnURL'];
} elseif($parent = $comment->getParent()) { } elseif ($parent = $comment->getParent()) {
$url = $parent->Link(); $url = $parent->Link();
} else { } else {
return $this->redirectBack(); return $this->redirectBack();
} }
// Given a redirect page exists, attempt to link to the correct anchor // Given a redirect page exists, attempt to link to the correct anchor
if($comment->IsSpam) { if ($comment->IsSpam) {
// Link to the form with the error message contained // Link to the form with the error message contained
$hash = $form->FormName(); $hash = $form->FormName();
} else if(!$comment->Moderated) { } elseif (!$comment->Moderated) {
// Display the "awaiting moderation" text // Display the "awaiting moderation" text
$holder = $this->getOption('comments_holder_id'); $holder = $this->getOption('comments_holder_id');
$hash = "{$holder}_PostCommentForm_error"; $hash = "{$holder}_PostCommentForm_error";
} else { } else {
// Link to the moderated, non-spam comment // Link to the moderated, non-spam comment
$hash = $comment->Permalink(); $hash = $comment->Permalink();
} }
return $this->redirect(Controller::join_links($url, "#{$hash}")); return $this->redirect(Controller::join_links($url, "#{$hash}"));
} }
public function doPreviewComment($data, $form) { public function doPreviewComment($data, $form)
$data['IsPreview'] = 1; {
$data['IsPreview'] = 1;
return $this->doPostComment($data, $form); return $this->doPostComment($data, $form);
} }
public function redirectBack() { public function redirectBack()
// Don't cache the redirect back ever {
HTTP::set_cache_age(0); // Don't cache the redirect back ever
HTTP::set_cache_age(0);
$url = null; $url = null;
// In edge-cases, this will be called outside of a handleRequest() context; in that case, // 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 // 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 // be calling from a test context or something else where the global state is inappropraite
if($this->request) { if ($this->request) {
if($this->request->requestVar('BackURL')) { if ($this->request->requestVar('BackURL')) {
$url = $this->request->requestVar('BackURL'); $url = $this->request->requestVar('BackURL');
} else if($this->request->isAjax() && $this->request->getHeader('X-Backurl')) { } elseif ($this->request->isAjax() && $this->request->getHeader('X-Backurl')) {
$url = $this->request->getHeader('X-Backurl'); $url = $this->request->getHeader('X-Backurl');
} else if($this->request->getHeader('Referer')) { } elseif ($this->request->getHeader('Referer')) {
$url = $this->request->getHeader('Referer'); $url = $this->request->getHeader('Referer');
} }
} }
if(!$url) $url = $this->fallbackReturnURL; if (!$url) {
if(!$url) $url = Director::baseURL(); $url = $this->fallbackReturnURL;
}
if (!$url) {
$url = Director::baseURL();
}
// absolute redirection URLs not located on this site may cause phishing // absolute redirection URLs not located on this site may cause phishing
if(Director::is_site_url($url)) { if (Director::is_site_url($url)) {
return $this->redirect($url); return $this->redirect($url);
} else { } else {
return false; return false;
} }
}
}
} }

View File

@ -5,558 +5,600 @@
* *
* @package comments * @package comments
*/ */
class CommentsExtension extends DataExtension { class CommentsExtension extends DataExtension
/** {
* Default configuration values /**
* * Default configuration values
* enabled: Allows commenting to be disabled even if the extension is present *
* enabled_cms: Allows commenting to be enabled or disabled via the CMS * enabled: Allows commenting to be disabled even if the extension is present
* require_login: Boolean, whether a user needs to login (required for required_permission) * enabled_cms: Allows commenting to be enabled or disabled via the CMS
* require_login_cms: Allows require_login to be set via the CMS * require_login: Boolean, whether a user needs to login (required for required_permission)
* required_permission: Permission (or array of permissions) required to comment * require_login_cms: Allows require_login to be set via the CMS
* include_js: Enhance operation by ajax behaviour on moderation links (required for use_preview) * required_permission: Permission (or array of permissions) required to comment
* use_gravatar: Set to true to show gravatar icons * include_js: Enhance operation by ajax behaviour on moderation links (required for use_preview)
* gravatar_default: Theme for 'not found' gravatar {@see http://gravatar.com/site/implement/images} * use_gravatar: Set to true to show gravatar icons
* gravatar_rating: Gravatar rating (same as the standard default) * gravatar_default: Theme for 'not found' gravatar {@see http://gravatar.com/site/implement/images}
* show_comments_when_disabled: Show older comments when commenting has been disabled. * gravatar_rating: Gravatar rating (same as the standard default)
* order_comments_by: Default sort order. * show_comments_when_disabled: Show older comments when commenting has been disabled.
* order_replies_by: Sort order for replies. * order_comments_by: Default sort order.
* comments_holder_id: ID for the comments holder * order_replies_by: Sort order for replies.
* comment_permalink_prefix: ID prefix for each comment * comments_holder_id: ID for the comments holder
* require_moderation: Require moderation for all comments * comment_permalink_prefix: ID prefix for each comment
* require_moderation_cms: Ignore other comment moderation config settings and set via CMS * require_moderation: Require moderation for all comments
* frontend_moderation: Display unmoderated comments in the frontend, if the user can moderate them. * require_moderation_cms: Ignore other comment moderation config settings and set via CMS
* frontend_spam: Display spam comments in the frontend, if the user can moderate them. * frontend_moderation: Display unmoderated comments in the frontend, if the user can moderate them.
* html_allowed: Allow for sanitized HTML in comments * frontend_spam: Display spam comments in the frontend, if the user can moderate them.
* use_preview: Preview formatted comment (when allowing HTML) * html_allowed: Allow for sanitized HTML in comments
* nested_comments: Enable nested comments * use_preview: Preview formatted comment (when allowing HTML)
* nested_depth: Max depth of nested comments in levels (where root is 1 depth) 0 means no limit. * 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 *
* * @var array
* @config *
*/ * @config
private static $comments = array( */
'enabled' => true, private static $comments = array(
'enabled_cms' => false, 'enabled' => true,
'require_login' => false, 'enabled_cms' => false,
'require_login_cms' => false, 'require_login' => false,
'required_permission' => false, 'require_login_cms' => false,
'include_js' => true, 'required_permission' => false,
'use_gravatar' => false, 'include_js' => true,
'gravatar_size' => 80, 'use_gravatar' => false,
'gravatar_default' => 'identicon', 'gravatar_size' => 80,
'gravatar_rating' => 'g', 'gravatar_default' => 'identicon',
'show_comments_when_disabled' => false, 'gravatar_rating' => 'g',
'order_comments_by' => '"Created" DESC', 'show_comments_when_disabled' => false,
'order_replies_by' => false, 'order_comments_by' => '"Created" DESC',
'comments_per_page' => 10, 'order_replies_by' => false,
'comments_holder_id' => 'comments-holder', 'comments_per_page' => 10,
'comment_permalink_prefix' => 'comment-', 'comments_holder_id' => 'comments-holder',
'require_moderation' => false, 'comment_permalink_prefix' => 'comment-',
'require_moderation_nonmembers' => false, 'require_moderation' => false,
'require_moderation_cms' => false, 'require_moderation_nonmembers' => false,
'frontend_moderation' => false, 'require_moderation_cms' => false,
'frontend_spam' => false, 'frontend_moderation' => false,
'html_allowed' => false, 'frontend_spam' => false,
'html_allowed_elements' => array('a', 'img', 'i', 'b'), 'html_allowed' => false,
'use_preview' => false, 'html_allowed_elements' => array('a', 'img', 'i', 'b'),
'nested_comments' => false, 'use_preview' => false,
'nested_depth' => 2, 'nested_comments' => false,
); 'nested_depth' => 2,
);
/** /**
* @var array * @var array
*/ */
private static $db = array( private static $db = array(
'ProvideComments' => 'Boolean', 'ProvideComments' => 'Boolean',
'ModerationRequired' => 'Enum(\'None,Required,NonMembersOnly\',\'None\')', 'ModerationRequired' => 'Enum(\'None,Required,NonMembersOnly\',\'None\')',
'CommentsRequireLogin' => 'Boolean', 'CommentsRequireLogin' => 'Boolean',
); );
/** /**
* CMS configurable options should default to the config values, but respect * CMS configurable options should default to the config values, but respect
* default values specified by the object * default values specified by the object
*/ */
public function populateDefaults() { public function populateDefaults()
$defaults = $this->owner->config()->defaults; {
$defaults = $this->owner->config()->defaults;
// Set if comments should be enabled by default // Set if comments should be enabled by default
if(isset($defaults['ProvideComments'])) { if (isset($defaults['ProvideComments'])) {
$this->owner->ProvideComments = $defaults['ProvideComments']; $this->owner->ProvideComments = $defaults['ProvideComments'];
} else { } else {
$this->owner->ProvideComments = $this->owner->getCommentsOption('enabled') ? 1 : 0; $this->owner->ProvideComments = $this->owner->getCommentsOption('enabled') ? 1 : 0;
} }
// If moderation options should be configurable via the CMS then // If moderation options should be configurable via the CMS then
if(isset($defaults['ModerationRequired'])) { if (isset($defaults['ModerationRequired'])) {
$this->owner->ModerationRequired = $defaults['ModerationRequired']; $this->owner->ModerationRequired = $defaults['ModerationRequired'];
} elseif($this->owner->getCommentsOption('require_moderation')) { } elseif ($this->owner->getCommentsOption('require_moderation')) {
$this->owner->ModerationRequired = 'Required'; $this->owner->ModerationRequired = 'Required';
} elseif($this->owner->getCommentsOption('require_moderation_nonmembers')) { } elseif ($this->owner->getCommentsOption('require_moderation_nonmembers')) {
$this->owner->ModerationRequired = 'NonMembersOnly'; $this->owner->ModerationRequired = 'NonMembersOnly';
} else { } else {
$this->owner->ModerationRequired = 'None'; $this->owner->ModerationRequired = 'None';
} }
// Set login required // Set login required
if(isset($defaults['CommentsRequireLogin'])) { if (isset($defaults['CommentsRequireLogin'])) {
$this->owner->CommentsRequireLogin = $defaults['CommentsRequireLogin']; $this->owner->CommentsRequireLogin = $defaults['CommentsRequireLogin'];
} else { } else {
$this->owner->CommentsRequireLogin = $this->owner->getCommentsOption('require_login') ? 1 : 0; $this->owner->CommentsRequireLogin = $this->owner->getCommentsOption('require_login') ? 1 : 0;
} }
} }
/** /**
* If this extension is applied to a {@link SiteTree} record then * If this extension is applied to a {@link SiteTree} record then
* append a Provide Comments checkbox to allow authors to trigger * append a Provide Comments checkbox to allow authors to trigger
* whether or not to display comments * whether or not to display comments
* *
* @todo Allow customization of other {@link Commenting} configuration * @todo Allow customization of other {@link Commenting} configuration
* *
* @param FieldList $fields * @param FieldList $fields
*/ */
public function updateSettingsFields(FieldList $fields) { public function updateSettingsFields(FieldList $fields)
{
$options = FieldGroup::create()->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')));
}
// Check if enabled setting should be cms configurable // Check if we should require users to login to comment
if($this->owner->getCommentsOption('enabled_cms')) { if ($this->owner->getCommentsOption('require_login_cms')) {
$options->push(new CheckboxField('ProvideComments', _t('Comment.ALLOWCOMMENTS', 'Allow Comments'))); $options->push(
} new CheckboxField(
'CommentsRequireLogin',
_t('Comments.COMMENTSREQUIRELOGIN', 'Require login to comment')
)
);
}
// Check if we should require users to login to comment if ($options->FieldList()->count()) {
if($this->owner->getCommentsOption('require_login_cms')) { if ($fields->hasTabSet()) {
$options->push( $fields->addFieldsToTab('Root.Settings', $options);
new CheckboxField( } else {
'CommentsRequireLogin', $fields->push($options);
_t('Comments.COMMENTSREQUIRELOGIN', 'Require login to comment') }
) }
);
}
if($options->FieldList()->count()) { // Check if moderation should be enabled via cms configurable
if($fields->hasTabSet()) { if ($this->owner->getCommentsOption('require_moderation_cms')) {
$fields->addFieldsToTab('Root.Settings', $options); $moderationField = new DropdownField('ModerationRequired', 'Comment Moderation', array(
} else { 'None' => _t('CommentsExtension.MODERATIONREQUIRED_NONE', 'No moderation required'),
$fields->push($options); 'Required' => _t('CommentsExtension.MODERATIONREQUIRED_REQUIRED', 'Moderate all comments'),
} 'NonMembersOnly' => _t(
} 'CommentsExtension.MODERATIONREQUIRED_NONMEMBERSONLY',
'Only moderate non-members'
),
));
if ($fields->hasTabSet()) {
$fields->addFieldsToTab('Root.Settings', $moderationField);
} else {
$fields->push($moderationField);
}
}
}
// Check if moderation should be enabled via cms configurable /**
if($this->owner->getCommentsOption('require_moderation_cms')) { * Get comment moderation rules for this parent
$moderationField = new DropdownField('ModerationRequired', 'Comment Moderation', array( *
'None' => _t('CommentsExtension.MODERATIONREQUIRED_NONE', 'No moderation required'), * None: No moderation required
'Required' => _t('CommentsExtension.MODERATIONREQUIRED_REQUIRED', 'Moderate all comments'), * Required: All comments
'NonMembersOnly' => _t( * NonMembersOnly: Only anonymous users
'CommentsExtension.MODERATIONREQUIRED_NONMEMBERSONLY', *
'Only moderate non-members' * @return string
), */
)); public function getModerationRequired()
if($fields->hasTabSet()) { {
$fields->addFieldsToTab('Root.Settings', $moderationField); if ($this->owner->getCommentsOption('require_moderation_cms')) {
} else { return $this->owner->getField('ModerationRequired');
$fields->push($moderationField); } elseif ($this->owner->getCommentsOption('require_moderation')) {
} return 'Required';
} } elseif ($this->owner->getCommentsOption('require_moderation_nonmembers')) {
} return 'NonMembersOnly';
} else {
return 'None';
}
}
/** /**
* Get comment moderation rules for this parent * Determine if users must be logged in to post comments
* *
* None: No moderation required * @return boolean
* Required: All comments */
* NonMembersOnly: Only anonymous users public function getCommentsRequireLogin()
* {
* @return string if ($this->owner->getCommentsOption('require_login_cms')) {
*/ return (bool) $this->owner->getField('CommentsRequireLogin');
public function getModerationRequired() { } else {
if($this->owner->getCommentsOption('require_moderation_cms')) { return (bool) $this->owner->getCommentsOption('require_login');
return $this->owner->getField('ModerationRequired'); }
} elseif($this->owner->getCommentsOption('require_moderation')) { }
return 'Required';
} elseif($this->owner->getCommentsOption('require_moderation_nonmembers')) {
return 'NonMembersOnly';
} else {
return 'None';
}
}
/** /**
* Determine if users must be logged in to post comments * Returns the RelationList of all comments against this object. Can be used as a data source
* * for a gridfield with write access.
* @return boolean *
*/ * @return CommentList
public function getCommentsRequireLogin() { */
if($this->owner->getCommentsOption('require_login_cms')) { public function AllComments()
return (bool) $this->owner->getField('CommentsRequireLogin'); {
} else { $order = $this->owner->getCommentsOption('order_comments_by');
return (bool) $this->owner->getCommentsOption('require_login'); $comments = CommentList::create($this->ownerBaseClass)
} ->forForeignID($this->owner->ID)
} ->sort($order);
$this->owner->extend('updateAllComments', $comments);
return $comments;
}
/** /**
* Returns the RelationList of all comments against this object. Can be used as a data source * Returns all comments against this object, with with spam and unmoderated items excluded, for use in the frontend
* for a gridfield with write access. *
* * @return CommentList
* @return CommentList */
*/ public function AllVisibleComments()
public function AllComments() { {
$order = $this->owner->getCommentsOption('order_comments_by'); $list = $this->AllComments();
$comments = CommentList::create($this->ownerBaseClass)
->forForeignID($this->owner->ID)
->sort($order);
$this->owner->extend('updateAllComments', $comments);
return $comments;
}
/** // Filter spam comments for non-administrators if configured
* Returns all comments against this object, with with spam and unmoderated items excluded, for use in the frontend $showSpam = $this->owner->getCommentsOption('frontend_spam') && $this->owner->canModerateComments();
* if (!$showSpam) {
* @return CommentList $list = $list->filter('IsSpam', 0);
*/ }
public function AllVisibleComments() {
$list = $this->AllComments();
// Filter spam comments for non-administrators if configured // Filter un-moderated comments for non-administrators if moderation is enabled
$showSpam = $this->owner->getCommentsOption('frontend_spam') && $this->owner->canModerateComments(); $showUnmoderated = ($this->owner->ModerationRequired === 'None')
if(!$showSpam) { || ($this->owner->getCommentsOption('frontend_moderation') && $this->owner->canModerateComments());
$list = $list->filter('IsSpam', 0); if (!$showUnmoderated) {
} $list = $list->filter('Moderated', 1);
}
// Filter un-moderated comments for non-administrators if moderation is enabled $this->owner->extend('updateAllVisibleComments', $list);
$showUnmoderated = ($this->owner->ModerationRequired === 'None') return $list;
|| ($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
} *
* @return CommentList
*/
public function Comments()
{
$list = $this->AllVisibleComments();
/** // If nesting comments, only show root level
* Returns the root level comments, with spam and unmoderated items excluded, for use in the frontend if ($this->owner->getCommentsOption('nested_comments')) {
* $list = $list->filter('ParentCommentID', 0);
* @return CommentList }
*/
public function Comments() {
$list = $this->AllVisibleComments();
// If nesting comments, only show root level $this->owner->extend('updateComments', $list);
if($this->owner->getCommentsOption('nested_comments')) { return $list;
$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
*
* @return PaginatedList
*/
public function PagedComments()
{
$list = $this->Comments();
/** // Add pagination
* Returns a paged list of the root level comments, with spam and unmoderated items excluded, $list = new PaginatedList($list, Controller::curr()->getRequest());
* for use in the frontend $list->setPaginationGetVar('commentsstart' . $this->owner->ID);
* $list->setPageLength($this->owner->getCommentsOption('comments_per_page'));
* @return PaginatedList
*/
public function PagedComments() {
$list = $this->Comments();
// Add pagination $this->owner->extend('updatePagedComments', $list);
$list = new PaginatedList($list, Controller::curr()->getRequest()); return $list;
$list->setPaginationGetVar('commentsstart' . $this->owner->ID); }
$list->setPageLength($this->owner->getCommentsOption('comments_per_page'));
$this->owner->extend('updatePagedComments', $list); /**
return $list; * Check if comments are configured for this page even if they are currently disabled.
} * Do not include the comments on pages which don't have id's such as security pages
*
* @deprecated since version 2.0
*
* @return boolean
*/
public function getCommentsConfigured()
{
Deprecation::notice('2.0', 'getCommentsConfigured is deprecated. Use getCommentsEnabled instead');
return true; // by virtue of all classes with this extension being 'configured'
}
/** /**
* Check if comments are configured for this page even if they are currently disabled. * Determine if comments are enabled for this instance
* Do not include the comments on pages which don't have id's such as security pages *
* * @return boolean
* @deprecated since version 2.0 */
* public function getCommentsEnabled()
* @return boolean {
*/ // Don't display comments form for pseudo-pages (such as the login form)
public function getCommentsConfigured() { if (!$this->owner->exists()) {
Deprecation::notice('2.0', 'getCommentsConfigured is deprecated. Use getCommentsEnabled instead'); return false;
return true; // by virtue of all classes with this extension being 'configured' }
}
/** // Determine which flag should be used to determine if this is enabled
* Determine if comments are enabled for this instance if ($this->owner->getCommentsOption('enabled_cms')) {
* return $this->owner->ProvideComments;
* @return boolean } else {
*/ return $this->owner->getCommentsOption('enabled');
public function getCommentsEnabled() { }
// Don't display comments form for pseudo-pages (such as the login form) }
if(!$this->owner->exists()) return false;
// Determine which flag should be used to determine if this is enabled /**
if($this->owner->getCommentsOption('enabled_cms')) { * Get the HTML ID for the comment holder in the template
return $this->owner->ProvideComments; *
} else { * @return string
return $this->owner->getCommentsOption('enabled'); */
} public function getCommentHolderID()
} {
return $this->owner->getCommentsOption('comments_holder_id');
}
/** /**
* Get the HTML ID for the comment holder in the template * @deprecated since version 2.0
* */
* @return string public function getPostingRequiresPermission()
*/ {
public function getCommentHolderID() { Deprecation::notice('2.0', 'Use getPostingRequiredPermission instead');
return $this->owner->getCommentsOption('comments_holder_id'); return $this->getPostingRequiredPermission();
} }
/** /**
* @deprecated since version 2.0 * Permission codes required in order to post (or empty if none required)
*/ *
public function getPostingRequiresPermission() { * @return string|array Permission or list of permissions, if required
Deprecation::notice('2.0', 'Use getPostingRequiredPermission instead'); */
return $this->getPostingRequiredPermission(); public function getPostingRequiredPermission()
} {
return $this->owner->getCommentsOption('required_permission');
}
/** public function canPost()
* Permission codes required in order to post (or empty if none required) {
* Deprecation::notice('2.0', 'Use canPostComment instead');
* @return string|array Permission or list of permissions, if required return $this->canPostComment();
*/ }
public function getPostingRequiredPermission() {
return $this->owner->getCommentsOption('required_permission');
}
public function canPost() { /**
Deprecation::notice('2.0', 'Use canPostComment instead'); * Determine if a user can post comments on this item
return $this->canPostComment(); *
} * @param Member $member Member to check
*
* @return boolean
*/
public function canPostComment($member = null)
{
// Deny if not enabled for this object
if (!$this->owner->CommentsEnabled) {
return false;
}
/** // Check if member is required
* Determine if a user can post comments on this item $requireLogin = $this->owner->CommentsRequireLogin;
* if (!$requireLogin) {
* @param Member $member Member to check return true;
* }
* @return boolean
*/
public function canPostComment($member = null) {
// Deny if not enabled for this object
if(!$this->owner->CommentsEnabled) return false;
// Check if member is required // Check member is logged in
$requireLogin = $this->owner->CommentsRequireLogin; $member = $member ?: Member::currentUser();
if(!$requireLogin) return true; if (!$member) {
return false;
}
// Check member is logged in // If member required check permissions
$member = $member ?: Member::currentUser(); $requiredPermission = $this->owner->PostingRequiredPermission;
if(!$member) return false; if ($requiredPermission && !Permission::checkMember($member, $requiredPermission)) {
return false;
}
// If member required check permissions return true;
$requiredPermission = $this->owner->PostingRequiredPermission; }
if($requiredPermission && !Permission::checkMember($member, $requiredPermission)) return false;
return true; /**
} * Determine if this member can moderate comments in the CMS
*
* @param Member $member
*
* @return boolean
*/
public function canModerateComments($member = null)
{
// Deny if not enabled for this object
if (!$this->owner->CommentsEnabled) {
return false;
}
/** // Fallback to can-edit
* Determine if this member can moderate comments in the CMS return $this->owner->canEdit($member);
* }
* @param Member $member
*
* @return boolean
*/
public function canModerateComments($member = null) {
// Deny if not enabled for this object
if(!$this->owner->CommentsEnabled) return false;
// Fallback to can-edit public function getRssLink()
return $this->owner->canEdit($member); {
} Deprecation::notice('2.0', 'Use getCommentRSSLink instead');
return $this->getCommentRSSLink();
}
public function getRssLink() { /**
Deprecation::notice('2.0', 'Use getCommentRSSLink instead'); * Gets the RSS link to all comments
return $this->getCommentRSSLink(); *
} * @return string
*/
public function getCommentRSSLink()
{
return Controller::join_links(Director::baseURL(), 'CommentingController/rss');
}
/** public function getRssLinkPage()
* Gets the RSS link to all comments {
* Deprecation::notice('2.0', 'Use getCommentRSSLinkPage instead');
* @return string return $this->getCommentRSSLinkPage();
*/ }
public function getCommentRSSLink() {
return Controller::join_links(Director::baseURL(), 'CommentingController/rss');
}
public function getRssLinkPage() { /**
Deprecation::notice('2.0', 'Use getCommentRSSLinkPage instead'); * Get the RSS link to all comments on this page
return $this->getCommentRSSLinkPage(); *
} * @return string
*/
public function getCommentRSSLinkPage()
{
return Controller::join_links(
$this->getCommentRSSLink(), $this->ownerBaseClass, $this->owner->ID
);
}
/** /**
* Get the RSS link to all comments on this page * Comments interface for the front end. Includes the CommentAddForm and the composition
* * of the comments display.
* @return string *
*/ * To customize the html see templates/CommentInterface.ss or extend this function with
public function getCommentRSSLinkPage() { * your own extension.
return Controller::join_links( *
$this->getCommentRSSLink(), $this->ownerBaseClass, $this->owner->ID * @todo Cleanup the passing of all this configuration based functionality
); *
} * @see docs/en/Extending
*/
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::add_i18n_javascript('comments/javascript/lang');
Requirements::javascript('comments/javascript/CommentsInterface.js');
}
/** $controller = CommentingController::create();
* Comments interface for the front end. Includes the CommentAddForm and the composition $controller->setOwnerRecord($this->owner);
* of the comments display. $controller->setBaseClass($this->ownerBaseClass);
* $controller->setOwnerController(Controller::curr());
* 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
*/
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::add_i18n_javascript('comments/javascript/lang');
Requirements::javascript('comments/javascript/CommentsInterface.js');
}
$controller = CommentingController::create(); $moderatedSubmitted = Session::get('CommentsModerated');
$controller->setOwnerRecord($this->owner); Session::clear('CommentsModerated');
$controller->setBaseClass($this->ownerBaseClass);
$controller->setOwnerController(Controller::curr());
$moderatedSubmitted = Session::get('CommentsModerated'); $form = ($enabled) ? $controller->CommentsForm() : false;
Session::clear('CommentsModerated');
$form = ($enabled) ? $controller->CommentsForm() : false; // 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(
'AddCommentForm' => $form,
'ModeratedSubmitted' => $moderatedSubmitted,
))
->renderWith('CommentsInterface');
}
// 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 * Returns whether this extension instance is attached to a {@link SiteTree} object
return $this *
->owner * @return bool
->customise(array( */
'AddCommentForm' => $form, public function attachedToSiteTree()
'ModeratedSubmitted' => $moderatedSubmitted, {
)) $class = $this->ownerBaseClass;
->renderWith('CommentsInterface');
}
/** return (is_subclass_of($class, 'SiteTree')) || ($class == 'SiteTree');
* 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'); /**
} * @deprecated 1.0 Please use {@link CommentsExtension->CommentsForm()}
*/
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();
}
/** /**
* @deprecated 1.0 Please use {@link CommentsExtension->CommentsForm()} * Get the commenting option for this object
*/ *
public function PageComments() { * This can be overridden in any instance or extension to customise the option available
// This method is very commonly used, don't throw a warning just yet *
Deprecation::notice('1.0', '$PageComments is deprecated. Please use $CommentsForm'); * @param string $key
return $this->CommentsForm(); *
} * @return mixed Result if the setting is available, or null otherwise
*/
public function getCommentsOption($key)
{
$settings = $this->owner // In case singleton is called on the extension directly
? $this->owner->config()->comments
: Config::inst()->get(__CLASS__, 'comments');
$value = null;
if (isset($settings[$key])) {
$value = $settings[$key];
}
/** // To allow other extensions to customise this option
* Get the commenting option for this object if ($this->owner) {
* $this->owner->extend('updateCommentsOption', $key, $value);
* This can be overridden in any instance or extension to customise the option available }
* return $value;
* @param string $key }
*
* @return mixed Result if the setting is available, or null otherwise
*/
public function getCommentsOption($key) {
$settings = $this->owner // In case singleton is called on the extension directly
? $this->owner->config()->comments
: Config::inst()->get(__CLASS__, 'comments');
$value = null;
if(isset($settings[$key])) $value = $settings[$key];
// To allow other extensions to customise this option /**
if($this->owner) $this->owner->extend('updateCommentsOption', $key, $value); * Add moderation functions to the current fieldlist
return $value; *
} * @param FieldList $fields
*/
protected function updateModerationFields(FieldList $fields)
{
Requirements::css(COMMENTS_DIR . '/css/cms.css');
/** $newComments = $this->owner->AllComments()->filter('Moderated', 0);
* Add moderation functions to the current fieldlist
*
* @param FieldList $fields
*/
protected function updateModerationFields(FieldList $fields) {
Requirements::css(COMMENTS_DIR . '/css/cms.css');
$newComments = $this->owner->AllComments()->filter('Moderated', 0); $newGrid = new CommentsGridField(
'NewComments',
_t('CommentsAdmin.NewComments', 'New'),
$newComments,
CommentsGridFieldConfig::create()
);
$newGrid = new CommentsGridField( $approvedComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 0);
'NewComments',
_t('CommentsAdmin.NewComments', 'New'),
$newComments,
CommentsGridFieldConfig::create()
);
$approvedComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 0); $approvedGrid = new CommentsGridField(
'ApprovedComments',
_t('CommentsAdmin.Comments', 'Approved'),
$approvedComments,
CommentsGridFieldConfig::create()
);
$approvedGrid = new CommentsGridField( $spamComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 1);
'ApprovedComments',
_t('CommentsAdmin.Comments', 'Approved'),
$approvedComments,
CommentsGridFieldConfig::create()
);
$spamComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 1); $spamGrid = new CommentsGridField(
'SpamComments',
_t('CommentsAdmin.SpamComments', 'Spam'),
$spamComments,
CommentsGridFieldConfig::create()
);
$spamGrid = new CommentsGridField( $newCount = '(' . count($newComments) . ')';
'SpamComments', $approvedCount = '(' . count($approvedComments) . ')';
_t('CommentsAdmin.SpamComments', 'Spam'), $spamCount = '(' . count($spamComments) . ')';
$spamComments,
CommentsGridFieldConfig::create()
);
$newCount = '(' . count($newComments) . ')'; if ($fields->hasTabSet()) {
$approvedCount = '(' . count($approvedComments) . ')'; $tabs = new TabSet(
$spamCount = '(' . count($spamComments) . ')'; 'Comments',
new Tab('CommentsNewCommentsTab', _t('CommentAdmin.NewComments', 'New') . ' ' . $newCount,
$newGrid
),
new Tab('CommentsCommentsTab', _t('CommentAdmin.Comments', 'Approved') . ' ' . $approvedCount,
$approvedGrid
),
new Tab('CommentsSpamCommentsTab', _t('CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount,
$spamGrid
)
);
$fields->addFieldToTab('Root', $tabs);
} else {
$fields->push($newGrid);
$fields->push($approvedGrid);
$fields->push($spamGrid);
}
}
if($fields->hasTabSet()) { public function updateCMSFields(FieldList $fields)
$tabs = new TabSet( {
'Comments', // Disable moderation if not permitted
new Tab('CommentsNewCommentsTab', _t('CommentAdmin.NewComments', 'New') . ' ' . $newCount, if ($this->owner->canModerateComments()) {
$newGrid $this->updateModerationFields($fields);
), }
new Tab('CommentsCommentsTab', _t('CommentAdmin.Comments', 'Approved') . ' ' . $approvedCount,
$approvedGrid
),
new Tab('CommentsSpamCommentsTab', _t('CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount,
$spamGrid
)
);
$fields->addFieldToTab('Root', $tabs);
} else {
$fields->push($newGrid);
$fields->push($approvedGrid);
$fields->push($spamGrid);
}
}
public function updateCMSFields(FieldList $fields) { // If this isn't a page we should merge the settings into the CMS fields
// Disable moderation if not permitted if (!$this->attachedToSiteTree()) {
if($this->owner->canModerateComments()) { $this->updateSettingsFields($fields);
$this->updateModerationFields($fields); }
} }
// If this isn't a page we should merge the settings into the CMS fields
if(!$this->attachedToSiteTree()) {
$this->updateSettingsFields($fields);
}
}
} }

View File

@ -20,556 +20,593 @@
* @method Comment ParentComment() Parent comment this is a reply to * @method Comment ParentComment() Parent comment this is a reply to
* @package comments * @package comments
*/ */
class Comment extends DataObject { class Comment extends DataObject
{
/**
* @var array /**
*/ * @var array
private static $db = array( */
'Name' => 'Varchar(200)', private static $db = array(
'Comment' => 'Text', 'Name' => 'Varchar(200)',
'Email' => 'Varchar(200)', 'Comment' => 'Text',
'URL' => 'Varchar(255)', 'Email' => 'Varchar(200)',
'BaseClass' => 'Varchar(200)', 'URL' => 'Varchar(255)',
'Moderated' => 'Boolean(0)', 'BaseClass' => 'Varchar(200)',
'IsSpam' => 'Boolean(0)', 'Moderated' => 'Boolean(0)',
'ParentID' => 'Int', 'IsSpam' => 'Boolean(0)',
'AllowHtml' => 'Boolean', 'ParentID' => 'Int',
'SecretToken' => 'Varchar(255)', 'AllowHtml' => 'Boolean',
'Depth' => 'Int', 'SecretToken' => 'Varchar(255)',
); 'Depth' => 'Int',
);
private static $has_one = array(
"Author" => "Member", private static $has_one = array(
"ParentComment" => "Comment", "Author" => "Member",
); "ParentComment" => "Comment",
);
private static $has_many = array(
"ChildComments" => "Comment" private static $has_many = array(
); "ChildComments" => "Comment"
);
private static $default_sort = '"Created" DESC';
private static $default_sort = '"Created" DESC';
private static $defaults = array(
'Moderated' => 0, private static $defaults = array(
'IsSpam' => 0, 'Moderated' => 0,
); 'IsSpam' => 0,
);
private static $casting = array(
'Title' => 'Varchar', private static $casting = array(
'ParentTitle' => 'Varchar', 'Title' => 'Varchar',
'ParentClassName' => 'Varchar', 'ParentTitle' => 'Varchar',
'AuthorName' => 'Varchar', 'ParentClassName' => 'Varchar',
'RSSName' => 'Varchar', 'AuthorName' => 'Varchar',
'DeleteLink' => 'Varchar', 'RSSName' => 'Varchar',
'SpamLink' => 'Varchar', 'DeleteLink' => 'Varchar',
'HamLink' => 'Varchar', 'SpamLink' => 'Varchar',
'ApproveLink' => 'Varchar', 'HamLink' => 'Varchar',
'Permalink' => 'Varchar', 'ApproveLink' => 'Varchar',
); 'Permalink' => 'Varchar',
);
private static $searchable_fields = array(
'Name', private static $searchable_fields = array(
'Email', 'Name',
'Comment', 'Email',
'Created', 'Comment',
'BaseClass', 'Created',
); 'BaseClass',
);
private static $summary_fields = array(
'Name' => 'Submitted By', private static $summary_fields = array(
'Email' => 'Email', 'Name' => 'Submitted By',
'Comment.LimitWordCount' => 'Comment', 'Email' => 'Email',
'Created' => 'Date Posted', 'Comment.LimitWordCount' => 'Comment',
'ParentTitle' => 'Post', 'Created' => 'Date Posted',
'IsSpam' => 'Is Spam', 'ParentTitle' => 'Post',
); 'IsSpam' => 'Is Spam',
);
private static $field_labels = array(
'Author' => 'Author Member', private static $field_labels = array(
); 'Author' => 'Author Member',
);
public function onBeforeWrite() {
parent::onBeforeWrite(); public function onBeforeWrite()
{
// Sanitize HTML, because its expected to be passed to the template unescaped later parent::onBeforeWrite();
if($this->AllowHtml) {
$this->Comment = $this->purifyHtml($this->Comment); // 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();
} // Check comment depth
$this->updateDepth();
public function onBeforeDelete() { }
parent::onBeforeDelete();
public function onBeforeDelete()
// Delete all children {
foreach($this->ChildComments() as $comment) { parent::onBeforeDelete();
$comment->delete();
} // Delete all children
} foreach ($this->ChildComments() as $comment) {
$comment->delete();
/** }
* @return Comment_SecurityToken }
*/
public function getSecurityToken() { /**
return Injector::inst()->createWithArgs('Comment_SecurityToken', array($this)); * @return Comment_SecurityToken
} */
public function getSecurityToken()
/** {
* Migrates the old {@link PageComment} objects to {@link Comment} return Injector::inst()->createWithArgs('Comment_SecurityToken', array($this));
*/ }
public function requireDefaultRecords() {
parent::requireDefaultRecords(); /**
* Migrates the old {@link PageComment} objects to {@link Comment}
if(DB::getConn()->hasTable('PageComment')) { */
$comments = DB::query('SELECT * FROM "PageComment"'); public function requireDefaultRecords()
{
if($comments) { parent::requireDefaultRecords();
while($pageComment = $comments->nextRecord()) {
// create a new comment from the older page comment if (DB::getConn()->hasTable('PageComment')) {
$comment = new Comment(); $comments = DB::query('SELECT * FROM "PageComment"');
$comment->update($pageComment);
if ($comments) {
// set the variables which have changed while ($pageComment = $comments->nextRecord()) {
$comment->BaseClass = 'SiteTree'; // create a new comment from the older page comment
$comment->URL = (isset($pageComment['CommenterURL'])) ? $pageComment['CommenterURL'] : ''; $comment = new Comment();
if((int) $pageComment['NeedsModeration'] == 0) $comment->Moderated = true; $comment->update($pageComment);
$comment->write(); // set the variables which have changed
} $comment->BaseClass = 'SiteTree';
} $comment->URL = (isset($pageComment['CommenterURL'])) ? $pageComment['CommenterURL'] : '';
if ((int) $pageComment['NeedsModeration'] == 0) {
DB::alteration_message('Migrated PageComment to Comment', 'changed'); $comment->Moderated = true;
DB::getConn()->dontRequireTable('PageComment'); }
}
} $comment->write();
}
/** }
* Return a link to this comment
* DB::alteration_message('Migrated PageComment to Comment', 'changed');
* @param string $action DB::getConn()->dontRequireTable('PageComment');
* }
* @return string link to this comment. }
*/
public function Link($action = '') { /**
if($parent = $this->getParent()) { * Return a link to this comment
return $parent->Link($action) . '#' . $this->Permalink(); *
} * @param string $action
} *
* @return string link to this comment.
/** */
* Returns the permalink for this {@link Comment}. Inserted into public function Link($action = '')
* the ID tag of the comment {
* if ($parent = $this->getParent()) {
* @return string return $parent->Link($action) . '#' . $this->Permalink();
*/ }
public function Permalink() { }
$prefix = $this->getOption('comment_permalink_prefix');
return $prefix . $this->ID; /**
} * Returns the permalink for this {@link Comment}. Inserted into
* the ID tag of the comment
/** *
* Translate the form field labels for the CMS administration * @return string
* */
* @param boolean $includerelations public function Permalink()
* {
* @return array $prefix = $this->getOption('comment_permalink_prefix');
*/ return $prefix . $this->ID;
public function fieldLabels($includerelations = true) { }
$labels = parent::fieldLabels($includerelations);
/**
$labels['Name'] = _t('Comment.NAME', 'Author Name'); * Translate the form field labels for the CMS administration
$labels['Comment'] = _t('Comment.COMMENT', 'Comment'); *
$labels['Email'] = _t('Comment.EMAIL', 'Email'); * @param boolean $includerelations
$labels['URL'] = _t('Comment.URL', 'URL'); *
$labels['IsSpam'] = _t('Comment.ISSPAM', 'Spam?'); * @return array
$labels['Moderated'] = _t('Comment.MODERATED', 'Moderated?'); */
$labels['ParentTitle'] = _t('Comment.PARENTTITLE', 'Parent'); public function fieldLabels($includerelations = true)
$labels['Created'] = _t('Comment.CREATED', 'Date posted'); {
$labels = parent::fieldLabels($includerelations);
return $labels;
} $labels['Name'] = _t('Comment.NAME', 'Author Name');
$labels['Comment'] = _t('Comment.COMMENT', 'Comment');
/** $labels['Email'] = _t('Comment.EMAIL', 'Email');
* Get the commenting option $labels['URL'] = _t('Comment.URL', 'URL');
* $labels['IsSpam'] = _t('Comment.ISSPAM', 'Spam?');
* @param string $key $labels['Moderated'] = _t('Comment.MODERATED', 'Moderated?');
* $labels['ParentTitle'] = _t('Comment.PARENTTITLE', 'Parent');
* @return mixed Result if the setting is available, or null otherwise $labels['Created'] = _t('Comment.CREATED', 'Date posted');
*/
public function getOption($key) { return $labels;
// If possible use the current record }
$record = $this->getParent();
/**
if(!$record && $this->BaseClass) { * Get the commenting option
// Otherwise a singleton of that record *
$record = singleton($this->BaseClass); * @param string $key
} *
else if(!$record) { * @return mixed Result if the setting is available, or null otherwise
// Otherwise just use the default options */
$record = singleton('CommentsExtension'); public function getOption($key)
} {
// If possible use the current record
return ($record->hasMethod('getCommentsOption')) ? $record->getCommentsOption($key) : null; $record = $this->getParent();
}
if (!$record && $this->BaseClass) {
/** // Otherwise a singleton of that record
* Returns the parent {@link DataObject} this comment is attached too $record = singleton($this->BaseClass);
* } elseif (!$record) {
* @return DataObject // Otherwise just use the default options
*/ $record = singleton('CommentsExtension');
public function getParent() { }
return $this->BaseClass && $this->ParentID
? DataObject::get_by_id($this->BaseClass, $this->ParentID, true) return ($record->hasMethod('getCommentsOption')) ? $record->getCommentsOption($key) : null;
: null; }
}
/**
* Returns the parent {@link DataObject} this comment is attached too
/** *
* Returns a string to help identify the parent of the comment * @return DataObject
* */
* @return string public function getParent()
*/ {
public function getParentTitle() { return $this->BaseClass && $this->ParentID
if($parent = $this->getParent()) { ? DataObject::get_by_id($this->BaseClass, $this->ParentID, true)
return $parent->Title ?: ($parent->ClassName . ' #' . $parent->ID); : null;
} }
}
/** /**
* Comment-parent classnames obviously vary, return the parent classname * Returns a string to help identify the parent of the comment
* *
* @return string * @return string
*/ */
public function getParentClassName() { public function getParentTitle()
return $this->BaseClass; {
} if ($parent = $this->getParent()) {
return $parent->Title ?: ($parent->ClassName . ' #' . $parent->ID);
public function castingHelper($field) { }
// Safely escape the comment }
if($field === 'EscapedComment') {
return $this->AllowHtml ? 'HTMLText' : 'Text'; /**
} * Comment-parent classnames obviously vary, return the parent classname
return parent::castingHelper($field); *
} * @return string
*/
/** public function getParentClassName()
* Content to be safely escaped on the frontend {
* return $this->BaseClass;
* @return string }
*/
public function getEscapedComment() { public function castingHelper($field)
return $this->Comment; {
} // Safely escape the comment
if ($field === 'EscapedComment') {
/** return $this->AllowHtml ? 'HTMLText' : 'Text';
* Return whether this comment is a preview (has not been written to the db) }
* return parent::castingHelper($field);
* @return boolean }
*/
public function isPreview() { /**
return !$this->exists(); * Content to be safely escaped on the frontend
} *
* @return string
/** */
* @todo needs to compare to the new {@link Commenting} configuration API public function getEscapedComment()
* {
* @param Member $member return $this->Comment;
* }
* @return bool
*/ /**
public function canCreate($member = null) { * Return whether this comment is a preview (has not been written to the db)
return false; *
} * @return boolean
*/
/** public function isPreview()
* Checks for association with a page, and {@link SiteTree->ProvidePermission} {
* flag being set to true. return !$this->exists();
* }
* @param Member $member
* /**
* @return Boolean * @todo needs to compare to the new {@link Commenting} configuration API
*/ *
public function canView($member = null) { * @param Member $member
$member = $this->getMember($member); *
* @return bool
$extended = $this->extendedCan('canView', $member); */
if($extended !== null) { public function canCreate($member = null)
return $extended; {
} return false;
}
if(Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
return true; /**
} * Checks for association with a page, and {@link SiteTree->ProvidePermission}
* flag being set to true.
if($parent = $this->getParent()) { *
return $parent->canView($member) * @param Member $member
&& $parent->has_extension('CommentsExtension') *
&& $parent->CommentsEnabled; * @return Boolean
} */
public function canView($member = null)
return false; {
} $member = $this->getMember($member);
/** $extended = $this->extendedCan('canView', $member);
* Checks if the comment can be edited. if ($extended !== null) {
* return $extended;
* @param null|int|Member $member }
*
* @return Boolean if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
*/ return true;
public function canEdit($member = null) { }
$member = $this->getMember($member);
if ($parent = $this->getParent()) {
if(!$member) { return $parent->canView($member)
return false; && $parent->has_extension('CommentsExtension')
} && $parent->CommentsEnabled;
}
$extended = $this->extendedCan('canEdit', $member);
if($extended !== null) { return false;
return $extended; }
}
/**
if(Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) { * Checks if the comment can be edited.
return true; *
} * @param null|int|Member $member
*
if($parent = $this->getParent()) { * @return Boolean
return $parent->canEdit($member); */
} public function canEdit($member = null)
{
return false; $member = $this->getMember($member);
}
if (!$member) {
/** return false;
* Checks if the comment can be deleted. }
*
* @param null|int|Member $member $extended = $this->extendedCan('canEdit', $member);
* if ($extended !== null) {
* @return Boolean return $extended;
*/ }
public function canDelete($member = null) {
$member = $this->getMember($member); if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
return true;
if(!$member) { }
return false;
} if ($parent = $this->getParent()) {
return $parent->canEdit($member);
$extended = $this->extendedCan('canDelete', $member); }
if($extended !== null) {
return $extended; return false;
} }
return $this->canEdit($member); /**
} * Checks if the comment can be deleted.
*
/** * @param null|int|Member $member
* Resolves Member object. *
* * @return Boolean
* @param Member|int|null $member */
* @return Member|null public function canDelete($member = null)
*/ {
protected function getMember($member = null) { $member = $this->getMember($member);
if(!$member) {
$member = Member::currentUser(); if (!$member) {
} return false;
}
if(is_numeric($member)) {
$member = DataObject::get_by_id('Member', $member, true); $extended = $this->extendedCan('canDelete', $member);
} if ($extended !== null) {
return $extended;
return $member; }
}
return $this->canEdit($member);
/** }
* Return the authors name for the comment
* /**
* @return string * Resolves Member object.
*/ *
public function getAuthorName() { * @param Member|int|null $member
if($this->Name) { * @return Member|null
return $this->Name; */
} else if($author = $this->Author()) { protected function getMember($member = null)
return $author->getName(); {
} if (!$member) {
} $member = Member::currentUser();
}
/**
* Generate a secure admin-action link authorised for the specified member if (is_numeric($member)) {
* $member = DataObject::get_by_id('Member', $member, true);
* @param string $action An action on CommentingController to link to }
* @param Member $member The member authorised to invoke this action
* return $member;
* @return string }
*/
protected function actionLink($action, $member = null) { /**
if(!$member) $member = Member::currentUser(); * Return the authors name for the comment
if(!$member) return false; *
* @return string
$url = Controller::join_links( */
Director::baseURL(), public function getAuthorName()
'CommentingController', {
$action, if ($this->Name) {
$this->ID return $this->Name;
); } elseif ($author = $this->Author()) {
return $author->getName();
// Limit access for this user }
$token = $this->getSecurityToken(); }
return $token->addToUrl($url, $member);
} /**
* Generate a secure admin-action link authorised for the specified member
/** *
* Link to delete this comment * @param string $action An action on CommentingController to link to
* * @param Member $member The member authorised to invoke this action
* @param Member $member *
* * @return string
* @return string */
*/ protected function actionLink($action, $member = null)
public function DeleteLink($member = null) { {
if($this->canDelete($member)) { if (!$member) {
return $this->actionLink('delete', $member); $member = Member::currentUser();
} }
} if (!$member) {
return false;
/** }
* Link to mark as spam
* $url = Controller::join_links(
* @param Member $member Director::baseURL(),
* 'CommentingController',
* @return string $action,
*/ $this->ID
public function SpamLink($member = null) { );
if($this->canEdit($member) && !$this->IsSpam) {
return $this->actionLink('spam', $member); // Limit access for this user
} $token = $this->getSecurityToken();
} return $token->addToUrl($url, $member);
}
/**
* Link to mark as not-spam (ham) /**
* * Link to delete this comment
* @param Member $member *
* * @param Member $member
* @return string *
*/ * @return string
public function HamLink($member = null) { */
if($this->canEdit($member) && $this->IsSpam) { public function DeleteLink($member = null)
return $this->actionLink('ham', $member); {
} if ($this->canDelete($member)) {
} return $this->actionLink('delete', $member);
}
/** }
* Link to approve this comment
* /**
* @param Member $member * Link to mark as spam
* *
* @return string * @param Member $member
*/ *
public function ApproveLink($member = null) { * @return string
if($this->canEdit($member) && !$this->Moderated) { */
return $this->actionLink('approve', $member); public function SpamLink($member = null)
} {
} if ($this->canEdit($member) && !$this->IsSpam) {
return $this->actionLink('spam', $member);
/** }
* Mark this comment as spam }
*/
public function markSpam() { /**
$this->IsSpam = true; * Link to mark as not-spam (ham)
$this->Moderated = true; *
$this->write(); * @param Member $member
$this->extend('afterMarkSpam'); *
} * @return string
*/
/** public function HamLink($member = null)
* Mark this comment as approved {
*/ if ($this->canEdit($member) && $this->IsSpam) {
public function markApproved() { return $this->actionLink('ham', $member);
$this->IsSpam = false; }
$this->Moderated = true; }
$this->write();
$this->extend('afterMarkApproved'); /**
} * Link to approve this comment
*
/** * @param Member $member
* Mark this comment as unapproved *
*/ * @return string
public function markUnapproved() { */
$this->Moderated = false; public function ApproveLink($member = null)
$this->write(); {
$this->extend('afterMarkUnapproved'); if ($this->canEdit($member) && !$this->Moderated) {
} return $this->actionLink('approve', $member);
}
/** }
* @return string
*/ /**
public function SpamClass() { * Mark this comment as spam
if($this->IsSpam) { */
return 'spam'; public function markSpam()
} else if(!$this->Moderated) { {
return 'unmoderated'; $this->IsSpam = true;
} else { $this->Moderated = true;
return 'notspam'; $this->write();
} $this->extend('afterMarkSpam');
} }
/** /**
* @return string * Mark this comment as approved
*/ */
public function getTitle() { public function markApproved()
$title = sprintf(_t('Comment.COMMENTBY', 'Comment by %s', 'Name'), $this->getAuthorName()); {
$this->IsSpam = false;
if($parent = $this->getParent()) { $this->Moderated = true;
if($parent->Title) { $this->write();
$title .= sprintf(' %s %s', _t('Comment.ON', 'on'), $parent->Title); $this->extend('afterMarkApproved');
} }
}
/**
return $title; * Mark this comment as unapproved
} */
public function markUnapproved()
/* {
* Modify the default fields shown to the user $this->Moderated = false;
*/ $this->write();
public function getCMSFields() { $this->extend('afterMarkUnapproved');
$commentField = $this->AllowHtml ? 'HtmlEditorField' : 'TextareaField'; }
$fields = new FieldList(
$this /**
->obj('Created') * @return string
->scaffoldFormField($this->fieldLabel('Created')) */
->performReadonlyTransformation(), public function SpamClass()
TextField::create('Name', $this->fieldLabel('AuthorName')), {
$commentField::create('Comment', $this->fieldLabel('Comment')), if ($this->IsSpam) {
EmailField::create('Email', $this->fieldLabel('Email')), return 'spam';
TextField::create('URL', $this->fieldLabel('URL')), } elseif (!$this->Moderated) {
FieldGroup::create(array( return 'unmoderated';
CheckboxField::create('Moderated', $this->fieldLabel('Moderated')), } else {
CheckboxField::create('IsSpam', $this->fieldLabel('IsSpam')), return 'notspam';
)) }
->setTitle('Options') }
->setDescription(_t(
'Comment.OPTION_DESCRIPTION', /**
'Unmoderated and spam comments will not be displayed until approved' * @return string
)) */
); public function getTitle()
{
// Show member name if given $title = sprintf(_t('Comment.COMMENTBY', 'Comment by %s', 'Name'), $this->getAuthorName());
if(($author = $this->Author()) && $author->exists()) {
$fields->insertAfter( if ($parent = $this->getParent()) {
TextField::create('AuthorMember', $this->fieldLabel('Author'), $author->Title) if ($parent->Title) {
->performReadonlyTransformation(), $title .= sprintf(' %s %s', _t('Comment.ON', 'on'), $parent->Title);
'Name' }
); }
}
return $title;
// Show parent comment if given }
if(($parent = $this->ParentComment()) && $parent->exists()) {
$fields->push(new HeaderField( /*
'ParentComment_Title', * Modify the default fields shown to the user
_t('Comment.ParentComment_Title', 'This comment is a reply to the below') */
)); public function getCMSFields()
// Created date {
$commentField = $this->AllowHtml ? 'HtmlEditorField' : 'TextareaField';
$fields = new FieldList(
$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(
'Comment.OPTION_DESCRIPTION',
'Unmoderated and spam comments will not be displayed until approved'
))
);
// Show member name if given
if (($author = $this->Author()) && $author->exists()) {
$fields->insertAfter(
TextField::create('AuthorMember', $this->fieldLabel('Author'), $author->Title)
->performReadonlyTransformation(),
'Name'
);
}
// Show parent comment if given
if (($parent = $this->ParentComment()) && $parent->exists()) {
$fields->push(new HeaderField(
'ParentComment_Title',
_t('Comment.ParentComment_Title', 'This comment is a reply to the below')
));
// Created date
// FIXME - the method setName in DatetimeField is not chainable, hence // FIXME - the method setName in DatetimeField is not chainable, hence
// the lack of chaining here // the lack of chaining here
$createdField = $parent $createdField = $parent
@ -580,46 +617,48 @@ class Comment extends DataObject {
$createdField->performReadonlyTransformation(); $createdField->performReadonlyTransformation();
$fields->push($createdField); $fields->push($createdField);
// Name (could be member or string value) // Name (could be member or string value)
$fields->push( $fields->push(
$parent $parent
->obj('AuthorName') ->obj('AuthorName')
->scaffoldFormField($parent->fieldLabel('AuthorName')) ->scaffoldFormField($parent->fieldLabel('AuthorName'))
->setName('ParentComment_AuthorName') ->setName('ParentComment_AuthorName')
->setValue($parent->getAuthorName()) ->setValue($parent->getAuthorName())
->performReadonlyTransformation() ->performReadonlyTransformation()
); );
// Comment body // Comment body
$fields->push( $fields->push(
$parent $parent
->obj('EscapedComment') ->obj('EscapedComment')
->scaffoldFormField($parent->fieldLabel('Comment')) ->scaffoldFormField($parent->fieldLabel('Comment'))
->setName('ParentComment_EscapedComment') ->setName('ParentComment_EscapedComment')
->setValue($parent->Comment) ->setValue($parent->Comment)
->performReadonlyTransformation() ->performReadonlyTransformation()
); );
} }
$this->extend('updateCMSFields', $fields); $this->extend('updateCMSFields', $fields);
return $fields; return $fields;
} }
/** /**
* @param String $dirtyHtml * @param String $dirtyHtml
* *
* @return String * @return String
*/ */
public function purifyHtml($dirtyHtml) { public function purifyHtml($dirtyHtml)
$purifier = $this->getHtmlPurifierService(); {
return $purifier->purify($dirtyHtml); $purifier = $this->getHtmlPurifierService();
} return $purifier->purify($dirtyHtml);
}
/** /**
* @return HTMLPurifier (or anything with a "purify()" method) * @return HTMLPurifier (or anything with a "purify()" method)
*/ */
public function getHtmlPurifierService() { public function getHtmlPurifierService()
$config = HTMLPurifier_Config::createDefault(); {
$config = HTMLPurifier_Config::createDefault();
$allowedElements = $this->getOption('html_allowed_elements'); $allowedElements = $this->getOption('html_allowed_elements');
$config->set('HTML.AllowedElements', $allowedElements); $config->set('HTML.AllowedElements', $allowedElements);
@ -628,256 +667,274 @@ class Comment extends DataObject {
$config->set('AutoFormat.AutoParagraph', true); $config->set('AutoFormat.AutoParagraph', true);
} }
$config->set('AutoFormat.Linkify', true); $config->set('AutoFormat.Linkify', true);
$config->set('URI.DisableExternalResources', true); $config->set('URI.DisableExternalResources', true);
$config->set('Cache.SerializerPath', getTempFolder()); $config->set('Cache.SerializerPath', getTempFolder());
return new HTMLPurifier($config); return new HTMLPurifier($config);
} }
/** /**
* Calculate the Gravatar link from the email address * Calculate the Gravatar link from the email address
* *
* @return string * @return string
*/ */
public function Gravatar() { public function Gravatar()
$gravatar = ''; {
$use_gravatar = $this->getOption('use_gravatar'); $gravatar = '';
if($use_gravatar) { $use_gravatar = $this->getOption('use_gravatar');
$gravatar = 'http://www.gravatar.com/avatar/' . md5(strtolower(trim($this->Email))); if ($use_gravatar) {
$gravatarsize = $this->getOption('gravatar_size'); $gravatar = 'http://www.gravatar.com/avatar/' . md5(strtolower(trim($this->Email)));
$gravatardefault = $this->getOption('gravatar_default'); $gravatarsize = $this->getOption('gravatar_size');
$gravatarrating = $this->getOption('gravatar_rating'); $gravatardefault = $this->getOption('gravatar_default');
$gravatar .= '?s=' . $gravatarsize . '&d=' . $gravatardefault . '&r=' . $gravatarrating; $gravatarrating = $this->getOption('gravatar_rating');
} $gravatar .= '?s=' . $gravatarsize . '&d=' . $gravatardefault . '&r=' . $gravatarrating;
}
return $gravatar; return $gravatar;
} }
/** /**
* Determine if replies are enabled for this instance * Determine if replies are enabled for this instance
* *
* @return boolean * @return boolean
*/ */
public function getRepliesEnabled() { public function getRepliesEnabled()
// Check reply option {
if(!$this->getOption('nested_comments')) { // Check reply option
return false; if (!$this->getOption('nested_comments')) {
} return false;
}
// Check if depth is limited // Check if depth is limited
$maxLevel = $this->getOption('nested_depth'); $maxLevel = $this->getOption('nested_depth');
$notSpam = ($this->SpamClass() == 'notspam'); $notSpam = ($this->SpamClass() == 'notspam');
return $notSpam && (!$maxLevel || $this->Depth < $maxLevel); return $notSpam && (!$maxLevel || $this->Depth < $maxLevel);
} }
/** /**
* Returns the list of all replies * Returns the list of all replies
* *
* @return SS_List * @return SS_List
*/ */
public function AllReplies() { public function AllReplies()
// No replies if disabled {
if(!$this->getRepliesEnabled()) { // No replies if disabled
return new ArrayList(); if (!$this->getRepliesEnabled()) {
} return new ArrayList();
}
// Get all non-spam comments // Get all non-spam comments
$order = $this->getOption('order_replies_by') $order = $this->getOption('order_replies_by')
?: $this->getOption('order_comments_by'); ?: $this->getOption('order_comments_by');
$list = $this $list = $this
->ChildComments() ->ChildComments()
->sort($order); ->sort($order);
$this->extend('updateAllReplies', $list); $this->extend('updateAllReplies', $list);
return $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 unmoderated items excluded, for use in the frontend
* *
* @return SS_List * @return SS_List
*/ */
public function Replies() { public function Replies()
// No replies if disabled {
if(!$this->getRepliesEnabled()) { // No replies if disabled
return new ArrayList(); if (!$this->getRepliesEnabled()) {
} return new ArrayList();
$list = $this->AllReplies(); }
$list = $this->AllReplies();
// Filter spam comments for non-administrators if configured // Filter spam comments for non-administrators if configured
$parent = $this->getParent(); $parent = $this->getParent();
$showSpam = $this->getOption('frontend_spam') && $parent && $parent->canModerateComments(); $showSpam = $this->getOption('frontend_spam') && $parent && $parent->canModerateComments();
if(!$showSpam) { if (!$showSpam) {
$list = $list->filter('IsSpam', 0); $list = $list->filter('IsSpam', 0);
} }
// Filter un-moderated comments for non-administrators if moderation is enabled // Filter un-moderated comments for non-administrators if moderation is enabled
$showUnmoderated = $parent && ( $showUnmoderated = $parent && (
($parent->ModerationRequired === 'None') ($parent->ModerationRequired === 'None')
|| ($this->getOption('frontend_moderation') && $parent->canModerateComments()) || ($this->getOption('frontend_moderation') && $parent->canModerateComments())
); );
if (!$showUnmoderated) { if (!$showUnmoderated) {
$list = $list->filter('Moderated', 1); $list = $list->filter('Moderated', 1);
} }
$this->extend('updateReplies', $list); $this->extend('updateReplies', $list);
return $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 unmoderated items excluded, for use in the frontend
* *
* @return PaginatedList * @return PaginatedList
*/ */
public function PagedReplies() { public function PagedReplies()
$list = $this->Replies(); {
$list = $this->Replies();
// Add pagination // Add pagination
$list = new PaginatedList($list, Controller::curr()->getRequest()); $list = new PaginatedList($list, Controller::curr()->getRequest());
$list->setPaginationGetVar('repliesstart'.$this->ID); $list->setPaginationGetVar('repliesstart'.$this->ID);
$list->setPageLength($this->getOption('comments_per_page')); $list->setPageLength($this->getOption('comments_per_page'));
$this->extend('updatePagedReplies', $list); $this->extend('updatePagedReplies', $list);
return $list; return $list;
} }
/** /**
* Generate a reply form for this comment * Generate a reply form for this comment
* *
* @return Form * @return Form
*/ */
public function ReplyForm() { public function ReplyForm()
// Ensure replies are enabled {
if(!$this->getRepliesEnabled()) { // Ensure replies are enabled
return null; if (!$this->getRepliesEnabled()) {
} return null;
}
// Check parent is available // Check parent is available
$parent = $this->getParent(); $parent = $this->getParent();
if(!$parent || !$parent->exists()) { if (!$parent || !$parent->exists()) {
return null; return null;
} }
// Build reply controller // Build reply controller
$controller = CommentingController::create(); $controller = CommentingController::create();
$controller->setOwnerRecord($parent); $controller->setOwnerRecord($parent);
$controller->setBaseClass($parent->ClassName); $controller->setBaseClass($parent->ClassName);
$controller->setOwnerController(Controller::curr()); $controller->setOwnerController(Controller::curr());
return $controller->ReplyForm($this); return $controller->ReplyForm($this);
} }
/** /**
* Refresh of this comment in the hierarchy * Refresh of this comment in the hierarchy
*/ */
public function updateDepth() { public function updateDepth()
$parent = $this->ParentComment(); {
if($parent && $parent->exists()) { $parent = $this->ParentComment();
$parent->updateDepth(); if ($parent && $parent->exists()) {
$this->Depth = $parent->Depth + 1; $parent->updateDepth();
} else { $this->Depth = $parent->Depth + 1;
$this->Depth = 1; } else {
} $this->Depth = 1;
} }
}
} }
/** /**
* 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 { class Comment_SecurityToken
{
private $secret = null; 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) { public function __construct($comment)
if(!$comment->SecretToken) { {
$comment->SecretToken = $this->generate(); if (!$comment->SecretToken) {
$comment->write(); $comment->SecretToken = $this->generate();
} $comment->write();
$this->secret = $comment->SecretToken; }
} $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 * @param string $salt
* *
* @return string * @return string
*/ */
protected function getToken($salt) { protected function getToken($salt)
return hash_pbkdf2('sha256', $this->secret, $salt, 1000, 30); {
} return hash_pbkdf2('sha256', $this->secret, $salt, 1000, 30);
}
/** /**
* Get the member-specific salt. * Get the member-specific salt.
* *
* The reason for making the salt specific to a user is that it cannot be "passed in" via a * The reason for making the salt specific to a user is that it cannot be "passed in" via a
* querystring, requiring the same user to be present at both the link generation and the * querystring, requiring the same user to be present at both the link generation and the
* controller action. * controller action.
* *
* @param string $salt Single use salt * @param string $salt Single use salt
* @param Member $member Member object * @param Member $member Member object
* *
* @return string Generated salt specific to this member * @return string Generated salt specific to this member
*/ */
protected function memberSalt($salt, $member) { protected function memberSalt($salt, $member)
// Fallback to salting with ID in case the member has not one set {
return $salt . ($member->Salt ?: $member->ID); // Fallback to salting with ID in case the member has not one set
} return $salt . ($member->Salt ?: $member->ID);
}
/** /**
* @param string $url Comment action URL * @param string $url Comment action URL
* @param Member $member Member to restrict access to this action to * @param Member $member Member to restrict access to this action to
* *
* @return string * @return string
*/ */
public function addToUrl($url, $member) { public function addToUrl($url, $member)
$salt = $this->generate(15); // New random salt; Will be passed into url {
// Generate salt specific to this member $salt = $this->generate(15); // New random salt; Will be passed into url
$memberSalt = $this->memberSalt($salt, $member); // Generate salt specific to this member
$token = $this->getToken($memberSalt); $memberSalt = $this->memberSalt($salt, $member);
return Controller::join_links( $token = $this->getToken($memberSalt);
$url, return Controller::join_links(
sprintf( $url,
'?t=%s&s=%s', sprintf(
urlencode($token), '?t=%s&s=%s',
urlencode($salt) urlencode($token),
) urlencode($salt)
); )
} );
}
/** /**
* @param SS_HTTPRequest $request * @param SS_HTTPRequest $request
* *
* @return boolean * @return boolean
*/ */
public function checkRequest($request) { public function checkRequest($request)
$member = Member::currentUser(); {
if(!$member) return false; $member = Member::currentUser();
if (!$member) {
return false;
}
$salt = $request->getVar('s'); $salt = $request->getVar('s');
$memberSalt = $this->memberSalt($salt, $member); $memberSalt = $this->memberSalt($salt, $member);
$token = $this->getToken($memberSalt); $token = $this->getToken($memberSalt);
// Ensure tokens match // Ensure tokens match
return $token === $request->getVar('t'); return $token === $request->getVar('t');
} }
/** /**
* Generates new random key * Generates new random key
* *
* @param integer $length * @param integer $length
* *
* @return string * @return string
*/ */
protected function generate($length = null) { protected function generate($length = null)
$generator = new RandomGenerator(); {
$result = $generator->randomToken('sha256'); $generator = new RandomGenerator();
if($length !== null) return substr($result, 0, $length); $result = $generator->randomToken('sha256');
return $result; if ($length !== null) {
} return substr($result, 0, $length);
}
return $result;
}
} }

View File

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

View File

@ -1,7 +1,9 @@
<?php <?php
class CommentAdminTest extends SapphireTest { class CommentAdminTest extends SapphireTest
public function testProvidePermissions() { {
public function testProvidePermissions()
{
$commentAdmin = new CommentAdmin(); $commentAdmin = new CommentAdmin();
$locale = i18n::get_locale(); $locale = i18n::get_locale();
@ -24,12 +26,13 @@ class CommentAdminTest extends SapphireTest {
) )
); );
$this->assertEquals($expected, $commentAdmin->providePermissions()); $this->assertEquals($expected, $commentAdmin->providePermissions());
} }
public function testGetEditForm() { public function testGetEditForm()
{
$commentAdmin = new CommentAdmin(); $commentAdmin = new CommentAdmin();
$this->logInWithPermission('CMS_ACCESS_CommentAdmin'); $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$form = $commentAdmin->getEditForm(); $form = $commentAdmin->getEditForm();
$names = $this->getFormFieldNames($form); $names = $this->getFormFieldNames($form);
$expected = array( $expected = array(
'NewComments', 'NewComments',
@ -38,12 +41,15 @@ class CommentAdminTest extends SapphireTest {
); );
$this->assertEquals($expected, $names); $this->assertEquals($expected, $names);
if($member = Member::currentUser()) $member->logOut(); if ($member = Member::currentUser()) {
$member->logOut();
}
$form = $commentAdmin->getEditForm(); $form = $commentAdmin->getEditForm();
} }
private function getFormFieldNames($form) { private function getFormFieldNames($form)
{
$result = array(); $result = array();
$fields = $form->Fields(); $fields = $form->Fields();
$tab = $fields->findOrMakeTab('Root'); $tab = $fields->findOrMakeTab('Root');
@ -53,5 +59,4 @@ class CommentAdminTest extends SapphireTest {
} }
return $result; return $result;
} }
} }

View File

@ -1,6 +1,7 @@
<?php <?php
class CommentListTest extends FunctionalTest { class CommentListTest extends FunctionalTest
{
public static $fixture_file = 'comments/tests/CommentsTest.yml'; public static $fixture_file = 'comments/tests/CommentsTest.yml';
@ -10,7 +11,8 @@ class CommentListTest extends FunctionalTest {
'CommentableItemDisabled' 'CommentableItemDisabled'
); );
public function setUp() { public function setUp()
{
parent::setUp(); parent::setUp();
Config::nest(); Config::nest();
@ -34,20 +36,23 @@ class CommentListTest extends FunctionalTest {
)); ));
} }
public function tearDown() { public function tearDown()
{
Config::unnest(); Config::unnest();
parent::tearDown(); parent::tearDown();
} }
public function testGetForeignClass() { public function testGetForeignClass()
{
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
// This is the class the Comments are related to // This is the class the Comments are related to
$this->assertEquals('CommentableItem', $this->assertEquals('CommentableItem',
$item->Comments()->getForeignClass()); $item->Comments()->getForeignClass());
} }
public function testAddNonComment() { public function testAddNonComment()
{
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$comments = $item->Comments(); $comments = $item->Comments();
$this->assertEquals(4, $comments->count()); $this->assertEquals(4, $comments->count());
@ -63,7 +68,8 @@ class CommentListTest extends FunctionalTest {
} }
} }
public function testAddComment() { public function testAddComment()
{
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$firstComment = $this->objFromFixture('Comment', 'firstComA'); $firstComment = $this->objFromFixture('Comment', 'firstComA');
$comments = $item->Comments();//->sort('Created'); $comments = $item->Comments();//->sort('Created');
@ -97,9 +103,10 @@ class CommentListTest extends FunctionalTest {
); );
$list = new CommentList('CommentableItem'); $list = new CommentList('CommentableItem');
$list->add($newComment); $list->add($newComment);
} }
public function testRemoveComment() { public function testRemoveComment()
{
// remove by comment // remove by comment
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$this->assertEquals(4, $item->Comments()->count()); $this->assertEquals(4, $item->Comments()->count());
@ -114,7 +121,8 @@ class CommentListTest extends FunctionalTest {
$this->assertEquals(2, $item->Comments()->count()); $this->assertEquals(2, $item->Comments()->count());
} }
public function testRemoveNonComment() { public function testRemoveNonComment()
{
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$this->assertEquals(4, $item->Comments()->count()); $this->assertEquals(4, $item->Comments()->count());
$comments = $item->Comments(); $comments = $item->Comments();
@ -134,5 +142,4 @@ class CommentListTest extends FunctionalTest {
); );
} }
} }
} }

View File

@ -1,17 +1,20 @@
<?php <?php
class CommentTestHelper { class CommentTestHelper
{
/* /*
This only works if the last section is not a field group, e.g. a Comments This only works if the last section is not a field group, e.g. a Comments
field group inside of a Root.Settings tab will not work field group inside of a Root.Settings tab will not work
*/ */
public static function assertFieldsForTab($context, $tabName, $expected, $fields) { public static function assertFieldsForTab($context, $tabName, $expected, $fields)
{
$tab = $fields->findOrMakeTab($tabName); $tab = $fields->findOrMakeTab($tabName);
$fields = $tab->FieldList(); $fields = $tab->FieldList();
CommentTestHelper::assertFieldNames($context, $expected, $fields); CommentTestHelper::assertFieldNames($context, $expected, $fields);
} }
public static function assertFieldNames($context, $expected, $fields) { public static function assertFieldNames($context, $expected, $fields)
{
$actual = array(); $actual = array();
foreach ($fields as $field) { foreach ($fields as $field) {
if (get_class($field) == 'FieldGroup') { if (get_class($field) == 'FieldGroup') {

View File

@ -4,31 +4,35 @@
* @package comments * @package comments
* @subpackage tests * @subpackage tests
*/ */
class CommentingControllerTest extends FunctionalTest { class CommentingControllerTest extends FunctionalTest
{
public static $fixture_file = 'CommentsTest.yml'; public static $fixture_file = 'CommentsTest.yml';
protected $extraDataObjects = array( protected $extraDataObjects = array(
'CommentableItem' 'CommentableItem'
); );
protected $securityEnabled; protected $securityEnabled;
public function tearDown() { public function tearDown()
if($this->securityEnabled) { {
SecurityToken::enable(); if ($this->securityEnabled) {
} else { SecurityToken::enable();
SecurityToken::disable(); } else {
} SecurityToken::disable();
parent::tearDown(); }
} parent::tearDown();
}
public function setUp() { public function setUp()
parent::setUp(); {
$this->securityEnabled = SecurityToken::is_enabled(); parent::setUp();
} $this->securityEnabled = SecurityToken::is_enabled();
}
public function testApprove() { public function testApprove()
{
SecurityToken::disable(); SecurityToken::disable();
// mark a comment as spam then approve it // mark a comment as spam then approve it
@ -49,10 +53,10 @@ class CommentingControllerTest extends FunctionalTest {
// try and approve a non existent comment // try and approve a non existent comment
$response = $this->get('CommentingController/approve/100000'); $response = $this->get('CommentingController/approve/100000');
$this->assertEquals(404, $response->getStatusCode()); $this->assertEquals(404, $response->getStatusCode());
} }
public function testSetGetOwnerController() { public function testSetGetOwnerController()
{
$commController = new CommentingController(); $commController = new CommentingController();
$commController->setOwnerController(Controller::curr()); $commController->setOwnerController(Controller::curr());
$this->assertEquals(Controller::curr(), $commController->getOwnerController()); $this->assertEquals(Controller::curr(), $commController->getOwnerController());
@ -60,7 +64,8 @@ class CommentingControllerTest extends FunctionalTest {
$this->assertNull($commController->getOwnerController()); $this->assertNull($commController->getOwnerController());
} }
public function testHam() { public function testHam()
{
SecurityToken::disable(); SecurityToken::disable();
// mark a comment as spam then ham it // mark a comment as spam then ham it
@ -81,10 +86,10 @@ class CommentingControllerTest extends FunctionalTest {
// try and ham a non existent comment // try and ham a non existent comment
$response = $this->get('CommentingController/ham/100000'); $response = $this->get('CommentingController/ham/100000');
$this->assertEquals(404, $response->getStatusCode()); $this->assertEquals(404, $response->getStatusCode());
} }
public function testSpam() { public function testSpam()
{
// mark a comment as approved then spam it // mark a comment as approved then spam it
$this->logInWithPermission('CMS_ACCESS_CommentAdmin'); $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
@ -103,10 +108,10 @@ class CommentingControllerTest extends FunctionalTest {
// try and spam a non existent comment // try and spam a non existent comment
$response = $this->get('CommentingController/spam/100000'); $response = $this->get('CommentingController/spam/100000');
$this->assertEquals(404, $response->getStatusCode()); $this->assertEquals(404, $response->getStatusCode());
} }
public function testRSS() { public function testRSS()
{
// Delete the newly added children of firstComA so as not to have to recalculate values below // Delete the newly added children of firstComA so as not to have to recalculate values below
$this->objFromFixture('Comment', 'firstComAChild1')->delete(); $this->objFromFixture('Comment', 'firstComAChild1')->delete();
$this->objFromFixture('Comment', 'firstComAChild2')->delete(); $this->objFromFixture('Comment', 'firstComAChild2')->delete();
@ -115,29 +120,29 @@ class CommentingControllerTest extends FunctionalTest {
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
// comments sitewide // comments sitewide
$response = $this->get('CommentingController/rss'); $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'); $response = $this->get('CommentingController/rss?start=10');
$this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2"); $this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
// all comments on a type // all comments on a type
$response = $this->get('CommentingController/rss/CommentableItem'); $response = $this->get('CommentingController/rss/CommentableItem');
$this->assertEquals(10, substr_count($response->getBody(), "<item>")); $this->assertEquals(10, substr_count($response->getBody(), "<item>"));
$response = $this->get('CommentingController/rss/CommentableItem?start=10'); $response = $this->get('CommentingController/rss/CommentableItem?start=10');
$this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2"); $this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
// specific page // specific page
$response = $this->get('CommentingController/rss/CommentableItem/'.$item->ID); $response = $this->get('CommentingController/rss/CommentableItem/'.$item->ID);
$this->assertEquals(1, substr_count($response->getBody(), "<item>")); $this->assertEquals(1, substr_count($response->getBody(), "<item>"));
$this->assertContains('<dc:creator>FA</dc:creator>', $response->getBody()); $this->assertContains('<dc:creator>FA</dc:creator>', $response->getBody());
// test accessing comments on a type that doesn't exist // test accessing comments on a type that doesn't exist
$response = $this->get('CommentingController/rss/Fake'); $response = $this->get('CommentingController/rss/Fake');
$this->assertEquals(404, $response->getStatusCode()); $this->assertEquals(404, $response->getStatusCode());
} }
// This is returning a 404 which looks logical code wise but also a bit weird. // This is returning a 404 which looks logical code wise but also a bit weird.
// Test module on a clean install and check what the actual URL is first // Test module on a clean install and check what the actual URL is first
@ -177,7 +182,8 @@ class CommentingControllerTest extends FunctionalTest {
} }
*/ */
public function testCommentsFormUsePreview() { public function testCommentsFormUsePreview()
{
// test with preview on // test with preview on
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'use_preview' => true 'use_preview' => true
@ -208,67 +214,66 @@ class CommentingControllerTest extends FunctionalTest {
CommentTestHelper::assertFieldNames($this, $expected, $commentsFields); CommentTestHelper::assertFieldNames($this, $expected, $commentsFields);
} }
public function testCommentsForm() { public function testCommentsForm()
{
// Delete the newly added children of firstComA so as not to change this test // Delete the newly added children of firstComA so as not to change this test
$this->objFromFixture('Comment', 'firstComAChild1')->delete(); $this->objFromFixture('Comment', 'firstComAChild1')->delete();
$this->objFromFixture('Comment', 'firstComAChild2')->delete(); $this->objFromFixture('Comment', 'firstComAChild2')->delete();
$this->objFromFixture('Comment', 'firstComAChild3')->delete(); $this->objFromFixture('Comment', 'firstComAChild3')->delete();
SecurityToken::disable(); SecurityToken::disable();
$this->autoFollowRedirection = false; $this->autoFollowRedirection = false;
$parent = $this->objFromFixture('CommentableItem', 'first'); $parent = $this->objFromFixture('CommentableItem', 'first');
// Test posting to base comment // Test posting to base comment
$response = $this->post('CommentingController/CommentsForm', $response = $this->post('CommentingController/CommentsForm',
array( array(
'Name' => 'Poster', 'Name' => 'Poster',
'Email' => 'guy@test.com', 'Email' => 'guy@test.com',
'Comment' => 'My Comment', 'Comment' => 'My Comment',
'ParentID' => $parent->ID, 'ParentID' => $parent->ID,
'BaseClass' => 'CommentableItem', 'BaseClass' => 'CommentableItem',
'action_doPostComment' => 'Post' 'action_doPostComment' => 'Post'
) )
); );
$this->assertEquals(302, $response->getStatusCode()); $this->assertEquals(302, $response->getStatusCode());
$this->assertStringStartsWith('CommentableItem_Controller#comment-', $response->getHeader('Location')); $this->assertStringStartsWith('CommentableItem_Controller#comment-', $response->getHeader('Location'));
$this->assertDOSEquals( $this->assertDOSEquals(
array(array( array(array(
'Name' => 'Poster', 'Name' => 'Poster',
'Email' => 'guy@test.com', 'Email' => 'guy@test.com',
'Comment' => 'My Comment', 'Comment' => 'My Comment',
'ParentID' => $parent->ID, 'ParentID' => $parent->ID,
'BaseClass' => 'CommentableItem', 'BaseClass' => 'CommentableItem',
)), )),
Comment::get()->filter('Email', 'guy@test.com') Comment::get()->filter('Email', 'guy@test.com')
); );
// Test posting to parent comment // Test posting to parent comment
$parentComment = $this->objFromFixture('Comment', 'firstComA'); $parentComment = $this->objFromFixture('Comment', 'firstComA');
$this->assertEquals(0, $parentComment->ChildComments()->count()); $this->assertEquals(0, $parentComment->ChildComments()->count());
$response = $this->post( $response = $this->post(
'CommentingController/reply/'.$parentComment->ID, 'CommentingController/reply/'.$parentComment->ID,
array( array(
'Name' => 'Test Author', 'Name' => 'Test Author',
'Email' => 'test@test.com', 'Email' => 'test@test.com',
'Comment' => 'Making a reply to firstComA', 'Comment' => 'Making a reply to firstComA',
'ParentID' => $parent->ID, 'ParentID' => $parent->ID,
'BaseClass' => 'CommentableItem', 'BaseClass' => 'CommentableItem',
'ParentCommentID' => $parentComment->ID, 'ParentCommentID' => $parentComment->ID,
'action_doPostComment' => 'Post' 'action_doPostComment' => 'Post'
) )
); );
$this->assertEquals(302, $response->getStatusCode()); $this->assertEquals(302, $response->getStatusCode());
$this->assertStringStartsWith('CommentableItem_Controller#comment-', $response->getHeader('Location')); $this->assertStringStartsWith('CommentableItem_Controller#comment-', $response->getHeader('Location'));
$this->assertDOSEquals(array(array( $this->assertDOSEquals(array(array(
'Name' => 'Test Author', 'Name' => 'Test Author',
'Email' => 'test@test.com', 'Email' => 'test@test.com',
'Comment' => 'Making a reply to firstComA', 'Comment' => 'Making a reply to firstComA',
'ParentID' => $parent->ID, 'ParentID' => $parent->ID,
'BaseClass' => 'CommentableItem', 'BaseClass' => 'CommentableItem',
'ParentCommentID' => $parentComment->ID 'ParentCommentID' => $parentComment->ID
)), $parentComment->ChildComments()); )), $parentComment->ChildComments());
}
}
} }

View File

@ -1,12 +1,15 @@
<?php <?php
class CommentingTest extends SapphireTest { class CommentingTest extends SapphireTest
{
public function setUpOnce() { public function setUpOnce()
{
parent::setUpOnce(); parent::setUpOnce();
} }
public function testDeprecatedMethods() { public function testDeprecatedMethods()
{
$methods = array('add', 'remove', 'has_commenting'); $methods = array('add', 'remove', 'has_commenting');
foreach ($methods as $methodName) { foreach ($methods as $methodName) {
try { try {
@ -20,8 +23,9 @@ class CommentingTest extends SapphireTest {
} }
public function test_set_config_value() { public function test_set_config_value()
// public static function set_config_value($class, $key, $value = false) { {
// public static function set_config_value($class, $key, $value = false) {
Commenting::set_config_value( Commenting::set_config_value(
'CommentableItem', 'CommentableItem',
'comments_holder_id', 'comments_holder_id',
@ -44,7 +48,7 @@ class CommentingTest extends SapphireTest {
'all_items_actually_commentsextension' 'all_items_actually_commentsextension'
); );
$config = Config::inst()->get( $config = Config::inst()->get(
'CommentsExtension', 'CommentsExtension',
'comments' 'comments'
); );
@ -53,10 +57,11 @@ class CommentingTest extends SapphireTest {
'all_items_actually_commentsextension', 'all_items_actually_commentsextension',
$actual $actual
); );
} }
public function test_get_config_value() { public function test_get_config_value()
Config::inst()->update('CommentableItem', 'comments', {
Config::inst()->update('CommentableItem', 'comments',
array( array(
'comments_holder_id' => 'commentable_item' 'comments_holder_id' => 'commentable_item'
) )
@ -82,10 +87,11 @@ class CommentingTest extends SapphireTest {
'Member does not have commenting enabled' 'Member does not have commenting enabled'
); );
Commenting::get_config_value('Member', 'comments_holder_id'); Commenting::get_config_value('Member', 'comments_holder_id');
} }
public function test_config_value_equals() { public function test_config_value_equals()
Config::inst()->update('CommentableItem', 'comments', {
Config::inst()->update('CommentableItem', 'comments',
array( array(
'comments_holder_id' => 'some_value' 'comments_holder_id' => 'some_value'
) )
@ -106,9 +112,10 @@ class CommentingTest extends SapphireTest {
'not_some_value' 'not_some_value'
) )
); );
} }
public function test_add() { public function test_add()
{
Commenting::add('Member', array('comments_holder_id' => 'test_add_value')); Commenting::add('Member', array('comments_holder_id' => 'test_add_value'));
$config = Config::inst()->get( $config = Config::inst()->get(
@ -136,13 +143,14 @@ class CommentingTest extends SapphireTest {
$this->setExpectedException('InvalidArgumentException', "\$settings needs to be an array or null"); $this->setExpectedException('InvalidArgumentException', "\$settings needs to be an array or null");
Commenting::add('Member', 'illegal format, not an array'); Commenting::add('Member', 'illegal format, not an array');
} }
public function test_can_member_post() { public function test_can_member_post()
{
// logout // logout
if($member = Member::currentUser()) $member->logOut(); if ($member = Member::currentUser()) {
$member->logOut();
}
Config::inst()->update('CommentableItem', 'comments', Config::inst()->update('CommentableItem', 'comments',
array( array(
@ -168,6 +176,5 @@ class CommentingTest extends SapphireTest {
); );
$this->assertTrue(Commenting::can_member_post('CommentableItem')); $this->assertTrue(Commenting::can_member_post('CommentableItem'));
} }
} }

View File

@ -1,6 +1,7 @@
<?php <?php
class CommentsExtensionTest extends SapphireTest { class CommentsExtensionTest extends SapphireTest
{
public static $fixture_file = 'comments/tests/CommentsTest.yml'; public static $fixture_file = 'comments/tests/CommentsTest.yml';
@ -10,7 +11,8 @@ class CommentsExtensionTest extends SapphireTest {
'CommentableItemDisabled' 'CommentableItemDisabled'
); );
public function setUp() { public function setUp()
{
parent::setUp(); parent::setUp();
Config::nest(); Config::nest();
@ -38,20 +40,24 @@ class CommentsExtensionTest extends SapphireTest {
)); ));
} }
public function tearDown() { public function tearDown()
{
Config::unnest(); Config::unnest();
parent::tearDown(); parent::tearDown();
} }
public function testPopulateDefaults() { public function testPopulateDefaults()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testUpdateSettingsFields() { public function testUpdateSettingsFields()
{
$this->markTestSkipped('This needs SiteTree installed'); $this->markTestSkipped('This needs SiteTree installed');
} }
public function testGetModerationRequired() { public function testGetModerationRequired()
{
// the 3 options take precedence in this order, executed if true // the 3 options take precedence in this order, executed if true
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
@ -90,10 +96,11 @@ class CommentsExtensionTest extends SapphireTest {
'require_moderation_nonmembers' => false 'require_moderation_nonmembers' => false
)); ));
$this->assertEquals('None', $item->getModerationRequired()); $this->assertEquals('None', $item->getModerationRequired());
} }
public function testGetCommentsRequireLogin() { public function testGetCommentsRequireLogin()
Config::inst()->update('CommentableItem', 'comments', array( {
Config::inst()->update('CommentableItem', 'comments', array(
'require_login_cms' => true 'require_login_cms' => true
)); ));
@ -115,26 +122,30 @@ class CommentsExtensionTest extends SapphireTest {
'require_login' => true 'require_login' => true
)); ));
$this->assertTrue($item->getCommentsRequireLogin()); $this->assertTrue($item->getCommentsRequireLogin());
}
} public function testAllComments()
{
$this->markTestSkipped('TODO');
}
public function testAllComments() { public function testAllVisibleComments()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testAllVisibleComments() { public function testComments()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testComments() { public function testGetCommentsEnabled()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testGetCommentsEnabled() { public function testGetCommentHolderID()
$this->markTestSkipped('TODO'); {
}
public function testGetCommentHolderID() {
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'comments_holder_id' => 'commentid_test1', 'comments_holder_id' => 'commentid_test1',
@ -145,47 +156,53 @@ class CommentsExtensionTest extends SapphireTest {
'comments_holder_id' => 'commtentid_test_another', 'comments_holder_id' => 'commtentid_test_another',
)); ));
$this->assertEquals('commtentid_test_another', $item->getCommentHolderID()); $this->assertEquals('commtentid_test_another', $item->getCommentHolderID());
} }
public function testGetPostingRequiredPermission() { public function testGetPostingRequiredPermission()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testCanModerateComments() { public function testCanModerateComments()
{
// ensure nobody logged in // ensure nobody logged in
if(Member::currentUser()) { Member::currentUser()->logOut(); } if (Member::currentUser()) {
Member::currentUser()->logOut();
}
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$this->assertFalse($item->canModerateComments()); $this->assertFalse($item->canModerateComments());
$this->logInWithPermission('CMS_ACCESS_CommentAdmin'); $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$this->assertTrue($item->canModerateComments()); $this->assertTrue($item->canModerateComments());
}
} public function testGetCommentRSSLink()
{
public function testGetCommentRSSLink() { $item = $this->objFromFixture('CommentableItem', 'first');
$item = $this->objFromFixture('CommentableItem', 'first'); $link = $item->getCommentRSSLink();
$link = $item->getCommentRSSLink(); $this->assertEquals('/CommentingController/rss', $link);
$this->assertEquals('/CommentingController/rss', $link); }
}
public function testGetCommentRSSLinkPage() { public function testGetCommentRSSLinkPage()
$item = $this->objFromFixture('CommentableItem', 'first'); {
$item = $this->objFromFixture('CommentableItem', 'first');
$page = $item->getCommentRSSLinkPage(); $page = $item->getCommentRSSLinkPage();
$this->assertEquals( $this->assertEquals(
'/CommentingController/rss/CommentableItem/' . $item->ID, '/CommentingController/rss/CommentableItem/' . $item->ID,
$page $page
); );
} }
public function testCommentsForm() { public function testCommentsForm()
{
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'include_js' => false 'include_js' => false
) )
); );
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
// The comments form is HTML to do assertions by contains // The comments form is HTML to do assertions by contains
$cf = $item->CommentsForm(); $cf = $item->CommentsForm();
@ -255,13 +272,15 @@ class CommentsExtensionTest extends SapphireTest {
), ),
$backend->get_javascript() $backend->get_javascript()
); );
} }
public function testAttachedToSiteTree() { public function testAttachedToSiteTree()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testPagedComments() { public function testPagedComments()
{
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
// Ensure Created times are set, as order not guaranteed if all set to 0 // Ensure Created times are set, as order not guaranteed if all set to 0
$comments = $item->PagedComments()->sort('ID'); $comments = $item->PagedComments()->sort('ID');
@ -276,7 +295,7 @@ class CommentsExtensionTest extends SapphireTest {
$results = $item->PagedComments()->toArray(); $results = $item->PagedComments()->toArray();
foreach ($results as $result) { foreach ($results as $result) {
$result->sourceQueryParams = null; $result->sourceQueryParams = null;
} }
$this->assertEquals( $this->assertEquals(
@ -297,23 +316,26 @@ class CommentsExtensionTest extends SapphireTest {
); );
$this->assertEquals(4, sizeof($results)); $this->assertEquals(4, sizeof($results));
} }
public function testGetCommentsOption() { public function testGetCommentsOption()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testUpdateModerationFields() { public function testUpdateModerationFields()
$this->markTestSkipped('TODO'); {
} $this->markTestSkipped('TODO');
}
public function testUpdateCMSFields() { public function testUpdateCMSFields()
{
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'require_login_cms' => false 'require_login_cms' => false
) )
); );
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$item->ProvideComments = true; $item->ProvideComments = true;
$item->write(); $item->write();
$fields = $item->getCMSFields(); $fields = $item->getCMSFields();
@ -376,11 +398,12 @@ class CommentsExtensionTest extends SapphireTest {
array('ProvideComments', 'CommentsRequireLogin'), array('ProvideComments', 'CommentsRequireLogin'),
$fields $fields
); );
} }
public function testDeprecatedMethods() { public function testDeprecatedMethods()
{
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$methodNames = array( $methodNames = array(
'getRssLinkPage', 'getRssLinkPage',
@ -403,7 +426,5 @@ class CommentsExtensionTest extends SapphireTest {
} }
// ooh, $this->setExpectedException('ExpectedException', 'Expected Message'); // ooh, $this->setExpectedException('ExpectedException', 'Expected Message');
} }
} }

View File

@ -1,6 +1,7 @@
<?php <?php
class CommentsGridFieldActionTest extends SapphireTest { class CommentsGridFieldActionTest extends SapphireTest
{
/** @var ArrayList */ /** @var ArrayList */
protected $list; protected $list;
@ -11,7 +12,8 @@ class CommentsGridFieldActionTest extends SapphireTest {
/** @var Form */ /** @var Form */
protected $form; protected $form;
public function setUp() { public function setUp()
{
parent::setUp(); parent::setUp();
$this->list = new DataList('GridFieldAction_Delete_Team'); $this->list = new DataList('GridFieldAction_Delete_Team');
$config = CommentsGridFieldConfig::create()->addComponent(new GridFieldDeleteAction()); $config = CommentsGridFieldConfig::create()->addComponent(new GridFieldDeleteAction());
@ -19,7 +21,8 @@ class CommentsGridFieldActionTest extends SapphireTest {
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList()); $this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
} }
public function testAugmentColumns() { public function testAugmentColumns()
{
$action = new CommentsGridFieldAction(); $action = new CommentsGridFieldAction();
// an entry called 'Actions' is added to the columns array // an entry called 'Actions' is added to the columns array
@ -32,32 +35,36 @@ class CommentsGridFieldActionTest extends SapphireTest {
$action->augmentColumns($this->gridField, $columns); $action->augmentColumns($this->gridField, $columns);
$expected = array('Actions'); $expected = array('Actions');
$this->assertEquals($expected, $columns); $this->assertEquals($expected, $columns);
} }
public function testGetColumnAttributes() { public function testGetColumnAttributes()
$action = new CommentsGridFieldAction(); {
$action = new CommentsGridFieldAction();
$record = new Comment(); $record = new Comment();
$attrs = $action->getColumnAttributes($this->gridField, $record, 'Comment'); $attrs = $action->getColumnAttributes($this->gridField, $record, 'Comment');
$this->assertEquals(array('class' => 'col-buttons'), $attrs); $this->assertEquals(array('class' => 'col-buttons'), $attrs);
} }
public function testGetColumnMetadata() { public function testGetColumnMetadata()
$action = new CommentsGridFieldAction(); {
$action = new CommentsGridFieldAction();
$result = $action->getColumnMetadata($this->gridField, 'Actions'); $result = $action->getColumnMetadata($this->gridField, 'Actions');
$this->assertEquals(array('title' => ''), $result); $this->assertEquals(array('title' => ''), $result);
$result = $action->getColumnMetadata($this->gridField, 'SomethingElse'); $result = $action->getColumnMetadata($this->gridField, 'SomethingElse');
$this->assertNull($result); $this->assertNull($result);
} }
public function testGetColumnsHandled() { public function testGetColumnsHandled()
$action = new CommentsGridFieldAction(); {
$action = new CommentsGridFieldAction();
$result = $action->getColumnsHandled($this->gridField); $result = $action->getColumnsHandled($this->gridField);
$this->assertEquals(array('Actions'), $result); $this->assertEquals(array('Actions'), $result);
} }
public function testGetColumnContent() { public function testGetColumnContent()
{
$this->logInWithPermission('CMS_ACCESS_CommentAdmin'); $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$action = new CommentsGridFieldAction(); $action = new CommentsGridFieldAction();
$record = new Comment(); $record = new Comment();
$record->Name = 'Name of commeter'; $record->Name = 'Name of commeter';
$record->Comment = 'This is a comment'; $record->Comment = 'This is a comment';
@ -87,17 +94,19 @@ class CommentsGridFieldActionTest extends SapphireTest {
$html = $action->getColumnContent($this->gridField, $record, 'Comment'); $html = $action->getColumnContent($this->gridField, $record, 'Comment');
$this->assertNotContains($approveAction, $html); $this->assertNotContains($approveAction, $html);
$this->assertContains($spamAction, $html); $this->assertContains($spamAction, $html);
} }
public function testGetActions() { public function testGetActions()
$action = new CommentsGridFieldAction(); {
$action = new CommentsGridFieldAction();
$result = $action->getActions($this->gridField); $result = $action->getActions($this->gridField);
$this->assertEquals(array('spam', 'approve'), $result); $this->assertEquals(array('spam', 'approve'), $result);
} }
public function testHandleAction() { public function testHandleAction()
$action = new CommentsGridFieldAction(); {
$action = new CommentsGridFieldAction();
$record = new Comment(); $record = new Comment();
$record->Name = 'Name of commeter'; $record->Name = 'Name of commeter';
$record->Comment = 'This is a comment'; $record->Comment = 'This is a comment';
@ -105,7 +114,7 @@ class CommentsGridFieldActionTest extends SapphireTest {
$recordID = $record->ID; $recordID = $record->ID;
$arguments = array('RecordID' => $recordID); $arguments = array('RecordID' => $recordID);
$data = array(); $data = array();
$result = $action->handleAction($this->gridField, 'spam', $arguments, $data ); $result = $action->handleAction($this->gridField, 'spam', $arguments, $data);
$this->assertEquals(200, Controller::curr()->getResponse()->getStatusCode()); $this->assertEquals(200, Controller::curr()->getResponse()->getStatusCode());
$this->assertEquals( $this->assertEquals(
'Comment marked as spam.', 'Comment marked as spam.',
@ -116,7 +125,7 @@ class CommentsGridFieldActionTest extends SapphireTest {
$this->assertEquals(1, $record->IsSpam); $this->assertEquals(1, $record->IsSpam);
//getStatusDescription //getStatusDescription
$result = $action->handleAction($this->gridField, 'approve', $arguments, $data ); $result = $action->handleAction($this->gridField, 'approve', $arguments, $data);
$this->assertEquals(200, Controller::curr()->getResponse()->getStatusCode()); $this->assertEquals(200, Controller::curr()->getResponse()->getStatusCode());
$this->assertEquals( $this->assertEquals(
'Comment approved.', 'Comment approved.',
@ -128,6 +137,5 @@ class CommentsGridFieldActionTest extends SapphireTest {
$this->assertEquals(0, $record->IsSpam); $this->assertEquals(0, $record->IsSpam);
error_log(Controller::curr()->getResponse()->getStatusCode()); error_log(Controller::curr()->getResponse()->getStatusCode());
} }
} }

View File

@ -1,12 +1,14 @@
<?php <?php
class CommentsGridFieldBulkActionTest extends SapphireTest { class CommentsGridFieldBulkActionTest extends SapphireTest
public function testSpam() { {
$this->markTestSkipped('TODO'); public function testSpam()
} {
$this->markTestSkipped('TODO');
public function testApprove() { }
$this->markTestSkipped('TODO');
}
public function testApprove()
{
$this->markTestSkipped('TODO');
}
} }

View File

@ -1,9 +1,10 @@
<?php <?php
class CommentsGridFieldConfigTest extends SapphireTest { class CommentsGridFieldConfigTest extends SapphireTest
public function test__construct() { {
public function test__construct()
{
$config = new CommentsGridFieldConfigTest(); $config = new CommentsGridFieldConfigTest();
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
} }

View File

@ -1,22 +1,22 @@
<?php <?php
class CommentsGridFieldTest extends SapphireTest { class CommentsGridFieldTest extends SapphireTest
public function testNewRow() { {
$gridfield = new CommentsGridField('testfield', 'testfield'); public function testNewRow()
{
$gridfield = new CommentsGridField('testfield', 'testfield');
// protected function newRow($total, $index, $record, $attributes, $content) { // protected function newRow($total, $index, $record, $attributes, $content) {
$comment = new Comment(); $comment = new Comment();
$comment->Name = 'Fred Bloggs'; $comment->Name = 'Fred Bloggs';
$comment->Comment = 'This is a comment'; $comment->Comment = 'This is a comment';
$attr = array(); $attr = array();
try { try {
$class = new ReflectionClass($gridfield); $class = new ReflectionClass($gridfield);
$method = $class->getMethod('newRow'); $method = $class->getMethod('newRow');
$method->setAccessible(true); $method->setAccessible(true);
} } catch (ReflectionException $e) {
catch (ReflectionException $e) {
$this->fail($e->getMessage()); $this->fail($e->getMessage());
} }
@ -33,8 +33,5 @@ class CommentsGridFieldTest extends SapphireTest {
$params = array(1, 1, $comment, $attr, $comment->Comment); $params = array(1, 1, $comment, $attr, $comment->Comment);
$newRow = $method->invokeArgs($gridfield, $params); $newRow = $method->invokeArgs($gridfield, $params);
$this->assertEquals('<tr class="cssClass spam">This is a comment</tr>', $newRow); $this->assertEquals('<tr class="cssClass spam">This is a comment</tr>', $newRow);
}
}
} }

View File

@ -3,405 +3,434 @@
/** /**
* @package comments * @package comments
*/ */
class CommentsTest extends FunctionalTest { class CommentsTest extends FunctionalTest
{
public static $fixture_file = 'comments/tests/CommentsTest.yml'; public static $fixture_file = 'comments/tests/CommentsTest.yml';
protected $extraDataObjects = array( protected $extraDataObjects = array(
'CommentableItem', 'CommentableItem',
'CommentableItemEnabled', 'CommentableItemEnabled',
'CommentableItemDisabled' 'CommentableItemDisabled'
); );
public function setUp() { public function setUp()
parent::setUp(); {
Config::nest(); parent::setUp();
Config::nest();
// Set good default values // Set good default values
Config::inst()->update('CommentsExtension', 'comments', array( Config::inst()->update('CommentsExtension', 'comments', array(
'enabled' => true, 'enabled' => true,
'enabled_cms' => false, 'enabled_cms' => false,
'require_login' => false, 'require_login' => false,
'require_login_cms' => false, 'require_login_cms' => false,
'required_permission' => false, 'required_permission' => false,
'require_moderation_nonmembers' => false, 'require_moderation_nonmembers' => false,
'require_moderation' => false, 'require_moderation' => false,
'require_moderation_cms' => false, 'require_moderation_cms' => false,
'frontend_moderation' => false, 'frontend_moderation' => false,
'frontend_spam' => false, 'frontend_spam' => false,
)); ));
// Configure this dataobject // Configure this dataobject
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'enabled_cms' => true 'enabled_cms' => true
)); ));
} }
public function tearDown() { public function tearDown()
Config::unnest(); {
parent::tearDown(); Config::unnest();
} parent::tearDown();
}
public function testCommentsList() { public function testCommentsList()
// comments don't require moderation so unmoderated comments can be {
// shown but not spam posts // comments don't require moderation so unmoderated comments can be
Config::inst()->update('CommentableItem', 'comments', array( // shown but not spam posts
'require_moderation_nonmembers' => false, Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation' => false, 'require_moderation_nonmembers' => false,
'require_moderation_cms' => false, 'require_moderation' => false,
)); 'require_moderation_cms' => false,
));
$item = $this->objFromFixture('CommentableItem', 'spammed'); $item = $this->objFromFixture('CommentableItem', 'spammed');
$this->assertEquals('None', $item->ModerationRequired); $this->assertEquals('None', $item->ModerationRequired);
$this->assertDOSEquals(array( $this->assertDOSEquals(array(
array('Name' => 'Comment 1'), array('Name' => 'Comment 1'),
array('Name' => 'Comment 3') 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. // when moderated, only moderated, non spam posts should be shown.
Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => true)); Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => true));
$this->assertEquals('NonMembersOnly', $item->ModerationRequired); $this->assertEquals('NonMembersOnly', $item->ModerationRequired);
// Check that require_moderation overrides this option // Check that require_moderation overrides this option
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => true)); Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => true));
$this->assertEquals('Required', $item->ModerationRequired); $this->assertEquals('Required', $item->ModerationRequired);
$this->assertDOSEquals(array( $this->assertDOSEquals(array(
array('Name' => 'Comment 3') 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()); $this->assertEquals(1, $item->Comments()->Count());
// require_moderation_nonmembers still filters out unmoderated comments // require_moderation_nonmembers still filters out unmoderated comments
Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => false)); Config::inst()->update('CommentableItem', 'comments', array('require_moderation' => false));
$this->assertEquals(1, $item->Comments()->Count()); $this->assertEquals(1, $item->Comments()->Count());
Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => false)); Config::inst()->update('CommentableItem', 'comments', array('require_moderation_nonmembers' => false));
$this->assertEquals(2, $item->Comments()->Count()); $this->assertEquals(2, $item->Comments()->Count());
// With unmoderated comments set to display in frontend // With unmoderated comments set to display in frontend
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation' => true, 'require_moderation' => true,
'frontend_moderation' => true 'frontend_moderation' => true
)); ));
$this->assertEquals(1, $item->Comments()->Count()); $this->assertEquals(1, $item->Comments()->Count());
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$this->assertEquals(2, $item->Comments()->Count()); $this->assertEquals(2, $item->Comments()->Count());
// With spam comments set to display in frontend // With spam comments set to display in frontend
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation' => true, 'require_moderation' => true,
'frontend_moderation' => false, 'frontend_moderation' => false,
'frontend_spam' => true, 'frontend_spam' => true,
)); ));
if($member = Member::currentUser()) $member->logOut(); if ($member = Member::currentUser()) {
$this->assertEquals(1, $item->Comments()->Count()); $member->logOut();
}
$this->assertEquals(1, $item->Comments()->Count());
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$this->assertEquals(2, $item->Comments()->Count()); $this->assertEquals(2, $item->Comments()->Count());
// With spam and unmoderated comments set to display in frontend // With spam and unmoderated comments set to display in frontend
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation' => true, 'require_moderation' => true,
'frontend_moderation' => true, 'frontend_moderation' => true,
'frontend_spam' => true, 'frontend_spam' => true,
)); ));
if($member = Member::currentUser()) $member->logOut(); if ($member = Member::currentUser()) {
$this->assertEquals(1, $item->Comments()->Count()); $member->logOut();
}
$this->assertEquals(1, $item->Comments()->Count());
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$this->assertEquals(4, $item->Comments()->Count()); $this->assertEquals(4, $item->Comments()->Count());
} }
/** /**
* Test moderation options configured via the CMS * Test moderation options configured via the CMS
*/ */
public function testCommentCMSModerationList() { public function testCommentCMSModerationList()
// comments don't require moderation so unmoderated comments can be {
// shown but not spam posts // comments don't require moderation so unmoderated comments can be
Config::inst()->update('CommentableItem', 'comments', array( // shown but not spam posts
'require_moderation' => true, Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation_cms' => true, 'require_moderation' => true,
)); 'require_moderation_cms' => true,
));
$item = $this->objFromFixture('CommentableItem', 'spammed'); $item = $this->objFromFixture('CommentableItem', 'spammed');
$this->assertEquals('None', $item->ModerationRequired); $this->assertEquals('None', $item->ModerationRequired);
$this->assertDOSEquals(array( $this->assertDOSEquals(array(
array('Name' => 'Comment 1'), array('Name' => 'Comment 1'),
array('Name' => 'Comment 3') 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. // when moderated, only moderated, non spam posts should be shown.
$item->ModerationRequired = 'NonMembersOnly'; $item->ModerationRequired = 'NonMembersOnly';
$item->write(); $item->write();
$this->assertEquals('NonMembersOnly', $item->ModerationRequired); $this->assertEquals('NonMembersOnly', $item->ModerationRequired);
// Check that require_moderation overrides this option // Check that require_moderation overrides this option
$item->ModerationRequired = 'Required'; $item->ModerationRequired = 'Required';
$item->write(); $item->write();
$this->assertEquals('Required', $item->ModerationRequired); $this->assertEquals('Required', $item->ModerationRequired);
$this->assertDOSEquals(array( $this->assertDOSEquals(array(
array('Name' => 'Comment 3') 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()); $this->assertEquals(1, $item->Comments()->Count());
// require_moderation_nonmembers still filters out unmoderated comments // require_moderation_nonmembers still filters out unmoderated comments
$item->ModerationRequired = 'NonMembersOnly'; $item->ModerationRequired = 'NonMembersOnly';
$item->write(); $item->write();
$this->assertEquals(1, $item->Comments()->Count()); $this->assertEquals(1, $item->Comments()->Count());
$item->ModerationRequired = 'None'; $item->ModerationRequired = 'None';
$item->write(); $item->write();
$this->assertEquals(2, $item->Comments()->Count()); $this->assertEquals(2, $item->Comments()->Count());
} }
public function testCanPostComment() { public function testCanPostComment()
Config::inst()->update('CommentableItem', 'comments', array( {
'require_login' => false, Config::inst()->update('CommentableItem', 'comments', array(
'require_login_cms' => false, 'require_login' => false,
'required_permission' => false, 'require_login_cms' => false,
)); 'required_permission' => false,
$item = $this->objFromFixture('CommentableItem', 'first'); ));
$item2 = $this->objFromFixture('CommentableItem', 'second'); $item = $this->objFromFixture('CommentableItem', 'first');
$item2 = $this->objFromFixture('CommentableItem', 'second');
// Test restriction free commenting // Test restriction free commenting
if($member = Member::currentUser()) $member->logOut(); if ($member = Member::currentUser()) {
$this->assertFalse($item->CommentsRequireLogin); $member->logOut();
$this->assertTrue($item->canPostComment()); }
$this->assertFalse($item->CommentsRequireLogin);
$this->assertTrue($item->canPostComment());
// Test permission required to post // Test permission required to post
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'require_login' => true, 'require_login' => true,
'required_permission' => 'POSTING_PERMISSION', 'required_permission' => 'POSTING_PERMISSION',
)); ));
$this->assertTrue($item->CommentsRequireLogin); $this->assertTrue($item->CommentsRequireLogin);
$this->assertFalse($item->canPostComment()); $this->assertFalse($item->canPostComment());
$this->logInWithPermission('WRONG_ONE'); $this->logInWithPermission('WRONG_ONE');
$this->assertFalse($item->canPostComment()); $this->assertFalse($item->canPostComment());
$this->logInWithPermission('POSTING_PERMISSION'); $this->logInWithPermission('POSTING_PERMISSION');
$this->assertTrue($item->canPostComment()); $this->assertTrue($item->canPostComment());
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$this->assertTrue($item->canPostComment()); $this->assertTrue($item->canPostComment());
// Test require login to post, but not any permissions // Test require login to post, but not any permissions
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'required_permission' => false, 'required_permission' => false,
)); ));
$this->assertTrue($item->CommentsRequireLogin); $this->assertTrue($item->CommentsRequireLogin);
if($member = Member::currentUser()) $member->logOut(); if ($member = Member::currentUser()) {
$this->assertFalse($item->canPostComment()); $member->logOut();
$this->logInWithPermission('ANY_PERMISSION'); }
$this->assertTrue($item->canPostComment()); $this->assertFalse($item->canPostComment());
$this->logInWithPermission('ANY_PERMISSION');
$this->assertTrue($item->canPostComment());
// Test options set via CMS // Test options set via CMS
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'require_login' => true, 'require_login' => true,
'require_login_cms' => true, 'require_login_cms' => true,
)); ));
$this->assertFalse($item->CommentsRequireLogin); $this->assertFalse($item->CommentsRequireLogin);
$this->assertTrue($item2->CommentsRequireLogin); $this->assertTrue($item2->CommentsRequireLogin);
if($member = Member::currentUser()) $member->logOut(); if ($member = Member::currentUser()) {
$this->assertTrue($item->canPostComment()); $member->logOut();
$this->assertFalse($item2->canPostComment()); }
$this->assertTrue($item->canPostComment());
$this->assertFalse($item2->canPostComment());
// Login grants permission to post // Login grants permission to post
$this->logInWithPermission('ANY_PERMISSION'); $this->logInWithPermission('ANY_PERMISSION');
$this->assertTrue($item->canPostComment()); $this->assertTrue($item->canPostComment());
$this->assertTrue($item2->canPostComment()); $this->assertTrue($item2->canPostComment());
}
public function testDeleteComment()
{
// Test anonymous user
if ($member = Member::currentUser()) {
$member->logOut();
}
$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');
$this->assertEquals(403, $delete->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertTrue($check && $check->exists());
} // Test non-authenticated user
public function testDeleteComment() { $this->logInAs('visitor');
// Test anonymous user $this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
if($member = Member::currentUser()) $member->logOut();
$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');
$this->assertEquals(403, $delete->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertTrue($check && $check->exists());
// Test non-authenticated user // Test authenticated user
$this->logInAs('visitor'); $this->logInAs('commentadmin');
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link'); $comment = $this->objFromFixture('Comment', 'firstComA');
$commentID = $comment->ID;
$adminComment1Link = $comment->DeleteLink();
$this->assertContains('CommentingController/delete/'.$commentID.'?t=', $adminComment1Link);
// Test authenticated user // Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin'); $this->logInAs('commentadmin2');
$comment = $this->objFromFixture('Comment', 'firstComA'); $delete = $this->get($adminComment1Link);
$commentID = $comment->ID; $this->assertEquals(400, $delete->getStatusCode());
$adminComment1Link = $comment->DeleteLink(); $check = DataObject::get_by_id('Comment', $commentID);
$this->assertContains('CommentingController/delete/'.$commentID.'?t=', $adminComment1Link); $this->assertTrue($check && $check->exists());
// Test that this link can't be shared / XSS exploited // Test that this other admin can delete the comment with their own link
$this->logInAs('commentadmin2'); $adminComment2Link = $comment->DeleteLink();
$delete = $this->get($adminComment1Link); $this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->assertEquals(400, $delete->getStatusCode()); $this->autoFollowRedirection = false;
$check = DataObject::get_by_id('Comment', $commentID); $delete = $this->get($adminComment2Link);
$this->assertTrue($check && $check->exists()); $this->assertEquals(302, $delete->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertFalse($check && $check->exists());
}
// Test that this other admin can delete the comment with their own link public function testSpamComment()
$adminComment2Link = $comment->DeleteLink(); {
$this->assertNotEquals($adminComment2Link, $adminComment1Link); // Test anonymous user
$this->autoFollowRedirection = false; if ($member = Member::currentUser()) {
$delete = $this->get($adminComment2Link); $member->logOut();
$this->assertEquals(302, $delete->getStatusCode()); }
$check = DataObject::get_by_id('Comment', $commentID); $comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertFalse($check && $check->exists()); $commentID = $comment->ID;
} $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
$spam = $this->get('CommentingController/spam/'.$comment->ID.'?ajax=1');
$this->assertEquals(403, $spam->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
public function testSpamComment() { // Test non-authenticated user
// Test anonymous user $this->logInAs('visitor');
if($member = Member::currentUser()) $member->logOut(); $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
$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');
$this->assertEquals(403, $spam->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
// Test non-authenticated user // Test authenticated user
$this->logInAs('visitor'); $this->logInAs('commentadmin');
$this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link'); $comment = $this->objFromFixture('Comment', 'firstComA');
$commentID = $comment->ID;
$adminComment1Link = $comment->SpamLink();
$this->assertContains('CommentingController/spam/'.$commentID.'?t=', $adminComment1Link);
// Test authenticated user // Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin'); $this->logInAs('commentadmin2');
$comment = $this->objFromFixture('Comment', 'firstComA'); $spam = $this->get($adminComment1Link);
$commentID = $comment->ID; $this->assertEquals(400, $spam->getStatusCode());
$adminComment1Link = $comment->SpamLink(); $check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertContains('CommentingController/spam/'.$commentID.'?t=', $adminComment1Link); $this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
// Test that this link can't be shared / XSS exploited // Test that this other admin can spam the comment with their own link
$this->logInAs('commentadmin2'); $adminComment2Link = $comment->SpamLink();
$spam = $this->get($adminComment1Link); $this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->assertEquals(400, $spam->getStatusCode()); $this->autoFollowRedirection = false;
$check = DataObject::get_by_id('Comment', $comment->ID); $spam = $this->get($adminComment2Link);
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam'); $this->assertEquals(302, $spam->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertEquals(1, $check->IsSpam);
// Test that this other admin can spam the comment with their own link // Cannot re-spam spammed comment
$adminComment2Link = $comment->SpamLink(); $this->assertNull($check->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 public function testHamComment()
$this->assertNull($check->SpamLink()); {
} // Test anonymous user
if ($member = Member::currentUser()) {
$member->logOut();
}
$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');
$this->assertEquals(403, $ham->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
public function testHamComment() { // Test non-authenticated user
// Test anonymous user $this->logInAs('visitor');
if($member = Member::currentUser()) $member->logOut(); $this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
$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');
$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 // Test authenticated user
$this->logInAs('visitor'); $this->logInAs('commentadmin');
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link'); $comment = $this->objFromFixture('Comment', 'secondComC');
$commentID = $comment->ID;
$adminComment1Link = $comment->HamLink();
$this->assertContains('CommentingController/ham/'.$commentID.'?t=', $adminComment1Link);
// Test authenticated user // Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin'); $this->logInAs('commentadmin2');
$comment = $this->objFromFixture('Comment', 'secondComC'); $ham = $this->get($adminComment1Link);
$commentID = $comment->ID; $this->assertEquals(400, $ham->getStatusCode());
$adminComment1Link = $comment->HamLink(); $check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertContains('CommentingController/ham/'.$commentID.'?t=', $adminComment1Link); $this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
// Test that this link can't be shared / XSS exploited // Test that this other admin can ham the comment with their own link
$this->logInAs('commentadmin2'); $adminComment2Link = $comment->HamLink();
$ham = $this->get($adminComment1Link); $this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->assertEquals(400, $ham->getStatusCode()); $this->autoFollowRedirection = false;
$check = DataObject::get_by_id('Comment', $comment->ID); $ham = $this->get($adminComment2Link);
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham'); $this->assertEquals(302, $ham->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertEquals(0, $check->IsSpam);
// Test that this other admin can ham the comment with their own link // Cannot re-ham hammed comment
$adminComment2Link = $comment->HamLink(); $this->assertNull($check->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 public function testApproveComment()
$this->assertNull($check->HamLink()); {
} // Test anonymous user
if ($member = Member::currentUser()) {
$member->logOut();
}
$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');
$this->assertEquals(403, $approve->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertEquals(0, $check->Moderated, 'No permission to approve');
public function testApproveComment() { // Test non-authenticated user
// Test anonymous user $this->logInAs('visitor');
if($member = Member::currentUser()) $member->logOut(); $this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
$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');
$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 // Test authenticated user
$this->logInAs('visitor'); $this->logInAs('commentadmin');
$this->assertNull($comment->ApproveLink(), 'No permission to see approve link'); $comment = $this->objFromFixture('Comment', 'secondComB');
$commentID = $comment->ID;
$adminComment1Link = $comment->ApproveLink();
$this->assertContains('CommentingController/approve/'.$commentID.'?t=', $adminComment1Link);
// Test authenticated user // Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin'); $this->logInAs('commentadmin2');
$comment = $this->objFromFixture('Comment', 'secondComB'); $approve = $this->get($adminComment1Link);
$commentID = $comment->ID; $this->assertEquals(400, $approve->getStatusCode());
$adminComment1Link = $comment->ApproveLink(); $check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertContains('CommentingController/approve/'.$commentID.'?t=', $adminComment1Link); $this->assertEquals(0, $check->Moderated, 'No permission to approve');
// Test that this link can't be shared / XSS exploited // Test that this other admin can approve the comment with their own link
$this->logInAs('commentadmin2'); $adminComment2Link = $comment->ApproveLink();
$approve = $this->get($adminComment1Link); $this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->assertEquals(400, $approve->getStatusCode()); $this->autoFollowRedirection = false;
$check = DataObject::get_by_id('Comment', $comment->ID); $approve = $this->get($adminComment2Link);
$this->assertEquals(0, $check->Moderated, 'No permission to approve'); $this->assertEquals(302, $approve->getStatusCode());
$check = DataObject::get_by_id('Comment', $commentID);
$this->assertEquals(1, $check->Moderated);
// Test that this other admin can approve the comment with their own link // Cannot re-approve approved comment
$adminComment2Link = $comment->ApproveLink(); $this->assertNull($check->ApproveLink());
$this->assertNotEquals($adminComment2Link, $adminComment1Link); }
$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 public function testCommenterURLWrite()
$this->assertNull($check->ApproveLink()); {
} $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',
);
$url = '://example.com';
public function testCommenterURLWrite() { foreach ($protocols as $protocol) {
$comment = new Comment(); $comment->CommenterURL = $protocol . $url;
// We only care about the CommenterURL, so only set that // The protocol should stay as if, assuming it is valid
// Check a http and https URL. Add more test urls here as needed. $comment->write();
$protocols = array( $this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol');
'Http', }
'Https', }
);
$url = '://example.com';
foreach($protocols as $protocol) { public function testSanitizesWithAllowHtml()
$comment->CommenterURL = $protocol . $url; {
// The protocol should stay as if, assuming it is valid if (!class_exists('HTMLPurifier')) {
$comment->write(); $this->markTestSkipped('HTMLPurifier class not found');
$this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol'); return;
} }
}
public function testSanitizesWithAllowHtml() {
if(!class_exists('HTMLPurifier')) {
$this->markTestSkipped('HTMLPurifier class not found');
return;
}
// Add p for paragraph // Add p for paragraph
// NOTE: The config method appears to append to the existing array // NOTE: The config method appears to append to the existing array
@ -409,125 +438,127 @@ class CommentsTest extends FunctionalTest {
'html_allowed_elements' => array('p'), 'html_allowed_elements' => array('p'),
)); ));
// Without HTML allowed // Without HTML allowed
$comment1 = new Comment(); $comment1 = new Comment();
$comment1->AllowHtml = false; $comment1->AllowHtml = false;
$comment1->BaseClass = 'CommentableItem'; $comment1->BaseClass = 'CommentableItem';
$comment1->Comment = '<p><script>alert("w00t")</script>my comment</p>'; $comment1->Comment = '<p><script>alert("w00t")</script>my comment</p>';
$comment1->write(); $comment1->write();
$this->assertEquals( $this->assertEquals(
'<p><script>alert("w00t")</script>my comment</p>', '<p><script>alert("w00t")</script>my comment</p>',
$comment1->Comment, $comment1->Comment,
'Does not remove HTML tags with html_allowed=false, ' . 'Does not remove HTML tags with html_allowed=false, ' .
'which is correct behaviour because the HTML will be escaped' 'which is correct behaviour because the HTML will be escaped'
); );
// With HTML allowed // With HTML allowed
$comment2 = new Comment(); $comment2 = new Comment();
$comment2->AllowHtml = true; $comment2->AllowHtml = true;
$comment2->BaseClass = 'CommentableItem'; $comment2->BaseClass = 'CommentableItem';
$comment2->Comment = '<p><script>alert("w00t")</script>my comment</p>'; $comment2->Comment = '<p><script>alert("w00t")</script>my comment</p>';
$comment2->write(); $comment2->write();
$this->assertEquals( $this->assertEquals(
'<p>my comment</p>', '<p>my comment</p>',
$comment2->Comment, $comment2->Comment,
'Removes HTML tags which are not on the whitelist' 'Removes HTML tags which are not on the whitelist'
); );
} }
public function testDefaultTemplateRendersHtmlWithAllowHtml() { public function testDefaultTemplateRendersHtmlWithAllowHtml()
if(!class_exists('HTMLPurifier')) { {
$this->markTestSkipped('HTMLPurifier class not found'); if (!class_exists('HTMLPurifier')) {
} $this->markTestSkipped('HTMLPurifier class not found');
}
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'html_allowed_elements' => array('p'), 'html_allowed_elements' => array('p'),
)); ));
$item = new CommentableItem(); $item = new CommentableItem();
$item->write(); $item->write();
// Without HTML allowed // Without HTML allowed
$comment = new Comment(); $comment = new Comment();
$comment->Comment = '<p>my comment</p>'; $comment->Comment = '<p>my comment</p>';
$comment->AllowHtml = false; $comment->AllowHtml = false;
$comment->ParentID = $item->ID; $comment->ParentID = $item->ID;
$comment->BaseClass = 'CommentableItem'; $comment->BaseClass = 'CommentableItem';
$comment->write(); $comment->write();
$html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface'); $html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
$this->assertContains( $this->assertContains(
'&lt;p&gt;my comment&lt;/p&gt;', '&lt;p&gt;my comment&lt;/p&gt;',
$html $html
); );
$comment->AllowHtml = true; $comment->AllowHtml = true;
$comment->write(); $comment->write();
$html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface'); $html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
$this->assertContains( $this->assertContains(
'<p>my comment</p>', '<p>my comment</p>',
$html $html
); );
}
}
/** /**
* Tests whether comments are enabled or disabled by default * Tests whether comments are enabled or disabled by default
*/ */
public function testDefaultEnabled() { public function testDefaultEnabled()
// Ensure values are set via cms (not via config) {
Config::inst()->update('CommentableItem', 'comments', array( // Ensure values are set via cms (not via config)
'enabled_cms' => true, Config::inst()->update('CommentableItem', 'comments', array(
'require_moderation_cms' => true, 'enabled_cms' => true,
'require_login_cms' => true 'require_moderation_cms' => true,
)); 'require_login_cms' => true
));
// With default = true // With default = true
$obj = new CommentableItem(); $obj = new CommentableItem();
$this->assertTrue((bool)$obj->getCommentsOption('enabled'), "Default setting is enabled"); $this->assertTrue((bool)$obj->getCommentsOption('enabled'), "Default setting is enabled");
$this->assertTrue((bool)$obj->ProvideComments); $this->assertTrue((bool)$obj->ProvideComments);
$this->assertEquals('None', $obj->ModerationRequired); $this->assertEquals('None', $obj->ModerationRequired);
$this->assertFalse((bool)$obj->CommentsRequireLogin); $this->assertFalse((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemEnabled(); $obj = new CommentableItemEnabled();
$this->assertTrue((bool)$obj->ProvideComments); $this->assertTrue((bool)$obj->ProvideComments);
$this->assertEquals('Required', $obj->ModerationRequired); $this->assertEquals('Required', $obj->ModerationRequired);
$this->assertTrue((bool)$obj->CommentsRequireLogin); $this->assertTrue((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemDisabled(); $obj = new CommentableItemDisabled();
$this->assertFalse((bool)$obj->ProvideComments); $this->assertFalse((bool)$obj->ProvideComments);
$this->assertEquals('None', $obj->ModerationRequired); $this->assertEquals('None', $obj->ModerationRequired);
$this->assertFalse((bool)$obj->CommentsRequireLogin); $this->assertFalse((bool)$obj->CommentsRequireLogin);
// With default = false // With default = false
// Because of config rules about falsey values, apply config to object directly // Because of config rules about falsey values, apply config to object directly
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'enabled' => false, 'enabled' => false,
'require_login' => true, 'require_login' => true,
'require_moderation' => true 'require_moderation' => true
)); ));
$obj = new CommentableItem(); $obj = new CommentableItem();
$this->assertFalse((bool)$obj->getCommentsOption('enabled'), "Default setting is disabled"); $this->assertFalse((bool)$obj->getCommentsOption('enabled'), "Default setting is disabled");
$this->assertFalse((bool)$obj->ProvideComments); $this->assertFalse((bool)$obj->ProvideComments);
$this->assertEquals('Required', $obj->ModerationRequired); $this->assertEquals('Required', $obj->ModerationRequired);
$this->assertTrue((bool)$obj->CommentsRequireLogin); $this->assertTrue((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemEnabled(); $obj = new CommentableItemEnabled();
$this->assertTrue((bool)$obj->ProvideComments); $this->assertTrue((bool)$obj->ProvideComments);
$this->assertEquals('Required', $obj->ModerationRequired); $this->assertEquals('Required', $obj->ModerationRequired);
$this->assertTrue((bool)$obj->CommentsRequireLogin); $this->assertTrue((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemDisabled(); $obj = new CommentableItemDisabled();
$this->assertFalse((bool)$obj->ProvideComments); $this->assertFalse((bool)$obj->ProvideComments);
$this->assertEquals('None', $obj->ModerationRequired); $this->assertEquals('None', $obj->ModerationRequired);
$this->assertFalse((bool)$obj->CommentsRequireLogin); $this->assertFalse((bool)$obj->CommentsRequireLogin);
} }
/* /*
When a parent comment is deleted, remove the children When a parent comment is deleted, remove the children
*/ */
public function testOnBeforeDelete() { public function testOnBeforeDelete()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$child = new Comment(); $child = new Comment();
@ -547,11 +578,13 @@ class CommentsTest extends FunctionalTest {
$this->assertFalse(DataObject::get_by_id('Comment', $childCommentID)); $this->assertFalse(DataObject::get_by_id('Comment', $childCommentID));
} }
public function testRequireDefaultRecords() { public function testRequireDefaultRecords()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testLink() { public function testLink()
{
$comment = $this->objFromFixture('Comment', 'thirdComD'); $comment = $this->objFromFixture('Comment', 'thirdComD');
$this->assertEquals('CommentableItem_Controller#comment-'.$comment->ID, $this->assertEquals('CommentableItem_Controller#comment-'.$comment->ID,
$comment->Link()); $comment->Link());
@ -563,7 +596,8 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals('', $comment->Link()); $this->assertEquals('', $comment->Link());
} }
public function testPermalink() { public function testPermalink()
{
$comment = $this->objFromFixture('Comment', 'thirdComD'); $comment = $this->objFromFixture('Comment', 'thirdComD');
$this->assertEquals('comment-' . $comment->ID, $comment->Permalink()); $this->assertEquals('comment-' . $comment->ID, $comment->Permalink());
} }
@ -571,7 +605,8 @@ class CommentsTest extends FunctionalTest {
/* /*
Test field labels in 2 languages Test field labels in 2 languages
*/ */
public function testFieldLabels() { public function testFieldLabels()
{
$locale = i18n::get_locale(); $locale = i18n::get_locale();
i18n::set_locale('fr'); i18n::set_locale('fr');
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
@ -619,18 +654,21 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals($expected, $labels); $this->assertEquals($expected, $labels);
} }
public function testGetOption() { public function testGetOption()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testGetParent() { public function testGetParent()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$item = $this->objFromFixture('CommentableItem', 'first'); $item = $this->objFromFixture('CommentableItem', 'first');
$parent = $comment->getParent(); $parent = $comment->getParent();
$this->assertEquals($item, $parent); $this->assertEquals($item, $parent);
} }
public function testGetParentTitle() { public function testGetParentTitle()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$title = $comment->getParentTitle(); $title = $comment->getParentTitle();
$this->assertEquals('First', $title); $this->assertEquals('First', $title);
@ -641,21 +679,25 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals('', $comment->getParentTitle()); $this->assertEquals('', $comment->getParentTitle());
} }
public function testGetParentClassName() { public function testGetParentClassName()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$className = $comment->getParentClassName(); $className = $comment->getParentClassName();
$this->assertEquals('CommentableItem', $className); $this->assertEquals('CommentableItem', $className);
} }
public function testCastingHelper() { public function testCastingHelper()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testGetEscapedComment() { public function testGetEscapedComment()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testIsPreview() { public function testIsPreview()
{
$comment = new Comment(); $comment = new Comment();
$comment->Name = 'Fred Bloggs'; $comment->Name = 'Fred Bloggs';
$comment->Comment = 'this is a test comment'; $comment->Comment = 'this is a test comment';
@ -664,7 +706,8 @@ class CommentsTest extends FunctionalTest {
$this->assertFalse($comment->isPreview()); $this->assertFalse($comment->isPreview());
} }
public function testCanCreate() { public function testCanCreate()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
// admin can create - this is always false // admin can create - this is always false
@ -676,7 +719,8 @@ class CommentsTest extends FunctionalTest {
$this->assertFalse($comment->canCreate()); $this->assertFalse($comment->canCreate());
} }
public function testCanView() { public function testCanView()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
// admin can view // admin can view
@ -692,7 +736,8 @@ class CommentsTest extends FunctionalTest {
$this->assertFalse($comment->canView()); $this->assertFalse($comment->canView());
} }
public function testCanEdit() { public function testCanEdit()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
// admin can edit // admin can edit
@ -708,7 +753,8 @@ class CommentsTest extends FunctionalTest {
$this->assertFalse($comment->canEdit()); $this->assertFalse($comment->canEdit());
} }
public function testCanDelete() { public function testCanDelete()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
// admin can delete // admin can delete
@ -724,7 +770,8 @@ class CommentsTest extends FunctionalTest {
$this->assertFalse($comment->canDelete()); $this->assertFalse($comment->canDelete());
} }
public function testGetMember() { public function testGetMember()
{
$this->logInAs('visitor'); $this->logInAs('visitor');
$current = Member::currentUser(); $current = Member::currentUser();
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
@ -743,7 +790,8 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals($current, $member); $this->assertEquals($current, $member);
} }
public function testGetAuthorName() { public function testGetAuthorName()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertEquals( $this->assertEquals(
'FA', 'FA',
@ -768,11 +816,11 @@ class CommentsTest extends FunctionalTest {
$comment->Name = null; $comment->Name = null;
$comment->AuthorID = 0; $comment->AuthorID = 0;
$this->assertNull($comment->getAuthorName()); $this->assertNull($comment->getAuthorName());
} }
public function testLinks() { public function testLinks()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$this->logInAs('commentadmin'); $this->logInAs('commentadmin');
@ -808,27 +856,31 @@ class CommentsTest extends FunctionalTest {
); );
} }
public function testMarkSpam() { public function testMarkSpam()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$comment->markSpam(); $comment->markSpam();
$this->assertTrue($comment->Moderated); $this->assertTrue($comment->Moderated);
$this->assertTrue($comment->IsSpam); $this->assertTrue($comment->IsSpam);
} }
public function testMarkApproved() { public function testMarkApproved()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$comment->markApproved(); $comment->markApproved();
$this->assertTrue($comment->Moderated); $this->assertTrue($comment->Moderated);
$this->assertFalse($comment->IsSpam); $this->assertFalse($comment->IsSpam);
} }
public function testMarkUnapproved() { public function testMarkUnapproved()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$comment->markApproved(); $comment->markApproved();
$this->assertTrue($comment->Moderated); $this->assertTrue($comment->Moderated);
} }
public function testSpamClass() { public function testSpamClass()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertEquals('notspam', $comment->spamClass()); $this->assertEquals('notspam', $comment->spamClass());
$comment->Moderated = false; $comment->Moderated = false;
@ -837,7 +889,8 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals('spam', $comment->spamClass()); $this->assertEquals('spam', $comment->spamClass());
} }
public function testGetTitle() { public function testGetTitle()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertEquals( $this->assertEquals(
'Comment by FA on First', 'Comment by FA on First',
@ -845,7 +898,8 @@ class CommentsTest extends FunctionalTest {
); );
} }
public function testGetCMSFields() { public function testGetCMSFields()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$fields = $comment->getCMSFields(); $fields = $comment->getCMSFields();
$names = array(); $names = array();
@ -863,7 +917,8 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals($expected, $names); $this->assertEquals($expected, $names);
} }
public function testGetCMSFieldsCommentHasAuthor() { public function testGetCMSFieldsCommentHasAuthor()
{
$member = Member::get()->filter('FirstName', 'visitor')->first(); $member = Member::get()->filter('FirstName', 'visitor')->first();
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$comment->AuthorID = $member->ID; $comment->AuthorID = $member->ID;
@ -886,7 +941,8 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals($expected, $names); $this->assertEquals($expected, $names);
} }
public function testGetCMSFieldsWithParentComment() { public function testGetCMSFieldsWithParentComment()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$child = new Comment(); $child = new Comment();
@ -916,7 +972,8 @@ class CommentsTest extends FunctionalTest {
} }
public function testPurifyHtml() { public function testPurifyHtml()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
$dirtyHTML = '<p><script>alert("w00t")</script>my comment</p>'; $dirtyHTML = '<p><script>alert("w00t")</script>my comment</p>';
@ -926,7 +983,8 @@ class CommentsTest extends FunctionalTest {
); );
} }
public function testGravatar() { public function testGravatar()
{
// Turn gravatars on // Turn gravatars on
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'use_gravatar' => true 'use_gravatar' => true
@ -951,7 +1009,8 @@ class CommentsTest extends FunctionalTest {
); );
} }
public function testGetRepliesEnabled() { public function testGetRepliesEnabled()
{
$comment = $this->objFromFixture('Comment', 'firstComA'); $comment = $this->objFromFixture('Comment', 'firstComA');
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'nested_comments' => false 'nested_comments' => false
@ -983,11 +1042,10 @@ class CommentsTest extends FunctionalTest {
$comment->markApproved(); $comment->markApproved();
$this->assertTrue($comment->getRepliesEnabled()); $this->assertTrue($comment->getRepliesEnabled());
} }
public function testAllReplies() { public function testAllReplies()
{
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'nested_comments' => true, 'nested_comments' => true,
'nested_depth' => 4 'nested_depth' => 4
@ -1018,7 +1076,8 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals(0, $comment->allReplies()->count()); $this->assertEquals(0, $comment->allReplies()->count());
} }
public function testReplies() { public function testReplies()
{
CommentableItem::add_extension('CommentsExtension'); CommentableItem::add_extension('CommentsExtension');
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
@ -1085,7 +1144,8 @@ class CommentsTest extends FunctionalTest {
CommentableItem::remove_extension('CommentsExtension'); CommentableItem::remove_extension('CommentsExtension');
} }
public function testPagedReplies() { public function testPagedReplies()
{
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'nested_comments' => true, 'nested_comments' => true,
'nested_depth' => 4, 'nested_depth' => 4,
@ -1111,7 +1171,8 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals(0, $comment->PagedReplies()->count()); $this->assertEquals(0, $comment->PagedReplies()->count());
} }
public function testReplyForm() { public function testReplyForm()
{
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'nested_comments' => false, 'nested_comments' => false,
'nested_depth' => 4 'nested_depth' => 4
@ -1154,7 +1215,8 @@ class CommentsTest extends FunctionalTest {
$this->assertNull($form); $this->assertNull($form);
} }
public function testUpdateDepth() { public function testUpdateDepth()
{
Config::inst()->update('CommentableItem', 'comments', array( Config::inst()->update('CommentableItem', 'comments', array(
'nested_comments' => true, 'nested_comments' => true,
'nested_depth' => 4 'nested_depth' => 4
@ -1175,34 +1237,39 @@ class CommentsTest extends FunctionalTest {
$this->assertEquals(4, $reply3->Depth); $this->assertEquals(4, $reply3->Depth);
} }
public function testGetToken() { public function testGetToken()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testMemberSalt() { public function testMemberSalt()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testAddToUrl() { public function testAddToUrl()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testCheckRequest() { public function testCheckRequest()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
public function testGenerate() { public function testGenerate()
{
$this->markTestSkipped('TODO'); $this->markTestSkipped('TODO');
} }
protected static function getMethod($name) { protected static function getMethod($name)
{
$class = new ReflectionClass('Comment'); $class = new ReflectionClass('Comment');
$method = $class->getMethod($name); $method = $class->getMethod($name);
$method->setAccessible(true); $method->setAccessible(true);
return $method; return $method;
} }
} }
@ -1210,67 +1277,83 @@ class CommentsTest extends FunctionalTest {
* @package comments * @package comments
* @subpackage tests * @subpackage tests
*/ */
class CommentableItem extends DataObject implements TestOnly { class CommentableItem extends DataObject implements TestOnly
{
private static $db = array( private static $db = array(
'Title' => 'Varchar' 'Title' => 'Varchar'
); );
private static $extensions = array( private static $extensions = array(
'CommentsExtension' 'CommentsExtension'
); );
public function RelativeLink() { public function RelativeLink()
return "CommentableItem_Controller"; {
} return "CommentableItem_Controller";
}
public function canView($member = null) { public function canView($member = null)
{
return true; return true;
} }
// This is needed for canModerateComments // This is needed for canModerateComments
public function canEdit($member = null) { public function canEdit($member = null)
if($member instanceof Member) $memberID = $member->ID; {
else if(is_numeric($member)) $memberID = $member; if ($member instanceof Member) {
else $memberID = Member::currentUserID(); $memberID = $member->ID;
} elseif (is_numeric($member)) {
$memberID = $member;
} else {
$memberID = Member::currentUserID();
}
if($memberID && Permission::checkMember($memberID, array("ADMIN", "CMS_ACCESS_CommentAdmin"))) return true; if ($memberID && Permission::checkMember($memberID, array("ADMIN", "CMS_ACCESS_CommentAdmin"))) {
return true;
}
return false; return false;
} }
public function Link() { public function Link()
return $this->RelativeLink(); {
} return $this->RelativeLink();
}
public function AbsoluteLink() { public function AbsoluteLink()
return Director::absoluteURL($this->RelativeLink()); {
} return Director::absoluteURL($this->RelativeLink());
}
} }
class CommentableItemEnabled extends CommentableItem { class CommentableItemEnabled extends CommentableItem
private static $defaults = array( {
'ProvideComments' => true, private static $defaults = array(
'ModerationRequired' => 'Required', 'ProvideComments' => true,
'CommentsRequireLogin' => true 'ModerationRequired' => 'Required',
); 'CommentsRequireLogin' => true
);
} }
class CommentableItemDisabled extends CommentableItem { class CommentableItemDisabled extends CommentableItem
private static $defaults = array( {
'ProvideComments' => false, private static $defaults = array(
'ModerationRequired' => 'None', 'ProvideComments' => false,
'CommentsRequireLogin' => false 'ModerationRequired' => 'None',
); 'CommentsRequireLogin' => false
);
} }
/** /**
* @package comments * @package comments
* @subpackage tests * @subpackage tests
*/ */
class CommentableItem_Controller extends Controller implements TestOnly { class CommentableItem_Controller extends Controller implements TestOnly
{
public function index() { public function index()
return CommentableItem::get()->first()->CommentsForm(); {
} return CommentableItem::get()->first()->CommentsForm();
}
} }