Skip to content

Commit

Permalink
Merge branch 'v0-1' into v0-2
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesDPC committed Oct 27, 2022
2 parents a304f21 + 2918b91 commit 69e438b
Show file tree
Hide file tree
Showing 10 changed files with 553 additions and 14 deletions.
6 changes: 6 additions & 0 deletions _config/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
Name: nswdpc_recaptchav3_userforms
---
SilverStripe\UserForms\Control\UserDefinedFormController:
extensions:
- 'NSWDPC\SpamProtection\UserDefinedFormControllerExtension'
12 changes: 11 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@
"silverstripe/userforms" : "^5"
},
"require-dev": {
"phpunit/phpunit": "^5.7 | ^7",
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "^3.0"
},
"autoload": {
"psr-4": {
"NSWDPC\\SpamProtection\\Tests\\": [
"tests/"
],
"NSWDPC\\SpamProtection\\": [
"src/"
]
}
}
}
26 changes: 24 additions & 2 deletions docs/en/001_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@ reCAPTCHA returns a score between 0 and 1 based on visitor interaction with the
+ If you set the threshold to 100, all submissions on the form will be blocked
+ If you set the threshold to zero, all submissions will be allowed

### Action
### Custom action

This is a value used for analytics purposes in the [reCAPTCHA admin](https://www.google.com/recaptcha/admin/).
This is a value used for analytics purposes in the [reCAPTCHA admin](https://www.google.com/recaptcha/admin/). You can use this to track scores per-form.

### Including the reCAPTCHAv3 verification value in email

Before v0.1.2, the reCAPTCHAv3 verification information for the submitted form was included in all emails:

```json
{"score":0.9,"hostname":"my.site","action":"\/some\/action"}
```

From v0.1.2, this is conditionally **excluded** by default for all emails recipients.

The reCAPTCHAv3 action feature provides the ability to report per-action analytics (Set a custom action).

Use the 'Include reCAPTCHAv3 verification information in emails' checkbox in the field settings to enable the value in emails.

Submitted reCAPTCHAv3 verification values are always saved and can be viewed in the stored submissions, if enabled.

Due to the way the userforms module includes email field data and merge fields, the reCAPTCHAv3 verification information value cannot currently be included on a per-recipient basis.

> You are using > v0.1.2 if you can see the 'Include reCAPTCHAv3 verification information in emails' checkbox
If you wish to retain the verification value in emails for historical fields, ask an administrator to run the 'IncludeInEmailsTask'.
36 changes: 36 additions & 0 deletions src/Extensions/UserDefinedFormControllerExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace NSWDPC\SpamProtection;

use SilverStripe\Core\Extension;
use SilverStripe\ORM\ArrayList;
use SilverStripe\Control\Email\Email;
use SilverStripe\UserForms\Model\Recipient\EmailRecipient;

/**
* Extension to handle email modification
* @author James
*/
class UserDefinedFormControllerExtension extends Extension {

/**
* Modify email data to take into account whether the reCAPTCHA value
* is to be included in the list of fields added to the email for all
* recipients
*/
public function updateEmailData(&$emailData, $attachments) {

if(!isset($emailData['Fields']) || !($emailData['Fields'] instanceof ArrayList)) {
// invalid field data
return;
}

foreach($emailData['Fields'] as $field) {
if( ($field instanceof SubmittedRecaptchaV3Field) && (!$field->getIncludeValueInEmails()) ) {
$emailData['Fields']->remove( $field );
}
}

}

}
50 changes: 39 additions & 11 deletions src/Models/EditableRecaptchaV3Field.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?php
namespace NSWDPC\SpamProtection;

use SilverStripe\Forms\CheckBoxField;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\HeaderField;
use SilverStripe\UserForms\Model\EditableFormField;
use SilverStripe\Control\Controller;

Expand All @@ -26,7 +27,8 @@ class EditableRecaptchaV3Field extends EditableFormField
*/
private static $db = [
'Score' => 'Int',// 0-100
'Action' => 'Varchar(255)'// custom action
'Action' => 'Varchar(255)',// custom action
'IncludeInEmails' => 'Boolean'
];

/**
Expand All @@ -35,6 +37,7 @@ class EditableRecaptchaV3Field extends EditableFormField
*/
private static $defaults = [
'Action' => 'submit',
'IncludeInEmails' => 0
];

/**
Expand All @@ -43,6 +46,28 @@ class EditableRecaptchaV3Field extends EditableFormField
*/
private static $table_name = 'EditableRecaptchaV3Field';

/**
* The reCAPTCHA verification value is always stored
* Use the IncludeInEmails value to determine whether the reCAPTCHA value is included in emails
* along with being saved to the submitted field
* @inheritdoc
*/
public function showInReports()
{
return true;
}

/**
* Return the submitted field instance, with the IncludeInEmails value set as a boolean property
* @inheritdoc
*/
public function getSubmittedFormField()
{
$field = SubmittedRecaptchaV3Field::create();
$field->setIncludeValueInEmails( $this->IncludeInEmails == 1 );
return $field;
}

/**
* Event handler called before writing to the database.
*/
Expand Down Expand Up @@ -124,15 +149,18 @@ public function getCMSFields()
$this->Action = $this->config()->get('defaults')['Action'];
}

$fields->addFieldsToTab(
"Root.Main", [
HeaderField::create(
'reCAPTCHAv3Header',
_t( 'NSWDPC\SpamProtection.RECAPTCHA_SETTINGS', 'reCAPTCHA v3 settings')
),
$range_field,
RecaptchaV3SpamProtector::getActionField('Action', $this->Action)
]
$fields->addFieldToTab(
"Root.Main",
CompositeField::create(
$range_field,
RecaptchaV3SpamProtector::getActionField('Action', $this->Action),
CheckboxField::create(
'IncludeInEmails',
_t( 'NSWDPC\SpamProtection.INCLUDE_CAPTCHA_RESULT_IN_EMAILS', 'Include captcha result in recipient emails')
)
)->setTitle(
_t( 'NSWDPC\SpamProtection.RECAPTCHA_SETTINGS', 'reCAPTCHA v3 settings')
)
);
return $fields;
}
Expand Down
33 changes: 33 additions & 0 deletions src/Models/SubmittedRecaptchaV3Field.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
namespace NSWDPC\SpamProtection;

use SilverStripe\UserForms\Model\Submission\SubmittedFormField;

/**
* SubmittedRecaptchaV3Field
* Used to allow determination of whether a value is a recaptcha score
* @author James
*/
class SubmittedRecaptchaV3Field extends SubmittedFormField
{

/**
* @var bool
*/
protected $includeValueInEmails = false;

/**
* Setter
*/
public function setIncludeValueInEmails(bool $include) {
$this->includeValueInEmails = $include;
return $this;
}

/**
* Getter
*/
public function getIncludeValueInEmails() : bool {
return $this->includeValueInEmails;
}
}
96 changes: 96 additions & 0 deletions src/Tasks/IncludeInEmailsTask.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
namespace NSWDPC\SpamProtection;

use Silverstripe\Core\Config\Config;
use SilverStripe\Dev\BuildTask;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\ORM\DB;

/**
* Update the IncludeInEmails value for historical field values
*
* This is a manual migration you can optionally run if you wish to retain
* Recaptchav3 verification values in emails
*
* Provide a before value to update fields created before that datetime eg. 2022-01-01
*
* This task will be removed or disabled in future releases
*
* @author James
*/
class IncludeInEmailsTask extends BuildTask
{

/**
* @inheritdoc
*/
protected $title = "reCAPTCHAv3 include in emails task for historical fields";

/**
* @inheritdoc
*/
protected $description = 'Retain the reCAPTCHAv3 verification values in emails sent. If you do not need the values in emails, do not run this task.';

/**
* @var string
*/
private static $segment = 'RecaptchaV3IncludeInEmailsTask';

/**
* @var string
*/
public function run($request) {

$before = $request->getVar('before');
$publish = $request->getVar('publish') == 1;
$commit = $request->getVar('commit') == 1;

if(!$commit) {
DB::alteration_message("Pass commit=1 to make changes", "info");
}
if(!$publish) {
DB::alteration_message("Pass publish=1 to publish changes", "info");
}

if(!$before) {
DB::alteration_message("You must provide a date/time as the 'before' param, to update all fields before that date/time. The value is anything understood by DateTime", "error");
return;
}

try {
$dt = new \DateTime($before);
$beforeFormatted = $dt->format('Y-m-d');
} catch(\Exception $e) {
DB::alteration_message("Could not understand the before value '{$before}'", "error");
return;
}

$fields = EditableRecaptchaV3Field::get()->filter([
'Created:LessThan' => $beforeFormatted
]);

if($fields->count() == 0) {
DB::alteration_message("No fields found to change before {$beforeFormatted}", "noop");
return;
}

foreach($fields as $field) {
try {
if($commit) {
$field->IncludeInEmails = 1;
$field->write();
DB::alteration_message("Changed field #{$field->ID} '{$field->Title}', created:{$field->Created}", "changed");
if($publish) {
$field->doPublish();
DB::alteration_message("Published field #{$field->ID} '{$field->Title}', created:{$field->Created}", "changed");
}
} else {
DB::alteration_message("Would have changed field #{$field->ID} '{$field->Title}', created:{$field->Created}", "info");
}
} catch (\Exception $e) {
DB::alteration_message("Failed to change field #{$field->ID} '{$field->Title}', created:{$field->Created}. Error:{$e->getMessage()}", "error");
}
}
}

}
Loading

0 comments on commit 69e438b

Please sign in to comment.