From ba052c0cc94a1b1acbe3322e9e3705f71f7134ac Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 7 Jan 2009 07:36:48 +0000 Subject: Extract the Akismet driver from the Spam_Filter module into a module in its own right. Clean up the tests, streamline the code and improve the admin interaction. Add a working stats page. --- modules/akismet/controllers/admin_akismet.php | 69 ++++++++++ modules/akismet/helpers/akismet.php | 142 +++++++++++++++++++++ modules/akismet/helpers/akismet_installer.php | 32 +++++ modules/akismet/helpers/akismet_menu.php | 36 ++++++ modules/akismet/module.info | 3 + modules/akismet/tests/Akismet_Helper_Test.php | 90 +++++++++++++ modules/akismet/views/admin_akismet.html.php | 15 +++ modules/akismet/views/admin_akismet_stats.html.php | 5 + themes/default/css/screen.css | 2 + 9 files changed, 394 insertions(+) create mode 100644 modules/akismet/controllers/admin_akismet.php create mode 100644 modules/akismet/helpers/akismet.php create mode 100644 modules/akismet/helpers/akismet_installer.php create mode 100644 modules/akismet/helpers/akismet_menu.php create mode 100644 modules/akismet/module.info create mode 100644 modules/akismet/tests/Akismet_Helper_Test.php create mode 100644 modules/akismet/views/admin_akismet.html.php create mode 100644 modules/akismet/views/admin_akismet_stats.html.php diff --git a/modules/akismet/controllers/admin_akismet.php b/modules/akismet/controllers/admin_akismet.php new file mode 100644 index 00000000..6d32868f --- /dev/null +++ b/modules/akismet/controllers/admin_akismet.php @@ -0,0 +1,69 @@ +validate(); + + if ($valid) { + $new_key = $form->configure_akismet->api_key->value; + if ($new_key && !akismet::validate_key($new_key)) { + $form->configure_akismet->api_key->add_error("invalid", 1); + $valid = false; + } + } + + if ($valid) { + $old_key = module::get_var("akismet", "api_key"); + if ($old_key && !$new_key) { + message::success(_("Your Akismet key has been cleared.")); + } else if ($old_key && $new_key && $old_key != $new_key) { + message::success(_("Your Akismet key has been changed.")); + } else if (!$old_key && $new_key) { + message::success(_("Your Akismet key has been saved.")); + } + + log::success("akismet", _("Akismet key changed to $new_key")); + module::set_var("akismet", "api_key", $new_key); + url::redirect("admin/akismet"); + } else { + $valid_key = false; + } + } else { + $valid_key = module::get_var("akismet", "api_key") ? 1 : 0; + } + + $view = new Admin_View("admin.html"); + $view->content = new View("admin_akismet.html"); + $view->content->valid_key = $valid_key; + $view->content->form = $form; + print $view; + } + + public function stats() { + $view = new Admin_View("admin.html"); + $view->content = new View("admin_akismet_stats.html"); + $view->content->api_key = module::get_var("akismet", "api_key"); + $view->content->blog_url = url::base(false, "http"); + print $view; + } +} \ No newline at end of file diff --git a/modules/akismet/helpers/akismet.php b/modules/akismet/helpers/akismet.php new file mode 100644 index 00000000..7dbbe98e --- /dev/null +++ b/modules/akismet/helpers/akismet.php @@ -0,0 +1,142 @@ +group("configure_akismet")->label(_("Configure Akismet")); + $group->input("api_key")->label(_("API Key"))->value(module::get_var("akismet", "api_key")); + $group->api_key->error_messages("invalid", _("The API key you provided is invalid.")); + $group->submit(_("Save")); + return $form; + } + + public static function check_comment($comment) { + $request = self::_build_request("comment-check", $comment); + $response = self::_http_post($request); + return "true" == $response->body[0]; + } + + public static function submit_spam($comment) { + $request = self::_build_request("submit-spam", $comment); + $response = self::_http_post($request); + return "true" == $response->body[0]; + } + + public static function submit_ham($comment) { + $request = self::_build_request("submit-ham", $comment); + $response = self::_http_post($request); + return "true" == $response->body[0]; + } + + public static function validate_key($api_key) { + $request = self::_build_verify_request($api_key); + $response = self::_http_post($request, "rest.akismet.com"); + return "valid" == $response->body[0]; + } + + public static function _build_verify_request($api_key) { + $base_url = url::base(false, "http"); + $query_string = "key={$api_key}&blog=$base_url"; + + $version = module::get_version("akismet"); + $http_request = "POST /1.1/verify-key HTTP/1.0\r\n"; + $http_request .= "Host: rest.akismet.com\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"; + $http_request .= "Content-Length: " . strlen($query_string) . "\r\n"; + $http_request .= "User-Agent: Gallery/3 | Akismet/$version\r\n"; + $http_request .= "\r\n"; + $http_request .= $query_string; + + return $http_request; + } + + public static function _build_request($function, $comment) { + $comment_data = array(); + $comment_data["user_ip"] = $comment->ip_addr; + $comment_data["permalink"] = url::site("comments/{$comment->id}"); + $comment_data["blog"] = url::base(false, "http"); + $comment_data["user_agent"] = $comment->user_agent; + $comment_data["referrer"] = $_SERVER["HTTP_REFERER"]; + $comment_data["comment_type"] = "comment"; + $comment_data["comment_author"] = $comment->author; + $comment_data["comment_author_email"] = $comment->email; + $comment_data["comment_author_url"] = $comment->url; + $comment_data["comment_content"] = $comment->text; + + foreach (self::$white_list as $key) { + if (array_key_exists($key, $_SERVER)) { + $comment_data[$key] = $_SERVER[$key]; + } + } + + $query_string = array(); + foreach ($comment_data as $key => $data) { + if (!is_array($data)) { + $query_string[] = "$key=" . urlencode($data); + } + } + $query_string = join("&", $query_string); + + $version = module::get_version("akismet"); + $http_request = "POST /1.1/$function HTTP/1.0\r\n"; + $http_request .= "Host: " . module::get_var("akismet", "api_key") . ".rest.akismet.com\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"; + $http_request .= "Content-Length: " . strlen($query_string) . "\r\n"; + $http_request .= "User-Agent: Gallery/3 | Akismet/$version\r\n"; + $http_request .= "\r\n"; + $http_request .= $query_string; + + return $http_request; + } + + private static function _http_post($http_request, $host=null) { + if (!$host) { + $host = module::get_var("akismet", "api_key") . ".rest.akismet.com"; + } + $response = ""; + + Kohana::log("debug", "Send request\n" . print_r($http_request, 1)); + if (false !== ($fs = @fsockopen($host, 80, $errno, $errstr, 5))) { + fwrite($fs, $http_request); + while ( !feof($fs) ) { + $response .= fgets($fs, 1160); // One TCP-IP packet + } + fclose($fs); + list($headers, $body) = explode("\r\n\r\n", $response); + $headers = explode("\r\n", $headers); + $body = explode("\r\n", $body); + $response = new ArrayObject( + array("headers" => $headers, "body" => $body), ArrayObject::ARRAY_AS_PROPS); + } else { + throw new Exception("@todo CONNECTION TO SPAM SERVICE FAILED"); + } + Kohana::log("debug", "Received response\n" . print_r($response, 1)); + + return $response; + } +} diff --git a/modules/akismet/helpers/akismet_installer.php b/modules/akismet/helpers/akismet_installer.php new file mode 100644 index 00000000..cb1bff34 --- /dev/null +++ b/modules/akismet/helpers/akismet_installer.php @@ -0,0 +1,32 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->id("akismet") + ->label(_("Akismet")) + ->url(url::site("admin/akismet"))); + + if (module::get_var("akismet", "api_key")) { + $menu->get("statistics_menu") + ->append(Menu::factory("link") + ->id("akismet") + ->label(_("Akismet")) + ->url(url::site("admin/akismet/stats"))); + } + } +} diff --git a/modules/akismet/module.info b/modules/akismet/module.info new file mode 100644 index 00000000..dd2df5dd --- /dev/null +++ b/modules/akismet/module.info @@ -0,0 +1,3 @@ +name = Akismet +description = "Filter comments through the Akismet web service to detect and eliminate spam. You'll need a WordPress.com API key to use it." +version = 1 diff --git a/modules/akismet/tests/Akismet_Helper_Test.php b/modules/akismet/tests/Akismet_Helper_Test.php new file mode 100644 index 00000000..a508f6c1 --- /dev/null +++ b/modules/akismet/tests/Akismet_Helper_Test.php @@ -0,0 +1,90 @@ +ip_address = "1.1.1.1"; + Kohana::$user_agent = "Akismet_Helper_Test"; + + $this->_comment = comment::create( + "John Doe", "John@gallery2.org", "This is a comment", 0, "http://gallery2.org"); + module::set_var("akismet", "api_key", "TEST_KEY"); + } + + public function build_verify_request_test() { + $request = akismet::_build_verify_request("TEST_KEY"); + $expected = + "POST /1.1/verify-key HTTP/1.0\r\n" . + "Host: rest.akismet.com\r\n" . + "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n" . + "Content-Length: 27\r\n" . + "User-Agent: Gallery/3 | Akismet/1\r\n\r\n" . + "key=TEST_KEY&blog=http://./"; + $this->assert_equal($expected, $request); + } + + public function build_comment_check_request_test() { + $request = akismet::_build_request("comment-check", $this->_comment); + $expected = "POST /1.1/comment-check HTTP/1.0\r\n" . + "Host: TEST_KEY.rest.akismet.com\r\n" . + "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n" . + "Content-Length: 296\r\n" . + "User-Agent: Gallery/3 | Akismet/1\r\n\r\n" . + "user_ip=1.1.1.1&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$this->_comment->id}" . + "&blog=http%3A%2F%2F.%2F&user_agent=Akismet_Helper_Test&referrer=&comment_type=comment" . + "&comment_author=John+Doe&comment_author_email=John%40gallery2.org" . + "&comment_author_url=http%3A%2F%2Fgallery2.org&comment_content=This+is+a+comment"; + $this->assert_equal($expected, $request); + } + + public function build_submit_spam_request_test() { + $request = akismet::_build_request("submit-spam", $this->_comment); + $expected = + "POST /1.1/submit-spam HTTP/1.0\r\n" . + "Host: TEST_KEY.rest.akismet.com\r\n" . + "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n" . + "Content-Length: 296\r\n" . + "User-Agent: Gallery/3 | Akismet/1\r\n\r\n" . + "user_ip=1.1.1.1&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$this->_comment->id}" . + "&blog=http%3A%2F%2F.%2F&user_agent=Akismet_Helper_Test&referrer=&comment_type=comment" . + "&comment_author=John+Doe&comment_author_email=John%40gallery2.org" . + "&comment_author_url=http%3A%2F%2Fgallery2.org&comment_content=This+is+a+comment"; + + $this->assert_equal($expected, $request); + } + + public function build_submit_ham_request_test() { + $request = akismet::_build_request("submit-ham", $this->_comment); + $expected = + "POST /1.1/submit-ham HTTP/1.0\r\n" . + "Host: TEST_KEY.rest.akismet.com\r\n" . + "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n" . + "Content-Length: 296\r\n" . + "User-Agent: Gallery/3 | Akismet/1\r\n\r\n" . + "user_ip=1.1.1.1&permalink=http%3A%2F%2F.%2Findex.php%2Fcomments%2F{$this->_comment->id}" . + "&blog=http%3A%2F%2F.%2F&user_agent=Akismet_Helper_Test&referrer=&comment_type=comment" . + "&comment_author=John+Doe&comment_author_email=John%40gallery2.org" . + "&comment_author_url=http%3A%2F%2Fgallery2.org&comment_content=This+is+a+comment"; + + $this->assert_equal($expected, $request); + } +} + diff --git a/modules/akismet/views/admin_akismet.html.php b/modules/akismet/views/admin_akismet.html.php new file mode 100644 index 00000000..34a5461b --- /dev/null +++ b/modules/akismet/views/admin_akismet.html.php @@ -0,0 +1,15 @@ + +
+

+

+ Wordpress.com API Key, which is also free. Your comments will be automatically relayed to Akismet.com where they'll be scanned for spam. Spam messages will be flagged accordingly and hidden from your vistors until you approve or delete them.") ?> +

+ + +
+ +
+ + + +
diff --git a/modules/akismet/views/admin_akismet_stats.html.php b/modules/akismet/views/admin_akismet_stats.html.php new file mode 100644 index 00000000..a36ef237 --- /dev/null +++ b/modules/akismet/views/admin_akismet_stats.html.php @@ -0,0 +1,5 @@ + + diff --git a/themes/default/css/screen.css b/themes/default/css/screen.css index e990571a..9a955699 100644 --- a/themes/default/css/screen.css +++ b/themes/default/css/screen.css @@ -266,6 +266,7 @@ li.gError select { /* Status messages ~~~~~~~~~~~~~~~~~~~~~~~ */ +#gAdminAkismet .gSuccess, #gSiteStatus li, #gMessage li { border: 1px solid #ccc; @@ -299,6 +300,7 @@ li.gError select { background-image: url('../images/ico-info.png'); } +#gAdminAkismet .gSuccess, #gSiteStatus .gSuccess, #gMessage .gSuccess { background-color: #cfc; -- cgit v1.2.3