diff --git a/mollom.install b/mollom.install index 5be0fa5..7680669 100644 --- a/mollom.install +++ b/mollom.install @@ -208,6 +208,23 @@ function mollom_schema() { 'not null' => TRUE, 'default' => '', ), + // Optional entity type and bundle columns are required for protected + // entity forms, in order to determine whether report options should + // appear on a delete confirmation form. Column lengths are copied from + // {field_config_instance}. + // @see field_schema() + 'entity' => array( + 'description' => 'Optional: Entity type of the form.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => FALSE, + ), + 'bundle' => array( + 'description' => 'Optional: Entity bundle of the form.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + ), 'mode' => array( 'description' => 'Protection mode for the form.', 'type' => 'int', @@ -960,3 +977,51 @@ function mollom_update_7204() { } } +/** + * Add {mollom_form}.entity and .bundle fields. + */ +function mollom_update_7205() { + if (!db_field_exists('mollom_form', 'entity')) { + db_add_field('mollom_form', 'entity', array( + 'description' => 'Optional: Entity type of the form.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => FALSE, + )); + } + if (!db_field_exists('mollom_form', 'bundle')) { + db_add_field('mollom_form', 'bundle', array( + 'description' => 'Optional: Entity bundle of the form.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + )); + } + // Populate entity type and bundle values for core entity types. + // Any other values for contributed entities need to be updated manually, + // which is possible by simply updating the form protection through the + // administration interface. + $result = db_query('SELECT form_id, module FROM {mollom_form}'); + foreach ($result as $mollom_form) { + if ($mollom_form->module == 'node') { + $bundle = substr($mollom_form->form_id, 0, -strlen('_node_form')); + db_update('mollom_form') + ->condition('form_id', $mollom_form->form_id) + ->fields(array('entity' => 'node', 'bundle' => $bundle)) + ->execute(); + } + elseif ($mollom_form->module == 'comment') { + $bundle = substr($mollom_form->form_id, 0, -strlen('_form')); + db_update('mollom_form') + ->condition('form_id', $mollom_form->form_id) + ->fields(array('entity' => 'comment', 'bundle' => $bundle)) + ->execute(); + } + elseif ($mollom_form->module == 'user') { + db_update('mollom_form') + ->condition('form_id', $mollom_form->form_id) + ->fields(array('entity' => 'user', 'bundle' => 'user')) + ->execute(); + } + } +} diff --git a/mollom.module b/mollom.module index 90b6058..cc1e0cb 100644 --- a/mollom.module +++ b/mollom.module @@ -762,6 +762,24 @@ function mollom_form_alter(&$form, &$form_state, $form_id) { $mollom_form_id = $forms['delete'][$form_id]; $module = $forms['protected'][$mollom_form_id]; $form_info = mollom_form_load($mollom_form_id, $module); + + // For entities, there is only one delete confirmation form per entity type. + // But not all of its bundles may be protected. We therefore need to figure + // out whether the bundle of the entity being deleted is protected - which + // is a reverse-mapping that does not exist in D7. + $is_protected = TRUE; + $is_entity = !empty($form_info['entity']); + $has_entity_argument = isset($form_state['build_info']['args'][0]) && is_object($form_state['build_info']['args'][0]); + if ($is_entity && $has_entity_argument) { + list(, , $bundle) = entity_extract_ids($form_info['entity'], $form_state['build_info']['args'][0]); + $is_protected = db_query_range('SELECT 1 FROM {mollom_form} WHERE entity = :entity AND bundle = :bundle', 0, 1, array( + ':entity' => $form_info['entity'], + ':bundle' => $bundle, + ))->fetchField(); + } + if (!$is_protected) { + return; + } // Check access, if there is a 'report access' permission list. if (isset($form_info['report access'])) { $access = FALSE; @@ -880,13 +898,27 @@ function mollom_form_list() { * The form id to return information for. * @param $module * The module name $form_id belongs to. + * @param array $form_list + * (optional) The return value of hook_mollom_form_list() of $module, if + * already kown. Primarily used by mollom_form_list(). */ -function mollom_form_info($form_id, $module) { - $form_info = module_invoke($module, 'mollom_form_info', $form_id); - if (empty($form_info)) { - $form_info = array(); +function mollom_form_info($form_id, $module, $form_list = NULL) { + $form_info = array(); + + // Fetch the basic form information from hook_mollom_form_list() first. + // This makes the integrating module (needlessly) rebuild all of its available + // forms, but the base properties are absolutely required here, so we can + // apply the default properties below. + if (!isset($form_list)) { + $form_list = module_invoke($module, 'mollom_form_list'); + } + if (isset($form_list[$form_id])) { + $form_info += $form_list[$form_id]; } + // Any information in hook_mollom_form_info() overrides the list info. + $form_info = array_merge($form_info, module_invoke($module, 'mollom_form_info', $form_id)); + // Ensure default properties. $form_info += array( // Base properties. @@ -894,6 +926,7 @@ function mollom_form_info($form_id, $module) { 'title' => $form_id, 'module' => $module, 'entity' => NULL, + 'bundle' => NULL, // Configuration properties. 'mode' => NULL, 'checks' => array(), @@ -963,7 +996,7 @@ function mollom_form_new($form_id) { if (isset($form_list[$form_id])) { $mollom_form += $form_list[$form_id]; } - $mollom_form += mollom_form_info($form_id, $form_list[$form_id]['module']); + $mollom_form += mollom_form_info($form_id, $form_list[$form_id]['module'], $form_list); // Enable all fields for textual analysis by default. if (!empty($mollom_form['elements'])) { @@ -989,11 +1022,22 @@ function mollom_form_load($form_id) { $mollom_form['enabled_fields'] = unserialize($mollom_form['enabled_fields']); // Attach form registry information. - $form_list = module_invoke($mollom_form['module'], 'mollom_form_list'); - if (isset($form_list[$form_id])) { - $mollom_form += $form_list[$form_id]; - } - $mollom_form += mollom_form_info($form_id, $mollom_form['module']); + $form_info = mollom_form_info($form_id, $mollom_form['module']); + $mollom_form += $form_info; + + // Override entity type and bundle information with current values from + // the form registry. These properties were originally not stored in + // {mollom_form} and only introduced in 7.x-2.2. The update path is only + // able to map entity types/bundles in Drupal core. Any other form + // protections need to be updated manually. That is the situation in which + // $mollom_form has NULL values from the database, but the form registry + // actually contains the proper values. + // @todo Remove in later versions. + // @todo Clean up _list() + _info() hook API design and pass the base + // $form_info from _list() into _info(), so that it extends that + // definition instead of replacing it. + $mollom_form['entity'] = $form_info['entity']; + $mollom_form['bundle'] = $form_info['bundle']; cache_set($cid, $mollom_form); } diff --git a/tests/mollom.test b/tests/mollom.test index f4d4c70..a3ea09c 100644 --- a/tests/mollom.test +++ b/tests/mollom.test @@ -139,6 +139,7 @@ class MollomWebTestCase extends DrupalWebTestCase { 'administer content types', 'administer permissions', 'administer users', + 'bypass node access', ); if (module_exists('comment')) { $permissions[] = 'access comments'; @@ -2599,6 +2600,72 @@ class MollomNodeFormTestCase extends MollomWebTestCase { $this->assertUrl('node/' . $this->node->nid); $this->assertMollomData('node', $this->node->nid); } + + /** + * Tests appearance of feedback options on node delete forms. + */ + function testFeedback() { + // Create a second node type, which is not protected. + $this->drupalCreateContentType(array('type' => 'unprotected', 'name' => 'Unprotected')); + user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'create unprotected content', + 'delete own unprotected content', + 'delete own article content', + )); + + // Protect the article node type. + $this->drupalLogin($this->admin_user); + $this->setProtection('article_node_form', MOLLOM_MODE_ANALYSIS); + $this->drupalLogout(); + + // Login and submit a protected article node. + $this->drupalLogin($this->web_user); + $this->drupalGet('node/add/article'); + $edit = array( + 'title' => 'protected ham', + 'body[und][0][value]' => 'ham', + ); + $this->drupalPost(NULL, $edit, t('Save')); + $this->node = $this->drupalGetNodeByTitle($edit['title']); + $this->assertUrl('node/' . $this->node->nid); + $this->assertMollomData('node', $this->node->nid); + + // Verify that no feedback options appear on the delete confirmation form + // for the node author. + $this->drupalGet('node/' . $this->node->nid . '/delete'); + $this->assertResponse(200); + $this->assertNoText(t('Report as inappropriate')); + + // Verify that feedback options appear for the admin user. + $this->drupalLogin($this->admin_user); + $this->drupalGet('node/' . $this->node->nid . '/delete'); + $this->assertResponse(200); + $this->assertText(t('Report as inappropriate')); + + // Login and submit an unprotected node. + $this->drupalLogin($this->web_user); + $this->drupalGet('node/add/unprotected'); + $edit = array( + 'title' => 'unprotected spam', + 'body[und][0][value]' => 'spam', + ); + $this->drupalPost(NULL, $edit, t('Save')); + $this->node = $this->drupalGetNodeByTitle($edit['title']); + $this->assertUrl('node/' . $this->node->nid); + $this->assertNoMollomData('node', $this->node->nid); + + // Verify that no feedback options appear on the delete confirmation form + // for the node author. + $this->drupalGet('node/' . $this->node->nid . '/delete'); + $this->assertResponse(200); + $this->assertNoText(t('Report as inappropriate')); + + // Verify that no feedback options appear for the admin user. + $this->drupalLogin($this->admin_user); + $this->drupalGet('node/' . $this->node->nid . '/delete'); + $this->assertResponse(200); + $this->assertNoText(t('Report as inappropriate')); + } } class MollomCommentFormTestCase extends MollomWebTestCase {