Merge pull request #51 from Firesphere/master

SilverStripe 4 and Namespacing
This commit is contained in:
Franco Springveldt 2017-09-05 09:29:33 +12:00 committed by GitHub
commit 42c733061d
32 changed files with 558 additions and 395 deletions

1
.gitattributes vendored
View File

@ -4,3 +4,4 @@
/.gitignore export-ignore /.gitignore export-ignore
/.travis.yml export-ignore /.travis.yml export-ignore
/.scrutinizer.yml export-ignore /.scrutinizer.yml export-ignore
/codecov.yml export-ignore

View File

@ -1,33 +1,31 @@
# See https://github.com/silverstripe/silverstripe-travis-support for setup details
sudo: false
language: php language: php
php:
- 5.3
- 5.4
- 5.5
env: env:
- DB=MYSQL CORE_RELEASE=3.5 global:
- COMPOSER_ROOT_VERSION="4.0.x-dev"
matrix: matrix:
include: include:
- php: 5.6 - php: 5.6
env: DB=MYSQL CORE_RELEASE=3 env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
- php: 5.6 - php: 7.0
env: DB=MYSQL CORE_RELEASE=3.1 env: DB=PGSQL PHPUNIT_TEST=1
- php: 5.6
env: DB=PGSQL CORE_RELEASE=3.2
- php: 7.1 - php: 7.1
env: DB=MYSQL CORE_RELEASE=3.6 env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
before_script: before_script:
- composer self-update || true - phpenv rehash
- git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support - phpenv config-rm xdebug.ini
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss --require silverstripe/userforms
- cd ~/builds/ss - composer install --prefer-dist
- composer require --prefer-dist --no-update silverstripe/framework:4.0.x-dev
- if [[ $DB == PGSQL ]]; then composer require --prefer-dist --no-update silverstripe/postgresql:2.0.x-dev; fi
- composer update
script: script:
- vendor/bin/phpunit spamprotection/tests - if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit tests/; fi
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=framework/phpcs.xml.dist code/ tests/ ; fi
after_success:
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi

11
.upgrade.yml Normal file
View File

@ -0,0 +1,11 @@
mappings:
SpamProtector: SilverStripe\SpamProtection\SpamProtector
CommentSpamProtection: SilverStripe\SpamProtection\Extension\CommentSpamProtection
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField
FormSpamProtectionExtension: SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension
EditableSpamProtectionFieldTest: SilverStripe\SpamProtection\Tests\EditableSpamProtectionFieldTest
EditableSpamProtectionFieldTest_Protector: SilverStripe\SpamProtection\Tests\Stub\Protector
FormSpamProtectionExtensionTest: SilverStripe\SpamProtection\Tests\FormSpamProtectionExtensionTest
FormSpamProtectionExtensionTest_BarProtector: SilverStripe\SpamProtection\Tests\Stub\BarProtector
FormSpamProtectionExtensionTest_BazProtector: SilverStripe\SpamProtection\Tests\Stub\BazProtector
FormSpamProtectionExtensionTest_FooProtector: SilverStripe\SpamProtection\Tests\Stub\FooProtector

127
README.md
View File

@ -1,8 +1,7 @@
# SpamProtection Module # SpamProtection Module
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-spamprotection.png?branch=master)](http://travis-ci.org/silverstripe/silverstripe-spamprotection) [![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-spamprotection.png?branch=master)](http://travis-ci.org/silverstripe/silverstripe-spamprotection)
![helpfulrobot](https://helpfulrobot.io/silverstripe/spamprotection/badge) [![Code Coverage](https://codecov.io/gh/silverstripe/silverstripe-spamprotection/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-spamprotection)
## Maintainer Contact ## Maintainer Contact
@ -14,13 +13,13 @@
## Requirements ## Requirements
SilverStripe 3.0.0 or greater SilverStripe 4.0 or greater
## Documentation ## Documentation
This module provides a generic, consistent API for adding spam protection to This module provides a generic, consistent API for adding spam protection to
your SilverStripe Forms. This does not provide any spam protection out of the your SilverStripe Forms. This does not provide any spam protection out of the
box, for that, you must also download one of the spam protection box, for that, you must also download one of the spam protection
implementations. Currently available options are: implementations. Currently available options are:
* [Mollom](https://github.com/silverstripe/silverstripe-mollom) * [Mollom](https://github.com/silverstripe/silverstripe-mollom)
@ -33,95 +32,113 @@ implements the `SpamProtector` interface. More on that below.
## Configuring ## Configuring
After installing this module and a protector of your choice (i.e mollom) you'll After installing this module and a protector of your choice (i.e mollom) you'll
need to rebuild your database through `dev/build` and set the default protector need to rebuild your database through `dev/build` and set the default protector
via SilverStripe's config system. This will update any Form instances that have via SilverStripe's config system. This will update any Form instances that have
spam protection hooks with that protector. spam protection hooks with that protector.
*mysite/_config/spamprotection.yml* *mysite/_config/spamprotection.yml*
--- ```yaml
name: spamprotection ---
--- name: spamprotection
FormSpamProtectionExtension: ---
default_spam_protector: MollomSpamProtector SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension:
default_spam_protector: MollomSpamProtector
```
To add spam protection to your form instance call `enableSpamProtection`. To add spam protection to your form instance call `enableSpamProtection`.
// your existing form code
$form = new Form( .. );
$form->enableSpamProtection();
The logic to perform the actual spam validation is controlled by each of the ```php
individual `SpamProtector` implementation since they each require a different // your existing form code
$form = new Form(/* .. */);
$form->enableSpamProtection();
```
The logic to perform the actual spam validation is controlled by each of the
individual `SpamProtector` implementation since they each require a different
implementation client side or server side. implementation client side or server side.
### Options ### Options
`enableSpamProtection` takes a hash of optional configuration values. `enableSpamProtection` takes a hash of optional configuration values.
$form->enableSpamProtection(array( ```php
'protector' => 'MathSpamProtector', $form->enableSpamProtection(array(
'name' => 'Captcha' 'protector' => MathSpamProtector::class,
)); 'name' => 'Captcha'
));
```
Options to configure are: Options to configure are:
*`protector`* a class name string or class instance which implements *`protector`* a class name string or class instance which implements
`SpamProtector`. Defaults to your `SpamProtector`. Defaults to your
`FormSpamProtectionExtension.default_spam_protector` value. `FormSpamProtectionExtension.default_spam_protector` value.
*`name`* the form field name argument for the Captcha. Defaults to `Catcha`. *`name`* the form field name argument for the Captcha. Defaults to `Catcha`.
*`title`* title of the Captcha form field. Defaults to `''` *`title`* title of the Captcha form field. Defaults to `''`
*`insertBefore`* name of existing field to insert the spam protection field prior to *`insertBefore`* name of existing field to insert the spam protection field prior to
*`mapping`* an array mapping of the Form fields to the standardized list of *`mapping`* an array mapping of the Form fields to the standardized list of
field names. The list of standardized fields to pass to the spam protector are: field names. The list of standardized fields to pass to the spam protector are:
title ```
body title
contextUrl body
contextTitle contextUrl
authorName contextTitle
authorMail authorName
authorUrl authorMail
authorIp authorUrl
authorId authorIp
authorId
```
## Defining your own `SpamProtector` ## Defining your own `SpamProtector`
Any class that implements `SpamProtector` and the `getFormField()` method can Any class that implements `SpamProtector` and the `getFormField()` method can
be set as the spam protector. The `getFormField()` method returns the be set as the spam protector. The `getFormField()` method returns the
`FormField` to be inserted into the `Form`. The `FormField` returned should be `FormField` to be inserted into the `Form`. The `FormField` returned should be
in charge of the validation process. in charge of the validation process.
<?php ```php
<?php
class CustomSpamProtector implements SpamProtector { use CaptchaField;
use SilverStripe\SpamProtection\SpamProtector;
public function getFormField($name = null, $title = null, $value = null) { class CustomSpamProtector implements SpamProtector
// CaptchaField is a imagined class which has some functionality. {
// See silverstripe-mollom module for an example. public function getFormField($name = null, $title = null, $value = null)
return new CaptchaField($name, $title, $value); {
} // CaptchaField is an imagined class which has some functionality.
// See silverstripe-mollom module for an example.
return new CaptchaField($name, $title, $value);
} }
}
```
## Using Spam Protection with User Forms ## Using Spam Protection with User Forms
This module provides an EditableSpamProtectionField wrapper which you can add This module provides an `EditableSpamProtectionField` wrapper which you can add
to your UserForm instances. After installing this module and running /dev/build to your UserForm instances. After installing this module and running `/dev/build`
to rebuild the database, your Form Builder interface will have an option for to rebuild the database, your Form Builder interface will have an option for
`Spam Protection Field`. The type of spam protection used will be based on your `Spam Protection Field`. The type of spam protection used will be based on your
currently selected SpamProtector instance. currently selected SpamProtector instance.
## Releasing code with Spam Protection support ## Releasing code with Spam Protection support
Spam protection is useful to provide but in some cases we do not want to require Spam protection is useful to provide but in some cases we do not want to require
the developer to use spam protection. In that case, modules can provide the the developer to use spam protection. In that case, modules can provide the
following pattern following pattern
$form = new Form(..); ```php
use SilverStripe\Forms\Form;
use SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension;
if($form->hasExtension('FormSpamProtectionExtension')) { $form = new Form(/* .. */);
$form->enableSpamProtection();
} if ($form->hasExtension(FormSpamProtectionExtension::class)) {
$form->enableSpamProtection();
}
```

View File

@ -8,4 +8,7 @@
* *
* @package spamprotection * @package spamprotection
*/ */
Deprecation::notification_version('1.1', 'spamprotection');
use SilverStripe\Dev\Deprecation;
Deprecation::notification_version('3.0', 'spamprotection');

View File

@ -1,6 +1,6 @@
--- ---
name: spamprotection Name: spamprotection
--- ---
Form: SilverStripe\Forms\Form:
extensions: extensions:
- FormSpamProtectionExtension - SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension

View File

@ -1,258 +1,273 @@
<?php <?php
namespace SilverStripe\SpamProtection;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FormField;
use SilverStripe\ORM\UnsavedRelationList;
use SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension;
use SilverStripe\UserForms\Model\EditableFormField;
use SilverStripe\UserForms\Model\EditableFormField\EditableEmailField;
use SilverStripe\UserForms\Model\EditableFormField\EditableNumericField;
use SilverStripe\UserForms\Model\EditableFormField\EditableTextField;
/**
* @todo The userforms namespaces may still change, as the branch is not merged in yet
*/
if (!class_exists(EditableFormField::class)) {
return;
}
/** /**
* Editable Spam Protecter Field. Used with the User Defined Forms module (if * Editable Spam Protecter Field. Used with the User Defined Forms module (if
* installed) to allow the user to have captcha fields with their custom forms * installed) to allow the user to have captcha fields with their custom forms
* *
* @package spamprotection * @package spamprotection
*/ */
if (class_exists('EditableFormField')) { class EditableSpamProtectionField extends EditableFormField
class EditableSpamProtectionField extends EditableFormField {
private static $singular_name = 'Spam Protection Field';
private static $plural_name = 'Spam Protection Fields';
private static $table_name = 'EditableSpamProtectionField';
/**
* Fields to include spam detection for
*
* @var array
* @config
*/
private static $check_fields = array(
EditableEmailField::class,
EditableTextField::class,
EditableNumericField::class
);
private static $db = array(
'SpamFieldSettings' => 'Text'
);
/**
* @var FormField
*/
protected $formField = null;
public function getFormField()
{ {
private static $singular_name = 'Spam Protection Field'; if ($this->formField) {
return $this->formField;
private static $plural_name = 'Spam Protection Fields';
/**
* Fields to include spam detection for
*
* @var array
* @config
*/
private static $check_fields = array(
'EditableEmailField',
'EditableTextField',
'EditableNumericField'
);
private static $db = array(
'SpamFieldSettings' => 'Text'
);
/**
* @var FormField
*/
protected $formField = null;
public function getFormField()
{
if ($this->formField) {
return $this->formField;
}
// Get protector
$protector = FormSpamProtectionExtension::get_protector();
if (!$protector) {
return false;
}
// Extract saved field mappings and update this field.
$fieldMapping = array();
foreach ($this->getCandidateFields() as $otherField) {
$mapSetting = "Map-{$otherField->Name}";
$spamField = $this->spamMapValue($mapSetting);
$fieldMapping[$otherField->Name] = $spamField;
}
$protector->setFieldMapping($fieldMapping);
// Generate field
return $protector->getFormField($this->Name, $this->Title, null);
} }
/** // Get protector
* @param FormField $field $protector = FormSpamProtectionExtension::get_protector();
* @return self if (!$protector) {
*/ return false;
public function setFormField(FormField $field)
{
$this->formField = $field;
return $this;
} }
/** // Extract saved field mappings and update this field.
* Gets the list of all candidate spam detectable fields on this field's form $fieldMapping = array();
* foreach ($this->getCandidateFields() as $otherField) {
* @return DataList $mapSetting = "Map-{$otherField->Name}";
*/ $spamField = $this->spamMapValue($mapSetting);
protected function getCandidateFields() $fieldMapping[$otherField->Name] = $spamField;
{ }
$protector->setFieldMapping($fieldMapping);
// Get list of all configured classes available for spam detection // Generate field
$types = self::config()->check_fields; return $protector->getFormField($this->Name, $this->Title, null);
$typesInherit = array(); }
foreach ($types as $type) {
$subTypes = ClassInfo::subclassesFor($type);
$typesInherit = array_merge($typesInherit, $subTypes);
}
// Get all candidates of the above types /**
return $this * @param FormField $field
->Parent() * @return self
->Fields() */
->filter('ClassName', $typesInherit) public function setFormField(FormField $field)
->exclude('Title', ''); // Ignore this field and those without titles {
$this->formField = $field;
return $this;
}
/**
* Gets the list of all candidate spam detectable fields on this field's form
*
* @return DataList
*/
protected function getCandidateFields()
{
// Get list of all configured classes available for spam detection
$types = $this->config()->get('check_fields');
$typesInherit = array();
foreach ($types as $type) {
$subTypes = ClassInfo::subclassesFor($type);
$typesInherit = array_merge($typesInherit, $subTypes);
} }
/** // Get all candidates of the above types
* This method is in place for userforms 2.x return $this
* ->Parent()
* @deprecated 3.0 Please use {@link getCMSFields()} instead ->Fields()
*/ ->filter('ClassName', $typesInherit)
public function getFieldConfiguration() ->exclude('Title', ''); // Ignore this field and those without titles
{ }
return $this->getCMSFields();
/**
* Write the spam field mapping values to a serialised DB field
*
* {@inheritDoc}
*/
public function onBeforeWrite()
{
$fieldMap = Convert::json2array($this->SpamFieldSettings);
if (empty($fieldMap)) {
$fieldMap = array();
} }
/** foreach ($this->record as $key => $value) {
* Write the spam field mapping values to a serialised DB field if (substr($key, 0, 8) === 'spammap-') {
* $fieldMap[substr($key, 8)] = $value;
* {@inheritDoc}
*/
public function onBeforeWrite()
{
$fieldMap = Convert::json2array($this->SpamFieldSettings);
if (empty($fieldMap)) {
$fieldMap = array();
} }
foreach ($this->record as $key => $value) {
if (substr($key, 0, 8) === 'spammap-') {
$fieldMap[substr($key, 8)] = $value;
}
}
$this->setField('SpamFieldSettings', Convert::raw2json($fieldMap));
return parent::onBeforeWrite();
} }
$this->setField('SpamFieldSettings', Convert::raw2json($fieldMap));
/** return parent::onBeforeWrite();
* Used in userforms 3.x and above }
*
* {@inheritDoc}
*/
public function getCMSFields()
{
/** @var FieldList $fields */
$fields = parent::getCMSFields();
// Get protector /**
$protector = FormSpamProtectionExtension::get_protector(); * Used in userforms 3.x and above
if (!$protector) { *
return $fields; * {@inheritDoc}
} */
public function getCMSFields()
if ($this->Parent()->Fields() instanceof UnsavedRelationList) { {
return $fields; /** @var FieldList $fields */
} $fields = parent::getCMSFields();
// Each other text field in this group can be assigned a field mapping
$mapGroup = FieldGroup::create()
->setTitle(_t('EditableSpamProtectionField.SPAMFIELDMAPPING', 'Spam Field Mapping'))
->setName('SpamFieldMapping')
->setDescription(_t(
'EditableSpamProtectionField.SPAMFIELDMAPPINGDESCRIPTION',
'Select the form fields that correspond to any relevant spam protection identifiers'
));
// Generate field specific settings
$mappableFields = Config::inst()->get('FormSpamProtectionExtension', 'mappable_fields');
$mappableFieldsMerged = array_combine($mappableFields, $mappableFields);
foreach ($this->getCandidateFields() as $otherField) {
$mapSetting = "Map-{$otherField->Name}";
$fieldOption = DropdownField::create(
'spammap-' . $mapSetting,
$otherField->Title,
$mappableFieldsMerged,
$this->spamMapValue($mapSetting)
)->setEmptyString('');
$mapGroup->push($fieldOption);
}
$fields->addFieldToTab('Root.Main', $mapGroup);
// Get protector
$protector = FormSpamProtectionExtension::get_protector();
if (!$protector) {
var_dump('a');
return $fields; return $fields;
} }
/** if ($this->Parent()->Fields() instanceof UnsavedRelationList) {
* Try to retrieve a value for the given spam field map name from the serialised data var_dump('b');
* return $fields;
* @param string $mapSetting
* @return string
*/
public function spamMapValue($mapSetting)
{
$map = Convert::json2array($this->SpamFieldSettings);
if (empty($map)) {
$map = array();
}
if (array_key_exists($mapSetting, $map)) {
return $map[$mapSetting];
}
return '';
} }
/** // Each other text field in this group can be assigned a field mapping
* Using custom validateField method $mapGroup = FieldGroup::create()
* as Spam Protection Field implementations may have their own error messages ->setTitle(_t(__CLASS__.'.SPAMFIELDMAPPING', 'Spam Field Mapping'))
* and may not be based on the field being required, e.g. Honeypot Field ->setName('SpamFieldMapping')
* ->setDescription(_t(
* @param array $data __CLASS__.'.SPAMFIELDMAPPINGDESCRIPTION',
* @param Form $form 'Select the form fields that correspond to any relevant spam protection identifiers'
* @return void ));
*/
public function validateField($data, $form)
{
$formField = $this->getFormField();
$formField->setForm($form);
if (isset($data[$this->Name])) { // Generate field specific settings
$formField->setValue($data[$this->Name]); $mappableFields = FormSpamProtectionExtension::config()->get('mappable_fields');
} $mappableFieldsMerged = array_combine($mappableFields, $mappableFields);
foreach ($this->getCandidateFields() as $otherField) {
$mapSetting = "Map-{$otherField->Name}";
$fieldOption = DropdownField::create(
'spammap-' . $mapSetting,
$otherField->Title,
$mappableFieldsMerged,
$this->spamMapValue($mapSetting)
)->setEmptyString('');
$mapGroup->push($fieldOption);
}
$fields->addFieldToTab('Root.Main', $mapGroup);
$validator = $form->getValidator(); return $fields;
if (!$formField->validate($validator)) { }
$errors = $validator->getErrors();
$foundError = false;
// field validate implementation may not add error to validator /**
if (count($errors) > 0) { * Try to retrieve a value for the given spam field map name from the serialised data
// check if error already added from fields' validate method *
foreach ($errors as $error) { * @param string $mapSetting
if ($error['fieldName'] == $this->Name) { * @return string
$foundError = $error; */
break; public function spamMapValue($mapSetting)
} {
$map = Convert::json2array($this->SpamFieldSettings);
if (empty($map)) {
$map = array();
}
if (array_key_exists($mapSetting, $map)) {
return $map[$mapSetting];
}
return '';
}
/**
* Using custom validateField method
* as Spam Protection Field implementations may have their own error messages
* and may not be based on the field being required, e.g. Honeypot Field
*
* @param array $data
* @param Form $form
* @return void
*/
public function validateField($data, $form)
{
$formField = $this->getFormField();
$formField->setForm($form);
if (isset($data[$this->Name])) {
$formField->setValue($data[$this->Name]);
}
$validator = $form->getValidator();
if (!$formField->validate($validator)) {
$errors = $validator->getErrors();
$foundError = false;
// field validate implementation may not add error to validator
if (count($errors) > 0) {
// check if error already added from fields' validate method
foreach ($errors as $error) {
if ($error['fieldName'] == $this->Name) {
$foundError = $error;
break;
} }
} }
}
if ($foundError !== false) { if ($foundError !== false) {
// use error messaging already set from validate method // use error messaging already set from validate method
$form->addErrorMessage($this->Name, $foundError['message'], $foundError['messageType'], false); $form->sessionMessage($foundError['message'], $foundError['messageType']);
} else { } else {
// fallback to custom message set in CMS or default message if none set // fallback to custom message set in CMS or default message if none set
$form->addErrorMessage($this->Name, $this->getErrorMessage()->HTML(), 'error', false); $form->sessionError($this->getErrorMessage()->HTML());
}
} }
} }
}
public function getFieldValidationOptions() public function getFieldValidationOptions()
{ {
return new FieldList(); return FieldList::create();
} }
public function getRequired() public function getRequired()
{ {
return false; return false;
} }
public function getIcon() public function getIcon()
{ {
return 'spamprotection/images/' . strtolower($this->class) . '.png'; return 'spamprotection/images/' . strtolower($this->class) . '.png';
} }
public function showInReports() public function showInReports()
{ {
return false; return false;
}
} }
} }

View File

@ -1,5 +1,9 @@
<?php <?php
namespace SilverStripe\SpamProtection\Extension;
use SilverStripe\Core\Extension;
/** /**
* Apply the spam protection to the comments module if it is installed. * Apply the spam protection to the comments module if it is installed.
* *

View File

@ -1,5 +1,12 @@
<?php <?php
namespace SilverStripe\SpamProtection\Extension;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Extension;
use SilverStripe\Core\Injector\Injector;
/** /**
* An extension to the {@link Form} class which provides the method * An extension to the {@link Form} class which provides the method
* {@link enableSpamProtection()} helper. * {@link enableSpamProtection()} helper.
@ -9,6 +16,8 @@
class FormSpamProtectionExtension extends Extension class FormSpamProtectionExtension extends Extension
{ {
use Configurable;
/** /**
* @config * @config
* *
@ -40,7 +49,7 @@ class FormSpamProtectionExtension extends Extension
'authorIp', 'authorIp',
'authorId' 'authorId'
); );
/** /**
* @config * @config
* *
@ -49,12 +58,12 @@ class FormSpamProtectionExtension extends Extension
* @var string $spam_protector * @var string $spam_protector
*/ */
private static $field_name = "Captcha"; private static $field_name = "Captcha";
/** /**
* Instantiate a SpamProtector instance * Instantiate a SpamProtector instance
* *
* @param array $options Configuration options * @param array $options Configuration options
* @return SpamProtector * @return SpamProtector|null
*/ */
public static function get_protector($options = null) public static function get_protector($options = null)
{ {
@ -62,7 +71,7 @@ class FormSpamProtectionExtension extends Extension
if (isset($options['protector'])) { if (isset($options['protector'])) {
$protector = $options['protector']; $protector = $options['protector'];
} else { } else {
$protector = Config::inst()->get('FormSpamProtectionExtension', 'default_spam_protector'); $protector = Config::inst()->get(self::class, 'default_spam_protector');
} }
if ($protector && class_exists($protector)) { if ($protector && class_exists($protector)) {
@ -76,15 +85,16 @@ class FormSpamProtectionExtension extends Extension
* Activates the spam protection module. * Activates the spam protection module.
* *
* @param array $options * @param array $options
* @return Object
*/ */
public function enableSpamProtection($options = array()) public function enableSpamProtection($options = array())
{ {
// captcha form field name (must be unique) // captcha form field name (must be unique)
if (isset($options['name'])) { if (isset($options['name'])) {
$name = $options['name']; $name = $options['name'];
} else { } else {
$name = Config::inst()->get('FormSpamProtectionExtension', 'field_name'); $name = Config::inst()->get(self::class, 'field_name');
} }
// captcha field title // captcha field title
@ -105,7 +115,7 @@ class FormSpamProtectionExtension extends Extension
// add the form field // add the form field
if ($field = $protector->getFormField($name, $title)) { if ($field = $protector->getFormField($name, $title)) {
$field->setForm($this->owner); $field->setForm($this->owner);
// Add before field specified by insertBefore // Add before field specified by insertBefore
$inserted = false; $inserted = false;
if (!empty($options['insertBefore'])) { if (!empty($options['insertBefore'])) {
@ -117,7 +127,7 @@ class FormSpamProtectionExtension extends Extension
} }
} }
} }
return $this->owner; return $this->owner;
} }
} }

View File

@ -1,5 +1,9 @@
<?php <?php
namespace SilverStripe\SpamProtection;
use SilverStripe\Forms\FormField;
/** /**
* SpamProtector base interface. * SpamProtector base interface.
* *

View File

@ -1,45 +0,0 @@
<?php
/**
* @package spamprotection
*
* @deprecated 1.0
*/
class SpamProtectorManager
{
private static $spam_protector = null;
public static function set_spam_protector($protector)
{
Deprecation::notice(
'1.1',
'SpamProtectorManager::set_spam_protector() is deprecated. '.
'Use the new config system. FormSpamProtectorExtension.default_spam_protector'
);
self::$spam_protector = $protector;
}
public static function get_spam_protector()
{
Deprecation::notice(
'1.1',
'SpamProtectorManager::get_spam_protector() is deprecated'.
'Use the new config system. FormSpamProtectorExtension.default_spam_protector'
);
return self::$spam_protector;
}
public static function update_form($form, $before = null, $fieldsToSpamServiceMapping = array(), $title = null, $rightTitle = null)
{
Deprecation::notice(
'1.1',
'SpamProtectorManager::update_form is deprecated'.
'Please use $form->enableSpamProtection() for adding spamprotection'
);
return $form->enableSpamProtection();
}
}

1
codecov.yml Normal file
View File

@ -0,0 +1 @@
comment: false

View File

@ -11,15 +11,25 @@
"email": "will@fullscreen.io" "email": "will@fullscreen.io"
}], }],
"require": { "require": {
"silverstripe/framework": "~3.1" "silverstripe/framework": "^4@dev"
}, },
"require-dev": { "require-dev": {
"phpunit/PHPUnit": "~3.7@stable" "phpunit/phpunit": "^5.7",
"silverstripe/versioned": "^1@dev",
"squizlabs/php_codesniffer": "^3.0"
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.0.x-dev" "dev-master": "3.0.x-dev"
} }
}, },
"license": "BSD-3-Clause" "autoload": {
"psr-4": {
"SilverStripe\\SpamProtection\\": "code/",
"SilverStripe\\SpamProtection\\Tests\\": "tests/"
}
},
"license": "BSD-3-Clause",
"minimum-stability": "dev",
"prefer-stable": true
} }

View File

@ -1,5 +1,5 @@
de: de:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: Spamschutzfelder PLURALNAME: Spamschutzfelder
SINGULARNAME: Spamschutzfeld SINGULARNAME: Spamschutzfeld
SPAMFIELDMAPPING: 'Spamschutzfeld Zuordnung' SPAMFIELDMAPPING: 'Spamschutzfeld Zuordnung'

View File

@ -1,5 +1,5 @@
en: en:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Spam Protection Fields' PLURALNAME: 'Spam Protection Fields'
SINGULARNAME: 'Spam Protection Field' SINGULARNAME: 'Spam Protection Field'
SPAMFIELDMAPPING: 'Spam Field Mapping' SPAMFIELDMAPPING: 'Spam Field Mapping'

View File

@ -1,5 +1,5 @@
eo: eo:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Spamprotektaj kampoj' PLURALNAME: 'Spamprotektaj kampoj'
SINGULARNAME: 'Spamprotekta kampo' SINGULARNAME: 'Spamprotekta kampo'
SPAMFIELDMAPPING: 'Spamkampa mapigo' SPAMFIELDMAPPING: 'Spamkampa mapigo'

View File

@ -1,5 +1,5 @@
es: es:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Campos de protección de spam' PLURALNAME: 'Campos de protección de spam'
SINGULARNAME: 'Campo de protección de spam' SINGULARNAME: 'Campo de protección de spam'
SPAMFIELDMAPPING: 'Mapeo del campo spam' SPAMFIELDMAPPING: 'Mapeo del campo spam'

View File

@ -1,5 +1,5 @@
fa_IR: fa_IR:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'فیلدهای محافظت از هرزنوشته' PLURALNAME: 'فیلدهای محافظت از هرزنوشته'
SINGULARNAME: 'فیلد محافظت از هرزنوشته' SINGULARNAME: 'فیلد محافظت از هرزنوشته'
SPAMFIELDMAPPING: 'نقشه‌برداری فیلد هرزنوشته' SPAMFIELDMAPPING: 'نقشه‌برداری فیلد هرزنوشته'

View File

@ -1,5 +1,5 @@
hr: hr:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Polja Spam zaštite' PLURALNAME: 'Polja Spam zaštite'
SINGULARNAME: 'Polje Spam zaštite' SINGULARNAME: 'Polje Spam zaštite'
SPAMFIELDMAPPING: 'Mapiranje polja Spama' SPAMFIELDMAPPING: 'Mapiranje polja Spama'

View File

@ -1,5 +1,5 @@
ru: ru:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Поля защиты от спама' PLURALNAME: 'Поля защиты от спама'
SINGULARNAME: 'Поле защиты от спама' SINGULARNAME: 'Поле защиты от спама'
SPAMFIELDMAPPING: 'Привязка полей для защиты от спама' SPAMFIELDMAPPING: 'Привязка полей для защиты от спама'

View File

@ -1,5 +1,5 @@
sk: sk:
EditableSpamProtectionField: SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Polia ochrany proti spamu' PLURALNAME: 'Polia ochrany proti spamu'
SINGULARNAME: 'Pole ochrany proti spamu' SINGULARNAME: 'Pole ochrany proti spamu'
SPAMFIELDMAPPING: 'Mapovanie spamového poľa' SPAMFIELDMAPPING: 'Mapovanie spamového poľa'

View File

@ -1,4 +1,4 @@
Copyright (c) 2016, SilverStripe Limited Copyright (c) 2017, SilverStripe Limited
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

14
phpunit.xml.dist Normal file
View File

@ -0,0 +1,14 @@
<phpunit bootstrap="framework/tests/bootstrap.php" colors="true">
<testsuite name="Default">
<directory>tests</directory>
</testsuite>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">code/</directory>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -1,22 +1,34 @@
<?php <?php
namespace SilverStripe\SpamProtection\Tests;
use SilverStripe\UserForms\Model\UserDefinedForm;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\RequiredFields;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\EditableSpamProtectionField;
use SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension;
use SilverStripe\SpamProtection\Tests\Stub\Protector;
class EditableSpamProtectionFieldTest extends SapphireTest class EditableSpamProtectionFieldTest extends SapphireTest
{ {
protected $usesDatabase = true; protected $usesDatabase = true;
public function setUp() protected function setUp()
{ {
parent::setUp(); parent::setUp();
if (!class_exists('EditableSpamProtectionField')) { if (!class_exists(EditableSpamProtectionField::class)) {
$this->markTestSkipped('"userforms" module not installed'); $this->markTestSkipped('"userforms" module not installed');
} }
Config::inst()->update( Config::modify()->set(
'FormSpamProtectionExtension', FormSpamProtectionExtension::class,
'default_spam_protector', 'default_spam_protector',
'EditableSpamProtectionFieldTest_Protector' Protector::class
); );
} }
@ -33,7 +45,7 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$formMock $formMock
->expects($this->never()) ->expects($this->never())
->method('addErrorMessage'); ->method('sessionMessage');
$formFieldMock->validateField(array('MyField' => null), $formMock); $formFieldMock->validateField(array('MyField' => null), $formMock);
} }
@ -53,9 +65,8 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$formMock $formMock
->expects($this->once()) ->expects($this->once())
->method('addErrorMessage') ->method('sessionMessage')
->with($this->anything(), $this->stringContains('some field message'), $this->anything(), $this->anything()); ->with($this->stringContains('some field message'), $this->anything());
;
$formFieldMock->validateField(array('MyField' => null), $formMock); $formFieldMock->validateField(array('MyField' => null), $formMock);
} }
@ -75,8 +86,8 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$formMock $formMock
->expects($this->once()) ->expects($this->once())
->method('addErrorMessage') ->method('sessionError')
->with($this->anything(), $this->stringContains('default error message'), $this->anything(), $this->anything()); ->with($this->stringContains('default error message'));
$formFieldMock->validateField(array('MyField' => null), $formMock); $formFieldMock->validateField(array('MyField' => null), $formMock);
} }
@ -86,7 +97,7 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$field = $this->getEditableFormFieldMock(); $field = $this->getEditableFormFieldMock();
$fields = $field->getCMSFields(); $fields = $field->getCMSFields();
$this->assertInstanceOf('FieldGroup', $fields->fieldByName('Root.Main.SpamFieldMapping')); $this->assertInstanceOf(FieldGroup::class, $fields->fieldByName('Root.Main.SpamFieldMapping'));
} }
public function testSpamMapSettingsAreSerialised() public function testSpamMapSettingsAreSerialised()
@ -102,9 +113,11 @@ class EditableSpamProtectionFieldTest extends SapphireTest
protected function getFormMock() protected function getFormMock()
{ {
$formMock = $this->getMockBuilder('Form', array('addErrorMessage')) $formMock = $this->getMockBuilder(Form::class)
->setMethods(['sessionMessage', 'sessionError', 'getValidator'])
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$formMock $formMock
->expects($this->any()) ->expects($this->any())
->method('getValidator') ->method('getValidator')
@ -118,7 +131,7 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$page = new UserDefinedForm(); $page = new UserDefinedForm();
$page->write(); $page->write();
$formFieldMock = $this->getMockBuilder('TextField') $formFieldMock = $this->getMockBuilder(TextField::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();

View File

@ -1,5 +1,18 @@
<?php <?php
namespace SilverStripe\SpamProtection\Tests;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension;
use SilverStripe\SpamProtection\Tests\Stub\FooProtector;
use SilverStripe\SpamProtection\Tests\Stub\BarProtector;
use SilverStripe\SpamProtection\Tests\Stub\BazProtector;
/** /**
* @package spamprotection * @package spamprotection
*/ */
@ -12,24 +25,25 @@ class FormSpamProtectionExtensionTest extends SapphireTest
*/ */
protected $form = null; protected $form = null;
public function setUp() protected function setUp()
{ {
parent::setUp(); parent::setUp();
$this->form = new Form($this, 'Form', new FieldList( $this->form = new Form(new Controller, 'Form', new FieldList(
new TextField('Title'), new TextField('Title'),
new TextField('Comment'), new TextField('Comment'),
new TextField('URL') new TextField('URL')
), new FieldList()); ), new FieldList());
$this->form->disableSecurityToken(); $this->form->disableSecurityToken();
} }
public function testEnableSpamProtection() public function testEnableSpamProtection()
{ {
Config::inst()->update( Config::modify()->set(
'FormSpamProtectionExtension', FormSpamProtectionExtension::class,
'default_spam_protector', 'default_spam_protector',
'FormSpamProtectionExtensionTest_FooProtector' FooProtector::class
); );
$form = $this->form->enableSpamProtection(); $form = $this->form->enableSpamProtection();
@ -40,7 +54,7 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testEnableSpamProtectionCustomProtector() public function testEnableSpamProtectionCustomProtector()
{ {
$form = $this->form->enableSpamProtection(array( $form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_BarProtector' 'protector' => BarProtector::class
)); ));
$this->assertEquals('Bar', $form->Fields()->fieldByName('Captcha')->Title()); $this->assertEquals('Bar', $form->Fields()->fieldByName('Captcha')->Title());
@ -49,7 +63,7 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testEnableSpamProtectionCustomTitle() public function testEnableSpamProtectionCustomTitle()
{ {
$form = $this->form->enableSpamProtection(array( $form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_BarProtector', 'protector' => BarProtector::class,
'title' => 'Baz', 'title' => 'Baz',
)); ));
@ -59,7 +73,7 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testCustomOptions() public function testCustomOptions()
{ {
$form = $this->form->enableSpamProtection(array( $form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_BazProtector', 'protector' => BazProtector::class,
'title' => 'Qux', 'title' => 'Qux',
'name' => 'Borris' 'name' => 'Borris'
)); ));
@ -70,17 +84,19 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testConfigurableName() public function testConfigurableName()
{ {
$field_name = "test_configurable_name"; $field_name = "test_configurable_name";
Config::inst()->update( Config::modify()->set(
'FormSpamProtectionExtension', 'default_spam_protector', FormSpamProtectionExtension::class,
'FormSpamProtectionExtensionTest_FooProtector' 'default_spam_protector',
FooProtector::class
); );
Config::inst()->update( Config::modify()->set(
'FormSpamProtectionExtension', 'field_name', FormSpamProtectionExtension::class,
'field_name',
$field_name $field_name
); );
$form = $this->form->enableSpamProtection(); $form = $this->form->enableSpamProtection();
// remove for subsequent tests // remove for subsequent tests
Config::inst()->remove('FormSpamProtectionExtension', 'field_name'); Config::modify()->remove(FormSpamProtectionExtension::class, 'field_name');
// field should take up configured name // field should take up configured name
$this->assertEquals('Foo', $form->Fields()->fieldByName($field_name)->Title()); $this->assertEquals('Foo', $form->Fields()->fieldByName($field_name)->Title());
} }
@ -88,7 +104,7 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testInsertBefore() public function testInsertBefore()
{ {
$form = $this->form->enableSpamProtection(array( $form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_FooProtector', 'protector' => FooProtector::class,
'insertBefore' => 'URL' 'insertBefore' => 'URL'
)); ));
@ -102,7 +118,7 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testInsertBeforeMissing() public function testInsertBeforeMissing()
{ {
$form = $this->form->enableSpamProtection(array( $form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_FooProtector', 'protector' => FooProtector::class,
'insertBefore' => 'NotAField' 'insertBefore' => 'NotAField'
)); ));

View File

@ -0,0 +1,23 @@
<?php
namespace SilverStripe\SpamProtection\Tests\FormSpamProtectionExtensionTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\SpamProtector;
/**
* @package spamprotection
*/
class BarProtector implements SpamProtector, TestOnly
{
public function getFormField($name = null, $title = null, $value = null)
{
$title = $title ?: 'Bar';
return new TextField($name, $title, $value);
}
public function setFieldMapping($fieldMapping)
{
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace SilverStripe\SpamProtection\Tests\FormSpamProtectionExtensionTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\SpamProtector;
/**
* @package spamprotection
*/
class BazProtector implements SpamProtector, TestOnly
{
public function getFormField($name = null, $title = null, $value = null)
{
return new TextField($name, $title, $value);
}
public function setFieldMapping($fieldMapping)
{
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace SilverStripe\SpamProtection\Tests\FormSpamProtectionExtensionTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\SpamProtector;
/**
* @package spamprotection
*/
class FooProtector implements SpamProtector, TestOnly
{
public function getFormField($name = null, $title = null, $value = null)
{
return new TextField($name, 'Foo', $value);
}
public function setFieldMapping($fieldMapping)
{
}
}

View File

@ -1,9 +1,15 @@
<?php <?php
namespace SilverStripe\SpamProtection\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\SpamProtector;
/** /**
* @package spamprotection * @package spamprotection
*/ */
class FormSpamProtectionExtensionTest_BarProtector implements SpamProtector, TestOnly class BarProtector implements SpamProtector, TestOnly
{ {
public function getFormField($name = null, $title = null, $value = null) public function getFormField($name = null, $title = null, $value = null)
{ {

View File

@ -1,9 +1,15 @@
<?php <?php
namespace SilverStripe\SpamProtection\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\SpamProtector;
/** /**
* @package spamprotection * @package spamprotection
*/ */
class FormSpamProtectionExtensionTest_BazProtector implements SpamProtector, TestOnly class BazProtector implements SpamProtector, TestOnly
{ {
public function getFormField($name = null, $title = null, $value = null) public function getFormField($name = null, $title = null, $value = null)
{ {

View File

@ -1,9 +1,15 @@
<?php <?php
namespace SilverStripe\SpamProtection\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\SpamProtector;
/** /**
* @package spamprotection * @package spamprotection
*/ */
class FormSpamProtectionExtensionTest_FooProtector implements SpamProtector, TestOnly class FooProtector implements SpamProtector, TestOnly
{ {
public function getFormField($name = null, $title = null, $value = null) public function getFormField($name = null, $title = null, $value = null)
{ {

View File

@ -1,6 +1,12 @@
<?php <?php
class EditableSpamProtectionFieldTest_Protector implements SpamProtector, TestOnly namespace SilverStripe\SpamProtection\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\TextField;
use SilverStripe\SpamProtection\SpamProtector;
class Protector implements SpamProtector, TestOnly
{ {
public function getFormField($name = null, $title = null, $value = null) public function getFormField($name = null, $title = null, $value = null)
{ {