Compare commits

...

No commits in common. "0.3-rc1" and "3" have entirely different histories.
0.3-rc1 ... 3

71 changed files with 1454 additions and 753 deletions

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
# For more information about the properties used in this file,
# please see the EditorConfig documentation:
# http://editorconfig.org
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[{*.yml,package.json}]
indent_size = 2
# The indent size used in the package.json file cannot be changed:
# https://github.com/npm/npm/pull/3180#issuecomment-16336516

7
.gitattributes vendored Normal file
View File

@ -0,0 +1,7 @@
/tests export-ignore
/docs export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/.scrutinizer.yml export-ignore
/codecov.yml export-ignore

11
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
name: CI
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Dispatch CI
on:
# At 11:10 AM UTC, only on Saturday and Sunday
schedule:
- cron: '10 11 * * 6,0'
jobs:
dispatch-ci:
name: Dispatch CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Dispatch CI
uses: silverstripe/gha-dispatch-ci@v1

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
schedule:
- cron: '50 10 4 * *'
jobs:
keepalive:
name: Keepalive
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Keepalive
uses: silverstripe/gha-keepalive@v1

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

9
.tx/config Normal file
View File

@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com
[o:silverstripe:p:silverstripe-spamprotection:r:master]
file_filter = lang/<lang>.yml
source_file = lang/en.yml
source_lang = en
type = YML

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

View File

@ -1,53 +0,0 @@
SilverStripe SpamProtection Module 0.1 beta
=======================================
INSTALLATION
------------
1. Unzip this file (spamprotection-0.1.tar.gz) inside your SilverStripe installation directory.
It should be at the same level as 'jsparty', 'cms' and 'sapphire' modules.
2. Ensure the directory name for the module is 'spamprotection'.
3. Visit your SilverStripe site.
5. We now need to setup some basic features to get the module up and running. Open up _config.php
inside project directory (typically 'mysite/_config.php') with your favourite text editor.
Read the instructions below to setup the initial configuration of the module.
SETTING UP THE MODULE (in 'mysite/_config.php')
------------------------------------------------------
Before putting the following code in '_config.php', make sure you have a subclass of 'SpamProtecterField' installed or written. One
example of 'SpamProtecter' subclass is 'MollomField'.
<<<< CODE STARTS >>>>
SpamProtecterManager::set_spam_protecter('MollomSpamProtector');
<<<< CODE ENDS >>>>
What does this do?
------------------
This tell 'SpamProtection' module that you want to use 'MollomField' as a spam protection module across your site.
UPDATING A FORM TO INCLUDE THE SPAM PROTECTER FIELD
---------------------------------------------------
This following code should appear after the form creation.
<<<< CODE STARTS >>>>
$protector = SpamProtecterManager::update_form($form, 'Message');
<<<< CODE ENDS >>>>
What does this do?
------------------
This code add an instance of a 'SpamProtecterField' class specified in SETTING UP THE MODULE section. The newly created field will have MollomField field. The first parameter is a Form object in which the field will be added into and the second parameter tells SpamProtecterManager to place the new field before a field named 'Message'. The last parameter is an object that get notified when the spam verification is done and given the form object.
The purpose of having callback object is we can process the submission differently according to the spam status of the submission. For example, put any submission with 'spam' status into spam queue.

View File

@ -1,24 +0,0 @@
###############################################
SpamProtection Module
###############################################
Maintainer Contact
-----------------------------------------------
Saophalkun Ponlu
<phalkunz (at) silverstripe (dot) com>
Requirements
-----------------------------------------------
Documentation
-----------------------------------------------
Installation Instructions
-----------------------------------------------
See INSTALL
Usage Overview
-----------------------------------------------
Known issues
-----------------------------------------------

View File

@ -1,3 +0,0 @@
<?php
?>

View File

@ -1,40 +0,0 @@
<?php
/**
* This class acts as a template for spam protecting form field, for instance MollomField.
* It provides a number of properties for mapping fields of the form which this object belongs to
* to spam checking service fields.
*
* In order to further process the form values or take any action according the status of spam checking,
* markAsSpam() and markAsHam should be called in validate() after the status of the spam checking has
* been obtained.
*
*/
class SpamProtecterField extends FormField {
protected $spanControlCallbackObj = null;
function setCallbackObject($callbackObject) {
$this->spanControlCallbackObj = $callbackObject;
}
/**
* Tell the callback object the submission is spam
*/
protected function markAsSpam() {
if ($this->spanControlCallbackObj && $this->spanControlCallbackObj instanceof Spamable) {
$this->spanControlCallbackObj->markAsSpam($this->getForm());
}
}
/**
* Tell the callback object the submission is ham
*/
protected function markAsHam() {
if ($this->spanControlCallbackObj && $this->spanControlCallbackObj instanceof Spamable) {
$this->spanControlCallbackObj->markAsHam($this->getForm());
}
}
}
?>

View File

@ -1,63 +0,0 @@
<?php
/**
* This class is responsible for setting an system-wide spam protecter field
* and add the protecter field to a form
*/
class SpamProtecterManager {
static $spam_protecter = null;
/**
* Set the name of the spam protecter class
* @param string the name of protecter field class
*/
static function set_spam_protecter($protecter) {
self::$spam_protecter = $protecter;
}
/**
* Add the spam protecter field to a form
* @param Form the form that the protecter field added into
* @param string the name of the field that the protecter field will be added in front of
* @param array an associative array
* with the name of the spam web service's field, for example post_title, post_body, author_name
* and a string of field names (seperated by comma) as a value.
* The naming of the fields is based on the implementation of the subclass of SpamProtecterField.
* *** Most of the web service doesn't require this.
* @return SpamProtector object on success or null if the spamprotecter class is not found
* also null if spamprotecterfield creation fails.
*/
static function update_form($form, $before=null, $fieldsToSpamServiceMapping=null) {
if (!class_exists(self::$spam_protecter)) return null;
$protecter = new self::$spam_protecter();
try {
$check = $protecter->updateForm($form, $before, $fieldsToSpamServiceMapping);
}
catch (Exception $e) {
$form->setMessage(
_t("SpamProtection.SPAMSPECTIONFAILED", "This website is designed to be protected against spam by " . self::$spam_protecter . " but this is not correctly set up currently."),
"warning"
);
return null;
}
if (!$check) return null;
return $protecter;
}
/**
* Send Feedback to the Spam Protection. The level of feedback
* will depend on the Protector class.
*
* @param DataObject The Object which you want to send feedback about. Must have a
* SessionID field.
* @param String Feedback on the $object usually 'spam' or 'ham' for non spam entries
*/
static function send_feedback($object, $feedback) {
$protecter = new self::$spam_protecter();
return $protecter->sendFeedback($object, $feedback);
}
}
?>

View File

@ -1,19 +0,0 @@
<?php
/**
* Spam Protector base interface. All Protectors should implement this interface
* to ensure that they contain all the correct methods and we do not get too many
* odd missing function errors
*
* @package SpamProtection
*/
interface SpamProtector {
public function sendFeedback($object = null, $feedback = "");
public function updateForm($form, $before = null, $fieldsToSpamServiceMapping = null);
public function setFieldMapping($fieldToPostTitle, $fieldsToPostBody = null, $fieldToAuthorName = null, $fieldToAuthorUrl = null, $fieldToAuthorEmail = null, $fieldToAuthorOpenId = null);
}
?>

View File

@ -1,53 +0,0 @@
SilverStripe SpamProtection Module 0.1 beta
=======================================
INSTALLATION
------------
1. Unzip this file (spamprotection-0.1.tar.gz) inside your SilverStripe installation directory.
It should be at the same level as 'jsparty', 'cms' and 'sapphire' modules.
2. Ensure the directory name for the module is 'spamprotection'.
3. Visit your SilverStripe site.
5. We now need to setup some basic features to get the module up and running. Open up _config.php
inside project directory (typically 'mysite/_config.php') with your favourite text editor.
Read the instructions below to setup the initial configuration of the module.
SETTING UP THE MODULE (in 'mysite/_config.php')
------------------------------------------------------
Before putting the following code in '_config.php', make sure you have a subclass of 'SpamProtectorField' installed or written. One
example of 'SpamProtector' subclass is 'MollomField'.
<<<< CODE STARTS >>>>
SpamProtectorManager::set_spam_protector('MollomSpamProtector');
<<<< CODE ENDS >>>>
What does this do?
------------------
This tell 'SpamProtection' module that you want to use 'MollomField' as a spam protection module across your site.
UPDATING A FORM TO INCLUDE THE SPAM PROTECTOR FIELD
---------------------------------------------------
This following code should appear after the form creation.
<<<< CODE STARTS >>>>
$protector = SpamProtectorManager::update_form($form, 'Message');
<<<< CODE ENDS >>>>
What does this do?
------------------
This code add an instance of a 'SpamProtectorField' class specified in SETTING UP THE MODULE section. The newly created field will have
MollomField field. The first parameter is a Form object in which the field will be added into and the second parameter tells
SpamProtectorManagor to place the new field before a field named 'Message'.

View File

@ -1,24 +0,0 @@
###############################################
SpamProtection Module
###############################################
Maintainer Contact
-----------------------------------------------
Saophalkun Ponlu
<phalkunz (at) silverstripe (dot) com>
Requirements
-----------------------------------------------
Documentation
-----------------------------------------------
Installation Instructions
-----------------------------------------------
See INSTALL
Usage Overview
-----------------------------------------------
Known issues
-----------------------------------------------

View File

@ -1,3 +0,0 @@
<?php
?>

View File

@ -1,50 +0,0 @@
<?php
/**
* 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
*
* @package SpamProtection
*/
class EditableSpamProtectionField extends EditableFormField {
static $singular_name = 'Spam Protection Field';
static $plural_name = 'Spam Protection Fields';
function __construct( $record = null, $isSingleton = false ) {
parent::__construct( $record, $isSingleton );
}
function getFormField() {
return $this->createField();
}
function getFilterField() {
return $this->createField(true);
}
function createField() {
if($protector = SpamProtectorManager::get_spam_protector()) {
if($protector) {
$protector = new $protector();
if($class = $protector->getFieldName()) {
return new $class($class, $this->Title);
}
}
}
return false;
}
/**
* @return string
*/
public function Icon() {
return 'spamprotection/images/' . strtolower($this->class) . '.png';
}
function showInReports() {
return false;
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* Spam Protector base interface. All Protectors should implement this interface
* to ensure that they contain all the correct methods and we do not get too many
* odd missing function errors
*
* @package SpamProtection
*/
interface SpamProtector {
/**
* Return the name of the Field Associated with this protector
*/
public function getFieldName();
/**
* Function required to handle dynamic feedback of the system.
* if unneeded just return true
*/
public function sendFeedback($object = null, $feedback = "");
/**
* Updates the form with the given protection
*/
public function updateForm($form, $before = null, $fieldsToSpamServiceMapping = null);
/**
* Set which fields need to be mapped for protection
*/
public function setFieldMapping($fieldToPostTitle, $fieldsToPostBody = null, $fieldToAuthorName = null, $fieldToAuthorUrl = null, $fieldToAuthorEmail = null, $fieldToAuthorOpenId = null);
}
?>

View File

@ -1,40 +0,0 @@
<?php
/**
* This class acts as a template for spam protecting form field, for instance MollomField.
* It provides a number of properties for mapping fields of the form which this object belongs to
* to spam checking service fields.
*
* In order to further process the form values or take any action according the status of spam checking,
* markAsSpam() and markAsHam should be called in validate() after the status of the spam checking has
* been obtained.
*
*/
class SpamProtectorField extends FormField {
protected $spanControlCallbackObj = null;
function setCallbackObject($callbackObject) {
$this->spanControlCallbackObj = $callbackObject;
}
/**
* Tell the callback object the submission is spam
*/
protected function markAsSpam() {
if ($this->spanControlCallbackObj && $this->spanControlCallbackObj instanceof Spamable) {
$this->spanControlCallbackObj->markAsSpam($this->getForm());
}
}
/**
* Tell the callback object the submission is ham
*/
protected function markAsHam() {
if ($this->spanControlCallbackObj && $this->spanControlCallbackObj instanceof Spamable) {
$this->spanControlCallbackObj->markAsHam($this->getForm());
}
}
}
?>

View File

@ -1,67 +0,0 @@
<?php
/**
* This class is responsible for setting an system-wide spam protecter field
* and add the protecter field to a form
*/
class SpamProtectorManager {
static $spam_protector = null;
/**
* Set the name of the spam protecter class
* @param string the name of protecter field class
*/
static function set_spam_protector($protector) {
self::$spam_protector = $protector;
}
/**
* Get the name of the spam protector class
*/
static function get_spam_protector() {
return self::$spam_protector;
}
/**
* Add the spam protector field to a form
* @param Form the form that the protecter field added into
* @param string the name of the field that the protecter field will be added in front of
* @param array an associative array
* with the name of the spam web service's field, for example post_title, post_body, author_name
* and a string of field names (seperated by comma) as a value.
* The naming of the fields is based on the implementation of the subclass of SpamProtectorField.
* *** Most of the web service doesn't require this.
* @return SpamProtector object on success or null if the spamprotector class is not found
* also null if spamprotectorfield creation fails.
*/
static function update_form($form, $before=null, $fieldsToSpamServiceMapping=null) {
$check = null;
$protectorClass = self::$spam_protector;
if(!class_exists($protectorClass)) return null;
$protector = new $protectorClass();
try {
$check = $protector->updateForm($form, $before, $fieldsToSpamServiceMapping);
} catch (Exception $e) {
user_error("SpamProtectorManager::update_form(): '$protectorClass' is not correctly set up.", E_USER_WARNING);
}
if(!$check) return null;
return $protector;
}
/**
* Send Feedback to the Spam Protection. The level of feedback
* will depend on the Protector class.
*
* @param DataObject The Object which you want to send feedback about. Must have a
* SessionID field.
* @param String Feedback on the $object usually 'spam' or 'ham' for non spam entries
*/
static function send_feedback($object, $feedback) {
$protector = new self::$spam_protector();
return $protector->sendFeedback($object, $feedback);
}
}
?>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,8 +0,0 @@
0.1:
- initial release
0.2
- Renamed 'SpamProtecterManager' to 'SpamProtectorManager'. Note the typo with the 'er'
0.3 (trunk)
- Updated Error Reporting to be a bit more verbose

View File

@ -1,38 +0,0 @@
SilverStripe SpamProtection Module 0.1 beta
=======================================
INSTALLATION
------------
1. Unzip this file (spamprotection-0.1.tar.gz) inside your SilverStripe installation directory.
It should be at the same level as 'jsparty', 'cms' and 'sapphire' modules.
2. Ensure the directory name for the module is 'spamprotection'.
3. Visit your SilverStripe site in a webbrowser and run www.yoursite.com/dev/build
5. We now need to setup some basic features to get the module up and running. Open up _config.php
inside project directory (typically 'mysite/_config.php') with your favourite text editor.
Read the instructions below to setup the initial configuration of the module.
SETTING UP THE MODULE (in 'mysite/_config.php')
------------------------------------------------------
Before putting the following code in '_config.php', make sure you have a subclass of 'SpamProtectorField' installed or written. One
example of 'SpamProtector' subclass is 'MollomField'.
SpamProtectorManager::set_spam_protector('MollomSpamProtector');
UPDATING A FORM TO INCLUDE THE SPAM PROTECTOR FIELD
---------------------------------------------------
This following code should appear after the form creation.
$protector = SpamProtectorManager::update_form($form, 'Message');
This code add an instance of a 'SpamProtectorField' class specified in SETTING UP THE MODULE section. The newly created field will have
MollomField field. The first parameter is a Form object in which the field will be added into and the second parameter tells
SpamProtectorManagor to place the new field before a field named 'Message'.

View File

@ -1,30 +0,0 @@
###############################################
SpamProtection Module
###############################################
Maintainer Contact
-----------------------------------------------
Saophalkun Ponlu
<phalkunz (at) silverstripe (dot) com>
Will Rossiter
<will (at) silverstripe (dot) com>
Requirements
-----------------------------------------------
SilverStripe 2.3
Documentation
-----------------------------------------------
http://doc.silverstripe.com/doku.php?id=modules:spamprotection
Installation Instructions
-----------------------------------------------
See INSTALL
Usage Overview
-----------------------------------------------
See INSTALL
Known issues
-----------------------------------------------

View File

@ -1,3 +0,0 @@
<?php
?>

View File

@ -1,33 +0,0 @@
<?php
/**
* 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
*
* @package spamprotection
*/
class EditableSpamProtectionField extends EditableFormField {
static $singular_name = 'Spam Protection Field';
static $plural_name = 'Spam Protection Fields';
function getFormField() {
if($protector = SpamProtectorManager::get_spam_protector()) {
if($protector) {
$protector = new $protector();
return $protector->getFormField($this->Name, $this->Title, null);
}
}
return false;
}
public function Icon() {
return 'spamprotection/images/' . strtolower($this->class) . '.png';
}
function showInReports() {
return false;
}
}

View File

@ -1,23 +0,0 @@
<?php
/**
* Spam Protector base interface. All Protectors should implement this interface
* to ensure that they contain all the correct methods.
*
* @package spamprotection
*/
interface SpamProtector {
/**
* Return the Field Associated with this protector
*/
public function getFormField($name = null, $title = null, $value = null, $form = null, $rightTitle = null);
/**
* Function required to handle dynamic feedback of the system.
* if unneeded just return true
*/
public function sendFeedback($object = null, $feedback = "");
}

View File

@ -1,34 +0,0 @@
<?php
/**
* This class acts as a template for spam protecting form field, for instance MollomField.
*
* @package spamprotection
*/
abstract class SpamProtectorField extends FormField {
/**
* Fields to map spam protection too.
*
* @var array
*/
private $spamFieldMapping = array();
/**
* Set the fields to map spam protection too
*
* @param Array array of Field Names, where the indexes of the array are the field names of the form and the values are the field names of the spam/captcha service
*/
public function setFieldMapping($array) {
$this->spamFieldMapping = $array;
}
/**
* Get the fields that are mapped via spam protection
*
* @return Array
*/
public function getFieldMapping() {
return $this->spamFieldMapping;
}
}

View File

@ -1,95 +0,0 @@
<?php
/**
* This class is responsible for setting an system-wide spam protector field
* and add the protecter field to a form.
*
* @package spamprotection
*/
class SpamProtectorManager {
/**
* Current Spam Protector used on the site
*
* @var SpamProtector
*/
private static $spam_protector = null;
/**
* Set the name of the spam protecter class
*
* @param String the name of protecter field class
*/
public static function set_spam_protector($protector) {
self::$spam_protector = $protector;
}
/**
* Get the name of the spam protector class
*/
public static function get_spam_protector() {
return self::$spam_protector;
}
/**
* Add the spam protector field to a form
* @param Form the form that the protecter field added into
* @param string the name of the field that the protecter field will be added in front of
* @param array an associative array
* with the name of the spam web service's field, for example post_title, post_body, author_name
* and a string of field names
* @param String Title for the captcha field
* @param String RightTitle for the captcha field
* @return SpamProtector object on success or null if the spamprotector class is not found
* also null if spamprotectorfield creation fails.
*/
static function update_form($form, $before = null, $fieldsToSpamServiceMapping = array(), $title = null, $rightTitle = null) {
$protectorClass = self::get_spam_protector();
// Don't update if no protector is set
if(!$protectorClass) return false;
if(!class_exists($protectorClass)) {
return user_error("Spam Protector class '$protectorClass' does not exist. Please define a valid Spam Protector", E_USER_WARNING);
}
try {
$protector = new $protectorClass();
$field = $protector->getFormField("Captcha", $title, null, $form, $rightTitle);
if($field) {
// update the mapping
$field->setFieldMapping($fieldsToSpamServiceMapping);
// add the form field
if($before && $form->Fields()->fieldByName($before)) {
$form->Fields()->insertBefore($field, $before);
}
else {
$form->Fields()->push($field);
}
}
} catch (Exception $e) {
return user_error("SpamProtectorManager::update_form(): '$protectorClass' is not correctly set up. " . $e, E_USER_WARNING);
}
}
/**
* Send Feedback to the Spam Protection. The level of feedback
* will depend on the Protector class.
*
* @param DataObject The Object which you want to send feedback about. Must have a
* SessionID field.
* @param String Feedback on the $object usually 'spam' or 'ham' for non spam entries
*/
static function send_feedback($object, $feedback) {
$protectorClass = self::get_spam_protector();
if(!$protectorClass) return false;
$protector = new $protectorClass();
return $protector->sendFeedback($object, $feedback);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,16 +0,0 @@
<?php
global $lang;
$lang['en_US']['EditableSpamProtectionField']['PLURALNAME'] = array(
'Spam Protection Fields',
50,
'Pural name of the object, used in dropdowns and to generally identify a collection of this object in the interface'
);
$lang['en_US']['EditableSpamProtectionField']['SINGULARNAME'] = array(
'Spam Protection Field',
50,
'Singular name of the object, used in dropdowns and to generally identify a single object in the interface'
);
?>

150
README.md Normal file
View File

@ -0,0 +1,150 @@
# SpamProtection Module
[![CI](https://github.com/silverstripe/silverstripe-spamprotection/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-spamprotection/actions/workflows/ci.yml)
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
## Maintainer Contact
* Saophalkun Ponlu
<phalkunz (at) silverstripe (dot) com>
* Will Rossiter
<will (at) fullscreen (dot) io>
## Requirements
Silverstripe 4.0+
**Note:** For Silverstripe 3.x, please use the [2.x release line](https://github.com/silverstripe/silverstripe-spamprotection/tree/2.0).
## Install
To install run `composer require silverstripe/spamprotection`.
## Documentation
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
box. For that, you must also download one of the spam protection
implementations. Currently available options are:
* reCAPTCHA v2 (two implementations: [one](https://github.com/chillu/silverstripe-recaptcha), [two](https://github.com/UndefinedOffset/silverstripe-nocaptcha))
* [MathSpamProtection](https://github.com/silverstripe/silverstripe-mathspamprotection)
* [Akismet](https://github.com/silverstripe/silverstripe-akismet)
* [Mollom](https://github.com/silverstripe-archive/silverstripe-mollom)
As a developer you can also provide your own protector by creating a class which
implements the `\SilverStripe\SpamProtection\SpamProtector` interface. More on that below.
## Configuring
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
via SilverStripe's config system. This will update any Form instances that have
spam protection hooks with that protector.
*mysite/_config/spamprotection.yml*
```yaml
---
name: mycustomspamprotection
---
SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension:
default_spam_protector: MollomSpamProtector
```
To add spam protection to your form instance call `enableSpamProtection`.
```php
// 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` implementations since they each require a different
implementation client side or server side.
### Options
`enableSpamProtection` takes a hash of optional configuration values.
```php
$form->enableSpamProtection(array(
'protector' => MathSpamProtector::class,
'name' => 'Captcha'
));
```
Options to configure are:
* `protector`: a class name string or class instance which implements
`\SilverStripe\SpamProtection\SpamProtector`. Defaults to your
`SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension.default_spam_protector` value.
* `name`: the form field name argument for the Captcha. Defaults to `Captcha`.
* `title`: title of the Captcha form field. Defaults to `''`
* `insertBefore`: name of existing field to insert the spam protection field prior to
* `mapping`: an array mapping of the Form fields to the standardised list of
field names. The list of standardised fields to pass to the spam protector are:
```
title
body
contextUrl
contextTitle
authorName
authorMail
authorUrl
authorIp
authorId
```
## Defining your own `SpamProtector`
Any class that implements `\SilverStripe\SpamProtection\SpamProtector` and the `getFormField()` method can
be set as the spam protector. The `getFormField()` method returns the
`FormField` to be inserted into the `Form`. The `FormField` returned should be
in charge of the validation process.
```php
<?php
use CaptchaField;
use SilverStripe\SpamProtection\SpamProtector;
class CustomSpamProtector implements SpamProtector
{
public function getFormField($name = null, $title = null, $value = null)
{
// 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
This module provides an `EditableSpamProtectionField` wrapper which you can add
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
`Spam Protection Field`. The type of spam protection used will be based on your
currently selected SpamProtector instance.
## Releasing code with Spam Protection support
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
following pattern:
```php
use SilverStripe\Forms\Form;
use SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension;
$form = new Form(/* .. */);
if ($form->hasExtension(FormSpamProtectionExtension::class)) {
$form->enableSpamProtection();
}
```

View File

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

30
changelog.md Normal file
View File

@ -0,0 +1,30 @@
# Changelog
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [2.0.4]
* Set Form for formField when validating
* Update translations.
## [2.0.3]
* Fix validateField method on EditableSpamProtectionField to use correct error message
* Converted to PSR-2
* Added standard Scrutinizer config
* Added standard code of conduct
* Added standard editor config
* Added standard Travis config
* Added standard license
* Added license to composer.json
* Added standard git attributes
## [2.0.2]
* Changelog added.
* Create sk.yml
* Update translations
* Add tests for php 5.6 and framework 3.2
* Fix for compatibility with userforms 3.0

1
code-of-conduct.md Normal file
View File

@ -0,0 +1 @@
When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct).

View File

@ -0,0 +1,284 @@
<?php
namespace SilverStripe\SpamProtection;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FormField;
use SilverStripe\ORM\DataList;
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;
if (!class_exists(EditableFormField::class)) {
return;
}
/**
* 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
*
* @package spamprotection
*/
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()
{
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
$field = $protector->getFormField($this->Name, $this->Title, null);
$this->doUpdateFormField($field);
return $field;
}
/**
* @param FormField $field
* @return self
*/
public function setFormField(FormField $field)
{
$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
$parent = $this->Parent();
if (!$parent) {
return DataList::create(EditableFormField::class);
}
return $parent
->Fields()
->filter('ClassName', $typesInherit)
->exclude('Title', ''); // Ignore this field and those without titles
}
/**
* Write the spam field mapping values to a serialised DB field
*
* {@inheritDoc}
*/
public function onBeforeWrite()
{
$fieldMap = json_decode($this->SpamFieldSettings ?? '', true);
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', json_encode($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();
if (!$protector) {
return $fields;
}
if ($this->Parent()->Fields() instanceof UnsavedRelationList) {
return $fields;
}
// Each other text field in this group can be assigned a field mapping
$mapGroup = FieldGroup::create()
->setTitle(_t(__CLASS__.'.SPAMFIELDMAPPING', 'Spam Field Mapping'))
->setName('SpamFieldMapping')
->setDescription(_t(
__CLASS__.'.SPAMFIELDMAPPINGDESCRIPTION',
'Select the form fields that correspond to any relevant spam protection identifiers'
));
// Generate field specific settings
$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);
return $fields;
}
/**
* Try to retrieve a value for the given spam field map name from the serialised data
*
* @param string $mapSetting
* @return string
*/
public function spamMapValue($mapSetting)
{
$map = json_decode($this->SpamFieldSettings ?? '', true);
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) {
// use error messaging already set from validate method
$form->sessionMessage($foundError['message'], $foundError['messageType']);
} else {
// fallback to custom message set in CMS or default message if none set
$form->sessionError($this->getErrorMessage()->HTML());
}
}
}
public function getFieldValidationOptions()
{
return FieldList::create();
}
public function getRequired()
{
return false;
}
public function getIcon()
{
$resource = ModuleLoader::getModule('silverstripe/spamprotection')
->getResource('images/editablespamprotectionfield.png');
if (!$resource->exists()) {
return '';
}
return $resource->getURL();
}
public function showInReports()
{
return false;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace SilverStripe\SpamProtection\Extension;
use SilverStripe\Core\Extension;
/**
* Apply the spam protection to the comments module if it is installed.
*
* @package spamprotection
*/
class CommentSpamProtection extends Extension
{
public function alterCommentForm(&$form)
{
$form->enableSpamProtection(array(
'name' => 'IsSpam',
'mapping' => array(
'Name' => 'authorName',
'Email' => 'authorEmail',
'URL' => 'authorUrl',
'Comment' => 'body',
'ReturnURL' => 'contextUrl'
),
'checks' => array(
'spam',
'profanity'
)
));
}
}

View File

@ -0,0 +1,138 @@
<?php
namespace SilverStripe\SpamProtection\Extension;
use LogicException;
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
* {@link enableSpamProtection()} helper.
*
* @package spamprotection
*/
class FormSpamProtectionExtension extends Extension
{
use Configurable;
/**
* @config
*
* The default spam protector class name to use. Class should implement the
* {@link SpamProtector} interface.
*
* @var string $spam_protector
*/
private static $default_spam_protector;
/**
* @config
*
* The {@link enableSpamProtection} method will define which of the form
* values correlates to this form mapped fields list. Totally custom forms
* and subclassed SpamProtector instances are define their own mapping
*
* @var array $mappable_fields
*/
private static $mappable_fields = array(
'id',
'title',
'body',
'contextUrl',
'contextTitle',
'authorName',
'authorMail',
'authorUrl',
'authorIp',
'authorId'
);
/**
* @config
*
* The field name to use for the {@link SpamProtector} {@link FormField}
*
* @var string $spam_protector
*/
private static $field_name = "Captcha";
/**
* Instantiate a SpamProtector instance
*
* @param array $options Configuration options
* @return SpamProtector|null
*/
public static function get_protector($options = null)
{
// generate the spam protector
if (isset($options['protector'])) {
$protector = $options['protector'];
} else {
$protector = self::config()->get('default_spam_protector');
}
if ($protector && class_exists($protector ?? '')) {
return Injector::inst()->create($protector);
} else {
return null;
}
}
/**
* Activates the spam protection module.
*
* @param array $options
* @throws LogicException when get_protector method returns NULL.
* @return Object
*/
public function enableSpamProtection($options = array())
{
// captcha form field name (must be unique)
if (isset($options['name'])) {
$name = $options['name'];
} else {
$name = $this->config()->get('field_name');
}
// captcha field title
if (isset($options['title'])) {
$title = $options['title'];
} else {
$title = '';
}
// set custom mapping on this form
$protector = self::get_protector($options);
if ($protector === null) {
throw new LogicException('No spam protector has been set. Null is not valid value.');
}
if ($protector && isset($options['mapping'])) {
$protector->setFieldMapping($options['mapping']);
}
if ($protector) {
// add the form field
if ($field = $protector->getFormField($name, $title)) {
$field->setForm($this->owner);
// Add before field specified by insertBefore
$inserted = false;
if (!empty($options['insertBefore'])) {
$inserted = $this->owner->Fields()->insertBefore($options['insertBefore'], $field);
}
if (!$inserted) {
// Add field to end if not added already
$this->owner->Fields()->push($field);
}
}
}
return $this->owner;
}
}

44
code/SpamProtector.php Normal file
View File

@ -0,0 +1,44 @@
<?php
namespace SilverStripe\SpamProtection;
use SilverStripe\Forms\FormField;
/**
* SpamProtector base interface.
*
* All Protectors are required implement this interface if they want to appear
* on the form.
*
* Classes with this interface are used to generate helper lists to allow the
* user to select the protector.
*
* @package spamprotection
*/
interface SpamProtector
{
/**
* Return the {@link FormField} associated with this protector.
*
* Most spam methods will simply return a piece of HTML to be injected at
* the end of the form. If a spam method needs to inject more than one
* form field (i.e a hidden field and a text field) then return a
* {@link FieldGroup} from this method to include both.
*
* @param string $name
* @param string $title
* @param mixed $value
* @return FormField The resulting field
*/
public function getFormField($name = null, $title = null, $value = null);
/**
* Set the fields to map spam protection too
*
* @param array $fieldMapping array of Field Names, where the indexes of the array are
* the field names of the form and the values are the standard spamprotection
* fields used by the protector
*/
public function setFieldMapping($fieldMapping);
}

1
codecov.yml Normal file
View File

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

43
composer.json Normal file
View File

@ -0,0 +1,43 @@
{
"name": "silverstripe/spamprotection",
"description": "Spam protection module for SilverStripe.",
"type": "silverstripe-vendormodule",
"keywords": [
"silverstripe",
"spamprotection"
],
"authors": [
{
"name": "Saophalkun Ponlu",
"email": "phalkunz@silverstripe.com"
},
{
"name": "Will Rossiter",
"email": "will@fullscreen.io"
}
],
"require": {
"php": "^7.4 || ^8.0",
"silverstripe/framework": "^4.10"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"silverstripe/versioned": "^1.0",
"squizlabs/php_codesniffer": "^3.0",
"silverstripe/userforms": "^5"
},
"extra": {
"expose": [
"images"
]
},
"autoload": {
"psr-4": {
"SilverStripe\\SpamProtection\\": "code/",
"SilverStripe\\SpamProtection\\Tests\\": "tests/"
}
},
"license": "BSD-3-Clause",
"minimum-stability": "dev",
"prefer-stable": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

9
lang/da.yml Normal file
View File

@ -0,0 +1,9 @@
da:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Spam beskyttelsefelter'
PLURALS:
one: 'Et spam beskyttelsefelt'
other: '{count} Spambeskyttelsefelter'
SINGULARNAME: 'Spam beskyttelsesfelt'
SPAMFIELDMAPPING: 'Spam felt mapping'
SPAMFIELDMAPPINGDESCRIPTION: 'Vælg de formfelter der matcher relevante spam beskyttelses identifikatorer'

9
lang/de.yml Normal file
View File

@ -0,0 +1,9 @@
de:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: Spamschutzfelder
PLURALS:
one: 'Ein Spamschutzfeld'
other: '{count} Spamschutzfelder'
SINGULARNAME: Spamschutzfeld
SPAMFIELDMAPPING: 'Spamschutzfeld Zuordnung'
SPAMFIELDMAPPINGDESCRIPTION: 'Wähle die Formularfelder, die mit Spamschutz verbunden sind'

10
lang/en.yml Normal file
View File

@ -0,0 +1,10 @@
en:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Spam Protection Fields'
PLURALS:
one: 'A Spam Protection Field'
other: '{count} Spam Protection Fields'
SINGULARNAME: 'Spam Protection Field'
SPAMFIELDMAPPING: 'Spam Field Mapping'
SPAMFIELDMAPPINGDESCRIPTION: 'Select the form fields that correspond to any relevant spam protection identifiers'
db_SpamFieldSettings: 'Spam field settings'

10
lang/eo.yml Normal file
View File

@ -0,0 +1,10 @@
eo:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Spamprotektaj kampoj'
PLURALS:
one: 'Unu spamprotekta kampo'
other: '{count} spamprotektaj kampoj'
SINGULARNAME: 'Spamprotekta kampo'
SPAMFIELDMAPPING: 'Spamkampa mapigo'
SPAMFIELDMAPPINGDESCRIPTION: 'Elektu la kampojn kiuj rilatas al eventualaj rilataj spamprotektaj identigiloj'
db_SpamFieldSettings: 'Spamkampaj agordoj'

6
lang/es.yml Normal file
View File

@ -0,0 +1,6 @@
es:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Campos de protección de spam'
SINGULARNAME: 'Campo de protección de spam'
SPAMFIELDMAPPING: 'Mapeo del campo spam'
SPAMFIELDMAPPINGDESCRIPTION: 'Seleccionar los campos del formulario que corresponden a cualquier identificador de protección de spam relevante'

6
lang/fa_IR.yml Normal file
View File

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

9
lang/fi.yml Normal file
View File

@ -0,0 +1,9 @@
fi:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Roskapostisuojauksen kentät'
PLURALS:
one: 'Roskapostin suodatuskenttä'
other: '{count} Roskapostin suodatuskenttää'
SINGULARNAME: 'Roskapostisuojauksen kenttä'
SPAMFIELDMAPPING: 'Roskapostikentän kuvaus'
SPAMFIELDMAPPINGDESCRIPTION: 'Valitse lomakekenttä, joka vastaa mitä tahansa oleellista roskapostisuodatuksen tunnistetta'

9
lang/fi_FI.yml Normal file
View File

@ -0,0 +1,9 @@
fi_FI:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Roskapostisuojauksen kentät'
PLURALS:
one: 'Roskapostin suodatuskenttä'
other: '{count} Roskapostin suodatuskenttää'
SINGULARNAME: 'Roskapostisuojauksen kenttä'
SPAMFIELDMAPPING: 'Roskapostikentän kuvaus'
SPAMFIELDMAPPINGDESCRIPTION: 'Valitse lomakekenttä, joka vastaa mitä tahansa oleellista roskapostisuodatuksen tunnistetta'

6
lang/hr.yml Normal file
View File

@ -0,0 +1,6 @@
hr:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Polja Spam zaštite'
SINGULARNAME: 'Polje Spam zaštite'
SPAMFIELDMAPPING: 'Mapiranje polja Spama'
SPAMFIELDMAPPINGDESCRIPTION: 'Odaberite polja forme koji odgovaraju relevantnim identifikatorima za zaštitu od spama'

10
lang/it.yml Normal file
View File

@ -0,0 +1,10 @@
it:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Campi di Prevenzione Spam'
PLURALS:
many: '{count} Campi di Prevenzione Spam'
one: 'Un Campo di Prevenzione Spam'
other: '{count} Campi di Prevenzione Spam'
SINGULARNAME: 'Campo di Prevenzione Spam'
SPAMFIELDMAPPING: 'Mappatura Campo Spam'
SPAMFIELDMAPPINGDESCRIPTION: 'Selezionare i campi della form che forniscono una qualche protezione dallo spam'

9
lang/nl.yml Normal file
View File

@ -0,0 +1,9 @@
nl:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Anti-spam velden'
PLURALS:
one: 'Een anti-spam veld'
other: '{count} Anti-spam velden'
SINGULARNAME: 'Anti-spam veld'
SPAMFIELDMAPPING: 'Spam-velden koppelen'
SPAMFIELDMAPPINGDESCRIPTION: 'Kies de velden die overeenkomen met de anti-spam identifiers'

6
lang/ru.yml Normal file
View File

@ -0,0 +1,6 @@
ru:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Поля защиты от спама'
SINGULARNAME: 'Поле защиты от спама'
SPAMFIELDMAPPING: 'Привязка полей для защиты от спама'
SPAMFIELDMAPPINGDESCRIPTION: 'Выберите поля формы, которые соответствуют любому индентификатору защиты от спама'

12
lang/sk.yml Normal file
View File

@ -0,0 +1,12 @@
sk:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Polia ochrany proti spamu'
PLURALS:
few: '{count} polia ochrany proti spamu'
many: '{count} polí ochrany proti spamu'
one: 'Pole ochrany proti spamu'
other: '{count} polí ochrany proti spamu'
SINGULARNAME: 'Pole ochrany proti spamu'
SPAMFIELDMAPPING: 'Mapovanie spamového poľa'
SPAMFIELDMAPPINGDESCRIPTION: 'Vyberte polia formulára, ktoré zodpovedajú všetkým príslušným identifikátorom ochrany proti spamu'
db_SpamFieldSettings: 'Nastavenia poľa spamu'

4
lang/sl.yml Normal file
View File

@ -0,0 +1,4 @@
sl:
SilverStripe\SpamProtection\EditableSpamProtectionField:
PLURALNAME: 'Polja za zaščito pred neželeno pošto'
SINGULARNAME: 'Polje za zaščito pred neželeno pošto'

6
legacy.yml Normal file
View File

@ -0,0 +1,6 @@
---
Name: spamprotectionlegacy
---
SilverStripe\ORM\DatabaseAdmin:
classname_value_remapping:
EditableSpamProtectionField: 'SilverStripe\SpamProtection\EditableSpamProtectionField'

12
license.md Normal file
View File

@ -0,0 +1,12 @@
Copyright (c) 2017, SilverStripe Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

13
phpcs.xml.dist Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>code</file>
<file>tests</file>
<rule ref="PSR2" >
<!-- Current exclusions -->
<exclude name="PSR1.Methods.CamelCapsMethodName" />
<exclude name="PSR1.Files.SideEffects.FoundWithSymbols" />
</rule>
</ruleset>

17
phpunit.xml.dist Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Default">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">code/</directory>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,178 @@
<?php
namespace SilverStripe\SpamProtection\Tests;
use ReflectionClass;
use SilverStripe\ORM\DataList;
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
{
protected $usesDatabase = true;
protected function setUp(): void
{
parent::setUp();
if (!class_exists(EditableSpamProtectionField::class)) {
$this->markTestSkipped('"userforms" module not installed');
}
Config::modify()->set(
FormSpamProtectionExtension::class,
'default_spam_protector',
Protector::class
);
}
public function testValidateFieldDoesntAddErrorOnSuccess()
{
$formMock = $this->getFormMock();
$formFieldMock = $this->getEditableFormFieldMock();
$formFieldMock
->getFormField() // mock
->expects($this->once())
->method('validate')
->will($this->returnValue(true));
$formMock
->expects($this->never())
->method('sessionMessage');
$formFieldMock->validateField(array('MyField' => null), $formMock);
}
public function testValidateFieldAddsErrorFromField()
{
$formMock = $this->getFormMock();
$formFieldMock = $this->getEditableFormFieldMock();
$formFieldMock
->getFormField() // mock
->expects($this->once())
->method('validate')
->will($this->returnValue(false));
$formMock->getValidator()->validationError('MyField', 'some field message', 'required');
$formMock
->expects($this->once())
->method('sessionMessage')
->with($this->stringContains('some field message'), $this->anything());
$formFieldMock->validateField(array('MyField' => null), $formMock);
}
public function testValidateFieldAddsDefaultError()
{
$formMock = $this->getFormMock();
$formFieldMock = $this->getEditableFormFieldMock();
$formFieldMock
->getFormField() // mock
->expects($this->once())
->method('validate')
->will($this->returnValue(false));
// field doesn't set any validation errors here
$formMock
->expects($this->once())
->method('sessionError')
->with($this->stringContains('default error message'));
$formFieldMock->validateField(array('MyField' => null), $formMock);
}
public function testSpamConfigurationShowsInCms()
{
$field = $this->getEditableFormFieldMock();
$fields = $field->getCMSFields();
$this->assertInstanceOf(FieldGroup::class, $fields->fieldByName('Root.Main.SpamFieldMapping'));
}
public function testSpamMapSettingsAreSerialised()
{
$field = $this->getEditableFormFieldMock();
$field->SpamFieldSettings = json_encode(array('foo' => 'bar', 'bar' => 'baz'));
$field->write();
$this->assertJson($field->SpamFieldSettings);
$this->assertSame('bar', $field->spamMapValue('foo'));
$this->assertSame('baz', $field->spamMapValue('bar'));
}
public function testGetIcon()
{
$field = new EditableSpamProtectionField;
$this->assertStringContainsString('/images/editablespamprotectionfield.png', $field->getIcon());
}
protected function getFormMock()
{
$formMock = $this->getMockBuilder(Form::class)
->setMethods(['sessionMessage', 'sessionError', 'getValidator'])
->disableOriginalConstructor()
->getMock();
$formMock
->expects($this->any())
->method('getValidator')
->will($this->returnValue(new RequiredFields()));
return $formMock;
}
protected function getEditableFormFieldMock()
{
$page = new UserDefinedForm();
$page->write();
$formFieldMock = $this->getMockBuilder(TextField::class)
->disableOriginalConstructor()
->getMock();
$editableFormFieldMock = new EditableSpamProtectionField(array(
'ParentID' => $page->ID,
'ParentClass' => get_class($page),
'Name' => 'MyField',
'CustomErrorMessage' => 'default error message'
));
$editableFormFieldMock->write();
$editableFormFieldMock->setFormField($formFieldMock);
return $editableFormFieldMock;
}
public function testGetCandidateFieldsParentStatus()
{
$field = new EditableSpamProtectionField();
$field->Name = 'MyField';
$reflection = new ReflectionClass($field);
$method = $reflection->getMethod('getCandidateFields');
$method->setAccessible(true);
// Assert with no parent
$list = $method->invoke($field);
$this->assertTrue($list instanceof DataList);
// Assert with parent
$page = new UserDefinedForm();
$page->write();
$field->ParentID = $page->ID;
$field->ParentClass = get_class($page);
$field->write();
$list = $method->invoke($field);
$this->assertTrue($list instanceof DataList);
}
}

View File

@ -0,0 +1,142 @@
<?php
namespace SilverStripe\SpamProtection\Tests;
use LogicException;
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
*/
class FormSpamProtectionExtensionTest extends SapphireTest
{
protected $usesDatabase = false;
/**
* @var Form
*/
protected $form = null;
protected function setUp(): void
{
parent::setUp();
$this->form = new Form(new Controller, 'Form', new FieldList(
new TextField('Title'),
new TextField('Comment'),
new TextField('URL')
), new FieldList());
$this->form->disableSecurityToken();
}
public function testEnableSpamProtectionThrowsException()
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('No spam protector has been set. Null is not valid value.');
Config::modify()->set(FormSpamProtectionExtension::class, 'default_spam_protector', null);
$this->form->enableSpamProtection();
}
public function testEnableSpamProtection()
{
Config::modify()->set(
FormSpamProtectionExtension::class,
'default_spam_protector',
FooProtector::class
);
$form = $this->form->enableSpamProtection();
$this->assertEquals('Foo', $form->Fields()->fieldByName('Captcha')->Title());
}
public function testEnableSpamProtectionCustomProtector()
{
$form = $this->form->enableSpamProtection(array(
'protector' => BarProtector::class
));
$this->assertEquals('Bar', $form->Fields()->fieldByName('Captcha')->Title());
}
public function testEnableSpamProtectionCustomTitle()
{
$form = $this->form->enableSpamProtection(array(
'protector' => BarProtector::class,
'title' => 'Baz',
));
$this->assertEquals('Baz', $form->Fields()->fieldByName('Captcha')->Title());
}
public function testCustomOptions()
{
$form = $this->form->enableSpamProtection(array(
'protector' => BazProtector::class,
'title' => 'Qux',
'name' => 'Borris'
));
$this->assertEquals('Qux', $form->Fields()->fieldByName('Borris')->Title());
}
public function testConfigurableName()
{
$field_name = "test_configurable_name";
Config::modify()->set(
FormSpamProtectionExtension::class,
'default_spam_protector',
FooProtector::class
);
Config::modify()->set(
FormSpamProtectionExtension::class,
'field_name',
$field_name
);
$form = $this->form->enableSpamProtection();
// remove for subsequent tests
Config::modify()->remove(FormSpamProtectionExtension::class, 'field_name');
// field should take up configured name
$this->assertEquals('Foo', $form->Fields()->fieldByName($field_name)->Title());
}
public function testInsertBefore()
{
$form = $this->form->enableSpamProtection(array(
'protector' => FooProtector::class,
'insertBefore' => 'URL'
));
$fields = $form->Fields();
$this->assertEquals('Title', $fields[0]->Title());
$this->assertEquals('Comment', $fields[1]->Title());
$this->assertEquals('Foo', $fields[2]->Title());
$this->assertEquals('URL', $fields[3]->Title());
}
public function testInsertBeforeMissing()
{
$form = $this->form->enableSpamProtection(array(
'protector' => FooProtector::class,
'insertBefore' => 'NotAField'
));
// field should default to the end instead
$fields = $form->Fields();
$this->assertEquals('Title', $fields[0]->Title());
$this->assertEquals('Comment', $fields[1]->Title());
$this->assertEquals('URL', $fields[2]->Title());
$this->assertEquals('Foo', $fields[3]->Title());
}
}

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

@ -0,0 +1,23 @@
<?php
namespace SilverStripe\SpamProtection\Tests\Stub;
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\Stub;
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\Stub;
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)
{
}
}

19
tests/Stub/Protector.php Normal file
View File

@ -0,0 +1,19 @@
<?php
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)
{
return new TextField($name, 'Foo', $value);
}
public function setFieldMapping($fieldMapping)
{
}
}