1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-26 08:04:38 +02:00

Upgrades and improvements to CommentFilterAkismet class

This commit is contained in:
Ryan Cramer
2019-04-19 09:22:46 -04:00
parent 2a63a44485
commit f41c61e490
3 changed files with 193 additions and 44 deletions

View File

@@ -13,6 +13,7 @@
* @property string $text * @property string $text
* @property int $sort * @property int $sort
* @property int $status * @property int $status
* @property int|null $prevStatus
* @property int $flags * @property int $flags
* @property int $created * @property int $created
* @property string $email * @property string $email
@@ -49,6 +50,12 @@ class Comment extends WireData {
*/ */
const statusApproved = 1; const statusApproved = 1;
/**
* Status for comment that's been approved and featured
*
*/
const statusFeatured = 2;
/** /**
* Status for Comment to indicate pending deletion * Status for Comment to indicate pending deletion
* *
@@ -150,7 +157,7 @@ class Comment extends WireData {
$this->set('website', ''); $this->set('website', '');
$this->set('ip', ''); $this->set('ip', '');
$this->set('user_agent', ''); $this->set('user_agent', '');
$this->set('created_users_id', $this->config->guestUserID); $this->set('created_users_id', $this->config->guestUserPageID);
$this->set('code', ''); // approval code $this->set('code', ''); // approval code
$this->set('subcode', ''); // subscriber code (for later user modifications to comment) $this->set('subcode', ''); // subscriber code (for later user modifications to comment)
$this->set('upvotes', 0); $this->set('upvotes', 0);
@@ -161,8 +168,8 @@ class Comment extends WireData {
public function get($key) { public function get($key) {
if($key == 'user' || $key == 'createdUser') { if($key == 'user' || $key == 'createdUser') {
if(!$this->settings['created_users_id']) return $this->users->get($this->config->guestUserID); if(!$this->created_users_id) return $this->users->get($this->config->guestUserPageID);
return $this->users->get($this->settings['created_users_id']); return $this->users->get($this->created_users_id);
} else if($key == 'gravatar') { } else if($key == 'gravatar') {
return $this->gravatar(); return $this->gravatar();
@@ -187,6 +194,9 @@ class Comment extends WireData {
} else if($key == 'editUrl' || $key == 'editURL') { } else if($key == 'editUrl' || $key == 'editURL') {
return $this->editUrl(); return $this->editUrl();
} else if($key == 'prevStatus') {
return $this->prevStatus;
} }
return parent::get($key); return parent::get($key);
@@ -454,9 +464,12 @@ class Comment extends WireData {
* *
*/ */
public function url($http = false) { public function url($http = false) {
$fragment = "#Comment$this->id"; if($this->page && $this->page->id) {
if(!$this->page || !$this->page->id) return $fragment; $url = $http ? $this->page->httpUrl() : $this->page->url;
return ($http ? $this->page->httpUrl() : $this->page->url) . $fragment; } else {
$url = $http ? $this->wire('config')->urls->httpRoot : $this->wire('config')->urls->root;
}
return $url . "#Comment$this->id";
} }
/** /**

View File

@@ -13,30 +13,54 @@
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer * ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* @property string $appUserAgent
* @property string $charset
* @property string $homeURL @deprecated
* @property string $apiKey
*
* *
*/ */
abstract class CommentFilter extends WireData { abstract class CommentFilter extends WireData {
/**
* @var Comment
*
*/
protected $comment; protected $comment;
public function __construct() { public function __construct() {
$this->set('appUserAgent', 'ProcessWire'); $this->set('appUserAgent', 'ProcessWire');
$this->set('charset', 'utf-8'); $this->set('charset', 'utf-8');
$this->set('homeURL', 'http://' . $this->config->httpHost);
$this->set('apiKey', ''); $this->set('apiKey', '');
} }
public function init() { public function init() {
/** @var Paths $urls */
$urls = $this->wire('config')->urls;
$this->set('homeURL', $urls->httpRoot);
} }
public function setComment(Comment $comment) { public function setComment(Comment $comment) {
$this->comment = $comment; $this->comment = $comment;
$this->set('pageUrl', $this->homeURL . $this->wire('page')->url); $page = $comment->getPage();
if(!$comment->ip) $comment->ip = $_SERVER['REMOTE_ADDR']; if(!$page || !$page->id) $page = $this->wire('page');
$this->set('pageUrl', $page->httpUrl);
if(!$comment->ip) $comment->ip = $this->wire('session')->getIP();
if(!$comment->user_agent) $comment->user_agent = $_SERVER['HTTP_USER_AGENT']; if(!$comment->user_agent) $comment->user_agent = $_SERVER['HTTP_USER_AGENT'];
} }
/**
* Send an HTTP POST request
*
* @param $request
* @param $host
* @param $path
* @param int $port
* @return array|string
* @deprecated no longer in use (replaced with WireHttp)
*
*/
protected function httpPost($request, $host, $path, $port = 80) { protected function httpPost($request, $host, $path, $port = 80) {
// from ksd_http_post() - http://akismet.com/development/api/ // from ksd_http_post() - http://akismet.com/development/api/

View File

@@ -5,7 +5,7 @@
* *
* Implementation of a CommentFilter class specific to the Akismet filtering service. * Implementation of a CommentFilter class specific to the Akismet filtering service.
* *
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer * ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* *
@@ -17,13 +17,19 @@ require_once($dirname . "/CommentFilter.php");
/** /**
* Uses the Akismet service to identify comment spam. Module plugin for the Comments Fieldtype. * Uses the Akismet service to identify comment spam. Module plugin for the Comments Fieldtype.
* *
* @property string $apiKey
* @property string $validKey
* @property Comment $comment
* @property string $pageUrl
*
*
*/ */
class CommentFilterAkismet extends CommentFilter implements Module, ConfigurableModule { class CommentFilterAkismet extends CommentFilter implements Module, ConfigurableModule {
public static function getModuleInfo() { public static function getModuleInfo() {
return array( return array(
'title' => __('Comment Filter: Akismet', __FILE__), 'title' => __('Comment Filter: Akismet', __FILE__),
'version' => 102, 'version' => 200,
'summary' => __('Uses the Akismet service to identify comment spam. Module plugin for the Comments Fieldtype.', __FILE__), 'summary' => __('Uses the Akismet service to identify comment spam. Module plugin for the Comments Fieldtype.', __FILE__),
'permanent' => false, 'permanent' => false,
'singular' => false, 'singular' => false,
@@ -34,61 +40,166 @@ class CommentFilterAkismet extends CommentFilter implements Module, Configurable
public function __construct() { public function __construct() {
parent::__construct(); parent::__construct();
$this->set('appUserAgent', "ProcessWire/2 | AkismetCommentFilter/1"); $this->set('appUserAgent', "ProcessWire/3 | AkismetCommentFilter/2");
$this->set('apiKey', ''); $this->set('apiKey', '');
$this->set('validKey', '');
} }
protected function verifyKey() { protected function akismetHttpPost($action, array $data) {
if(!$this->comment) throw new WireException("No Comment provided to CommentFilter");
if(!$this->apiKey) throw new WireException("apiKey must be set to use this filter"); $http = new WireHttp();
$request = "key={$this->apiKey}&blog=" . urlencode($this->homeURL); $http->setHeader('content-type', "application/x-www-form-urlencoded; charset={$this->charset}");
$response = $this->httpPost($request, 'rest.akismet.com', '/1.1/verify-key'); $http->setHeader('user-agent', $this->appUserAgent);
if($response[1] == 'valid') return true;
if($response[1] == 'invalid') throw new WireException("Invalid Akismet Key $request, " . print_r($response, true)); $response = $http->post('https://' . $this->apiKey . ".rest.akismet.com/1.1/$action", $data);
// some other error
$result = $response;
if($action === 'comment-check') $result = $result === 'true' ? 'SPAM' : 'not spam';
$msg = "$action: $result ";
$comment = $this->comment;
if($comment) {
if($comment->id) $msg .= "comment $comment->id ";
$msg .= "by $comment->email ";
$page = $this->comment->getPage();
if($page && $page->id) $msg .= "on page $page->path";
}
$this->saveLog($msg);
return trim($response);
}
/**
* Verify Akismet API key
*
* @param string $apiKey
* @return bool
*
*/
protected function verifyKey($apiKey = '') {
if(empty($apiKey)) $apiKey = $this->apiKey;
if(empty($apiKey)) {
if(strlen($this->validKey)) $this->wire('modules')->saveConfig($this, 'validKey', '');
return false;
}
if($apiKey === $this->validKey) return true;
$request = array(
'key' => $this->apiKey,
'blog' => $this->wire('config')->urls->httpRoot
);
$response = $this->akismetHttpPost('verify-key', $request);
if($response === 'valid') {
$this->validKey = $apiKey;
$this->wire('modules')->saveConfig($this, array(
'apiKey' => $apiKey,
'validKey' => $apiKey,
));
$msg = "Akismet API key has been validated";
$this->message($msg);
$this->saveLog($msg);
return true;
} else if($response === 'invalid') {
$msg = 'Invalid Akismet API key provided';
if(strlen($this->validKey)) $this->wire('modules')->saveConfig($this, 'validKey', '');
$this->error($msg);
$this->saveLog("$msg: $apiKey");
return false;
}
return false; return false;
} }
protected function buildRequest() { /**
$request = * Build Akismet POST request
"blog=" . urlencode($this->homeURL) . *
"&user_ip={$this->comment->ip}" . * @return array
"&user_agent=" . urlencode($this->comment->user_agent) . * @throws WireException
// "&referrer=" . urlencode($this->referrer) . *
"&permalink=" . urlencode($this->pageUrl) . */
"&comment_type=comment" . public function buildRequest() {
"&comment_author=" . urlencode($this->comment->cite) . return array(
"&comment_author_email=" . urlencode($this->comment->email) . 'blog' => $this->wire('config')->urls->httpRoot,
"&comment_author_url=" . urlencode($this->comment->website) . 'user_ip' => $this->comment->ip,
"&comment_content=" . urlencode($this->comment->text); 'user_agent' => $this->comment->user_agent,
return $request; 'permalink' => $this->comment->httpUrl(),
'comment_type' => 'comment',
'comment_author' => $this->comment->cite,
'comment_author_email' => $this->comment->email,
'comment_author_url' => $this->comment->website,
'comment_content' => $this->comment->text
);
} }
/**
* Check if comment is spam
*
* @return bool
*
*/
public function checkSpam() { public function checkSpam() {
if($this->comment->status == Comment::statusSpam) return true; if($this->comment->status == Comment::statusSpam) return true;
$this->verifyKey(); if(!$this->verifyKey()) return false;
$request = $this->buildRequest(); $request = $this->buildRequest();
$response = $this->httpPost($request, $this->apiKey . ".rest.akismet.com", '/1.1/comment-check'); $response = $this->akismetHttpPost('comment-check', $request);
$isSpam = $response[1] == 'true'; $isSpam = $response === 'true';
$this->setIsSpam($isSpam); $this->setIsSpam($isSpam);
//print_r($response);
return $isSpam; return $isSpam;
} }
/**
* Tell Akismet comment is spam
*
*/
public function submitSpam() { public function submitSpam() {
$this->verifyKey(); if(!$this->verifyKey()) return false;
$request = $this->buildRequest(); $request = $this->buildRequest();
$this->httpPost($request, $this->apiKey . ".rest.akismet.com", '/1.1/submit-spam'); $this->akismetHttpPost('submit-spam', $request);
$this->message("Notified Akismet of spam that it didn't identify"); $this->message("Notified Akismet of spam that it didn't identify");
return true;
} }
/**
* Tell Akismet comment is not spam and they made an error
*
*/
public function submitHam() { public function submitHam() {
$this->verifyKey(); if(!$this->verifyKey()) return false;
$request = $this->buildRequest(); $request = $this->buildRequest();
$this->httpPost($request, $this->apiKey . ".rest.akismet.com", '/1.1/submit-ham'); $this->akismetHttpPost('submit-ham', $request);
$this->message("Notified Akismet of a spam false positive (ham)"); $this->message("Notified Akismet of a spam false positive (ham)");
return true;
} }
/**
* Save log entry
*
* @param string $msg
*
*/
public function saveLog($msg) {
$this->wire('log')->save('comment-filter-akismet', $msg);
}
/**
* Configure module
*
* @param array $data
* @return InputfieldWrapper
*
*/
public function getModuleConfigInputfields(array $data) { public function getModuleConfigInputfields(array $data) {
$inputfields = $this->wire(new InputfieldWrapper()); $inputfields = $this->wire(new InputfieldWrapper());
@@ -102,6 +213,7 @@ class CommentFilterAkismet extends CommentFilter implements Module, Configurable
$f->description = $this->_('If you want to have comments automatically identified as spam, the Comments fieldtype can utilize the Akismet service to do this. In order to use it, you must enter an Akismet API key obtained from akismet.com. Use of this service is optional but recommended.'); // Akismet description $f->description = $this->_('If you want to have comments automatically identified as spam, the Comments fieldtype can utilize the Akismet service to do this. In order to use it, you must enter an Akismet API key obtained from akismet.com. Use of this service is optional but recommended.'); // Akismet description
$inputfields->append($f); $inputfields->append($f);
$this->verifyKey($data[$name]);
return $inputfields; return $inputfields;