Compare commits

...

128 Commits
2.0.4 ... 3

Author SHA1 Message Date
github-actions 26936f33e4 Merge branch '3.4' into 3 2024-02-07 11:15:05 +00:00
Guy Sartorelli 5badc345de
TLN Update translations (#111) 2024-02-07 16:12:33 +13:00
github-actions eb858ca76e Merge branch '3.4' into 3 2023-08-23 11:14:45 +00:00
Guy Sartorelli 19603c31bc
ENH Update translations (#101) 2023-08-21 13:16:40 +12:00
Steve Boyd 17411fbb95 Merge branch '3.4' into 3 2023-06-16 12:09:40 +12:00
Guy Sartorelli 404ae173d0
Merge pull request #98 from creative-commoners/pulls/3.4/tx-1686724855
ENH Update translations
2023-06-15 10:06:13 +12:00
Steve Boyd afbcf59ec4 ENH Update translations 2023-06-14 18:40:55 +12:00
Guy Sartorelli 5742971245
Merge branch '3.4' into 3 2023-04-26 12:46:05 +12:00
Guy Sartorelli 23843cd97f
MNT Revert erroneous dependency changes (#95) 2023-03-28 17:02:59 +13:00
Maxime Rainville 6ef3acd518
Merge pull request #94 from creative-commoners/pulls/3.4/no-deprecations
FIX Don't use deprecated API
2023-03-28 11:18:04 +13:00
Guy Sartorelli 3f62dfd4a4
FIX Don't use deprecated API 2023-03-28 10:23:53 +13:00
Maxime Rainville 7c81d075d6
Merge pull request #93 from creative-commoners/pulls/3/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:19:24 +13:00
Steve Boyd 04aefedeef MNT Use gha-dispatch-ci 2023-03-21 13:41:47 +13:00
Guy Sartorelli 754bcee5da
MNT Update development dependencies 2023-03-10 16:32:56 +13:00
Guy Sartorelli 4971ddf3cb
MNT Update release dependencies 2023-03-10 16:32:52 +13:00
Guy Sartorelli 9fd768d5ad
MNT Update development dependencies 2023-03-10 12:21:29 +13:00
Guy Sartorelli 4e3f68cdb1
Merge pull request #92 from creative-commoners/pulls/3/tx-1678080129
ENH Update translations
2023-03-08 10:32:21 +13:00
Steve Boyd c5d644eb71 ENH Update translations 2023-03-06 18:22:09 +13:00
Maxime Rainville 9a7ed5f091
Merge pull request #91 from creative-commoners/pulls/3/broken-build-cms4
MNT Explicitly test with default_spam_protector set to null
2023-01-26 17:01:35 +13:00
Steve Boyd ed1d210f0d MNT Explicitly test with default_spam_protector set to null 2023-01-24 10:34:16 +13:00
Maxime Rainville 1c99bc8b92
Merge pull request #88 from creative-commoners/pulls/3/fix-invoke-method-on-null
FIX Check  value is not NULL
2023-01-17 10:25:18 +13:00
Sabina Talipova 2a55708ae8 FIX Check value is not NULL 2023-01-17 08:30:17 +13:00
Sabina Talipova 2c8fc03f2f
Merge pull request #85 from creative-commoners/pulls/3/stop-using-depr
API Stop using deprecated API
2022-12-05 16:40:37 +13:00
Steve Boyd 688b438e88 API Stop using deprecated API 2022-11-24 17:36:45 +13:00
Steve Boyd fc36247598 Merge branch '3.3' into 3 2022-08-02 18:58:04 +12:00
Steve Boyd 0d7a57cedf Merge branch '3.2' into 3.3 2022-08-02 18:58:00 +12:00
Guy Sartorelli 0e5a2dc66d
Merge pull request #84 from creative-commoners/pulls/3.2/standardise-modules
MNT Standardise modules
2022-08-02 15:41:11 +12:00
Steve Boyd 3b0d8d08ed MNT Standardise modules 2022-08-01 16:23:11 +12:00
Steve Boyd e028d76a34 Merge branch '3.3' into 3 2022-07-25 11:38:54 +12:00
Steve Boyd 4b7d18fe0e Merge branch '3.2' into 3.3 2022-07-25 11:38:51 +12:00
Guy Sartorelli 8eb3e05f76
Merge pull request #83 from creative-commoners/pulls/3.2/module-standards
MNT Use GitHub Actions CI
2022-07-15 17:20:32 +12:00
Steve Boyd 63fbae7413 MNT Use GitHub Actions CI 2022-07-05 19:07:16 +12:00
Steve Boyd 3e9f292082 Merge branch '3.3' into 3 2022-05-10 22:10:54 +12:00
Steve Boyd af01b0ad01 Update translations 2022-05-04 13:29:41 +12:00
Guy Sartorelli ed7dc6e38e
Merge pull request #82 from creative-commoners/pulls/3/php81
ENH PHP 8.1 compatibility
2022-04-26 17:57:43 +12:00
Steve Boyd d452367262 ENH PHP 8.1 compatibility 2022-04-13 13:47:43 +12:00
Maxime Rainville a2a21988ed Merge branch '3.2' into 3 2022-03-19 22:58:09 +13:00
Maxime Rainville 228f2a3058 Merge branch '3.1' into 3.2 2022-03-17 15:20:37 +13:00
Maxime Rainville 68d550b817
Merge pull request #79 from creative-commoners/pulls/3.1/write-without-parent
FIX Allow fields without parents to be programatically created
2022-03-17 15:18:02 +13:00
Maxime Rainville 514b6d7a17
Merge pull request #81 from creative-commoners/pulls/3/php74
DEP Set PHP 7.4 as the minimum version
2022-02-18 22:08:44 +13:00
Steve Boyd f7febc1e98 DEP Set PHP 7.4 as the minimum version 2022-02-10 17:37:49 +13:00
Maxime Rainville 04eb8df284
Merge pull request #80 from creative-commoners/pulls/3/sapphire-test-nine
API phpunit 9 support
2021-11-01 22:24:02 +13:00
Steve Boyd 46169f71a0 API phpunit 9 support 2021-10-27 18:14:42 +13:00
Steve Boyd 643fedaf21 FIX Allow fields without parents to be programatically created 2021-08-12 13:35:02 +12:00
Steve Boyd 9c9900f75a Merge branch '3.1' into 3 2021-05-21 14:03:17 +12:00
Maxime Rainville ca6f507af8 Update translations 2021-05-17 15:09:54 +12:00
Maxime Rainville c2c4342c01 MNT Remove obsolete branch-alias 2021-05-05 11:17:33 +12:00
Steve Boyd 050ef82451 Merge branch '3.0' into 3 2021-02-02 14:11:10 +13:00
Steve Boyd 3a8702b1fa
Merge pull request #76 from creative-commoners/pulls/3.0/update-form-field
[Backport] Makes use of EditableFormField updateFormField
2021-02-02 14:09:40 +13:00
3Dgoo 857389c7ff Makes use of EditableFormField updateFormField
This fixes issue #61
2021-02-02 13:55:21 +13:00
Steve Boyd 1459ed89ff
Update build status badge 2021-01-21 16:41:59 +13:00
Steve Boyd e717411a9d
Merge pull request #75 from 3Dgoo/patch-1
ENH Make use of EditableFormField updateFormField
2021-01-14 16:16:06 +13:00
3Dgoo d5ae93c88e
Makes use of EditableFormField updateFormField
This fixes issue #61
2020-11-25 15:52:44 +10:30
Steve Boyd 0db3e7aee0 Merge branch '3.0' into 3 2020-11-11 17:45:16 +13:00
Serge Latyntsev 856cc80ffa
Merge pull request #74 from creative-commoners/pulls/3.0/shared-config
MNT Use shared travis config, use sminnee/phpunit
2020-11-10 16:46:52 +13:00
Steve Boyd a20b215e5c MNT Use shared travis config, use sminnee/phpunit 2020-11-10 14:03:59 +13:00
Steve Boyd a36bb28b0c
Merge pull request #73 from creative-commoners/pulls/3.0/travis
Update travis 3.0
2020-07-06 09:04:06 +12:00
Steve Boyd 6ecccd2625 Update travis 2020-06-23 16:11:03 +12:00
Garion Herman 669b189ba7 Merge branch '3.0' into 3 2019-11-20 22:15:43 +13:00
Robbie Averill dce36b8fe7
Merge pull request #71 from creative-commoners/pulls/3.0/xenial
Update to Xenial, expand build matrix
2019-11-20 00:18:27 -08:00
Garion Herman 5978c62022 Update to Xenial, expand build matrix 2019-11-20 18:35:30 +13:00
Robbie Averill f3fa9fe9fc Merge branch '3.0' 2019-05-10 10:14:27 +12:00
Robbie Averill e024613ede Update translations 2019-05-10 10:13:46 +12:00
Robbie Averill b88a706ae7 Merge branch '3.0' 2018-11-07 16:35:56 +02:00
Will Rossiter 0c445a1dbf Add legacy mapping 2018-11-07 16:35:45 +02:00
Guy Marriott 6d39bc23ec
Merge pull request #68 from creative-commoners/pulls/3.0/remove-json-methods
FIX Replace Convert JSON methods with json_* methods, deprecated from SilverStripe 4.4
2018-10-29 11:28:25 +13:00
Robbie Averill fc934d93fd FIX Replace Convert JSON methods with json_* methods, deprecated from SilverStripe 4.4 2018-10-28 21:40:35 +00:00
Will Rossiter 506d82fc9d Add legacy mapping 2018-09-19 21:01:36 +12:00
Robbie Averill 27a40c6210
Merge pull request #67 from creative-commoners/pulls/master/add-supported-module-badge
Add supported module badge to readme
2018-06-18 10:15:13 +12:00
Dylan Wagstaff fe2aed7e58 Add supported module badge to readme 2018-06-15 17:48:43 +12:00
Robbie Averill 4c65d66931 Update branch alias for 3.x-dev 2018-06-11 12:12:27 +12:00
Robbie Averill b826f232c4 Merge branch '3.0' 2018-06-11 12:12:07 +12:00
Robbie Averill 21d8212807 Remove obsolete branch alias 2018-06-11 12:11:43 +12:00
Dylan Wagstaff 192a1c4c2b
Merge pull request #66 from JorisDebonnet/patch-1
Removed still unsupported Invisible reCaptcha from README
2018-04-06 12:17:35 +12:00
JorisDebonnet 0fd7971cd9
Removed still unsupported Invisible reCaptcha from README
UndefinedOffset's plugin actually supports reCAPTCHA v2 (a.k.a. NoCaptcha), same as chillu's, so I merged them into one line.
2018-04-05 08:09:30 +02:00
Ingo Schommer f69ee07ec0
Mentioned new invisible Recaptcha option 2018-01-12 15:31:22 +13:00
Robbie Averill 059191d91e
Merge pull request #63 from creative-commoners/pulls/3.0/upgrade-upgrade-upgrade
FIX Check and make updates for SS4 compatibility
2017-12-20 15:22:53 +13:00
Raissa North 38ee7a76da ENHANCEMENT Check and make updates for SS4 compatability 2017-12-20 10:07:31 +13:00
Raissa North 46fde9a573 Merge branch '2' 2017-12-19 15:43:25 +13:00
Raissa North 79e2958fad Merge branch '2.0' into 2 2017-12-19 15:41:03 +13:00
Raissa North 68ba83dd6d Remove Transifex configuration. Please commit directly to lang files. 2017-12-19 15:39:26 +13:00
Raissa North 4513998f1e Remove PHP 5.3 from Travis builds 2017-12-19 15:38:57 +13:00
Dylan Wagstaff 1dda806f39
Merge pull request #60 from creative-commoners/pulls/3.0/ican
FIX Ensure getIcon returns exposed resource URL for icon
2017-11-03 14:38:09 +13:00
Robbie Averill 47eef922c7 FIX Ensure getIcon returns exposed resource URL for icon 2017-11-03 11:12:20 +13:00
Daniel Hensby 654f45292c
Merge pull request #59 from creative-commoners/pulls/3.0/update-ci-and-vendorise
Vendorise, update Travis config, fix polymorphic relationship
2017-11-02 11:04:14 +00:00
Robbie Averill e38c6c3a87 FIX Update for polymorphic relationship for UserDefinedForm and child Fields 2017-11-02 14:53:20 +13:00
Robbie Averill 88b1b10f61 FIX Vendorise module, update CI to include userforms 2017-11-02 14:42:30 +13:00
Damian Mooyman 0a7b3f768d Merge pull request #58 from creative-commoners/pulls/vendorise-ci
FIX convert CI bootstrap references to new their new locations in vendor
2017-10-05 13:25:46 +13:00
Dylan Wagstaff d09dbdd9f1 FIX convert CI bootstrap references to new their new locations in vendor 2017-10-05 10:07:59 +13:00
Franco Springveldt b4e9b6e5cb Merge pull request #57 from creative-commoners/pulls/3.0/update-docs
DOCS Add namespaces to class references in readme, fix typos
2017-09-05 09:46:30 +12:00
Robbie Averill 779b155ee0 DOCS Add namespaces to class references in readme, fix typos 2017-09-05 09:40:17 +12:00
Franco Springveldt 42c733061d Merge pull request #51 from Firesphere/master
SilverStripe 4 and Namespacing
2017-09-05 09:29:33 +12:00
Robbie Averill 05fcdb6f41 FIX Typo in userforms form field class name 2017-08-28 17:41:04 +12:00
Robbie Averill 680411e2ac FIX Missing syntax opening in readme, and tweaks for pre-emptive userforms compatibility 2017-08-28 13:44:53 +12:00
Robbie Averill a1ea0df540 Add upgrader mapping file 2017-08-28 13:20:19 +12:00
Robbie Averill cef1e24f86 FIX Add versioned to dev requirements 2017-08-28 13:19:39 +12:00
Robbie Averill 37dde110c9 Update usage examples in readme, minor fixes in travis configuration and gitattributes 2017-08-28 13:19:05 +12:00
Robbie Averill c5f0827c2e Add namespaces, separate test classes, add PSR-4 autoloader, update PSR-2 compliance 2017-08-28 13:19:03 +12:00
Robbie Averill a60f592598 Update Travis configuration to be standalone, add codecov.io, update license year 2017-08-28 13:11:49 +12:00
Simon Erkelens b757c3dc6d SilverStripe 4 and Namespacing 2017-08-28 13:11:48 +12:00
Robbie Averill 5f8d2af0d1 Merge branch '2' 2017-08-28 11:20:58 +12:00
Robbie Averill 27da2d6ab4 Add composer alias for 2.1.x-dev 2017-08-28 11:18:51 +12:00
James Ellis ef3e6d45b6 Configurable fieldname (#56)
* Allow for a configurable field name

* Add a test for configurable field name

* Remove extra conditional by storing the field name in a private static

* No need for field_name config removal as it's now specified as a class property
2017-08-24 11:16:19 +01:00
Daniel Hensby d2467ac73a Merge pull request #55 from creative-commoners/pulls/2.0/userforms-compat
FIX Compatibility with userforms 3/4 getCMSFields, remove deprecated getSettings etc use
2017-07-19 15:39:55 +01:00
Robbie Averill 3e7f021de7 FIX Compatibility with userforms 3/4 getCMSFields, remove deprecated getSettings use etc 2017-07-07 16:13:10 +12:00
Robbie Averill 5cc9312a8d Move stub classes to their own files, run PSR-2 linter 2017-07-07 14:23:01 +12:00
Robbie Averill 3d771d8179 Move userforms requirement to travis setup call 2017-07-07 14:08:25 +12:00
Robbie Averill 4cd01b2c82 Add PHP7 + SS3.6 build to Travis configuration 2017-07-07 14:08:12 +12:00
Daniel Hensby a25b052510 Merge pull request #53 from creative-commoners/pulls/travis-php7
Add PHP7 + SS3.6 build to Travis configuration
2017-06-15 16:49:46 +01:00
Robbie Averill 2af878f6fa Move userforms requirement to travis setup call 2017-06-15 13:24:36 +12:00
Robbie Averill 5aa1ca9afb Add PHP7 + SS3.6 build to Travis configuration 2017-06-15 11:36:19 +12:00
Brett Tasker 56ec1f42c6 Update translations 2017-02-09 11:28:52 +13:00
Daniel Hensby e9880ca3e5
Fixing travis setup 2017-01-31 17:45:39 +00:00
Daniel Hensby 3b4f757d6e
Merge branch '1.1' into 2.0 2017-01-31 12:11:24 +00:00
Daniel Hensby b1a8445a75
Merge branch '1.1' into 2.0 2017-01-31 12:09:41 +00:00
Daniel Hensby cc58c0b741
Merge branch '1.0' into 1.1 2017-01-31 12:08:34 +00:00
Daniel Hensby 1b3f3b9c62
Merge branch '0.4' into 1.0 2017-01-31 12:06:32 +00:00
Daniel Hensby 8e61aeb62b
Merge remote-tracking branch 'origin/0.3' into 0.4 2017-01-31 12:04:05 +00:00
Damian Mooyman df6e182dd2 Update translations 2016-11-17 12:39:49 +13:00
Damian Mooyman 344cf73ccf Remove obsolete branch-alias 2016-11-17 10:17:57 +13:00
Damian Mooyman 38d168bd01 Update translations 2016-08-17 11:08:00 +12:00
Will Rossiter 9db26d69f0 Merge pull request #48 from silverstripe/add-helpful-robot-badge
Added Helpful Robot badge
2016-05-22 08:44:21 +12:00
Christopher Pitt 3ca9a32bb2 Added Helpful Robot badge 2016-05-21 17:16:50 +12:00
Ingo Schommer a9545bc842 More accurate class_exists() check
The class "Comment" was actually defined by
phing/filters/StripLineComments.php. Since I have installed
phing globally through composer, its automatically in the include_path.
That's a separate problem, but this is a viable workaround.
2013-12-29 12:01:53 +01:00
Will Rossiter 74b7990058 Branch 1.0 for 3.0.* compatibility 2013-04-05 21:19:05 +13:00
Ingo Schommer 1c841b25e1 Create composer.json 2013-04-04 15:31:00 +03:00
Saophalkun Ponlu 5598dd3517 MINOR: merge changes from trunk at 107280 2010-06-30 19:07:48 +00:00
Will Rossiter 59c70f297c created branch for new version 2010-05-06 03:12:55 +00:00
45 changed files with 916 additions and 474 deletions

1
.gitattributes vendored
View File

@ -4,3 +4,4 @@
/.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

View File

@ -1,9 +0,0 @@
inherit: true
checks:
php:
code_rating: true
duplication: true
filter:
paths: [code/*, tests/*]

View File

@ -1,37 +0,0 @@
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
sudo: false
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
env:
- DB=MYSQL CORE_RELEASE=3.2
matrix:
include:
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3.1
- php: 5.6
env: DB=PGSQL CORE_RELEASE=3.2
allow_failures:
- php: 7.0
before_script:
- composer self-update || true
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
- cd ~/builds/ss
- composer install
- composer require silverstripe/userforms
script:
- vendor/bin/phpunit spamprotection/tests

View File

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

156
README.md
View File

@ -1,7 +1,7 @@
# SpamProtection Module
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-spamprotection.png?branch=master)](http://travis-ci.org/silverstripe/silverstripe-spamprotection)
[![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
@ -13,114 +13,138 @@
## Requirements
SilverStripe 3.0.0 or greater
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
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:
* [Mollom](https://github.com/silverstripe/silverstripe-mollom)
* [Recaptcha](https://github.com/chillu/silverstripe-recaptcha)
* 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/tractorcow/silverstripe-akismet)
* [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 `SpamProtector` interface. More on that below.
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
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*
---
name: spamprotection
---
FormSpamProtectionExtension:
default_spam_protector: MollomSpamProtector
```yaml
---
name: mycustomspamprotection
---
SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension:
default_spam_protector: MollomSpamProtector
```
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
individual `SpamProtector` implementation since they each require a different
```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.
`enableSpamProtection` takes a hash of optional configuration values.
$form->enableSpamProtection(array(
'protector' => 'MathSpamProtector',
'name' => 'Captcha'
));
```php
$form->enableSpamProtection(array(
'protector' => MathSpamProtector::class,
'name' => 'Captcha'
));
```
Options to configure are:
*`protector`* a class name string or class instance which implements
`SpamProtector`. Defaults to your
`FormSpamProtectionExtension.default_spam_protector` value.
* `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 `Catcha`.
*`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 standardized list of
field names. The list of standardized fields to pass to the spam protector are:
* `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
```
title
body
contextUrl
contextTitle
authorName
authorMail
authorUrl
authorIp
authorId
```
## Defining your own `SpamProtector`
Any class that implements `SpamProtector` and the `getFormField()` method can
be set as the spam protector. The `getFormField()` method returns the
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
<?php
class CustomSpamProtector implements SpamProtector {
use CaptchaField;
use SilverStripe\SpamProtection\SpamProtector;
public function getFormField($name = null, $title = null, $value = null) {
// CaptchaField is a imagined class which has some functionality.
// See silverstripe-mollom module for an example.
return new CaptchaField($name, $title, $value);
}
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
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
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:
$form = new Form(..);
```php
use SilverStripe\Forms\Form;
use SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension;
if($form->hasExtension('FormSpamProtectionExtension')) {
$form->enableSpamProtection();
}
$form = new Form(/* .. */);
if ($form->hasExtension(FormSpamProtectionExtension::class)) {
$form->enableSpamProtection();
}
```

View File

@ -1,11 +0,0 @@
<?php
/**
* Default configuration settings for the Spam Protection module.
*
* You should not put your own configuration in here rather use your
* mysite/_config.php file
*
* @package spamprotection
*/
Deprecation::notification_version('1.1', 'spamprotection');

View File

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

View File

@ -1,197 +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
*/
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';
private static $plural_name = 'Spam Protection Fields';
/**
* Fields to include spam detection for
*
* @var array
* @config
*/
private static $check_fields = array(
'EditableEmailField',
'EditableTextField',
'EditableNumericField'
);
/**
* @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->getSetting($mapSetting);
$fieldMapping[$otherField->Name] = $spamField;
}
$protector->setFieldMapping($fieldMapping);
// Generate field
return $protector->getFormField($this->Name, $this->Title, null);
if ($this->formField) {
return $this->formField;
}
/**
* @param FormField $field
* @return self
*/
public function setFormField(FormField $field)
{
$this->formField = $field;
return $this;
// Get protector
$protector = FormSpamProtectionExtension::get_protector();
if (!$protector) {
return false;
}
/**
* Gets the list of all candidate spam detectable fields on this field's form
*
* @return DataList
*/
protected function getCandidateFields()
{
// 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);
// Get list of all configured classes available for spam detection
$types = self::config()->check_fields;
$typesInherit = array();
foreach ($types as $type) {
$subTypes = ClassInfo::subclassesFor($type);
$typesInherit = array_merge($typesInherit, $subTypes);
}
// Generate field
$field = $protector->getFormField($this->Name, $this->Title, null);
// Get all candidates of the above types
return $this
->Parent()
->Fields()
->filter('ClassName', $typesInherit)
->exclude('Title', ''); // Ignore this field and those without titles
$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);
}
public function getFieldConfiguration()
{
$fields = parent::getFieldConfiguration();
// 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
}
// Get protector
$protector = FormSpamProtectionExtension::get_protector();
if (!$protector) {
return $fields;
/**
* 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));
if ($this->Parent()->Fields() instanceof UnsavedRelationList) {
return $fields;
}
return parent::onBeforeWrite();
}
// Each other text field in this group can be assigned a field mapping
$mapGroup = FieldGroup::create(_t(
'EditableSpamProtectionField.SPAMFIELDMAPPING',
'Spam Field Mapping'
))->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(
$this->getSettingName($mapSetting),
$otherField->Title,
$mappableFieldsMerged,
$this->getSetting($mapSetting)
)->setEmptyString('');
$mapGroup->push($fieldOption);
}
$fields->insertBefore($mapGroup, $this->getSettingName('ExtraClass'));
/**
* 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;
}
/**
* 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 ($this->Parent()->Fields() instanceof UnsavedRelationList) {
return $fields;
}
if (isset($data[$this->Name])) {
$formField->setValue($data[$this->Name]);
}
// 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'
));
$validator = $form->getValidator();
if (!$formField->validate($validator)) {
$errors = $validator->getErrors();
$foundError = false;
// 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);
// 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;
}
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->addErrorMessage($this->Name, $foundError['message'], $foundError['messageType'], false);
} else {
// fallback to custom message set in CMS or default message if none set
$form->addErrorMessage($this->Name, $this->getErrorMessage()->HTML(), 'error', false);
}
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 new FieldList();
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 '';
}
public function getRequired()
{
return false;
}
return $resource->getURL();
}
public function getIcon()
{
return 'spamprotection/images/' . strtolower($this->class) . '.png';
}
public function showInReports()
{
return false;
}
public function showInReports()
{
return false;
}
}

View File

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

View File

@ -1,7 +1,14 @@
<?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
* An extension to the {@link Form} class which provides the method
* {@link enableSpamProtection()} helper.
*
* @package spamprotection
@ -9,6 +16,8 @@
class FormSpamProtectionExtension extends Extension
{
use Configurable;
/**
* @config
*
@ -22,7 +31,7 @@ class FormSpamProtectionExtension extends Extension
/**
* @config
*
* The {@link enableSpamProtection} method will define which of the form
* 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
*
@ -40,12 +49,21 @@ class FormSpamProtectionExtension extends Extension
'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
* @return SpamProtector|null
*/
public static function get_protector($options = null)
{
@ -53,10 +71,10 @@ class FormSpamProtectionExtension extends Extension
if (isset($options['protector'])) {
$protector = $options['protector'];
} else {
$protector = Config::inst()->get('FormSpamProtectionExtension', 'default_spam_protector');
$protector = self::config()->get('default_spam_protector');
}
if ($protector && class_exists($protector)) {
if ($protector && class_exists($protector ?? '')) {
return Injector::inst()->create($protector);
} else {
return null;
@ -67,15 +85,17 @@ class FormSpamProtectionExtension extends Extension
* 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 = 'Captcha';
$name = $this->config()->get('field_name');
}
// captcha field title
@ -88,7 +108,11 @@ class FormSpamProtectionExtension extends Extension
// set custom mapping on this form
$protector = self::get_protector($options);
if (isset($options['mapping'])) {
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']);
}
@ -96,11 +120,11 @@ class FormSpamProtectionExtension extends Extension
// 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($field, $options['insertBefore']);
$inserted = $this->owner->Fields()->insertBefore($options['insertBefore'], $field);
}
if (!$inserted) {
// Add field to end if not added already
@ -108,7 +132,7 @@ class FormSpamProtectionExtension extends Extension
}
}
}
return $this->owner;
}
}

View File

@ -1,14 +1,18 @@
<?php
namespace SilverStripe\SpamProtection;
use SilverStripe\Forms\FormField;
/**
* SpamProtector base interface.
* 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
*/
@ -17,9 +21,9 @@ interface SpamProtector
/**
* Return the {@link FormField} associated with this protector.
*
* Most spam methods will simply return a piece of HTML to be injected at
* 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
* 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

View File

@ -1,41 +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

@ -1,25 +1,43 @@
{
"name": "silverstripe/spamprotection",
"description": "Spam protection module for SilverStripe.",
"type": "silverstripe-module",
"keywords": ["silverstripe", "spamprotection"],
"authors": [{
"name": "Saophalkun Ponlu",
"email": "phalkunz@silverstripe.com"
}, {
"name": "Will Rossiter",
"email": "will@fullscreen.io"
}],
"require": {
"silverstripe/framework": "~3.1"
},
"require-dev": {
"phpunit/PHPUnit": "~3.7@stable"
},
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"license": "BSD-3-Clause"
}
"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
}

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'

View File

@ -1,6 +1,9 @@
de:
EditableSpamProtectionField:
PLURALNAME: 'Spamschutzfelder'
SINGULARNAME: 'Spamschutzfeld'
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'

View File

@ -1,6 +1,10 @@
en:
EditableSpamProtectionField:
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'

View File

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

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: 'Выберите поля формы, которые соответствуют любому индентификатору защиты от спама'

View File

@ -1,6 +1,12 @@
sk:
EditableSpamProtectionField:
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'

View File

@ -1,4 +1,4 @@
Copyright (c) 2016, SilverStripe Limited
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:

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

@ -1,26 +1,41 @@
<?php
class EditableSpamProtectionFieldTest extends SapphireTest
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;
public function setUp()
protected function setUp(): void
{
parent::setUp();
Config::inst()->update(
'FormSpamProtectionExtension', 'default_spam_protector',
'EditableSpamProtectionFieldTest_Protector'
if (!class_exists(EditableSpamProtectionField::class)) {
$this->markTestSkipped('"userforms" module not installed');
}
Config::modify()->set(
FormSpamProtectionExtension::class,
'default_spam_protector',
Protector::class
);
}
public function testValidateFieldDoesntAddErrorOnSuccess()
{
if (!class_exists('EditableSpamProtectionField')) {
$this->markTestSkipped('"userforms" module not installed');
}
$formMock = $this->getFormMock();
$formFieldMock = $this->getEditableFormFieldMock();
@ -32,17 +47,13 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$formMock
->expects($this->never())
->method('addErrorMessage');
->method('sessionMessage');
$formFieldMock->validateField(array('MyField' => null), $formMock);
}
public function testValidateFieldAddsErrorFromField()
{
if (!class_exists('EditableSpamProtectionField')) {
$this->markTestSkipped('"userforms" module not installed');
}
$formMock = $this->getFormMock();
$formFieldMock = $this->getEditableFormFieldMock();
@ -56,18 +67,14 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$formMock
->expects($this->once())
->method('addErrorMessage')
->with($this->anything(), $this->stringContains('some field message'), $this->anything(), $this->anything());;
->method('sessionMessage')
->with($this->stringContains('some field message'), $this->anything());
$formFieldMock->validateField(array('MyField' => null), $formMock);
}
public function testValidateFieldAddsDefaultError()
{
if (!class_exists('EditableSpamProtectionField')) {
$this->markTestSkipped('"userforms" module not installed');
}
$formMock = $this->getFormMock();
$formFieldMock = $this->getEditableFormFieldMock();
@ -81,17 +88,45 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$formMock
->expects($this->once())
->method('addErrorMessage')
->with($this->anything(), $this->stringContains('default error message'), $this->anything(), $this->anything());
->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', array('addErrorMessage'))
$formMock = $this->getMockBuilder(Form::class)
->setMethods(['sessionMessage', 'sessionError', 'getValidator'])
->disableOriginalConstructor()
->getMock();
$formMock
->expects($this->any())
->method('getValidator')
@ -105,12 +140,13 @@ class EditableSpamProtectionFieldTest extends SapphireTest
$page = new UserDefinedForm();
$page->write();
$formFieldMock = $this->getMockBuilder('TextField')
$formFieldMock = $this->getMockBuilder(TextField::class)
->disableOriginalConstructor()
->getMock();
$editableFormFieldMock = new EditableSpamProtectionField(array(
'ParentID' => $page->ID,
'ParentClass' => get_class($page),
'Name' => 'MyField',
'CustomErrorMessage' => 'default error message'
));
@ -120,16 +156,23 @@ class EditableSpamProtectionFieldTest extends SapphireTest
return $editableFormFieldMock;
}
}
class EditableSpamProtectionFieldTest_Protector implements SpamProtector, TestOnly
{
public function getFormField($name = null, $title = null, $value = null)
{
return new TextField($name, 'Foo', $value);
}
public function setFieldMapping($fieldMapping)
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

@ -1,5 +1,19 @@
<?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
*/
@ -11,25 +25,35 @@ class FormSpamProtectionExtensionTest extends SapphireTest
* @var Form
*/
protected $form = null;
public function setUp()
protected function setUp(): void
{
parent::setUp();
$this->form = new Form($this, 'Form', new FieldList(
$this->form = new Form(new Controller, 'Form', new FieldList(
new TextField('Title'),
new TextField('Comment'),
new TextField('URL')
), new FieldList()
);
), 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::inst()->update(
'FormSpamProtectionExtension', 'default_spam_protector',
'FormSpamProtectionExtensionTest_FooProtector'
Config::modify()->set(
FormSpamProtectionExtension::class,
'default_spam_protector',
FooProtector::class
);
$form = $this->form->enableSpamProtection();
@ -40,7 +64,7 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testEnableSpamProtectionCustomProtector()
{
$form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_BarProtector'
'protector' => BarProtector::class
));
$this->assertEquals('Bar', $form->Fields()->fieldByName('Captcha')->Title());
@ -49,45 +73,65 @@ class FormSpamProtectionExtensionTest extends SapphireTest
public function testEnableSpamProtectionCustomTitle()
{
$form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_BarProtector',
'protector' => BarProtector::class,
'title' => 'Baz',
));
$this->assertEquals('Baz', $form->Fields()->fieldByName('Captcha')->Title());
}
public function testCustomOptions()
{
$form = $this->form->enableSpamProtection(array(
'protector' => 'FormSpamProtectionExtensionTest_BazProtector',
'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' => 'FormSpamProtectionExtensionTest_FooProtector',
'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' => 'FormSpamProtectionExtensionTest_FooProtector',
'protector' => FooProtector::class,
'insertBefore' => 'NotAField'
));
// field should default to the end instead
$fields = $form->Fields();
$this->assertEquals('Title', $fields[0]->Title());
@ -96,49 +140,3 @@ class FormSpamProtectionExtensionTest extends SapphireTest
$this->assertEquals('Foo', $fields[3]->Title());
}
}
/**
* @package spamprotection
*/
class FormSpamProtectionExtensionTest_BazProtector implements SpamProtector, TestOnly
{
public function getFormField($name = null, $title = null, $value = null)
{
return new TextField($name, $title, $value);
}
public function setFieldMapping($fieldMapping)
{
}
}
/**
* @package spamprotection
*/
class FormSpamProtectionExtensionTest_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)
{
}
}
/**
* @package spamprotection
*/
class FormSpamProtectionExtensionTest_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\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)
{
}
}