diff --git a/mollom.module b/mollom.module index 0113c5f..9badaac 100644 --- a/mollom.module +++ b/mollom.module @@ -709,26 +709,24 @@ function mollom_form_alter(&$form, &$form_state, $form_id) { } // Site administrators don't have their content checked with Mollom. - if (!user_access('bypass mollom protection')) { + if (1 || !user_access('bypass mollom protection')) { // Retrieve configuration for this form. if (isset($forms['protected'][$form_id]) && ($mollom_form = mollom_form_load($form_id))) { // Determine whether to bypass validation for the current user. foreach ($mollom_form['bypass access'] as $permission) { - if (user_access($permission)) { + if (0 && user_access($permission)) { return; } } // Add Mollom form widget. $form['mollom'] = array( - '#type' => 'mollom', +// '#type' => 'mollom', '#mollom_form' => $mollom_form, // #type 'actions' defaults to 100. '#weight' => (isset($form['actions']['#weight']) ? $form['actions']['#weight'] - 1 : 99), '#tree' => TRUE, ); - // Enable caching of this form; required for our form validation and - // submit handlers. - $form_state['cache'] = TRUE; + $form['mollom'] = mollom_process_mollom($form['mollom'], $form_state, $form); // Add Mollom form validation handlers. // Form-level validation handlers are required, since we need access to @@ -1464,7 +1462,9 @@ function _mollom_status($reset = FALSE) { * Outputs a warning message about enabled testing mode (once). */ function _mollom_testing_mode_warning() { - $warned = &drupal_static(__FUNCTION__); + // drupal_set_message() starts a session and disables page caching, which + // breaks testing. Therefore, tests set the verbose variable to TRUE. + $warned = &drupal_static(__FUNCTION__, variable_get('mollom_testing_mode_omit_warning', NULL)); if (isset($warned)) { return; } @@ -1562,10 +1562,7 @@ function mollom_process_mollom($element, &$form_state, $complete_form) { 'response' => array( ), ); - // Instantiate a new Mollom class, unless there is not a cached one. - if (!isset($form_state['mollom']['class'])) { - $form_state['mollom']['class'] = mollom(); - } +// debug($form_state['mollom']); // Add remaining information about the registered form. $form_state['mollom'] += $element['#mollom_form']; @@ -1594,14 +1591,25 @@ function mollom_process_mollom($element, &$form_state, $complete_form) { ); $element['contentId'] = array( '#type' => 'hidden', - '#default_value' => isset($form_state['mollom']['response']['content']['id']) ? $form_state['mollom']['response']['content']['id'] : '', +// '#default_value' => isset($form_state['values']['mollom']['contentId']) ? $form_state['values']['mollom']['contentId'] : '', '#attributes' => array('class' => 'mollom-content-id'), ); +// $element['_contentId'] = array( +// '#type' => 'item', +// '#title' => 'contentId', +// '#markup' => $element['contentId']['#default_value'], +// ); $element['captchaId'] = array( '#type' => 'hidden', '#default_value' => isset($form_state['mollom']['response']['captcha']['id']) ? $form_state['mollom']['response']['captcha']['id'] : '', +// '#default_value' => isset($form_state['values']['mollom']['captchaId']) ? $form_state['values']['mollom']['captchaId'] : '', '#attributes' => array('class' => 'mollom-captcha-id'), ); +// $element['_captchaId'] = array( +// '#type' => 'item', +// '#title' => 'captchaId', +// '#markup' => $element['captchaId']['#default_value'], +// ); $element['form_id'] = array( '#type' => 'value', '#value' => $form_state['mollom']['form_id'], @@ -1616,11 +1624,11 @@ function mollom_process_mollom($element, &$form_state, $complete_form) { ); $element['spamScore'] = $data_spec; $element['spamClassification'] = $data_spec; - $element['solved'] = $data_spec; $element['qualityScore'] = $data_spec; $element['profanityScore'] = $data_spec; - $element['reason'] = $data_spec; $element['languages'] = $data_spec; + $element['reason'] = $data_spec; + $element['solved'] = $data_spec; // Add the CAPTCHA element. $element['captcha'] = array( @@ -1630,19 +1638,22 @@ function mollom_process_mollom($element, &$form_state, $complete_form) { '#default_value' => '', '#description' => t("Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive."), ); + $element['#pre_render'][] = 'mollom_pre_render_mollom'; // Disable browser autocompletion, unless testing mode is enabled, in which // case autocompletion for 'correct' and 'incorrect' is handy. if (!variable_get('mollom_testing_mode', 0)) { $element['captcha']['#attributes']['autocomplete'] = 'off'; } - // Request and inject an initial CAPTCHA. - if ($form_state['mollom']['require_captcha'] && !$form_state['mollom']['passed_captcha'] && empty($form_state['mollom']['response']['captcha']['id'])) { - mollom_form_add_captcha($element, $form_state); - } - // If no CAPTCHA is required or it was solved already, hide the element. - elseif (!$form_state['mollom']['require_captcha'] || $form_state['mollom']['passed_captcha']) { - $element['captcha']['#access'] = FALSE; + if (!$form_state['mollom']['require_analysis'] && $form_state['mollom']['require_captcha']) { + if (!$form_state['mollom']['passed_captcha']) { + mollom_form_add_captcha($element, $form_state); + $form_state['cache'] = TRUE; + } + else { + // Only reached in case the form fully validated and is getting rebuilt. + $element['captcha']['#solved'] = TRUE; + } } // Add a spambot trap. Purposively use 'homepage' as field name. @@ -1685,9 +1696,6 @@ function mollom_process_mollom($element, &$form_state, $complete_form) { * The current state of the form. Passed by reference. */ function mollom_form_add_captcha(&$element, &$form_state) { - // UX: Empty the CAPTCHA field value, as the user has to re-enter a new one. - $element['captcha']['#value'] = ''; - // Prevent the page cache from storing a form containing a CAPTCHA element. drupal_page_is_cacheable(FALSE); @@ -1695,9 +1703,10 @@ function mollom_form_add_captcha(&$element, &$form_state) { // If we get a response, add the image CAPTCHA to the form element. if (!empty($captcha)) { $element['captcha']['#access'] = TRUE; - $element['captcha']['#required'] = TRUE; +// $element['captcha']['#required'] = TRUE; $element['captcha']['#field_prefix'] = $captcha; - // Assign the session ID returned by Mollom. + + // Assign the new CAPTCHA ID returned by Mollom. $element['captchaId']['#value'] = $form_state['mollom']['response']['captcha']['id']; $form_state['values']['mollom']['captchaId'] = $form_state['mollom']['response']['captcha']['id']; } @@ -1709,6 +1718,25 @@ function mollom_form_add_captcha(&$element, &$form_state) { } } +function mollom_pre_render_mollom($element) { +// krumo($element); +// krumo($element['captchaId']['#value']); + if (empty($element['captchaId']['#value']) || !empty($element['captcha']['#solved'])) { + $element['captcha']['#access'] = FALSE; + } + else { + // The form element cannot be marked as #required, since _form_validate() + // would throw an element validation error on an empty value otherwise, + // before the form-level validation handler is executed. + // #access cannot default to FALSE, since the $form may be cached, and + // Form API ignores user input for all elements that are not accessible. + $element['captcha']['#required'] = TRUE; + } + // UX: Empty the CAPTCHA field value, as the user has to re-enter a new one. + $element['captcha']['#value'] = ''; + return $element; +} + /** * Form validation handler to perform textual analysis of submitted form values. * @@ -1718,6 +1746,8 @@ function mollom_form_add_captcha(&$element, &$form_state) { * CAPTCHA was solved correctly. */ function mollom_validate_analysis(&$form, &$form_state) { +// debug($form_state['input']['mollom']); +// debug($form_state['values']['mollom']); // Text analysis may only ever be skipped, if we do not require it in the // first place. With regard to that, $form_state['mollom']['require_analysis'] // is only set once during initialization of $form_state['mollom'] in @@ -1737,8 +1767,8 @@ function mollom_validate_analysis(&$form, &$form_state) { if (isset($data['postId'])) { unset($data['postId']); } - if (isset($form_state['mollom']['response']['content']['id'])) { - $data['id'] = $form_state['mollom']['response']['content']['id']; + if (!empty($form_state['values']['mollom']['contentId'])) { + $data['id'] = $form_state['values']['mollom']['contentId']; } $data['checks'] = $form_state['mollom']['checks']; $data['strictness'] = $form_state['mollom']['strictness']; @@ -1748,7 +1778,8 @@ function mollom_validate_analysis(&$form, &$form_state) { if (in_array('spam', $data['checks']) && $form_state['mollom']['unsure'] == 'binary') { $data['unsure'] = 0; } - $result = $form_state['mollom']['class']->checkContent($data); + $result = mollom()->checkContent($data); +// debug($result); // Use all available data properties for log messages below. $data += $all_data; @@ -1760,11 +1791,12 @@ function mollom_validate_analysis(&$form, &$form_state) { // Store the response returned by Mollom. $form_state['mollom']['response']['content'] = $result; - // Set form element values accordingly. Do not overwrite the entity ID with - // the contentId, nor a possibly existing captchaId. + // Set form values accordingly. Do not overwrite the entity ID. + // @todo Rename 'id' to 'entity_id'. $result['contentId'] = $result['id']; unset($result['id']); $form_state['values']['mollom'] = array_merge($form_state['values']['mollom'], $result); + // #value has to be set manually to output it in case of a validation error, // since this is not a element-level but a form-level validation handler. $form['mollom']['contentId']['#value'] = $result['contentId']; @@ -1804,6 +1836,7 @@ function mollom_validate_analysis(&$form, &$form_state) { switch ($result['spamClassification']) { case 'ham': $form_state['mollom']['require_captcha'] = FALSE; + $form_state['mollom']['require_moderation'] = FALSE; mollom_log(array( 'message' => 'Ham: %teaser', 'arguments' => array('%teaser' => $teaser), @@ -1814,6 +1847,7 @@ function mollom_validate_analysis(&$form, &$form_state) { $form_state['mollom']['require_captcha'] = FALSE; if ($form_state['mollom']['discard']) { form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.')); + $form_state['mollom']['require_moderation'] = FALSE; } else { $form_state['mollom']['require_moderation'] = TRUE; @@ -1834,15 +1868,8 @@ function mollom_validate_analysis(&$form, &$form_state) { $form_state['mollom']['require_moderation'] = TRUE; } else { - // Only throw a validation error and retrieve a CAPTCHA, if we check - // this post for the first time. Otherwise, mollom_validate_captcha() - // issued the CAPTCHA and needs to validate it prior to throwing any - // errors. - if (!$form_state['mollom']['require_captcha']) { - $form_state['mollom']['require_captcha'] = TRUE; - form_set_error('mollom][captcha', t('To complete this form, please complete the word verification below.')); - mollom_form_add_captcha($form['mollom'], $form_state); - } + $form_state['mollom']['require_captcha'] = TRUE; + $form_state['mollom']['require_moderation'] = FALSE; } break; @@ -1869,28 +1896,26 @@ function mollom_validate_analysis(&$form, &$form_state) { * text analysis result is "unsure". */ function mollom_validate_captcha(&$form, &$form_state) { - // CAPTCHA validation may only be skipped, if we do not require it in the - // first place, or if the user already solved a CAPTCHA correctly. We need to - // validate, if $form_state['mollom']['require_captcha'] is TRUE, which is - // either set during initialization of $form_state['mollom'] in - // mollom_process_form(), or after performing text analysis. The second - // return condition, $form_state['mollom']['passed_captcha'], may only ever be - // set by this validation handler and must not be changed elsewhere. - if (!$form_state['mollom']['require_captcha'] || $form_state['mollom']['passed_captcha']) { - $form['mollom']['captcha']['#access'] = FALSE; +// debug($form_state['input']['mollom']); +// debug($form_state['values']['mollom']); +// debug($form_state['mollom']); + + if (!$form_state['mollom']['require_captcha']) { return; } - // If the form is protected via text analysis and the result was "unsure", - // then this validation handler also runs when the CAPTCHA is initially - // requested by mollom_validate_analysis(), is about to be rendered, and - // before the user had a chance to solve it. In that case, there is no value - // that could be validated yet; i.e., the CAPTCHA value is empty. However, in - // all other cases, an empty value must be validated and sent to Mollom. The - // initial case has a special condition: mollom_validate_analysis() has set - // a form error on the CAPTCHA element asking the user to solve it in order to - // submit the form. - if ($form_state['values']['mollom']['captcha'] === '' && form_get_error($form['mollom']['captcha'])) { + // If there is no CAPTCHA ID yet, retrieve one and throw an error. + if (empty($form_state['values']['mollom']['captchaId'])) { + mollom_form_add_captcha($form['mollom'], $form_state); + form_error($form['mollom']['captcha'], t('To complete this form, please complete the word verification below.')); + return; + } + + // $form_state['mollom']['passed_captcha'], may only ever be set by this + // validation handler and must not be changed elsewhere. + if ($form_state['mollom']['passed_captcha']) { + $form['mollom']['captcha']['#access'] = FALSE; + $form['mollom']['captcha']['#solved'] = TRUE; return; } @@ -1904,7 +1929,7 @@ function mollom_validate_captcha(&$form, &$form_state) { return; } $data = array( - 'id' => $form_state['mollom']['response']['captcha']['id'], + 'id' => $form_state['values']['mollom']['captchaId'], 'solution' => $form_state['values']['mollom']['captcha'], 'authorIp' => $all_data['authorIp'], ); @@ -1914,7 +1939,9 @@ function mollom_validate_captcha(&$form, &$form_state) { if (isset($all_data['honeypot'])) { $data['honeypot'] = $all_data['honeypot']; } - $result = $form_state['mollom']['class']->checkCaptcha($data); +// debug($data); + $result = mollom()->checkCaptcha($data); +// debug($result); // Use all available data properties for log messages below. $data += $all_data; @@ -1925,11 +1952,12 @@ function mollom_validate_captcha(&$form, &$form_state) { // Store the response for #submit handlers. $form_state['mollom']['response']['captcha'] = $result; - // Set form element values accordingly. Do not overwrite the entity ID with - // the contentId, nor a possibly existing captchaId. + // Set form values accordingly. Do not overwrite the entity ID. + // @todo Rename 'id' to 'entity_id'. $result['captchaId'] = $result['id']; unset($result['id']); $form_state['values']['mollom'] = array_merge($form_state['values']['mollom'], $result); + // #value has to be set manually to output it in case of a validation error, // since this is not a element-level but a form-level validation handler. $form['mollom']['captchaId']['#value'] = $result['captchaId']; @@ -1937,6 +1965,7 @@ function mollom_validate_captcha(&$form, &$form_state) { if (!empty($result['solved'])) { $form_state['mollom']['passed_captcha'] = TRUE; $form['mollom']['captcha']['#access'] = FALSE; + $form['mollom']['captcha']['#solved'] = TRUE; mollom_log(array( 'message' => 'Correct CAPTCHA', @@ -2457,10 +2486,7 @@ function mollom_get_captcha(&$form_state) { if (!empty($form_state['mollom']['response']['content']['id'])) { $data['contentId'] = $form_state['mollom']['response']['content']['id']; } - // @todo $form_state may not contain a Mollom class when being called from - // mollom_captcha_js() until that gets converted to #ajax framework. - $mollom = (isset($form_state['mollom']['class']) ? $form_state['mollom']['class'] : mollom()); - $result = $mollom->createCaptcha($data); + $result = mollom()->createCaptcha($data); // Add a log message to prevent the request log from appearing without a // message on CAPTCHA-only protected forms. @@ -2470,6 +2496,7 @@ 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']; } else { diff --git a/tests/mollom.test b/tests/mollom.test index b3cf50c..69216ba 100644 --- a/tests/mollom.test +++ b/tests/mollom.test @@ -125,6 +125,9 @@ class MollomWebTestCase extends DrupalWebTestCase { // automatically create testing API keys. variable_set('mollom_testing_create_keys', $this->createKeys); + // Disable testing mode warnings. + variable_set('mollom_testing_mode_omit_warning', TRUE); + // D7's new default theme Bartik is bogus in various locations, which leads // to failing tests. // @todo Remove this override. @@ -914,6 +917,306 @@ class MollomWebTestCase extends DrupalWebTestCase { } /** + * Tests protection of forms. + */ +class MollomFormProtectionTestCase extends MollomWebTestCase { + protected $disableDefaultSetup = TRUE; + + public static function getInfo() { + return array( + 'name' => 'Form protection', + 'description' => 'Tests protection of forms.', + 'group' => 'Mollom', + ); + } + + function setUp() { + parent::setUp(array('mollom', 'mollom_test')); + $this->setKeys(); + $this->assertValidKeys(); + + // Enable excessive/aggressive page caching to resemble reverse-proxies. + variable_set('cache', 1); + // A minimum cache lifetime causes cache_clear_all() to start a session. + //variable_set('cache_lifetime', 60); + variable_set('page_cache_maximum_age', 180); + } + + protected function setProtectionX($form_id, $mode = MOLLOM_MODE_ANALYSIS, $values = array()) { + if (!$mollom_form = mollom_form_load($form_id)) { + $mollom_form = mollom_form_new($form_id); + } + $mollom_form['mode'] = $mode; + if ($values) { + foreach ($values as $property => $value) { + $mollom_form[$property] = $value; + } + } + $status = mollom_form_save($mollom_form); + return $status; + } + + /** + * Tests binary unsure mode. + */ + function testContentUnsure() { + $this->setProtectionX('mollom_test_form'); + // Prime the form + page cache. + $this->drupalGet('mollom-test/form'); + $this->assertText('Views: 1'); + $this->drupalGet('mollom-test/form'); + $this->assertText('Views: 1'); + + $this->assertUnsureSubmit(NULL, array('title'), array(), 'Submit'); + + $this->drupalGet('mollom-test/form'); + $this->assertText('Views: 1'); + + $this->assertUnsureSubmit(NULL, array('title'), array(), 'Submit'); + + return; + + // Verify that an unsure post is ham. + // Note: The actual binary mode of Mollom's production API is more granular. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'unsure', + ); + $this->assertHamSubmit('mollom-test/form', array(), $edit, 'Submit'); + $mid = $this->assertTestSubmitData(); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 1, t('Published test post found.')); + $this->assertSame('spamScore', $data->spamScore, 0); + $this->assertSame('spamClassification', $data->spamClassification, 'ham'); + $this->assertSame('moderate', $data->moderate, 0); + + // Verify that a ham post is accepted. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'ham', + ); + $this->assertHamSubmit('mollom-test/form', array(), $edit, 'Submit'); + $mid = $this->assertTestSubmitData(); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 1, t('Published test post found.')); + $this->assertSame('spamScore', $data->spamScore, 0); + $this->assertSame('spamClassification', $data->spamClassification, 'ham'); + $this->assertSame('moderate', $data->moderate, 0); + + // Verify that a spam post is blocked. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'spam', + ); + $this->assertSpamSubmit('mollom-test/form', array(), $edit, 'Submit'); + } + + /** + * Tests binary unsure mode. + */ + function xtestUnsureBinary() { +// $this->drupalLogin($this->admin_user); + $this->setProtectionX('mollom_test_form', MOLLOM_MODE_ANALYSIS, array( + 'unsure' => 'binary', + )); +// $this->drupalLogout(); + + // Verify that an unsure post is ham. + // Note: The actual binary mode of Mollom's production API is more granular. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'unsure', + ); + $this->assertHamSubmit('mollom-test/form', array(), $edit, 'Submit'); + $mid = $this->assertTestSubmitData(); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 1, t('Published test post found.')); + $this->assertSame('spamScore', $data->spamScore, 0); + $this->assertSame('spamClassification', $data->spamClassification, 'ham'); + $this->assertSame('moderate', $data->moderate, 0); + + // Verify that a ham post is accepted. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'ham', + ); + $this->assertHamSubmit('mollom-test/form', array(), $edit, 'Submit'); + $mid = $this->assertTestSubmitData(); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 1, t('Published test post found.')); + $this->assertSame('spamScore', $data->spamScore, 0); + $this->assertSame('spamClassification', $data->spamClassification, 'ham'); + $this->assertSame('moderate', $data->moderate, 0); + + // Verify that a spam post is blocked. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'spam', + ); + $this->assertSpamSubmit('mollom-test/form', array(), $edit, 'Submit'); + } + + /** + * Tests retaining unsure posts and moderating them. + */ + function xtestRetainUnsure() { +// $this->drupalLogin($this->admin_user); +// // Verify that mollom_basic_elements_test_form cannot be configured to put +// // posts into moderation queue. +// $this->setProtectionX('mollom_basic_elements_test_form'); +// $this->drupalGet('admin/config/content/mollom/manage/mollom_basic_elements_test_form'); +// $this->assertNoFieldByName('mollom[unsure]'); + + // Configure mollom_test_form to retain unsure posts. + $this->setProtectionX('mollom_test_form', MOLLOM_MODE_ANALYSIS, array( + 'unsure' => 'moderate', + )); +// $this->drupalLogout(); + + // Verify that an unsure post gets unpublished. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'unsure', + ); + $this->drupalPost('mollom-test/form', $edit, 'Submit'); + $mid = $this->assertTestSubmitData(); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 0, t('Unpublished test post found.')); + $this->assertSame('spamScore', $data->spamScore, 0.5); + $this->assertSame('spamClassification', $data->spamClassification, 'unsure'); + $this->assertSame('moderate', $data->moderate, 1); + + // Verify that editing the post does neither change the session data, nor + // the publishing status. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'unsure unsure', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $mid = $this->assertTestSubmitData($mid); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 0, t('Unpublished test post found.')); + $this->assertSame('spamScore', $data->spamScore, 0.5); + $this->assertSame('spamClassification', $data->spamClassification, 'unsure'); + $this->assertSame('moderate', $data->moderate, 1); + + // Verify that publishing the post changes the session data accordingly. + $this->drupalLogin($this->admin_user); + $edit = array( + 'status' => TRUE, + ); + $this->drupalPost('mollom-test/form/' . $mid, $edit, 'Submit'); + $mid = $this->assertTestSubmitData($mid); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 1, t('Published test post found.')); + $this->assertSame('spamScore', $data->spamScore, 0.5); + $this->assertSame('spamClassification', $data->spamClassification, 'unsure'); + $this->assertSame('moderate', $data->moderate, 0); + } + + /** + * Tests retaining spam posts and moderating them. + */ + function xtestRetainSpam() { +// $this->drupalLogin($this->admin_user); +// // Verify that mollom_basic_test_form cannot be configured to put posts into +// // moderation queue. +// $this->setProtectionX('mollom_basic_elements_test_form'); +// $this->drupalGet('admin/config/content/mollom/manage/mollom_basic_elements_test_form'); +// $this->assertNoFieldByName('mollom[discard]'); + + // Configure mollom_test_form to accept bad posts. + $this->setProtectionX('mollom_test_form', MOLLOM_MODE_ANALYSIS, array( + 'checks' => array('spam', 'profanity'), + 'discard' => 0, + )); +// $this->drupalLogout(); + + // Verify that we are able to post spam and the post is unpublished. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'spam profanity', + ); + $this->drupalPost('mollom-test/form', $edit, 'Submit'); + $mid = $this->assertTestSubmitData(); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 0, t('Unpublished test post found.')); + $this->assertSame('spamScore', $data->spamScore, 1.0); + $this->assertSame('spamClassification', $data->spamClassification, 'spam'); + $this->assertSame('profanityScore', $data->profanityScore, 1); + $this->assertSame('moderate', $data->moderate, 1); + + // Verify that editing the post does neither change the session data, nor + // the publishing status. + $edit = array( + 'title' => $this->randomString(), + 'body' => 'spam profanity spam profanity', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $mid = $this->assertTestSubmitData($mid); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 0, t('Unpublished test post found.')); + $this->assertSame('spamScore', $data->spamScore, 1.0); + $this->assertSame('spamClassification', $data->spamClassification, 'spam'); + $this->assertSame('profanityScore', $data->profanityScore, 1); + $this->assertSame('moderate', $data->moderate, 1); + + // Verify that neither ham or unsure spam posts, nor non-profane posts are + // marked for moderation. + $expectations = array( + 'ham' => array('spamScore' => 0.0, 'spamClassification' => 'ham', 'profanityScore' => 0), + 'unsure' => array('spamScore' => 0.5, 'spamClassification' => 'unsure', 'profanityScore' => 0), + $this->randomString() => array('spamScore' => 0.5, 'spamClassification' => 'unsure', 'profanityScore' => 0), + ); + foreach ($expectations as $body => $expected) { + $edit = array( + 'title' => $this->randomString(), + 'body' => $body, + ); + $this->drupalPost('mollom-test/form', $edit, 'Submit'); + if ($expected['spamClassification'] == 'unsure') { + $this->postCorrectCaptcha(NULL, array(), 'Submit'); + } + $mid = $this->assertTestSubmitData(); + $data = $this->assertMollomData('mollom_test', $mid); + $record = mollom_test_load($mid); + $this->assertEqual($record->status, 1, t('Published test post %body found.', array('%body' => $body))); + $this->assertSame('spamScore', $data->spamScore, $expected['spamScore']); + $this->assertSame('spamClassification', $data->spamClassification, $expected['spamClassification']); + $this->assertSame('profanityScore', $data->profanityScore, $expected['profanityScore']); + $this->assertSame('moderate', $data->moderate, 0); + } + } + + /** + * Asserts a successful mollom_test_form submission. + * + * @param $old_mid + * (optional) The existing test record id to assert. + */ + protected function assertTestSubmitData($old_mid = NULL) { + $this->assertText('Successful form submission.'); + $mid = $this->getFieldValueByName('mid'); + if (isset($old_mid)) { + $this->assertSame('Test record id', $mid, $old_mid); + } + else { + $this->assertTrue($mid > 0, t('Test record id @id found.', array('@id' => $mid))); + } + return $mid; + } +} + +/** * Tests testing mode functionality. */ class MollomTestingModeTestCase extends MollomWebTestCase { @@ -941,6 +1244,9 @@ class MollomTestingModeTestCase extends MollomWebTestCase { function setUp() { parent::setUp(array('mollom', 'mollom_test')); + // Enable testing mode warnings. + variable_del('mollom_testing_mode_omit_warning'); + $this->admin_user = $this->drupalCreateUser(array( 'access administration pages', 'administer mollom', @@ -2875,7 +3181,7 @@ class MollomCommentFormTestCase extends MollomWebTestCase { // required field. $this->postIncorrectCaptcha(NULL, array(), t('Preview')); $this->assertText(t('Comment field is required.')); - $this->assertResponseIDInForm('captchaId', TRUE); + $this->assertResponseIDInForm('captchaId'); $this->assertNoPrivacyLink(); // Try to submit a correct answer for the CAPTCHA, still without required diff --git a/tests/mollom_test.module b/tests/mollom_test.module index 22e34ff..3832d0e 100644 --- a/tests/mollom_test.module +++ b/tests/mollom_test.module @@ -46,6 +46,11 @@ function mollom_test_menu() { 'page arguments' => array('mollom_test_delete_form', 2), 'access callback' => TRUE, ); + $items['mollom-test/form/views/reset'] = array( + 'page callback' => 'mollom_test_views_reset', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -115,6 +120,15 @@ function mollom_test_mollom_form_info($form_id) { } /** + * Page callback; Resets the page view counter. + */ +function mollom_test_views_reset() { + variable_del('mollom_test.form.views'); + cache_clear_all(); + drupal_goto(); +} + +/** * Form builder for Mollom test form. */ function mollom_test_form($form, &$form_state, $mid = NULL) { @@ -137,6 +151,14 @@ function mollom_test_form($form, &$form_state, $mid = NULL) { // Always add an empty field the user can submit. $form_state['storage']['field']['new'] = ''; + // Output a page view counter for page/form cache testing purposes. + $count = variable_get('mollom_test.form.views', 1); + $reset_link = l('Reset', 'mollom-test/form/views/reset', array('query' => drupal_get_destination())); + $form['views'] = array( + '#markup' => '

' . 'Views: ' . $count++ . ' ' . $reset_link . '

', + ); + variable_set('mollom_test.form.views', $count); + $form['#tree'] = TRUE; $form['mid'] = array( '#type' => 'hidden', diff --git a/tests/mollom_test_server.module b/tests/mollom_test_server.module index f4bef2b..2f2bbb0 100644 --- a/tests/mollom_test_server.module +++ b/tests/mollom_test_server.module @@ -97,6 +97,9 @@ function mollom_test_server_rest_get_auth_header() { * Delivery callback for REST API endpoints. */ function mollom_test_server_rest_deliver($page_callback_result) { + // All fake-server responses are not cached. + drupal_page_is_cacheable(FALSE); + drupal_add_http_header('Content-Type', 'application/xml; charset=utf-8'); $xml = new DOMDocument('1.0', 'utf-8');