diff --git a/mollom.module b/mollom.module index 9bf12ca..35b535d 100644 --- a/mollom.module +++ b/mollom.module @@ -1650,6 +1650,12 @@ function mollom_process_mollom($element, &$form_state, $complete_form) { '#default_value' => isset($form_state['mollom']['response']['captcha']['id']) ? $form_state['mollom']['response']['captcha']['id'] : '', '#attributes' => array('class' => 'mollom-captcha-id'), ); + $element['captcha_url_image'] = array( + '#type' => 'hidden', + ); + $element['captcha_url_audio'] = array( + '#type' => 'hidden', + ); $element['form_id'] = array( '#type' => 'value', '#value' => $form_state['mollom']['form_id'], @@ -1690,19 +1696,20 @@ function mollom_process_mollom($element, &$form_state, $complete_form) { if (!variable_get('mollom_testing_mode', 0)) { $element['captcha']['#attributes']['autocomplete'] = 'off'; } - // For CAPTCHA-only protected forms, retrieve and show an initial CAPTCHA. + // For CAPTCHA-only protected forms: if (!$form_state['mollom']['require_analysis'] && $form_state['mollom']['require_captcha']) { - // Unless the CAPTCHA was solved, display the CAPTCHA. - if (!$form_state['mollom']['passed_captcha']) { + // Retrieve and show an initial CAPTCHA. + if (empty($form_state['process_input'])) { // Enable Form API caching, in order to track the state of the CAPTCHA. $form_state['cache'] = TRUE; // mollom_form_add_captcha() adds the CAPTCHA and disables page caching. mollom_form_add_captcha($element, $form_state); } - // Otherwise, resemble mollom_validate_captcha(). This case is only reached - // in case the form 1) is not cached, 2) fully validated, 3) was submitted, - // and 4) is getting rebuilt; e.g., "Preview" on comment and node forms. - else { + // If the CAPTCHA was solved in a previous submission already, resemble + // mollom_validate_captcha(). This case is only reached in case the form + // 1) is not cached, 2) fully validated, 3) was submitted, and 4) is getting + // rebuilt; e.g., "Preview" on comment and node forms. + if ($form_state['mollom']['passed_captcha']) { $element['captcha']['#solved'] = TRUE; } } @@ -1756,9 +1763,15 @@ function mollom_form_add_captcha(&$element, &$form_state) { $element['captcha']['#access'] = TRUE; $element['captcha']['#field_prefix'] = $captcha; - // Ensure that the latest CAPTCHA ID is output as value. + // Ensure that the latest CAPTCHA ID and URL is output as value. $element['captchaId']['#value'] = $form_state['mollom']['response']['captcha']['id']; $form_state['values']['mollom']['captchaId'] = $form_state['mollom']['response']['captcha']['id']; + if (isset($form_state['values']['mollom']['captcha_url_image'])) { + $element['captcha_url_image']['#value'] = $form_state['values']['mollom']['captcha_url_image']; + } + if (isset($form_state['values']['mollom']['captcha_url_audio'])) { + $element['captcha_url_audio']['#value'] = $form_state['values']['mollom']['captcha_url_audio']; + } } // Otherwise, we have a communication or configuration error. else { @@ -1854,6 +1867,7 @@ function mollom_validate_analysis(&$form, &$form_state) { switch ($result['spamClassification']) { case 'ham': $form_state['mollom']['require_captcha'] = FALSE; + $form['mollom']['captcha']['#access'] = FALSE; mollom_log(array( 'message' => 'Ham: %teaser', 'arguments' => array('%teaser' => $teaser), @@ -1862,6 +1876,7 @@ function mollom_validate_analysis(&$form, &$form_state) { case 'spam': $form_state['mollom']['require_captcha'] = FALSE; + $form['mollom']['captcha']['#access'] = FALSE; if ($form_state['mollom']['discard']) { form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.') . ' ' . _mollom_format_message_falsepositive($form_state, $data)); } @@ -1880,6 +1895,7 @@ function mollom_validate_analysis(&$form, &$form_state) { } else { $form_state['mollom']['require_captcha'] = TRUE; + $form['mollom']['captcha']['#access'] = TRUE; } mollom_log(array( 'message' => 'Unsure: %teaser', @@ -1896,7 +1912,7 @@ function mollom_validate_analysis(&$form, &$form_state) { mollom_log(array( 'message' => 'Unknown: %teaser', 'arguments' => array('%teaser' => $teaser), - )); + ), WATCHDOG_ERROR); break; } } @@ -2514,13 +2530,13 @@ function mollom_field_extra_fields() { */ function mollom_get_captcha(&$form_state) { $key = 'captcha_url_' . $form_state['mollom']['captcha_type']; - if (empty($form_state['mollom']['response'][$key])) { + if (empty($form_state['values']['mollom'][$key])) { $data = array( 'type' => $form_state['mollom']['captcha_type'], 'ssl' => (int) (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'), ); - if (!empty($form_state['mollom']['response']['content']['id'])) { - $data['contentId'] = $form_state['mollom']['response']['content']['id']; + if (!empty($form_state['values']['mollom']['contentId'])) { + $data['contentId'] = $form_state['values']['mollom']['contentId']; } $result = mollom()->createCaptcha($data); @@ -2532,15 +2548,15 @@ function mollom_get_captcha(&$form_state) { if (is_array($result) && isset($result['url'])) { $url = $result['url']; - $form_state['mollom']['response'][$key] = $url; - $form_state['mollom']['response']['captcha']['id'] = $result['id']; + $form_state['values']['mollom'][$key] = $url; + $form_state['mollom']['response']['captcha'] = $result; } else { return ''; } } else { - $url = $form_state['mollom']['response'][$key]; + $url = $form_state['values']['mollom'][$key]; } // @todo Convert these to actual theme functions? diff --git a/mollom.pages.inc b/mollom.pages.inc index 4933673..87e805a 100644 --- a/mollom.pages.inc +++ b/mollom.pages.inc @@ -42,7 +42,7 @@ function mollom_captcha_js($type, $form_build_id, $contentId = NULL) { else { $form_state['mollom'] = array(); if (isset($contentId)) { - $form_state['mollom']['response']['content']['id'] = $contentId; + $form_state['values']['mollom']['contentId'] = $contentId; } } $form_state['mollom']['captcha_type'] = $type; diff --git a/tests/mollom.test b/tests/mollom.test index 4240a55..d5f5bf9 100644 --- a/tests/mollom.test +++ b/tests/mollom.test @@ -585,6 +585,7 @@ class MollomWebTestCase extends DrupalWebTestCase { * An optional message to test does appear after submission. */ protected function postCorrectCaptcha($url, array $edit = array(), $button, $success_message = '') { + $this->assertCaptchaField(); $edit['mollom[captcha]'] = 'correct'; $this->drupalPost($url, $edit, $button); $this->assertNoCaptchaField(); @@ -607,13 +608,12 @@ class MollomWebTestCase extends DrupalWebTestCase { * An optional message to test does not appear after submission. */ protected function postIncorrectCaptcha($url, array $edit = array(), $button, $success_message = '') { + $this->assertCaptchaField(); $edit['mollom[captcha]'] = 'incorrect'; $before_url = $this->getUrl(); $this->drupalPost($url, $edit, $button); - if ($this->getUrl() == $before_url) { - $this->assertCaptchaField(); - } $this->assertText($this->incorrect_message); + $this->assertCaptchaField(); if ($success_message) { $this->assertNoText($success_message); } @@ -636,7 +636,7 @@ class MollomWebTestCase extends DrupalWebTestCase { protected function assertSpamSubmit($url, array $spam_fields, array $edit = array(), $button, $success_message = '') { $edit += array_fill_keys($spam_fields, 'spam'); $this->drupalPost($url, $edit, $button); - $this->assertNoCaptchaField($url); + $this->assertNoCaptchaField(); $this->assertText($this->spam_message); if ($success_message) { $this->assertNoText($success_message); @@ -684,11 +684,8 @@ class MollomWebTestCase extends DrupalWebTestCase { */ protected function assertUnsureSubmit($url, array $unsure_fields, array $edit = array(), $button, $success_message = '') { $edit += array_fill_keys($unsure_fields, 'unsure'); - $before_url = $this->getUrl(); $this->drupalPost($url, $edit, $button); - if ($this->getUrl() == $before_url) { - $this->assertCaptchaField(); - } + $this->assertCaptchaField(); $this->assertText($this->unsure_message); if ($success_message) { $this->assertNoText($success_message); @@ -3823,6 +3820,10 @@ class MollomDataCRUDTestCase extends MollomWebTestCase { class MollomAnalysisTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; + // Re-route Mollom communication to this testing site. + // @todo Remove this when Testing API is fixed. + protected $mollomClass = 'MollomDrupalTestLocal'; + public static function getInfo() { return array( 'name' => 'Text analysis', @@ -3840,7 +3841,202 @@ class MollomAnalysisTestCase extends MollomWebTestCase { 'access administration pages', 'administer mollom', )); - $this->web_user = $this->drupalCreateUser(array()); + } + + function testUnsure() { + $methods = get_class_methods($this); + foreach ($methods as $method) { + if (substr($method, 0, 7) === 'subtest') { + //debug($method); + $this->$method(); + } + } + } + + /** + * Tests basic unsure submission flow. + */ + function subtestUnsureCorrect() { + $this->setProtection('mollom_test_form', MOLLOM_MODE_ANALYSIS); + + $this->drupalGet('mollom-test/form'); + $this->assertNoCaptchaField(); + $edit = array( + 'title' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId', TRUE); + $this->assertResponseIDInForm('captchaId', TRUE); + $this->assertCaptchaField(); + + $edit = array( + 'title' => 'unsure unsure', + 'mollom[captcha]' => 'correct', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertNoCaptchaField(); + $this->assertTestSubmitData(); + } + + /** + * Tests unsure post with repetitive incorrect CAPTCHA solution. + */ + function subtestUnsureIncorrect() { + $this->setProtection('mollom_test_form', MOLLOM_MODE_ANALYSIS); + + $this->drupalGet('mollom-test/form'); + $this->assertNoCaptchaField(); + $edit = array( + 'title' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId', TRUE); + $this->assertResponseIDInForm('captchaId', TRUE); + $this->assertCaptchaField(); + + $edit = array(); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertCaptchaField(); + $edit = array( + 'mollom[captcha]' => 'incorrect', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertCaptchaField(); + + $edit = array( + 'mollom[captcha]' => 'correct', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertNoCaptchaField(); + $this->assertTestSubmitData(); + } + + /** + * Tests unsure post with other validation errors. + */ + function subtestUnsureValidation() { + $this->setProtection('mollom_test_form', MOLLOM_MODE_ANALYSIS); + + // The 'title' field is required. Omit its value to verify that the CAPTCHA + // can be solved, repetitive form validations do not show a CAPTCHA again, + // and the post can finally be submitted by providing a title. + $this->drupalGet('mollom-test/form'); + $this->assertNoCaptchaField(); + $edit = array( + 'body' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId', TRUE); + $this->assertResponseIDInForm('captchaId', TRUE); + $this->assertCaptchaField(); + $edit = array( + 'body' => 'unsure unsure', + 'mollom[captcha]' => 'correct', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertNoCaptchaField(); + $edit = array( + 'body' => 'unsure unsure unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertNoCaptchaField(); + $edit = array( + 'title' => 'unsure', + 'body' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertNoCaptchaField(); + $this->assertTestSubmitData(); + } + + /** + * Tests unsure posts turning into ham. + */ + function subtestUnsureHam() { + $this->setProtection('mollom_test_form', MOLLOM_MODE_ANALYSIS); + + $this->drupalGet('mollom-test/form'); + $this->assertNoCaptchaField(); + // Posting ham as title would submit the form, so post as body instead. + $edit = array( + 'body' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId', TRUE); + $this->assertResponseIDInForm('captchaId', TRUE); + $this->assertCaptchaField(); + // Turn the post into ham. + $edit = array( + 'body' => 'ham', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertNoCaptchaField(); + // Turn the post back into unsure. + $edit = array( + 'body' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertCaptchaField(); + + $edit = array( + 'title' => 'irrelevant', + 'mollom[captcha]' => 'correct', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertNoCaptchaField(); + $this->assertTestSubmitData(); + } + + /** + * Tests unsure posts turning into spam. + */ + function subtestUnsureSpam() { + $this->setProtection('mollom_test_form', MOLLOM_MODE_ANALYSIS); + + $this->drupalGet('mollom-test/form'); + $this->assertNoCaptchaField(); + $edit = array( + 'title' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId', TRUE); + $this->assertResponseIDInForm('captchaId', TRUE); + $this->assertCaptchaField(); + // Turn the post into spam. + $edit = array( + 'title' => 'spam', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertNoCaptchaField(); + // Turn the post back into unsure. + $edit = array( + 'title' => 'unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertResponseIDInForm('contentId'); + $this->assertResponseIDInForm('captchaId'); + $this->assertCaptchaField(); + + $edit = array( + 'mollom[captcha]' => 'correct', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $this->assertNoCaptchaField(); + $this->assertTestSubmitData(); } /** diff --git a/tests/mollom_test_server.module b/tests/mollom_test_server.module index 8c54c27..babf138 100644 --- a/tests/mollom_test_server.module +++ b/tests/mollom_test_server.module @@ -529,9 +529,9 @@ function mollom_test_server_check_content($data) { } // In case a previous spam check was unsure and a CAPTCHA was solved, the // result is supposed to be ham - unless the new content is spam. - if ($response['spamClassification'] != 'spam') { - $captcha_sessions = variable_get('mollom_test_server_check_captcha_sessions', array()); - if (!empty($data['captchaId']) && !empty($captcha_sessions[$data['captchaId']])) { + if (!empty($data['id']) && $response['spamClassification'] == 'unsure') { + $content_captchas = variable_get('mollom_test_server_content_captcha', array()); + if (!empty($content_captchas[$data['id']])) { $response['spamScore'] = 0.0; $response['spamClassification'] = 'ham'; } @@ -631,7 +631,7 @@ function mollom_test_server_check_content_blacklist($string, $blacklist, $reason } /** - * API callback for mollom.getImageCaptcha to fetch a CATPCHA image. + * API callback for mollom.getImageCaptcha to fetch a CAPTCHA image. */ function mollom_test_server_get_captcha($data) { $response = array(); @@ -654,8 +654,6 @@ function mollom_test_server_get_captcha($data) { /** * API callback for mollom.checkCaptcha to validate a CAPTCHA response. - * - * @todo Add support for 'redirect' and 'refresh' values. */ function mollom_test_server_check_captcha($data) { $response = array(); @@ -673,13 +671,16 @@ function mollom_test_server_check_captcha($data) { if (!isset($storage[$captchaId])) { return MENU_NOT_FOUND; } - $storage[$captchaId] = $data; + $storage[$captchaId] = array_merge($storage[$captchaId], $data); $response['id'] = $captchaId; variable_set('mollom_test_server_captcha', $storage); - $captcha_sessions = variable_get('mollom_test_server_check_captcha_sessions', array()); - $captcha_sessions[$captchaId] = $response['solved']; - variable_set('mollom_test_server_check_captcha_sessions', $captcha_sessions); + if (isset($storage[$captchaId]['contentId'])) { + $contentId = $storage[$captchaId]['contentId']; + $content_captchas = variable_get('mollom_test_server_content_captcha', array()); + $content_captchas[$contentId] = $response['solved']; + variable_set('mollom_test_server_content_captcha', $content_captchas); + } return $response; }