? blacklist-ui.patch
Index: mollom.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.admin.inc,v
retrieving revision 1.10
diff -u -r1.10 mollom.admin.inc
--- mollom.admin.inc	30 Jan 2010 19:59:03 -0000	1.10
+++ mollom.admin.inc	31 Jan 2010 15:02:01 -0000
@@ -267,6 +267,218 @@
 }
 
 /**
+ * Menu callback that renders the blacklist overview page.
+ * @todo Do we want to keep both blacklist on the same page?
+ */
+function mollom_admin_blacklist() {
+  $output  = '<h2>'. t('Text blacklist') .'</h2>';
+  $output .= drupal_render(drupal_get_form('mollom_admin_blacklist_text_form'));
+  $output .= '<h2>'. t('URL blacklist') .'</h2>';
+  $output .= drupal_render(drupal_get_form('mollom_admin_blacklist_url_form'));
+  
+  return $output;
+}
+
+/**
+ * Form builder; declare the text blacklist form.
+ */
+function mollom_admin_blacklist_text_form() {
+
+  $form['match'] = array(
+    '#type' => 'select',
+    '#options' => array(
+      'exact' => t('Exact'),
+      'contains' => t('Contains'),
+    ),
+    '#default_value' => 'exact',
+    '#required' => TRUE,
+  );
+
+  $form['text'] = array(
+    '#type' => 'textfield',
+    '#size' => 30,
+    '#required' => TRUE,
+    '#maxlength' => 64,
+  );
+
+  $form['reason'] = array(
+    '#type' => 'select',
+    '#options' => array(
+      'spam' => t('Spam'),
+      'profanity' => t('Profanity'),
+      'unwanted' => t('Unwanted'),
+    ),
+    '#default_value' => 'spam',
+    '#required' => TRUE,
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+  );
+          
+  return $form;
+}
+
+/**
+ * Form submit handler for saving a text string to the Mollom blacklist.
+ */
+function mollom_admin_blacklist_text_form_submit($form, &$form_state) {
+  
+  $result = mollom('mollom.addBlacklistText', array(
+    'text' => $form_state['values']['text'],
+    'match' => $form_state['values']['match'],
+    'reason' => $form_state['values']['reason'],
+  ));
+
+  // @todo: verify that the call was successful.
+  drupal_set_message(t('The text has been added to the blacklist.'));
+}
+
+/**
+ * Theme function; theme the text blacklist table so we can integrate the form.
+ */
+function theme_mollom_admin_blacklist_text_form($variables) {
+  
+  $form = $variables['form'];
+
+  $header = array(
+    t('Match'),    
+    t('Text'),
+    t('Reason'),
+    t('Operations')
+  );
+  
+  $blacklist = mollom('mollom.listBlacklistText');
+
+  $rows = array();
+  foreach ($blacklist as $entry) {
+    $rows[] = array(
+      check_plain($entry['match']),
+      check_plain($entry['text']),
+      check_plain($entry['reason']),
+      l(t('delete'), 'admin/config/content/mollom/blacklist/delete/text/' . base64_encode($entry['text']))
+    );  
+  }
+  
+  $rows[] = array(
+    drupal_render($form['match']),
+    drupal_render($form['text']),
+    drupal_render($form['reason']),
+    drupal_render($form['submit'])
+  );
+  
+  $output  = theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No words have been added to the blacklist.')));
+  $output .= drupal_render_children($form);
+  
+  return $output;
+}
+
+/**
+ * Form builder; declare the URL blacklist form.
+ */
+function mollom_admin_blacklist_url_form() {
+
+  $form['url'] = array(
+    '#type' => 'textfield',
+    '#size' => 30,
+    '#required' => TRUE,
+    '#field_prefix' => 'http://',
+    '#maxlength' => 64,
+  );
+      
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+  );
+
+  return $form;
+}
+
+/**
+ * Form submit handler for saving an URL to the Mollom blacklist.
+ */
+function mollom_admin_blacklist_url_form_submit($form, &$form_state) {
+  
+  $result = mollom('mollom.addBlacklistURL', array(
+    'url' => 'http://' . $form_state['values']['url'],
+  ));
+  
+  // @todo: verify that the call was successful.
+  drupal_set_message(t('The URL has been added to the blacklist.'));
+}
+
+/**
+ * Theme function; theme the URL blacklist table so we can integrate the form.
+ */
+function theme_mollom_admin_blacklist_url_form($variables) {
+  
+  $form = $variables['form'];
+
+  $header = array(t('URL'),
+    t('Operations')
+  );
+  
+  $blacklist = mollom('mollom.listBlacklistURL');
+
+  $rows = array();
+  foreach ($blacklist as $entry) {
+    $rows[] = array(
+      check_plain($entry['url']),
+      // @todo the URL should be encoded, or we should pass in an ID ...
+      l(t('delete'), 'admin/config/content/mollom/blacklist/delete/url/' . base64_encode($entry['url']))
+    );  
+  }
+  
+  $rows[] = array(
+    drupal_render($form['url']),
+    drupal_render($form['submit'])
+  );
+  
+  $output  = theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No URLs have been added to the blacklist.')));
+  $output .= drupal_render_children($form);
+  
+  return $output;
+}
+
+/**
+ * Form builder; Builds the confirmation form for deleting a blacklist item.
+ *
+ * @ingroup forms
+ * @see mollom_admin_blacklist_delete_submit()
+ */
+function mollom_admin_blacklist_delete($form, &$form_state, $blacklist, $key) {
+  
+  $form['#mollom-blacklist-type'] = $blacklist;
+  $form['#mollom-blacklist-key'] = base64_decode($key);
+
+  return confirm_form(
+    $form,
+    t('Are you sure you want to delete %key from the blacklist?', array('%key' => $form['#mollom-blacklist-key'])),
+    'admin/config/content/mollom/blacklist',
+    t('This action cannot be undone.'),
+    t('Delete'), t('Cancel'),
+    'mollom_admin_blacklist_delete_submit');
+}
+
+/**
+ * Form submit handler to delete an entry from the blacklist.
+ */
+function mollom_admin_blacklist_delete_submit($form, &$form_state) {
+  if ($form['#mollom-blacklist-type'] == 'text') {
+    mollom('mollom.removeBlacklistText', array('text' => $form['#mollom-blacklist-key']));
+  }
+  else {
+    mollom('mollom.removeBlacklistURL', array('url' => $form['#mollom-blacklist-key']));
+  }
+  
+  // @todo: verify that the item was actually deleted.
+  drupal_set_message(t('The item has been removed from the blacklist.'));
+
+  $form_state['redirect'] = "admin/config/content/mollom/blacklist";
+}
+
+/**
  * Form builder; Global Mollom settings form.
  */
 function mollom_admin_settings($form, &$form_state) {
Index: mollom.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/mollom.module,v
retrieving revision 1.18
diff -u -r1.18 mollom.module
--- mollom.module	30 Jan 2010 14:35:08 -0000	1.18
+++ mollom.module	31 Jan 2010 15:02:01 -0000
@@ -143,8 +143,8 @@
     'access arguments' => array('administer mollom'),
     'file' => 'mollom.admin.inc',
   );
-  $items['admin/config/content/mollom/list'] = array(
-    'title' => 'List',
+  $items['admin/config/content/mollom/forms'] = array(
+    'title' => 'Forms',
     'type' => MENU_DEFAULT_LOCAL_TASK,
     'weight' => -10,
   );
@@ -171,6 +171,24 @@
     'type' => MENU_CALLBACK,
     'file' => 'mollom.admin.inc',
   );
+  $items['admin/config/content/mollom/blacklist'] = array(
+    'title' => 'Blacklist',
+    'description' => 'Configure word and URL blacklists.',
+    'page callback' => 'mollom_admin_blacklist',
+    'access arguments' => array('administer mollom'),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'mollom.admin.inc',
+  );
+  $items['admin/config/content/mollom/blacklist/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('mollom_admin_blacklist_delete', 6, 7),
+    'access arguments' => array('administer mollom'),
+    'type' => MENU_LOCAL_TASK,
+    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+    'file' => 'mollom.admin.inc',
+    'weight' => 2,
+  );
   $items['admin/config/content/mollom/settings'] = array(
     'title' => 'Settings',
     'description' => 'Configure Mollom keys and global settings.',
@@ -204,6 +222,22 @@
 }
 
 /**
+ * Implements hook_theme().
+ */
+function mollom_theme() {
+  return array(
+    'mollom_admin_blacklist_text_form' => array(
+      'render element' => 'form',
+      'file' => 'mollom.admin.inc',
+    ),
+    'mollom_admin_blacklist_url_form' => array(
+      'render element' => 'form',
+      'file' => 'mollom.admin.inc',
+    ),
+  );
+}
+
+/**
  * Access callback; check if the module is configured.
  *
  * This function does not actually check whether Mollom keys are valid for the
Index: tests/mollom.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/mollom/tests/mollom.test,v
retrieving revision 1.13
diff -u -r1.13 mollom.test
--- tests/mollom.test	31 Jan 2010 07:51:39 -0000	1.13
+++ tests/mollom.test	31 Jan 2010 15:02:02 -0000
@@ -1014,7 +1014,12 @@
   }
 }
 
-class MollomBlackListTestCase extends MollomWebTestCase {
+class MollomBlacklistTestCase extends MollomWebTestCase {
+
+  // Note: the blacklists are stored on the server. These tests can fail when
+  // different people run the tests at the same time because all tests share
+  // the same blacklist. You can configure a custom key to avoid this.
+  
   public static function getInfo() {
     return array(
       'name' => 'Blacklisting',
@@ -1026,7 +1031,7 @@
   /**
    * Test the URL blacklist functionality at the API level without using a web interface.
    */
-  function testUrlBlackListAPI() {
+  function testUrlBlacklistAPI() {
     // Blacklist a URL.
     mollom('mollom.addBlacklistURL', array('url' => 'http://unicorn.com'));
 
@@ -1070,7 +1075,7 @@
   /**
    * Test the text blacklist functionality at the API level without using a web interface.
    */
-  function testTextBlackListAPI() {
+  function testTextBlacklistAPI() {
     // Blacklist a word:
     mollom('mollom.addBlacklistText', array(
       'text' => 'unicorn',
@@ -1117,6 +1122,43 @@
     $blacklist = mollom('mollom.listBlacklistText');
     $this->assertEqual(count($blacklist), 0, t('The server-side blacklist is empty.'));
   }
+  
+  /**
+   * Test the blacklist administration interface.
+   */
+  function testBlacklistUI() {    
+    // Log in as an administrator and access the blacklist administration page.
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/content/mollom/blacklist');
+    $this->assertText('No words have been added to the blacklist.');
+    $this->assertText('No URLs have been added to the blacklist.');
+        
+    // Add a word to the text blacklist.
+    $this->drupalPost(NULL, array('match' => 'contains', 'text' => 'spam word', 'reason' => 'spam'), t('Add'));
+    $this->assertText(t('The text has been added to the blacklist.'));
+    $this->assertText(t('spam word'));
+    
+    // Remove the word from the text blacklist.
+    $this->clickLink(t('delete'));
+    $this->drupalPost(NULL, array(), t('Delete'));
+    $this->assertEqual($this->getUrl(), url('admin/config/content/mollom/blacklist', array('absolute' => TRUE)), t('Correct page redirection.'));
+    $this->assertNoText(t('spam word'), 'Text blacklist removed.');
+    
+    // Add an URL  to the text blacklist.
+    $this->drupalPost(NULL, array('url' => 'spam.com'), t('Add'));
+    $this->assertText(t('The URL has been added to the blacklist.'));
+    $this->assertText(t('http://spam.com'));
+    
+    // Remove the URL from the text blacklist.
+    $this->clickLink(t('delete'));
+    $this->drupalPost(NULL, array(), t('Delete'));
+    $this->assertEqual($this->getUrl(), url('admin/config/content/mollom/blacklist', array('absolute' => TRUE)), t('Correct page redirection.'));
+    $this->assertNoText(t('http://spam.com'), 'URL blacklist removed.');
+    
+    // Note: we don't need to check whether the blacklisting actually works
+    // (i.e. blocks posts) because we tested thatin testTextBlacklistAPI() and 
+    // testURLBlacklistAPI().
+  }
 }
 
 /**
