From 7b411af8ac3be41aae36781a71a1fb3b8123cbfb Mon Sep 17 00:00:00 2001 From: helpfulrobot Date: Sun, 20 Dec 2015 17:30:47 +1300 Subject: [PATCH] Converted to PSR-2 --- code/MollomSpamProtector.php | 362 +++++++++--------- code/formfields/MollomField.php | 646 +++++++++++++++++--------------- 2 files changed, 517 insertions(+), 491 deletions(-) diff --git a/code/MollomSpamProtector.php b/code/MollomSpamProtector.php index c35798f..e42a5a2 100644 --- a/code/MollomSpamProtector.php +++ b/code/MollomSpamProtector.php @@ -10,183 +10,187 @@ * @package mollom */ -class MollomSpamProtector extends Mollom implements SpamProtector { - - /** - * @var array - */ - private $fieldMapping = array(); - - /** - * @var array - */ - public $configurationMap = array( - 'publicKey' => 'public_key', - 'privateKey' => 'private_key', - ); - - /** - * Load configuration for a given variable such as privateKey. Since - * SilverStripe uses YAML conventions, look for those variables - * - * @param string $name - * - * @return mixed - */ - public function loadConfiguration($name) { - return Config::inst()->get('Mollom', $this->configurationMap[$name]); - } - - /** - * Save configuration value for a given variable - * - * @param string $name - * @param mixed $value - * - * @return void - */ - public function saveConfiguration($name, $value) { - return Config::inst()->update('Mollom', $this->configurationMap[$name], $value); - } - - /** - * Delete a configuration value. - * - * SilverStripe does not provide 'delete' as such, but let's just save null - * as the value for the session. - * - * @param string $name - */ - public function deleteConfiguration($name) { - return $this->saveConfiguration($name, null); - } - - /** - * Helper for Mollom to know this current client instance. - * - * @return array - */ - public function getClientInformation() { - $info = new SapphireInfo(); - $useragent = 'SilverStripe/' . $info->Version(); - - $data = array( - 'platformName' => $useragent, - 'platformVersion' => $info->Version(), - 'clientName' => 'MollomPHP', - 'clientVersion' => 'Unknown', - ); - - return $data; - } - - /** - * Send the request to Mollom. Must return the result in the format - * prescribed by the Mollom base class. - * - * @param string $method - * @param string $server - * @param string $path, - * @param string $data - * @param array $headers - */ - protected function request($method, $server, $path, $query = NULL, array $headers = array()) { - // if the user has turned on debug mode in the Config API, change the - // server to the dev version - if(Config::inst()->get('Mollom', 'dev')) { - $server = 'http://' . 'dev.mollom.com' . '/' . Mollom::API_VERSION; - } - else { // Mollom authentication headers should not be sent to the dev endpoint - // CURLOPT_HTTPHEADER expects all headers as values: - // @see http://php.net/manual/function.curl-setopt.php - foreach ($headers as $name => &$value) { - $value = $name . ': ' . $value; - } - } - - $ch = curl_init(); - - // Compose the Mollom endpoint URL. - $url = $server . '/' . $path; - - if (isset($query) && $method == 'GET') { - $url .= '?' . $query; - } - - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - - // Prevent API calls from taking too long. - // Under normal operations, API calls may time out for Mollom users without - // a paid subscription. - curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout); - - if ($method == 'POST') { - curl_setopt($ch, CURLOPT_POST, TRUE); - curl_setopt($ch, CURLOPT_POSTFIELDS, $query); - } - else { - curl_setopt($ch, CURLOPT_HTTPGET, TRUE); - } - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($ch, CURLOPT_HEADER, TRUE); - - // Execute the HTTP request. - if ($raw_response = curl_exec($ch)) { - // Split the response headers from the response body. - list($raw_response_headers, $response_body) = explode("\r\n\r\n", $raw_response, 2); - - // Parse HTTP response headers. - // @see http_parse_headers() - $raw_response_headers = str_replace("\r", '', $raw_response_headers); - $raw_response_headers = explode("\n", $raw_response_headers); - $message = array_shift($raw_response_headers); - $response_headers = array(); - - foreach ($raw_response_headers as $line) { - list($name, $value) = explode(': ', $line, 2); - // Mollom::handleRequest() expects response header names in lowercase. - $response_headers[strtolower($name)] = $value; - } - - $info = curl_getinfo($ch); - $response = array( - 'code' => $info['http_code'], - 'message' => $message, - 'headers' => $response_headers, - 'body' => $response_body, - ); - } - else { - $response = array( - 'code' => curl_errno($ch), - 'message' => curl_error($ch), - 'body' => null - ); - } - - curl_close($ch); - - return (object) $response; - } - - /** - * Return the Field that we will use in this protector. - * - * @param string $name - * @param string $title - * @param mixed $value - * @return MollomField - */ - public function getFormField($name = "MollomField", $title = "Captcha", $value = null) { - $field = new MollomField($name, $title, $value); - $field->setFieldMapping($this->fieldMapping); - return $field; - } - - public function setFieldMapping($fieldMapping) { - $this->fieldMapping = $fieldMapping; - } - +class MollomSpamProtector extends Mollom implements SpamProtector +{ + + /** + * @var array + */ + private $fieldMapping = array(); + + /** + * @var array + */ + public $configurationMap = array( + 'publicKey' => 'public_key', + 'privateKey' => 'private_key', + ); + + /** + * Load configuration for a given variable such as privateKey. Since + * SilverStripe uses YAML conventions, look for those variables + * + * @param string $name + * + * @return mixed + */ + public function loadConfiguration($name) + { + return Config::inst()->get('Mollom', $this->configurationMap[$name]); + } + + /** + * Save configuration value for a given variable + * + * @param string $name + * @param mixed $value + * + * @return void + */ + public function saveConfiguration($name, $value) + { + return Config::inst()->update('Mollom', $this->configurationMap[$name], $value); + } + + /** + * Delete a configuration value. + * + * SilverStripe does not provide 'delete' as such, but let's just save null + * as the value for the session. + * + * @param string $name + */ + public function deleteConfiguration($name) + { + return $this->saveConfiguration($name, null); + } + + /** + * Helper for Mollom to know this current client instance. + * + * @return array + */ + public function getClientInformation() + { + $info = new SapphireInfo(); + $useragent = 'SilverStripe/' . $info->Version(); + + $data = array( + 'platformName' => $useragent, + 'platformVersion' => $info->Version(), + 'clientName' => 'MollomPHP', + 'clientVersion' => 'Unknown', + ); + + return $data; + } + + /** + * Send the request to Mollom. Must return the result in the format + * prescribed by the Mollom base class. + * + * @param string $method + * @param string $server + * @param string $path, + * @param string $data + * @param array $headers + */ + protected function request($method, $server, $path, $query = null, array $headers = array()) + { + // if the user has turned on debug mode in the Config API, change the + // server to the dev version + if (Config::inst()->get('Mollom', 'dev')) { + $server = 'http://' . 'dev.mollom.com' . '/' . Mollom::API_VERSION; + } else { // Mollom authentication headers should not be sent to the dev endpoint + // CURLOPT_HTTPHEADER expects all headers as values: + // @see http://php.net/manual/function.curl-setopt.php + foreach ($headers as $name => &$value) { + $value = $name . ': ' . $value; + } + } + + $ch = curl_init(); + + // Compose the Mollom endpoint URL. + $url = $server . '/' . $path; + + if (isset($query) && $method == 'GET') { + $url .= '?' . $query; + } + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + // Prevent API calls from taking too long. + // Under normal operations, API calls may time out for Mollom users without + // a paid subscription. + curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout); + + if ($method == 'POST') { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $query); + } else { + curl_setopt($ch, CURLOPT_HTTPGET, true); + } + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, true); + + // Execute the HTTP request. + if ($raw_response = curl_exec($ch)) { + // Split the response headers from the response body. + list($raw_response_headers, $response_body) = explode("\r\n\r\n", $raw_response, 2); + + // Parse HTTP response headers. + // @see http_parse_headers() + $raw_response_headers = str_replace("\r", '', $raw_response_headers); + $raw_response_headers = explode("\n", $raw_response_headers); + $message = array_shift($raw_response_headers); + $response_headers = array(); + + foreach ($raw_response_headers as $line) { + list($name, $value) = explode(': ', $line, 2); + // Mollom::handleRequest() expects response header names in lowercase. + $response_headers[strtolower($name)] = $value; + } + + $info = curl_getinfo($ch); + $response = array( + 'code' => $info['http_code'], + 'message' => $message, + 'headers' => $response_headers, + 'body' => $response_body, + ); + } else { + $response = array( + 'code' => curl_errno($ch), + 'message' => curl_error($ch), + 'body' => null + ); + } + + curl_close($ch); + + return (object) $response; + } + + /** + * Return the Field that we will use in this protector. + * + * @param string $name + * @param string $title + * @param mixed $value + * @return MollomField + */ + public function getFormField($name = "MollomField", $title = "Captcha", $value = null) + { + $field = new MollomField($name, $title, $value); + $field->setFieldMapping($this->fieldMapping); + return $field; + } + + public function setFieldMapping($fieldMapping) + { + $this->fieldMapping = $fieldMapping; + } } diff --git a/code/formfields/MollomField.php b/code/formfields/MollomField.php index 4280abc..9301c4d 100644 --- a/code/formfields/MollomField.php +++ b/code/formfields/MollomField.php @@ -9,316 +9,338 @@ * @package mollom */ -class MollomField extends FormField { - - /** - * @var array - */ - private $fieldMapping = array(); - - /** - * @var string - */ - private $captchaType = 'image'; - - /** - * @config - * - * @see setAlwaysShowCaptcha - */ - private static $always_show_captcha = false; - - /** - * @config - * - * @see setForceCheckOnMembers - */ - private static $force_check_on_members = false; - - /** - * @var string - */ - protected $template = 'MollomField'; - - /** - * @return string - */ - public function getCaptchaAudioFile() { - return MOLLOM_DIR . '/mollom-captcha-player.swf'; - } - - /** - * Returns the captcha url if we need to get it from the user - * - * @return string - */ - public function getCaptcha() { - if($this->getShowCaptcha()) { - Requirements::css(MOLLOM_DIR . '/css/mollom.css'); - - $result = $this->getMollom()->createCaptcha(array( - 'type' => $this->getCaptchaType() - )); - - if(is_array($result)) { - Session::set('mollom_session_id', $result['id']); - - return $result['url']; - } - } - - return null; - } - - /** - * Set the captcha type. Mollom supports "image" and "audio" types. - * - * @return void - */ - public function setCaptchaType($Type) { - if (in_array($Type, array('image', 'audio'))) { - $this->captchaType = $Type; - } - } - - /** - * Returns the captcha type that has been set. Default is "image". - * - * @return string - */ - public function getCaptchaType() { - return $this->captchaType; - } - - - - /** - * Determines if the current user is exempt from spam detection - * - * @return boolean True if the user is exempt from spam detection - */ - protected function exemptUser() { - - // Never show captcha for admins - if (Permission::check('ADMIN')) { - return true; - } - - // Allow logged in members to bypass captcha if allowed - if (Member::currentUser() && !Config::inst()->get('MollomField', 'force_check_on_members')) { - return true; - } - - return false; - } - - /** - * Return if we should show the captcha to the user. - * - * @return boolean - */ - public function getShowCaptcha() { - - // If this user is eligible to bypass spam detection, don't show them the recaptcha - if($this->exemptUser()) return false; - - // Show captcha if always requested - if (Config::inst()->get('MollomField', 'always_show_captcha')) return true; - - // If a captcha is requested then we need to redisplay it to the user - if (Session::get('mollom_captcha_requested')) return true; - - // If there are no field mappings, then the captcha is mandatory - return empty($this->fieldMapping); - } - - /** - * Returns the field label if showing captcha - used by templates. - * - * @return string Title if field is showing the captcha - */ - public function Title() { - if ($this->getShowCaptcha()) { - return parent::Title(); - } - } - - /** - * @return MollomSpamProtector - */ - public function getMollom() { - if(!$this->_mollom) { - $this->_mollom = Injector::inst()->create('MollomSpamProtector'); - $this->_mollom ->publicKey = Config::inst()->get('Mollom', 'public_key'); - $this->_mollom ->privateKey = Config::inst()->get('Mollom', 'private_key'); - - if(Config::inst()->get('Mollom', 'dev')) { - $this->_mollom->server = 'dev.mollom.com'; - } - } - - return $this->_mollom; - } - - /** - * @return array - */ - public function getSpamMappedData() { - if(empty($this->fieldMapping)) return null; - - $result = array(); - $data = $this->form->getData(); - - foreach($this->fieldMapping as $fieldName => $mappedName) { - $result[$mappedName] = (isset($data[$fieldName])) ? $data[$fieldName] : null; - } - - return $result; - } - - /** - * Validate the captcha information - * - * @param Validator $validator - * - * @return boolean - */ - public function validate($validator) { - - // Bypass spam detection for eligible users - if($this->exemptUser()) { - $this->clearMollomSession(); - return true; - } - - $session_id = Session::get("mollom_session_id"); - $mapped = $this->getSpamMappedData(); - $data = array(); - - // prepare submission - foreach(array('authorName', 'authorUrl', 'authorMail', 'authorIp', 'authorId') as $k) { - if(isset($mapped[$k])) { - $data[$k] = $mapped[$k]; - } - } - - if($session_id) { - // session ID exists so has been checked by captcha - $data['id'] = $session_id; - $data['solution'] = $this->Value(); - - $result = $this->getMollom()->checkCaptcha($data); - - if(is_array($result)) { - if($result['solved']) { - $this->clearMollomSession(); - - return true; - } else { - $this->requestMollom(); - - $validator->validationError( - $this->name, - _t( - 'MollomCaptchaField.CAPTCHAREQUESTED', - "Please answer the captcha question", - "Mollom Captcha provides words in an image, and expects a user to type them in a textfield" - ), - "warning" - ); - - return false; - } - } - } else { - $contentMap = array( - 'id' => 'id', - 'title' => 'postTitle', - 'body' => 'postBody' - ); - - foreach($contentMap as $k => $v) { - if(isset($mapped[$k])) { - $data[$v] = $mapped[$k]; - } - } - - $result = $this->getMollom()->checkContent($data); - - // Mollom can do much more useful things. - // @todo handle profanityScore, qualityScore, sentimentScore, reason - if(is_array($result)) { - switch($result['spamClassification']) { - case 'ham': - $this->clearMollomSession(); - - return true; - - case 'unsure': - $this->requestMollom(); - - // we're unsure so request the captcha. - $validator->validationError( - $this->name, - _t( - 'MollomCaptchaField.CAPTCHAREQUESTED', - "Please answer the captcha question", - "Mollom Captcha provides words in an image, and expects a user to type them in a textfield" - ), - "warning" - ); - - return false; - - case 'spam': - $this->clearMollomSession(); - $this->requestMollom(); - - $validator->validationError( - $this->name, - _t( - 'MollomCaptchaField.SPAM', - "Your submission has been rejected because it was treated as spam.", - "Mollom Captcha provides words in an image, and expects a user to type them in a textfield" - ), - "error" - ); - - return false; - break; - } - } - } - - return true; - } - - /** - * @return void - */ - private function requestMollom() { - Session::set('mollom_captcha_requested', true); - } - - /** - * Helper to quickly clear all the mollom session settings. For example - * after a successful post. - * - * @return void - */ - private function clearMollomSession() { - Session::clear('mollom_session_id'); - Session::clear('mollom_captcha_requested'); - } - - /** - * Set the fields to map spam protection too - * - * @param array $fieldMapping array of Field Names, where the indexes of the array are - * the field names of the form and the values are the standard spamprotection - * fields used by the protector - */ - public function setFieldMapping($fieldMapping) { - $this->fieldMapping = $fieldMapping; - } +class MollomField extends FormField +{ + + /** + * @var array + */ + private $fieldMapping = array(); + + /** + * @var string + */ + private $captchaType = 'image'; + + /** + * @config + * + * @see setAlwaysShowCaptcha + */ + private static $always_show_captcha = false; + + /** + * @config + * + * @see setForceCheckOnMembers + */ + private static $force_check_on_members = false; + + /** + * @var string + */ + protected $template = 'MollomField'; + + /** + * @return string + */ + public function getCaptchaAudioFile() + { + return MOLLOM_DIR . '/mollom-captcha-player.swf'; + } + + /** + * Returns the captcha url if we need to get it from the user + * + * @return string + */ + public function getCaptcha() + { + if ($this->getShowCaptcha()) { + Requirements::css(MOLLOM_DIR . '/css/mollom.css'); + + $result = $this->getMollom()->createCaptcha(array( + 'type' => $this->getCaptchaType() + )); + + if (is_array($result)) { + Session::set('mollom_session_id', $result['id']); + + return $result['url']; + } + } + + return null; + } + + /** + * Set the captcha type. Mollom supports "image" and "audio" types. + * + * @return void + */ + public function setCaptchaType($Type) + { + if (in_array($Type, array('image', 'audio'))) { + $this->captchaType = $Type; + } + } + + /** + * Returns the captcha type that has been set. Default is "image". + * + * @return string + */ + public function getCaptchaType() + { + return $this->captchaType; + } + + + + /** + * Determines if the current user is exempt from spam detection + * + * @return boolean True if the user is exempt from spam detection + */ + protected function exemptUser() + { + + // Never show captcha for admins + if (Permission::check('ADMIN')) { + return true; + } + + // Allow logged in members to bypass captcha if allowed + if (Member::currentUser() && !Config::inst()->get('MollomField', 'force_check_on_members')) { + return true; + } + + return false; + } + + /** + * Return if we should show the captcha to the user. + * + * @return boolean + */ + public function getShowCaptcha() + { + + // If this user is eligible to bypass spam detection, don't show them the recaptcha + if ($this->exemptUser()) { + return false; + } + + // Show captcha if always requested + if (Config::inst()->get('MollomField', 'always_show_captcha')) { + return true; + } + + // If a captcha is requested then we need to redisplay it to the user + if (Session::get('mollom_captcha_requested')) { + return true; + } + + // If there are no field mappings, then the captcha is mandatory + return empty($this->fieldMapping); + } + + /** + * Returns the field label if showing captcha - used by templates. + * + * @return string Title if field is showing the captcha + */ + public function Title() + { + if ($this->getShowCaptcha()) { + return parent::Title(); + } + } + + /** + * @return MollomSpamProtector + */ + public function getMollom() + { + if (!$this->_mollom) { + $this->_mollom = Injector::inst()->create('MollomSpamProtector'); + $this->_mollom ->publicKey = Config::inst()->get('Mollom', 'public_key'); + $this->_mollom ->privateKey = Config::inst()->get('Mollom', 'private_key'); + + if (Config::inst()->get('Mollom', 'dev')) { + $this->_mollom->server = 'dev.mollom.com'; + } + } + + return $this->_mollom; + } + + /** + * @return array + */ + public function getSpamMappedData() + { + if (empty($this->fieldMapping)) { + return null; + } + + $result = array(); + $data = $this->form->getData(); + + foreach ($this->fieldMapping as $fieldName => $mappedName) { + $result[$mappedName] = (isset($data[$fieldName])) ? $data[$fieldName] : null; + } + + return $result; + } + + /** + * Validate the captcha information + * + * @param Validator $validator + * + * @return boolean + */ + public function validate($validator) + { + + // Bypass spam detection for eligible users + if ($this->exemptUser()) { + $this->clearMollomSession(); + return true; + } + + $session_id = Session::get("mollom_session_id"); + $mapped = $this->getSpamMappedData(); + $data = array(); + + // prepare submission + foreach (array('authorName', 'authorUrl', 'authorMail', 'authorIp', 'authorId') as $k) { + if (isset($mapped[$k])) { + $data[$k] = $mapped[$k]; + } + } + + if ($session_id) { + // session ID exists so has been checked by captcha + $data['id'] = $session_id; + $data['solution'] = $this->Value(); + + $result = $this->getMollom()->checkCaptcha($data); + + if (is_array($result)) { + if ($result['solved']) { + $this->clearMollomSession(); + + return true; + } else { + $this->requestMollom(); + + $validator->validationError( + $this->name, + _t( + 'MollomCaptchaField.CAPTCHAREQUESTED', + "Please answer the captcha question", + "Mollom Captcha provides words in an image, and expects a user to type them in a textfield" + ), + "warning" + ); + + return false; + } + } + } else { + $contentMap = array( + 'id' => 'id', + 'title' => 'postTitle', + 'body' => 'postBody' + ); + + foreach ($contentMap as $k => $v) { + if (isset($mapped[$k])) { + $data[$v] = $mapped[$k]; + } + } + + $result = $this->getMollom()->checkContent($data); + + // Mollom can do much more useful things. + // @todo handle profanityScore, qualityScore, sentimentScore, reason + if (is_array($result)) { + switch ($result['spamClassification']) { + case 'ham': + $this->clearMollomSession(); + + return true; + + case 'unsure': + $this->requestMollom(); + + // we're unsure so request the captcha. + $validator->validationError( + $this->name, + _t( + 'MollomCaptchaField.CAPTCHAREQUESTED', + "Please answer the captcha question", + "Mollom Captcha provides words in an image, and expects a user to type them in a textfield" + ), + "warning" + ); + + return false; + + case 'spam': + $this->clearMollomSession(); + $this->requestMollom(); + + $validator->validationError( + $this->name, + _t( + 'MollomCaptchaField.SPAM', + "Your submission has been rejected because it was treated as spam.", + "Mollom Captcha provides words in an image, and expects a user to type them in a textfield" + ), + "error" + ); + + return false; + break; + } + } + } + + return true; + } + + /** + * @return void + */ + private function requestMollom() + { + Session::set('mollom_captcha_requested', true); + } + + /** + * Helper to quickly clear all the mollom session settings. For example + * after a successful post. + * + * @return void + */ + private function clearMollomSession() + { + Session::clear('mollom_session_id'); + Session::clear('mollom_captcha_requested'); + } + + /** + * Set the fields to map spam protection too + * + * @param array $fieldMapping array of Field Names, where the indexes of the array are + * the field names of the form and the values are the standard spamprotection + * fields used by the protector + */ + public function setFieldMapping($fieldMapping) + { + $this->fieldMapping = $fieldMapping; + } }