Index: mollom.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.admin.inc,v retrieving revision 1.1.2.44 diff -u -p -r1.1.2.44 mollom.admin.inc --- mollom.admin.inc 16 Oct 2010 16:05:13 -0000 1.1.2.44 +++ mollom.admin.inc 16 Oct 2010 16:39:37 -0000 @@ -13,9 +13,8 @@ function mollom_admin_form_list() { _mollom_testing_mode_warning(); $modes = array( - MOLLOM_MODE_DISABLED => t('No protection'), - MOLLOM_MODE_CAPTCHA => t('CAPTCHA only'), - MOLLOM_MODE_ANALYSIS => t('Text analysis and CAPTCHA backup'), + MOLLOM_MODE_ANALYSIS => t('Text analysis'), + MOLLOM_MODE_CAPTCHA => t('CAPTCHA'), ); $header = array( @@ -29,7 +28,10 @@ function mollom_admin_form_list() { $mollom_form = mollom_form_load($form_id); $rows[] = array( $mollom_form['title'], - $modes[$mollom_form['mode']], + t('!protection-mode (@discard)', array( + '!protection-mode' => $modes[$mollom_form['mode']], + '@discard' => $mollom_form['discard'] ? t('discard') : t('retain'), + )), l(t('Configure'), 'admin/settings/mollom/manage/' . $form_id), l(t('Unprotect'), 'admin/settings/mollom/unprotect/' . $form_id), ); @@ -147,7 +149,7 @@ function mollom_admin_configure_form(&$f '#type' => 'radios', '#title' => t('Protection mode'), '#options' => $modes, - '#default_value' => $mollom_form['mode'], + '#default_value' => isset($mollom_form['mode']) ? $mollom_form['mode'] : key($modes), ); if (!empty($mollom_form['elements'])) { @@ -169,6 +171,18 @@ function mollom_admin_configure_form(&$f '#access' => FALSE, ); + $form['mollom']['discard'] = array( + '#type' => 'radios', + '#title' => t('When a post is blocked'), + '#default_value' => $mollom_form['discard'], + '#options' => array( + 1 => t('Automatically discard'), + 0 => t('Retain for manual moderation'), + ), + // Only possible for forms supporting moderation of unpublished posts. + '#access' => !empty($mollom_form['moderation callback']), + ); + // Form elements defined by hook_mollom_form_info() use the // 'parent][child' syntax, which Form API also uses internally for // form_set_error(), and which allows us to recurse into nested fields Index: mollom.api.php =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.api.php,v retrieving revision 1.1.2.7 diff -u -p -r1.1.2.7 mollom.api.php --- mollom.api.php 12 Sep 2010 23:44:14 -0000 1.1.2.7 +++ mollom.api.php 16 Oct 2010 16:07:48 -0000 @@ -150,6 +150,9 @@ * $form_info = array( * // Optional: User permission list to skip Mollom's protection for. * 'bypass access' => array('administer instant messages'), + * // Optional: Function to invoke to put a bad form submission into a + * // moderation queue instead of discarding it. + * 'moderation callback' => 'im_mollom_form_moderation', * // Optional: To allow textual analysis of the form values, the form * // elements needs to be registered individually. The keys are the * // field keys in $form_state['values']. Sub-keys are noted using "][" @@ -205,6 +208,16 @@ * Additionally, the "post_id" data property always needs to be mapped to a form * element that holds the entity id. * + * When registering a 'moderation callback', then the registered function needs + * to be available when the form is validated, and it is responsible for + * changing the submitted form values in a way that results in an unpublished + * post ending up in a moderation queue: + * @code + * function im_mollom_form_moderation(&$form, &$form_state) { + * $form_state['values']['status'] = 0; + * } + * @endcode + * * @see mollom_node * @see mollom_comment * @see mollom_user @@ -276,6 +289,10 @@ function hook_mollom_form_list() { * current user to determine whether to protect the form with Mollom or do * not validate submitted form values. If the current user has at least one * of the listed permissions, the form will not be protected. + * - moderation callback: (optional) A function name to invoke when a form + * submission would normally be discarded. This allows modules to put such + * posts into a moderation queue (i.e., to accept but not publish them) by + * altering the $form or $form_state that are passed by reference. * - mail ids: (optional) An array of mail IDs that will be sent as a result * of this form being submitted. When these mails are sent, a 'report to * Mollom' link will be included at the bottom of the mail body. Be sure to Index: mollom.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.install,v retrieving revision 1.2.2.34 diff -u -p -r1.2.2.34 mollom.install --- mollom.install 16 Oct 2010 16:05:13 -0000 1.2.2.34 +++ mollom.install 16 Oct 2010 17:49:17 -0000 @@ -113,6 +113,13 @@ function mollom_schema() { 'not null' => TRUE, 'default' => 0, ), + 'moderate' => array( + 'description' => 'Whether the content needs to be moderated.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 0, + ), // Server response columns are NULL by default, because any default value // would have an unintended meaning. Also, values are stored in individual // columns, so as to be able to join and filter/sort on these values for @@ -172,6 +179,13 @@ function mollom_schema() { 'not null' => FALSE, 'serialize' => TRUE, ), + 'discard' => array( + 'description' => 'Whether to discard (1) or retain (0) bad posts.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 1, + ), 'enabled_fields' => array( 'description' => 'Form elements to analyze.', 'type' => 'text', @@ -594,3 +608,29 @@ function mollom_update_6116() { db_add_index($ret, 'mollom', 'session_id', array('session_id')); return $ret; } + +/** + * Add {mollom_form}.discard and {mollom}.moderate columns. + */ +function mollom_update_6117() { + $ret = array(); + if (!db_column_exists('mollom_form', 'discard')) { + db_add_field($ret, 'mollom_form', 'discard', array( + 'description' => 'Whether to discard (1) or retain (0) bad posts.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 1, + )); + } + if (!db_column_exists('mollom', 'moderate')) { + db_add_field($ret, 'mollom', 'moderate', array( + 'description' => 'Whether the content needs to be moderated.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 0, + )); + } + return $ret; +} Index: mollom.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.module,v retrieving revision 1.2.2.174 diff -u -p -r1.2.2.174 mollom.module --- mollom.module 16 Oct 2010 16:05:13 -0000 1.2.2.174 +++ mollom.module 16 Oct 2010 17:45:52 -0000 @@ -470,6 +470,29 @@ function mollom_data_save($data) { } /** + * Updates stored Mollom session data to mark a bad post as moderated. + * + * @param $entity + * The entity type of the moderated post. + * @param $id + * The entity id of the moderated post. + */ +function mollom_data_moderate($entity, $id) { + $data = mollom_data_load($entity, $id); + // Nothing to do, if no data exists. + if (!$data) { + return; + } + + // Report the session to Mollom. + _mollom_send_feedback($data->session_id, 'ham'); + + // Mark the session data as moderated. + $data->moderate = 0; + mollom_data_save($data); +} + +/** * Deletes a Mollom session data record from the database. * * @param $entity @@ -656,6 +679,7 @@ function mollom_form_alter(&$form, &$for // Add Mollom form validation handlers. $form['#validate'][] = 'mollom_validate_analysis'; $form['#validate'][] = 'mollom_validate_captcha'; + $form['#validate'][] = 'mollom_validate_post'; // Add a submit handler to remove form state storage. $form['#submit'][] = 'mollom_form_submit'; @@ -816,6 +840,7 @@ function mollom_form_info($form_id, $mod 'module' => $module, 'entity' => NULL, 'mode' => NULL, + 'discard' => TRUE, 'bypass access' => array(), 'elements' => array(), 'mapping' => array(), @@ -1347,6 +1372,7 @@ function mollom_process_mollom($element, 'require_analysis' => $element['#mollom_form']['mode'] == MOLLOM_MODE_ANALYSIS, 'require_captcha' => $element['#mollom_form']['mode'] == MOLLOM_MODE_CAPTCHA, 'passed_captcha' => FALSE, + 'require_moderation' => FALSE, 'response' => array( 'session_id' => '', ), @@ -1354,6 +1380,13 @@ function mollom_process_mollom($element, } $form_state['mollom'] += $element['#mollom_form']; + // By default, bad form submissions are discarded, unless the form was + // configured to moderate bad posts. 'discard' may only be FALSE, if there is + // a valid 'moderation callback'. Otherwise, it must be TRUE. + if (empty($form_state['mollom']['moderation callback']) || !function_exists($form_state['mollom']['moderation callback'])) { + $form_state['mollom']['discard'] = TRUE; + } + // Add the Mollom session element. $element['session_id'] = array( '#type' => 'hidden', @@ -1466,6 +1499,9 @@ function mollom_validate_analysis(&$form // Store the response returned by Mollom. $form_state['mollom']['response'] = $result; + // Prepare watchdog message teaser text. + $teaser = truncate_utf8(strip_tags(isset($data['post_title']) ? $data['post_title'] : isset($data['post_body']) ? $data['post_body'] : '--'), 40); + // Handle the spam check result. // The Mollom backend is remembering results of previous mollom.checkContent // invocations for a single user/post session. When content is re-checked @@ -1474,7 +1510,6 @@ function mollom_validate_analysis(&$form // the spam check led to a MOLLOM_ANALYSIS_UNSURE result, and the user solved // the CAPTCHA correctly, subsequent spam check results will likely be // MOLLOM_ANALYSIS_HAM (though not guaranteed). - $teaser = truncate_utf8(strip_tags(isset($data['post_title']) ? $data['post_title'] : isset($data['post_body']) ? $data['post_body'] : '--'), 40); if (isset($result['spam'])) { switch ($result['spam']) { case MOLLOM_ANALYSIS_HAM: @@ -1488,7 +1523,12 @@ function mollom_validate_analysis(&$form case MOLLOM_ANALYSIS_SPAM: $form_state['mollom']['require_captcha'] = FALSE; - form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.')); + if ($form_state['mollom']['discard']) { + form_set_error('mollom', t('Your submission has triggered the spam filter and will not be accepted.')); + } + else { + $form_state['mollom']['require_moderation'] = TRUE; + } _mollom_watchdog(array( 'Spam: %teaser' => array('%teaser' => $teaser), 'Data:
@data
' => array('@data' => $data), @@ -1667,6 +1707,20 @@ function mollom_pre_render_mollom($eleme } /** + * Form validation handler to perform post-validation tasks. + */ +function mollom_validate_post(&$form, &$form_state) { + // Retain a post instead of discarding it. If 'discard' is FALSE, then the + // 'moderation callback' is responsible for altering $form_state in a way that + // the post ends up in a moderation queue. Most callbacks will only want to + // set or change a value in $form_state. + if ($form_state['mollom']['require_moderation']) { + $function = $form_state['mollom']['moderation callback']; + $function($form, $form_state); + } +} + +/** * Form submit handler to flush Mollom session and form information from cache. */ function mollom_form_submit($form, &$form_state) { @@ -1690,10 +1744,13 @@ function mollom_form_submit($form, &$for $values = mollom_form_get_values($form_state['values'], array(), $form_state['mollom']['mapping']); // We only consider non-empty and non-zero values as valid entity ids. if (!empty($values['post_id'])) { + // Save the Mollom session data. $data = (object) $form_state['mollom']['response']; $data->entity = $form_state['mollom']['entity']; $data->id = $values['post_id']; $data->form_id = $form_state['mollom']['form_id']; + // Set the moderation flag for forms accepting bad posts. + $data->moderate = $form_state['mollom']['require_moderation']; mollom_data_save($data); } } @@ -2194,6 +2251,7 @@ function node_mollom_form_info($form_id) // @todo This is incompatible with node access. 'bypass access' => array('administer nodes', 'edit any ' . $type->type . ' content'), 'bundle' => $type->type, + 'moderation callback' => 'node_mollom_form_moderation', 'elements' => array(), 'mapping' => array( 'post_id' => 'nid', @@ -2232,6 +2290,8 @@ function mollom_nodeapi($node, $op) { $data->entity = 'node'; $data->id = $node->nid; $data->form_id = $mollom['form_id']; + // Set the moderation flag for forms accepting bad posts. + $data->moderate = $mollom['require_moderation']; mollom_data_save($data); } elseif ($op == 'delete') { @@ -2240,6 +2300,24 @@ function mollom_nodeapi($node, $op) { } /** + * Mollom form moderation callback for nodes. + */ +function node_mollom_form_moderation(&$form, &$form_state) { + $form_state['values']['status'] = 0; +} + +/** + * Implements hook_node_presave(). + */ +function mollom_node_presave($comment) { + // If an existing node is published and we have session data stored for it, + // mark the data as moderated. + if (!empty($node->nid) && $node->status) { + mollom_data_moderate('node', $node->nid); + } +} + +/** * Implements hook_form_FORMID_alter(). * * Hook into the mass comment administration page and add some operations to @@ -2298,6 +2376,7 @@ function comment_mollom_form_info($form_ $form_info = array( 'mode' => MOLLOM_MODE_ANALYSIS, 'bypass access' => array('administer comments'), + 'moderation callback' => 'comment_mollom_form_moderation', 'elements' => array( 'subject' => t('Subject'), 'comment' => t('Comment'), @@ -2363,6 +2442,13 @@ function mollom_comment_form_validate($f } /** + * Mollom form moderation callback for comments. + */ +function comment_mollom_form_moderation(&$form, &$form_state) { + $form_state['values']['status'] = COMMENT_NOT_PUBLISHED; +} + +/** * Implements hook_comment(). */ function mollom_comment($comment, $op) { @@ -2372,9 +2458,28 @@ function mollom_comment($comment, $op) { $data->entity = 'comment'; $data->id = (isset($comment['cid']) ? $comment['cid'] : $comment->cid); $data->form_id = $mollom['form_id']; + // Set the moderation flag for forms accepting bad posts. + $data->moderate = $mollom['require_moderation']; mollom_data_save($data); } - elseif ($op == 'delete') { + // If an existing comment is published and we have session data stored for it, + // mark the data as moderated. Since Drupal 6 does not expose a + // hook_comment_presave(), the operation needs to be performed in the update + // case. In case a comment is updated through a comment form submission, this + // may mean that above code will insert or update the data, and the following + // code may update it again. However, the resulting logic is easier to follow + // this way. + if ($op == 'publish') { + if (is_array($comment)) { + if (!empty($comment['cid'])) { + mollom_data_moderate('comment', $comment['cid']); + } + } + elseif (!empty($comment->cid)) { + mollom_data_moderate('comment', $comment->cid); + } + } + if ($op == 'delete') { mollom_data_delete('comment', $comment->cid); } } @@ -2430,7 +2535,6 @@ function user_mollom_form_list() { ); $forms['user_pass'] = array( 'title' => t('User password request form'), - 'entity' => 'user', ); return $forms; } @@ -2444,6 +2548,7 @@ function user_mollom_form_info($form_id) $form_info = array( 'mode' => MOLLOM_MODE_CAPTCHA, 'bypass access' => array('administer users'), + 'moderation callback' => 'user_mollom_form_moderation', 'mapping' => array( 'post_id' => 'uid', 'author_name' => 'name', @@ -2468,6 +2573,13 @@ function user_mollom_form_info($form_id) } /** + * Mollom form moderation callback for user accounts. + */ +function user_mollom_form_moderation(&$form, &$form_state) { + $form_state['values']['status'] = 0; +} + +/** * @} End of "name mollom_user". */ Index: tests/mollom.test =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/tests/mollom.test,v retrieving revision 1.1.2.65 diff -u -p -r1.1.2.65 mollom.test --- tests/mollom.test 16 Oct 2010 16:05:13 -0000 1.1.2.65 +++ tests/mollom.test 16 Oct 2010 17:49:59 -0000 @@ -83,11 +83,16 @@ class MollomWebTestCase extends DrupalWe $this->resetSessionID(); $this->messages = array(); + // @see DrupalWebTestCase::setUp() + $modules = func_get_args(); + if (isset($modules[0]) && is_array($modules[0])) { + $modules = $modules[0]; + } + // If not explicitly disabled by a test, setup with Mollom and default admin // user. if (empty($this->disableDefaultSetup)) { // Call parent::setUp() allowing Mollom test cases to pass further modules. - $modules = func_get_args(); $modules[] = 'mollom'; $modules[] = 'dblog'; call_user_func_array(array($this, 'parent::setUp'), $modules); @@ -102,7 +107,6 @@ class MollomWebTestCase extends DrupalWe )); } else { - $modules = func_get_args(); $modules[] = 'dblog'; call_user_func_array(array($this, 'parent::setUp'), $modules); } @@ -1086,7 +1090,11 @@ class MollomAccessTestCase extends Mollo $this->drupalGet('node/' . $node->nid); $this->clickLink('edit'); - $this->drupalPost(NULL, array('subject' => '', 'comment' => 'spam'), t('Preview')); + $edit = array( + 'subject' => '', + 'comment' => 'spam', + ); + $this->drupalPost(NULL, $edit, t('Preview')); $this->assertNoText($this->spam_message); $this->drupalPost(NULL, array(), t('Save')); @@ -1934,7 +1942,7 @@ class MollomCommentFormTestCase extends $cid = db_result(db_query("SELECT cid FROM {comments} WHERE comment = '%s' ORDER BY timestamp DESC", array($edit['comment']))); $this->assertMollomData('comment', $cid, $session_id); - // Try to save a new 'spam' comment; it should be rejected, with no CAPTCHA + // Try to save a new 'spam' comment; it should be discarded, with no CAPTCHA // appearing on the page. $this->resetSessionID(); $this->drupalGet('comment/reply/' . $this->node->nid); @@ -1945,7 +1953,7 @@ class MollomCommentFormTestCase extends $this->assertCommentCount($this->node->nid, $original_number_of_comments); $this->assertPrivacyLink(); - // Try to save again; it should be rejected, with no CAPTCHA. + // Try to save again; it should be discarded, with no CAPTCHA. $this->assertSpamSubmit(NULL, array('comment'), array(), t('Save')); $session_id = $this->assertSessionIDInForm(); $this->assertCommentCount($this->node->nid, $original_number_of_comments); @@ -2449,6 +2457,137 @@ class MollomDataTestCase extends MollomW } /** + * Tests text analysis functionality. + * + * @todo Verify that no button captions appear in the data that is sent for + * analyis; i.e., no "Add" string for mollom_test_form. + */ +class MollomAnalysisTestCase extends MollomWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Text analysis', + 'description' => 'Tests text analysis functionality.', + 'group' => 'Mollom', + ); + } + + function setUp() { + // @todo This is the new default setUp() procedure for all new tests, which + // should be moved into MollomWebTestCase::setUp() after cleaning up the + // tests. + $this->disableDefaultSetup = TRUE; + parent::setUp(array('mollom', 'mollom_test')); + $this->setKeys(); + $this->assertValidKeys(); + + $this->admin_user = $this->drupalCreateUser(array( + 'access administration pages', + 'administer mollom', + )); + $this->web_user = $this->drupalCreateUser(array('access content')); + } + + /** + * Tests retaining bad posts and moderating them. + */ + function testRetain() { + $this->drupalLogin($this->admin_user); + // Verify that mollom_basic_test_form cannot be configured to put posts into + // moderation queue. + $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->setProtection('mollom_test_form', MOLLOM_MODE_ANALYSIS, NULL, array( + 'mollom[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('spam', $data->spam, MOLLOM_ANALYSIS_SPAM); + $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('spam', $data->spam, MOLLOM_ANALYSIS_SPAM); + $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('spam', $data->spam, MOLLOM_ANALYSIS_SPAM); + $this->assertSame('moderate', $data->moderate, 0); + + // Verify that neither ham or unsure spam posts, nor non-profane posts are + // marked for moderation. + $this->drupalLogout(); + $expectations = array( + 'ham' => array('spam' => MOLLOM_ANALYSIS_HAM, 'profanity' => 0), + 'unsure' => array('spam' => MOLLOM_ANALYSIS_UNSURE, 'profanity' => 0), + $this->randomString() => array('spam' => MOLLOM_ANALYSIS_UNSURE, 'profanity' => 0), + ); + foreach ($expectations as $body => $expected) { + $edit = array( + 'title' => $this->randomString(), + 'body' => $body, + ); + $this->drupalPost('mollom-test/form', $edit, 'Submit'); + if ($expected['spam'] == MOLLOM_ANALYSIS_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('spam', $data->spam, $expected['spam']); + $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 report to Mollom functionality. */ class MollomReportTestCase extends MollomWebTestCase { Index: tests/mollom_test.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/tests/mollom_test.info,v retrieving revision 1.1.2.3 diff -u -p -r1.1.2.3 mollom_test.info --- tests/mollom_test.info 31 Mar 2010 13:58:41 -0000 1.1.2.3 +++ tests/mollom_test.info 16 Oct 2010 17:52:49 -0000 @@ -4,3 +4,6 @@ description = Testing module for Mollom core = 6.x package = Testing hidden = TRUE +; The testing module requires PHP 5 in order to simplify maintenance and +; synchronization with HEAD. +php = 5 Index: tests/mollom_test.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/tests/mollom_test.install,v retrieving revision 1.1.2.1 diff -u -p -r1.1.2.1 mollom_test.install --- tests/mollom_test.install 5 Feb 2010 21:02:33 -0000 1.1.2.1 +++ tests/mollom_test.install 16 Oct 2010 16:07:48 -0000 @@ -13,11 +13,29 @@ function mollom_test_schema() { $schema['mollom_test'] = array( 'description' => 'Stores testing data for Mollom test form.', 'fields' => array( - 'mid' => array('type' => 'serial', 'not null' => TRUE, + 'mid' => array( 'description' => 'Primary key: Unique mollom_test entity ID.', + 'type' => 'serial', + 'not null' => TRUE, ), - 'body' => array('type' => 'text', 'not null' => TRUE, - 'description' => 'The body field of mollom_test_form().', + 'title' => array( + 'description' => 'Title of the post.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'body' => array( + 'description' => 'Body of the post.', + 'type' => 'text', + 'not null' => TRUE, + ), + 'status' => array( + 'description' => 'Publishing status.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 1, ), ), 'primary key' => array('mid'), Index: tests/mollom_test.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/mollom/tests/mollom_test.module,v retrieving revision 1.1.2.11 diff -u -p -r1.1.2.11 mollom_test.module --- tests/mollom_test.module 6 Oct 2010 23:55:31 -0000 1.1.2.11 +++ tests/mollom_test.module 16 Oct 2010 17:02:23 -0000 @@ -18,6 +18,7 @@ function mollom_test_xmlrpc() { 'mollom.checkContent' => 'mollom_test_check_content', 'mollom.getImageCaptcha' => 'mollom_test_get_captcha', 'mollom.checkCaptcha' => 'mollom_test_check_captcha', + 'mollom.sendFeedback' => 'mollom_test_send_feedback', ); } @@ -176,6 +177,20 @@ function mollom_test_check_captcha($data } /** + * XML-RPC callback for mollom.sendFeedback to send feedback for a moderated post. + */ +function mollom_test_send_feedback($data) { + $storage = variable_get(__FUNCTION__, array()); + $storage[] = $data; + variable_set(__FUNCTION__, $storage); + + if (in_array($data['feedback'], array('spam', 'profanity', 'low-quality', 'unwanted', 'ham'))) { + return TRUE; + } + xmlrpc_error(MOLLOM_ERROR); +} + +/** * Implements hook_menu(). */ function mollom_test_menu() { @@ -189,12 +204,34 @@ function mollom_test_menu() { } /** + * Implements hook_forms(). + */ +function mollom_forms() { + $forms['mollom_basic_test_form'] = array( + 'callback' => 'mollom_test_form', + ); + return $forms; +} + +/** * Implements hook_mollom_form_list(). */ function mollom_test_mollom_form_list() { $forms['mollom_test_form'] = array( 'title' => 'Mollom test form', 'entity' => 'mollom_test', + 'entity delete multiple callback' => 'mollom_test_delete_multiple', + 'moderation callback' => 'mollom_test_mollom_form_moderation', + ); + // The basic test form is identical to the mollom_test_form, but only + // registers minimal information (e.g., no entity or moderation callback) to + // integrate with Mollom. + $forms['mollom_basic_test_form'] = array( + 'title' => 'Mollom basic test form', + ); + // Same as above, but supports elements for text analysis. + $forms['mollom_basic_elements_test_form'] = array( + 'title' => 'Mollom basic elements test form', ); return $forms; } @@ -203,10 +240,11 @@ function mollom_test_mollom_form_list() * Implements hook_mollom_form_info(). */ function mollom_test_mollom_form_info($form_id) { + if ($form_id == 'mollom_basic_test_form') { + return array(); + } $form_info = array( - 'title' => 'Mollom test form', 'bypass access' => array('administer mollom'), - 'entity' => 'mollom_test', 'elements' => array( 'title' => 'Title', 'body' => 'Body', @@ -227,20 +265,22 @@ function mollom_test_mollom_form_info($f * Form builder for Mollom test form. */ function mollom_test_form(&$form_state, $mid = NULL) { - if (empty($form_state['storage'])) { - $body = ''; - if (isset($mid)) { - $body = db_result(db_query("SELECT body FROM {mollom_test} WHERE mid = %d", $mid)); - } - $form_state['storage'] = array( - 'mid' => $mid, - 'title' => '', - 'body' => $body, - 'exclude' => '', - 'parent' => array('child' => ''), - 'field' => array(), - ); - } + // Drupal 6 defaults 'storage' to NULL. + if (!isset($form_state['storage'])) { + $form_state['storage'] = array(); + } + if (isset($mid) && ($record = mollom_test_load($mid))) { + $form_state['storage'] = $record; + } + $form_state['storage'] += array( + 'mid' => $mid, + 'title' => '', + 'body' => '', + 'exclude' => '', + 'parent' => array('child' => ''), + 'field' => array(), + 'status' => 1, + ); // Always add an empty field the user can submit. $form_state['storage']['field']['new'] = ''; @@ -288,11 +328,18 @@ function mollom_test_form(&$form_state, $form['field']['submit'] = array( '#type' => 'submit', '#value' => 'Add', - '#validate_parents' => array('field'), '#submit' => array('mollom_test_form_field_submit'), '#weight' => 1000, ); + $form['status'] = array( + '#type' => 'checkbox', + '#title' => 'Published', + '#default_value' => $form_state['storage']['status'], + // For simplicity, re-use Mollom module's administration permission. + '#access' => user_access('administer mollom'), + ); + $form['submit'] = array('#type' => 'submit', '#value' => 'Submit'); return $form; @@ -302,10 +349,16 @@ function mollom_test_form(&$form_state, * Form element submit handler for mollom_test_form(). */ function mollom_test_form_field_submit($form, &$form_state) { - // Store the new value and clear out the 'new' field. + // Remove all empty values of the multiple value field. + $form_state['values']['field'] = array_filter($form_state['values']['field']); + // Update the storage with submitted values. $form_state['storage'] = $form_state['values']; - $form_state['storage']['field'][] = $form_state['values']['field']['new']; + // Store the new value and clear out the 'new' field. + if (isset($form_state['values']['field']['new'])) { + $form_state['storage']['field'][] = $form_state['values']['field']['new']; + } unset($form_state['input']['field']['new']); + $form_state['rebuild'] = TRUE; } @@ -319,6 +372,11 @@ function mollom_test_form_submit($form, $form_state['values']['field'][] = $form_state['values']['field']['new']; unset($form_state['values']['field']['new']); + // Allow modules to alter the record before saving. This is done for code + // consistency only. + // @see mollom_mollom_test_presave() + module_invoke_all('mollom_test_presave', $form_state['values']); + // Store submission. $update = !empty($form_state['values']['mid']) ? 'mid' : array(); drupal_write_record('mollom_test', $form_state['values'], $update); @@ -339,3 +397,39 @@ function mollom_test_form_alter(&$form, $form_state['mollom']['require_captcha'] = FALSE; } } + +/** + * Mollom form moderation callback for a mollom_test record. + */ +function mollom_test_mollom_form_moderation(&$form, &$form_state) { + $form_state['values']['status'] = 0; +} + +/** + * Implements hook_mollom_test_presave() on behalf of mollom.module. + */ +function mollom_mollom_test_presave($record) { + // If an existing record is published and we have session data stored for it, + // mark the data as moderated. + if (!empty($record['mid']) && $record['status']) { + mollom_data_moderate('mollom_test', $record['mid']); + } +} + +/** + * Loads a {mollom_test} data record by id. + */ +function mollom_test_load($mid) { + return db_fetch_array(db_query('SELECT * FROM {mollom_test} WHERE mid = %d', array($mid))); +} + +/** + * Deletes multiple stored {mollom_test} data records. + * + * @param $mids + * The mids to delete. + */ +function mollom_test_delete_multiple(array $mids) { + $placeholders = db_placeholders($mids); + db_query("DELETE FROM {mollom_test} WHERE mid IN ($placeholders)", $mids); +}