You are here

spamicide.module in Spamicide 6

Same filename and directory in other branches
  1. 8 spamicide.module
  2. 5 spamicide.module
  3. 7 spamicide.module

This module provides yet another tool to eliminate spam.

File

spamicide.module
View source
<?php

/**
 * @defgroup spamicide Spamicide: another tool to eliminate spam.
 *
 * The spamicide module provides the ability to prevent spam being submitted to your site on various drupal forms.
 *
 * Author:  Wes Roepken aka lipcpro (wes@lipcpro.com)
 * Date:    04/29/2011
 */

/**
 * @file
 * This module provides yet another tool to eliminate spam.
 *
 * @ingroup spamicide
 */

/**
 * Implementation of hook_menu().
 */
function spamicide_menu() {
  $items = array();
  $items['admin/settings/spamicide'] = array(
    'title' => 'Spamicide',
    'description' => 'Administer how and where Spamicide is used.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'spamicide_admin_form',
    ),
    'access arguments' => array(
      'administer spamicide',
    ),
    'type' => MENU_NORMAL_ITEM,
  );

  // form for adding/editing spamicide form
  $items['admin/settings/spamicide/spamicide_form/add/%'] = array(
    'title' => 'Set Spamicide form',
    'description' => 'Add or edit form_id\'s to protect with Spamicide.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'spamicide_admin_adform_form',
      5,
      TRUE,
    ),
    'access arguments' => array(
      'administer spamicide',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/spamicide/spamicide_form/delete/%'] = array(
    'title' => 'Delete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'spamicide_delete_confirm',
      5,
      TRUE,
    ),
    'access arguments' => array(
      'administer spamicide',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implementation of hook_help().
 */
function spamicide_help($path, $arg) {
  switch ($path) {
    case 'admin/settings/spamicide':
      return t('The spamicide module provides the ability to prevent spam being submitted to your site on various drupal forms');
    case 'admin/help#spamicide':
      $output = 'Spamicide';
      $output .= '<p>' . t("Spamicide is intended to prevent spam without user interaction. Select the forms you'd like to attach spamicide to and a form field will be added but hidden by css. If the field is filled by anything (including humans that think they know better) it will prevent submission into you site.") . '</p>';
      $output .= '<p>' . t('To add Spamicide to a form once you\'ve enabled adding admin links, navigate to that form, and click the link "Add Spamicide protection to this form"') . '</p>';
      return $output;
  }
}

/**
 * Implementation of hook_perm().
 */
function spamicide_perm() {
  return array(
    'administer spamicide',
  );
}

/**
 * Implementation of hook_theme().
 */
function spamicide_theme() {
  $spam_theme = array(
    'spamicide_admin_settings_spamicide' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'spamicide' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );

  /*  $spam_fields = _spamicide_get_all_fields();
    foreach($spam_fields as $key => $field) {
        $spam_theme[$field] = array(
        	'arguments' => array('element' => NULL)
        );
    }//*/
  return $spam_theme;
}

/**
 * Form for spamicide administration
 * @param $spamicide_form_id
 * @return unknown_type
 */
function spamicide_admin_form($form) {
  $form = array();
  $form['spamicide_administration_mode'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add Spamicide administration links to forms'),
    '#default_value' => variable_get('spamicide_administration_mode', TRUE),
    '#description' => t('This option will allow enabling/disabling Spamicide on forms. When enabled, users with the "%adminspamicide" permission will see Spamicide administration links on all forms (except on administrative pages, which shouldn\'t be accessible to untrusted users in the first place). These links make it possible to enable or disable it for the desired type.', array(
      '%adminspamicide' => t('administer Spamicide settings'),
    )),
  );
  $form['spamicide_forms'] = array(
    '#type' => 'fieldset',
    '#title' => t('Add Spamicide to a form or remove an added form'),
    '#description' => t('Select from the listed forms (identified by their so called <em>form_id</em>\'s). You can easily add arbitrary forms with the help of the \'%spamicide_admin_links\' option.', array(
      '%spamicide_admin_links' => t('Add Spamicide administration links to forms'),
      '!add_spamicide' => url('admin/settings/spamicide/add_form'),
    )),
    '#tree' => TRUE,
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
    '#theme' => 'spamicide_admin_settings_spamicide',
  );

  // list all possible form_id's
  $result = db_query("SELECT * FROM {spamicide} ORDER BY form_id");
  while ($spamicide = db_fetch_object($result)) {
    $form['spamicide_forms'][$spamicide->form_id]['enabled'] = array(
      '#type' => 'checkbox',
      '#default_value' => $spamicide->enabled,
    );
    $form['spamicide_forms'][$spamicide->form_id]['form_id'] = array(
      '#value' => $spamicide->form_id,
    );
    $form['spamicide_forms'][$spamicide->form_id]['form_field'] = array(
      '#type' => 'textfield',
      '#size' => 30,
      '#default_value' => $spamicide->form_field,
    );

    // additional operations
    if ($spamicide->removable) {
      $form['spamicide_forms'][$spamicide->form_id]['operations'] = array(
        '#value' => implode(", ", array(
          l(t('delete'), "admin/settings/spamicide/spamicide_form/delete/{$spamicide->form_id}"),
        )),
      );
    }
    else {
      $form['spamicide_forms'][$spamicide->form_id]['operations'] = array(
        '#value' => "N/A",
      );
    }
  }
  $form['spamicide_log_attempts'] = array(
    '#type' => 'checkbox',
    '#title' => t('Log attempts'),
    '#description' => t('Report information about attempts to the !watchdoglog.', array(
      '!watchdoglog' => l(t('log'), 'admin/reports/watchdog'),
    )),
    '#default_value' => variable_get('spamicide_log_attempts', TRUE),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}

/**
 * Custom theme function
 * @param $form
 * @return unknown_type
 */
function theme_spamicide_admin_settings_spamicide($form) {
  foreach (element_children($form) as $key) {
    $row = array();
    $row[] = drupal_render($form[$key]['enabled']);
    $row[] = drupal_render($form[$key]['form_id']);
    $row[] = drupal_render($form[$key]['form_field']);
    $row[] = drupal_render($form[$key]['operations']);
    $rows[] = $row;
  }
  $header = array(
    'Enabled',
    'Form_id',
    'Form field name',
    t('Delete'),
  );
  $output = theme('table', $header, $rows);
  return $output;
}

/**
 * Implementation of hook_form_validate
 * Enter description here ...
 * @param unknown_type $form
 * @param unknown_type $form_state
 */
function spamicide_admin_form_validate($form, &$form_state) {
  foreach ($form_state['values']['spamicide_forms'] as $spamicide_form_id => $data) {
    if (preg_match_all('[\\W]', $data['form_field'], $str)) {
      form_set_error('spamicide_forms][' . $spamicide_form_id . '][form_field', t("Only AlphaNumeric characters or the underscore please"));
    }
  }
}

/**
 * Implementation of hook_form_submit
 * @param $form
 * @param $form_values
 */
function spamicide_admin_form_submit($form, &$form_state) {
  variable_set('spamicide_administration_mode', $form_state['values']['spamicide_administration_mode']);
  foreach ($form_state['values']['spamicide_forms'] as $spamicide_form_id => $data) {
    if ($data['form_field'] != 'feed_me') {
      _spamicide_set_css_file($data['form_field']);
    }
    db_query("UPDATE {spamicide} SET enabled = '%s', form_field = '%s' WHERE form_id = '%s'", $data['enabled'], $data['form_field'], $spamicide_form_id);
  }
  variable_set('spamicide_log_attempts', $form_state['values']['spamicide_log_attempts']);
  drupal_set_message(t('The Spamicide settings were saved.'), 'status');
}

/**
 * Form for adding spamicide functionality to an existing form
 * @param $spamicide_form_id
 * @return $form
 */
function spamicide_admin_adform_form($form, $spamicide_form_id = NULL) {
  $form = array();

  // use given spamicide form_id
  $form['spamicide_form_id'] = array(
    '#type' => 'textfield',
    '#title' => t('Form ID'),
    '#description' => t('The Drupal form_id of the form to add the Spamicide to.'),
    '#value' => check_plain($spamicide_form_id),
    '#disabled' => TRUE,
  );
  $form['spamicide_form_field'] = array(
    '#type' => 'textfield',
    '#title' => t('Form field'),
    '#default_value' => 'feed_me',
    '#description' => t('The name you want for the field. Use only letters, numbers, and the underscore character(_).'),
  );

  // redirect to general Spamicide settings page after submission
  $form['#redirect'] = 'admin/settings/spamicide';

  // submit button
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}

/**
 * Implementation of hook_form_submit
 * @param $form
 * @param $form_values
 */
function spamicide_admin_adform_form_submit($form, &$form_state) {
  $spamicide_form_id = $form_state['values']['spamicide_form_id'];

  // remove old settings
  db_query("DELETE FROM {spamicide} WHERE form_id = '%s'", $spamicide_form_id);

  // save new settings
  db_query("INSERT INTO {spamicide} (form_id, form_field, enabled, removable) VALUES ('%s','%s', 1, 1)", $spamicide_form_id, $form_state['values']['spamicide_form_field']);
  _spamicide_set_css_file($form_state['values']['spamicide_form_field']);
  drupal_set_message(t('Saved Spamicide settings.'), 'status');
}

/**
 * Confirm dialog for disabling/deleting a Spamicide
 *
 * @param $spamicide_form_id the form_id of the form to delete the Spamicide
 *   from
 * @param $delete boolean for also deleting the Spamicide
 */
function spamicide_delete_confirm(&$form_state, $spamicide_form_id, $delete) {
  $form = array();
  $form['spamicide_form_id'] = array(
    '#type' => 'value',
    '#value' => $spamicide_form_id,
  );
  $form['spamicide_delete'] = array(
    '#type' => 'value',
    '#value' => $delete,
  );
  $message = t('Are you sure you want to delete the Spamicide for form_id %form_id?', array(
    '%form_id' => $spamicide_form_id,
  ));
  $yes = t('Delete');
  return confirm_form($form, $message, isset($_GET['destination']) ? $_GET['destination'] : 'admin/settings/spamicide/spamicide_form', '', $yes);
}

/**
 * submission handler of Spamicide delete confirm_form
 */
function spamicide_delete_confirm_submit($form, &$form_state) {
  $spamicide_form_id = $form_state['values']['spamicide_form_id'];
  $delete = $form_state['values']['spamicide_delete'];
  db_query("DELETE FROM {spamicide} WHERE form_id = '%s'", $spamicide_form_id);
  drupal_set_message(t('Deleted Spamicide for form %form_id.', array(
    '%form_id' => $spamicide_form_id,
  )));

  // redirect to Spamicide admin
  $form_state['redirect'] = 'admin/settings/spamicide';
}

/**
 * Implementation of hook_form_alter
 * @param $form_id
 * @param $form
 */
function spamicide_form_alter(&$form, $form_state, $form_id) {
  if (strpos($form_id, 'search') !== FALSE || strpos($form_id, 'update_script') !== FALSE) {

    //ignore the search & update forms
    return;
  }
  $spamicide_field = _spamicide_get_field($form_id);
  if ($spamicide_field) {
    $form['#pre_render'][] = '_spamicide_pre_render';
    $spamicide_description = _spamicide_get_description();
    $form[$spamicide_field] = array(
      '#type' => 'textfield',
      '#size' => 20,
      '#description' => $spamicide_description,
      '#name' => $spamicide_field,
      '#post_render' => array(
        '_spamicide_field_post_render',
      ),
    );
    $spam_fields = _spamicide_get_all_fields();
    $form['destination'] = array(
      '#type' => 'value',
      '#value' => drupal_get_destination(),
    );
    $form['spamicide']['#element_validate'] = array(
      'spamicide_validate',
    );
    spamicide_field_place_field($form, $form_id);
  }
  elseif (user_access('administer spamicide') && variable_get('spamicide_administration_mode', TRUE) && arg(0) != 'admin') {
    $form['spamicide'] = array(
      '#type' => 'fieldset',
      '#title' => t('Spamicide'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['spamicide']['add_spamicide'] = array(
      '#value' => l(t('Add Spamicide protection to this form.'), "admin/settings/spamicide/spamicide_form/add/{$form_id}", array()),
    );
  }
  else {
    return;
  }
}
function _spamicide_pre_render($form) {
  $spamicide_field = _spamicide_get_field($form['form_id']['#value']);
  if ($spamicide_field == 'feed_me') {
    drupal_add_css(drupal_get_path('module', 'spamicide') . '/feed_me.css');
  }
  else {
    drupal_add_css(file_directory_path() . '/spamicide/' . $spamicide_field . '.css');
  }
  return $form;
}

/**
 * Implementation of hook_validate
 * @param $form_values
 * @return none
 */
function spamicide_validate($form, &$form_state) {
  $form_id = $form_state['values']['form_id'];
  $spamicide_field = _spamicide_get_field($form_id);
  if (!$spamicide_field) {
    return;
  }
  elseif (empty($form_state['values'][$spamicide_field])) {
    return;
  }
  else {

    // update attempt counter
    variable_set('spamicide_attempt_counter', variable_get('spamicide_attempt_counter', 0) + 1);

    // log to watchdog if needed
    if (variable_get('spamicide_log_attempts', TRUE)) {
      watchdog('Spamicide', '%form_id post blocked by Spamicide module: their IP address is "%ipaddress".', array(
        '%form_id' => $form_id,
        '%ipaddress' => ip_address(),
      ), WATCHDOG_NOTICE);
    }

    // If Spamicide was on a login form: stop validating, quit the current request
    // and forward to the current page (like a reload) to prevent logging in.
    // We do that because the log in procedure, which happens after
    // spamicide_validate(), does not check error conditions of extra form
    // elements like the Spamicide.
    if ($form_id == 'user_login' || $form_id == 'user_login_block') {
      drupal_goto($_GET['q']);
    }
    else {
      drupal_goto($form_state['values']['destination']);
    }
  }
}

/**
 * Place the spamicide field just before the submit button
 * @param $form_id
 * @param $form
 * @todo figure out how to make this work
 */
function spamicide_field_place_field(&$form, $form_id) {

  // search the weights of the buttons in the form
  $button_weights = array();
  foreach (element_children($form) as $key) {
    if ($key == 'buttons' || isset($form[$key]['#type']) && $form[$key]['#type'] == 'submit') {
      if (!isset($form[$key]['#weight'])) {

        /*
         * forcing the issue of button weights
         * Setting a weight if there isn't one
         */
        $form[$key]['#weight'] = 1;
      }
      $button_weights[] = ++$form[$key]['#weight'];
    }
  }
  if (!empty($button_weights)) {

    /*
     * forcing the issue of button weights
     * push them all down 1
     */
    $spamicide_weight = min($button_weights) - 1;
    $spamicide_field = _spamicide_get_field($form_id);
    if ($spamicide_field) {
      $form[$spamicide_field]['#weight'] = $spamicide_weight;
    }
    else {
      $form['spamicide']['#weight'] = $spamicide_weight;
    }

    // make sure the form gets sorted before rendering
    unset($form['#sorted']);
  }
  return $form;
}

/**
 * Get the spamicide field name and .css file_name
 * @param $form
 * @return the spamicide field name and .css file_name to call or FALSE
 */
function _spamicide_get_field(&$form) {
  $result = db_query("SELECT enabled, form_field FROM {spamicide} WHERE form_id = '%s'", $form);
  if ($result) {
    $spamicide = db_fetch_object($result);
    if (is_object($spamicide) && $spamicide->enabled) {
      return $spamicide->form_field;
    }
    else {
      return FALSE;
    }
  }
}
function _spamicide_get_all_fields() {
  $spam_fields = array();
  $result = db_query("SELECT form_field FROM {spamicide} ORDER BY form_field");
  while ($spam_field = db_fetch_array($result)) {
    $temp[] = $spam_field['form_field'];
  }
  foreach ($temp as $field) {
    if (!in_array($field, $spam_fields)) {
      $spam_fields[] = $field;
    }
  }
  return $spam_fields;
}

/**
 * Set the spamicide field name and .css file_name
 * @param array $form_field
 */
function _spamicide_set_css_file($form_field) {
  $spam_path = file_directory_path() . "/spamicide";
  if (!file_check_directory($spam_path)) {
    mkdir($spam_path);
  }
  $css_file = $spam_path . '/' . $form_field . '.css';
  $path = file_create_path($css_file);
  if (!file_exists($path)) {
    $css = 'div.' . _spamicide_get_css_class($form_field) . ' { display:none; }';
    file_save_data($css, $path, FILE_EXISTS_REPLACE);
  }
}

/**
 * Show translation if available
 * @param $lang_code
 * @return translated field description
 * @todo change $default to be able to change it in the admin interface
 * 	 with the possibility of adding per form default description
 */
function _spamicide_get_description($lang_code = NULL) {
  global $language;
  $lang_code = isset($lang_code) ? $lang_code : $language->language;
  $default = t('To prevent automated spam submissions leave this field empty.');
  if (module_exists('locale')) {
    $description = variable_get("spamicide_description_{$lang_code}", $default);
  }
  else {
    $description = variable_get('spamicide_description', $default);
  }
  return $description;
}

/**
 * #post_render callback on the spamicide field. This will wrap the field
 * in a div so it can be hidden with CSS.
 *
 */
function _spamicide_field_post_render($content, $element) {
  return '<div class="' . _spamicide_get_css_class($element['#name']) . '">' . $content . '</div>';
}

/**
 * Returns a CSS class name based on the field's name. Should be used
 * to wrap around the field so it can be hidden.
 *
 * Note that it should be pretty generic so it can't be sniffed out
 * by a spammer.
 *
 * @param string $name
 * 		The field's name
 * @return string
 * 		The CSS class
 */
function _spamicide_get_css_class($name) {
  return 'edit-' . str_replace('_', '-', $name) . '-wrapper';
}

Related topics

Functions

Namesort descending Description
spamicide_admin_adform_form Form for adding spamicide functionality to an existing form
spamicide_admin_adform_form_submit Implementation of hook_form_submit
spamicide_admin_form Form for spamicide administration
spamicide_admin_form_submit Implementation of hook_form_submit
spamicide_admin_form_validate Implementation of hook_form_validate Enter description here ...
spamicide_delete_confirm Confirm dialog for disabling/deleting a Spamicide
spamicide_delete_confirm_submit submission handler of Spamicide delete confirm_form
spamicide_field_place_field Place the spamicide field just before the submit button
spamicide_form_alter Implementation of hook_form_alter
spamicide_help Implementation of hook_help().
spamicide_menu Implementation of hook_menu().
spamicide_perm Implementation of hook_perm().
spamicide_theme Implementation of hook_theme().
spamicide_validate Implementation of hook_validate
theme_spamicide_admin_settings_spamicide Custom theme function
_spamicide_field_post_render #post_render callback on the spamicide field. This will wrap the field in a div so it can be hidden with CSS.
_spamicide_get_all_fields
_spamicide_get_css_class Returns a CSS class name based on the field's name. Should be used to wrap around the field so it can be hidden.
_spamicide_get_description Show translation if available
_spamicide_get_field Get the spamicide field name and .css file_name
_spamicide_pre_render
_spamicide_set_css_file Set the spamicide field name and .css file_name