Skip to content

Commit

Permalink
Merge pull request #50 from wilr/4
Browse files Browse the repository at this point in the history
Upgrade to Silverstripe 5
  • Loading branch information
wilr authored Aug 5, 2024
2 parents 2335bea + cf0aa17 commit 9f00f2c
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 79 deletions.
3 changes: 1 addition & 2 deletions .upgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ mappings:
AkismetConfig: SilverStripe\Akismet\Config\AkismetConfig
AkismetProcessor: SilverStripe\Akismet\Config\AkismetProcessor
AkismetService: SilverStripe\Akismet\Service\AkismetService
AkismetServiceBackend: SilverStripe\Akismet\Service\AkismetServiceBackend
AkismetField: SilverStripe\Akismet\AkismetField
AkismetSpamProtector: SilverStripe\Akismet\AkismetSpamProtector
AkismetSpamProtector: SilverStripe\Akismet\AkismetSpamProtector
2 changes: 0 additions & 2 deletions _config/akismet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ SilverStripe\SpamProtection\Extension\FormSpamProtectionExtension:
default_spam_protector: SilverStripe\Akismet\AkismetSpamProtector

SilverStripe\Core\Injector\Injector:
SilverStripe\Akismet\Service\AkismetService:
class: SilverStripe\Akismet\Service\AkismetServiceBackend
SilverStripe\Control\Director:
properties:
Middlewares:
Expand Down
9 changes: 3 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@
"issues": "https://github.com/silverstripe/silverstripe-akismet/issues"
},
"require": {
"php": "^7.4 || ^8.0",
"silverstripe/framework": "^4.10",
"silverstripe/cms": "^4.0",
"tijsverkoyen/akismet": "1.1.0",
"silverstripe/spamprotection": "^3.0"
"silverstripe/framework": "^5",
"silverstripe/spamprotection": "^4"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
Expand All @@ -37,4 +34,4 @@
"extra": [],
"minimum-stability": "dev",
"prefer-stable": true
}
}
15 changes: 9 additions & 6 deletions src/AkismetField.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class AkismetField extends FormField
/**
* @var array
*/
private $fieldMapping = array();
private $fieldMapping = [];

/**
*
Expand Down Expand Up @@ -67,15 +67,17 @@ protected function confirmationField()
->setForm($this->getForm());
}

public function Field($properties = array())

public function Field($properties = [])
{
$checkbox = $this->confirmationField();
if ($checkbox) {
return $checkbox->Field($properties);
}
}

public function FieldHolder($properties = array())

public function FieldHolder($properties = [])
{
$checkbox = $this->confirmationField();
if ($checkbox) {
Expand All @@ -92,7 +94,7 @@ public function getSpamMappedData()
return null;
}

$result = array();
$result = [];
$data = $this->form->getData();

foreach ($this->fieldMapping as $fieldName => $mappedName) {
Expand Down Expand Up @@ -143,11 +145,12 @@ public function validate($validator)
if (Config::inst()->get(AkismetSpamProtector::class, 'save_spam')) {
// In order to save spam but still display the spam message, we must mock a form message
// without failing the validation
$errors = array(array(
$errors = [[
'fieldName' => $this->name,
'message' => $errorMessage,
'messageType' => 'error',
));
]];

$formName = $this->getForm()->FormName();

$this->getForm()->sessionMessage($errorMessage, ValidationResult::TYPE_GOOD);
Expand Down
18 changes: 9 additions & 9 deletions src/AkismetSpamProtector.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ class AkismetSpamProtector implements SpamProtector
* @config
*/
private static $save_spam = false;

/**
* @var array
*/
private $fieldMapping = array();
private $fieldMapping = [];

/**
* Set the API key
Expand All @@ -87,7 +87,7 @@ public function setApiKey($key)
$this->apiKey = $key;
return $this;
}

/**
* Get the API key. Priority is given first to explicitly set values on a singleton, then to configuration values
* and finally to environment values.
Expand All @@ -106,15 +106,15 @@ public function getApiKey()
if (!empty($key)) {
return $key;
}

// Check environment as last resort
if ($envApiKey = Environment::getEnv('SS_AKISMET_API_KEY')) {
return $envApiKey;
}

return '';
}

/**
* Retrieves Akismet API object, or null if not configured
*
Expand All @@ -124,16 +124,16 @@ public function getService()
{
// Get API key and URL
$key = $this->getApiKey();

if (empty($key)) {
user_error("AkismetSpamProtector is incorrectly configured. Please specify an API key.", E_USER_WARNING);
return null;
}
$url = Director::protocolAndHost();


// Generate API object
return Injector::inst()->get(AkismetService::class, false, array($key, $url));
return Injector::inst()->get(AkismetService::class, false, [$key]);
}

public function getFormField($name = null, $title = null, $value = null, $form = null, $rightTitle = null)
{
return AkismetField::create($name, $title, $value, $form, $rightTitle)
Expand Down
4 changes: 2 additions & 2 deletions src/Config/AkismetConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
*/
class AkismetConfig extends DataExtension
{
private static $db = array(
private static $db = [
'AkismetKey' => 'Varchar'
);
];

public function updateCMSFields(FieldList $fields)
{
Expand Down
171 changes: 136 additions & 35 deletions src/Service/AkismetService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,141 @@

namespace SilverStripe\Akismet\Service;

/**
* Describes TijsVerkoyen\Akismet\Akismet
*/
interface AkismetService
use SilverStripe\Control\Director;
use Exception;

class AkismetService
{
/**
* Check if the comment is spam or not
* This is basically the core of everything. This call takes a number of
* arguments and characteristics about the submitted content and then
* returns a thumbs up or thumbs down.
* Almost everything is optional, but performance can drop dramatically if
* you exclude certain elements.
* REMARK: If you are having trouble triggering you can send
* "viagra-test-123" as the author and it will trigger a true response,
* always.
*
* @param string[optional] $content The content that was submitted.
* @param string[optional] $author The name.
* @param string[optional] $email The email address.
* @param string[optional] $url The URL.
* @param string[optional] $permalink The permanent location of the entry
* the comment was submitted to.
* @param string[optional] $type The type, can be blank, comment,
* trackback, pingback, or a made up
* value like "registration".
* @return bool If the comment is spam true will be
* returned, otherwise false.
*/
public function isSpam(
$content,
$author = null,
$email = null,
$url = null,
$permalink = null,
$type = null
);
private $apiKey;

private $endpoint;

public function __construct($apiKey)
{
$this->apiKey = $apiKey;
$this->endpoint = sprintf('https://%s.rest.akismet.com/1.1/', $apiKey);
}


public function verifyKey()
{
$response = $this->post($this->endpoint . 'verify-key', [
'key' => $this->apiKey,
'blog' => Director::protocolAndHost()
]);

return 'valid' == trim(strtolower($response['body']));
}


public function buildData($content, $author = null, $email = null, $url = null, $permalink = null, $server = [])
{
$data = [
'blog' => Director::protocolAndHost(),
'user_ip' => (isset($server['REMOTE_ADDR'])) ? $server['REMOTE_ADDR'] : '',
'user_agent' => (isset($server['HTTP_USER_AGENT'])) ? $server['HTTP_USER_AGENT'] : '',
'referrer' => (isset($server['HTTP_REFERER'])) ? $server['HTTP_REFERER'] : '',
'permalink' => $permalink,
'comment_type' => 'comment',
'comment_author' => $author,
'comment_author_email' => $email,
'comment_author_url' => $url,
'comment_content' => $content,
];

return $data;
}


public function isSpam($content, $author = null, $email = null, $url = null, $permalink = null, $server = null)
{
if (is_null($server)) {
$server = $_SERVER;
}

$data = $this->buildData($content, $author, $email, $url, $permalink, $server);
$response = $this->checkSpam($data, $server);

return (isset($response['spam']) && $response['spam']);
}


public function submitSpam($content, $author = null, $email = null, $url = null, $permalink = null, $server = null)
{
if (is_null($server)) {
$server = $_SERVER;
}

$data = $this->buildData($content, $author, $email, $url, $permalink);
$this->post($this->endpoint . 'submit-spam', $data);
}


public function checkSpam($data, $state = [])
{
$keys = array_intersect_key($state, array_fill_keys([
'HTTP_HOST', 'HTTP_USER_AGENT', 'HTTP_ACCEPT', 'HTTP_ACCEPT_LANGUAGE', 'HTTP_ACCEPT_ENCODING',
'HTTP_ACCEPT_CHARSET', 'HTTP_KEEP_ALIVE', 'HTTP_REFERER', 'HTTP_CONNECTION', 'HTTP_FORWARDED',
'HTTP_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP',
'REMOTE_ADDR', 'REMOTE_HOST', 'REMOTE_PORT', 'SERVER_PROTOCOL', 'REQUEST_METHOD'],
0
));

$data = array_merge($keys, $data);
$response = $this->post($this->endpoint . 'comment-check', $data);
$response['error'] = $response['discard'] = $response['spam'] = null;

$body = trim(strtolower($response['body']));

if ('true' == $body) {
$response['spam'] = true;

if ( array_key_exists('x-akismet-pro-tip', $response['akismet_headers']) && $response['akismet_headers']['x-akismet-pro-tip'] == 'discard' ) {
$response['discard'] = true;
}
} else if ('false' == $body) {
$response['spam'] = false;
} else if (array_key_exists('x-akismet-debug-help', $response['akismet_headers'])) {
$response['error'] = $response['akismet_headers']['x-akismet-debug-help'];
}

return $response;
}

protected function post($endpoint, $data)
{
$response = [];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 6);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
$curl_response = curl_exec($ch);

if (false === $curl_response) {
throw new Exception('There was an error sending the Akismet request.');
}

$response['info'] = curl_getinfo($ch);
$response['info']['request_header'] .= http_build_query($data);
$response['header'] = substr($curl_response, 0, $response['info']['header_size']);
$response['body'] = substr($curl_response, $response['info']['header_size']);

$response['akismet_headers'] = [];

foreach (explode("\n", $response['header']) as $header) {
if (stripos($header, 'x-akismet') === 0) {
list($key, $value) = explode(':', $header, 2);
$response['akismet_headers'][strtolower($key)] = $value;
}
}

curl_close($ch);

return $response;
}
}
12 changes: 0 additions & 12 deletions src/Service/AkismetServiceBackend.php

This file was deleted.

6 changes: 3 additions & 3 deletions tests/AkismetTestService.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
use SilverStripe\Dev\TestOnly;
use SilverStripe\Akismet\Service\AkismetService;

class AkismetTestService implements TestOnly, AkismetService
class AkismetTestService extends AkismetService implements TestOnly
{
public function __construct($apiKey, $url)
public function __construct($apiKey)
{
if ($apiKey !== 'dummykey') {
throw new Exception("Invalid key");
}
}

public function isSpam($content, $author = null, $email = null, $url = null, $permalink = null, $type = null)
{
// This dummy service only checks the content
Expand Down
4 changes: 2 additions & 2 deletions tests/AkismetTestSubmission.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

class AkismetTestSubmission extends DataObject implements TestOnly
{
private static $db = array(
private static $db = [
'Name' => 'Varchar',
'Email' => 'Varchar',
'Content' => 'Text',
'IsSpam' => 'Boolean',
);
];

private static $default_sort = 'ID';
}

0 comments on commit 9f00f2c

Please sign in to comment.