You are here

spamicide.module in Spamicide 5

Same filename and directory in other branches
  1. 8 spamicide.module
  2. 6 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/13/2009
 */

/**
 * @file
 * This module provides yet another tool to eliminate spam.
 *
 * @ingroup spamicide
 */
function spamicide_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/spamicide',
      'title' => t('Spamicide'),
      'description' => t('Administer how and where Spamicide is used.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'spamicide_admin_form',
      ),
      'access' => user_access('administer spamicide'),
      'type' => MENU_NORMAL_ITEM,
    );
  }
  else {

    // Some non cachable menu items for disabling/deleting Spamicide forms
    // start with arg(3) == 'spamicide' for faster short circuit
    if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'spamicide' && arg(3) == 'spamicide_form' && arg(4) == 'delete' && !is_null(arg(5))) {
      $items[] = array(
        'path' => 'admin/settings/spamicide/spamicide_form/delete/' . arg(5),
        'title' => t('Delete'),
        'callback' => 'drupal_get_form',
        'callback arguments' => array(
          'spamicide_delete_confirm',
          arg(5),
          TRUE,
        ),
        'type' => MENU_CALLBACK,
      );
    }
    if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'spamicide' && arg(3) == 'spamicide_form' && arg(4) == 'add' && !is_null(arg(5))) {
      $items[] = array(
        'path' => 'admin/settings/spamicide/spamicide_form/add/' . arg(5),
        'title' => t('Add Spamicide to ' . arg(5)),
        'callback' => 'drupal_get_form',
        'callback arguments' => array(
          'spamicide_admin_add_form_id',
          arg(5),
          TRUE,
        ),
        'type' => MENU_CALLBACK,
      );
    }
  }
  return $items;
}
function spamicide_help($path) {
  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;
  }
}
function spamicide_perm() {
  return array(
    'administer spamicide',
  );
}

/**
 * Implementation of hook_requirements().
 */
function spamicide_requirements($phase) {
  $requirements = array();

  // Ensure translations don't break at install time.
  $t = get_t();
  if ($phase == 'runtime') {
    $spamicide_directory = file_create_path() . '/spamicide';
    if (!file_check_directory($spamicide_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
      if (!is_dir($spamicide_directory)) {
        $requirements['spamicide_directory'] = array(
          'title' => $t('Spamicide Directory'),
          'value' => $t('%p does not a directory or is not readable by the webserver.', array(
            '%p' => $spamicide_directory,
          )),
          'severity' => REQUIREMENT_ERROR,
        );
      }
      elseif (!is_writable($spamicide_directory)) {
        $requirements['spamicide_directory'] = array(
          'title' => $t('Spamicide Directory'),
          'value' => $t('%p is not writeable by the webserver.', array(
            '%p' => $spamicide_directory,
          )),
          'severity' => REQUIREMENT_ERROR,
        );
      }
      else {
        $requirements['spamicide_directory'] = array(
          'title' => $t('Spamicide Directory'),
          'value' => $t('An unknown error occured.'),
          'description' => $t('An unknown error occured trying to verify %p is a directory and is writable.', array(
            '%p' => $spamicide_directory,
          )),
          'severity' => REQUIREMENT_ERROR,
        );
      }
    }
    if (!is_writable(file_directory_temp())) {
      $requirements['spamicide_directory'] = array(
        'title' => $t('Spamicide Temp Directory'),
        'value' => $t('%p is not writeable by the webserver.', array(
          '%p' => file_directory_temp(),
        )),
        'severity' => REQUIREMENT_ERROR,
      );
    }

    //variable_set('spamicide_attempt_counter', variable_get('spamicide_attempt_counter', 0) + 1);
    $requirements['spamicide_attempt_counter'] = array(
      'title' => $t('Spamicide'),
      'value' => $t('Already blocked @counter form submissions', array(
        '@counter' => variable_get('spamicide_attempt_counter', 0),
      )),
      'severity' => REQUIREMENT_INFO,
    );
  }
  return $requirements;
}

/**
 * Form for spamicide administration
 * @param $spamicide_form_id
 * @return unknown_type
 */
function spamicide_admin_form($spamicide_form_id = NULL) {
  $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, then navigate to the form you\'d like to add Spamicide to and click the link "Add Spamicide protection to this form".', 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/logs/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;
}
function spamicide_admin_form_validate($form, $form_values) {
  foreach ($form_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_values) {
  variable_set('spamicide_administration_mode', $form_values['spamicide_administration_mode']);

  /*
   * need to implement admin links yet
   */
  foreach ($form_values['spamicide_forms'] as $spamicide_form_id => $data) {
    if ($data['form_field'] != 'feed_me') {
      _spamicide_set_css_file($data['form_field']);

      /*        $css_file = 'spamicide/'. $data['form_field'] .'.css';
              $path = file_create_path($css_file);
              if (!file_exists($path)) { // or !is_file or !file_exists or !file_check_location
                $css = "#edit-". str_replace('_', '-', $data['form_field']) ."-wrapper\n{\n  display: none;\n}\n";
                file_save_data($css, $css_file, FILE_EXISTS_REPLACE);
              }*/
    }
    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_values['spamicide_log_attempts']);
  drupal_set_message(t('The Spamicide settings were saved.'), 'status');
}

/**
 * Callback function for adding spamicide functionality to an existing form
 * @param $spamicide_form_id
 * @return $form
 */
function spamicide_admin_add_form_id($spamicide_form_id) {
  $form = array();
  $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_validate
 * @param $form
 * @param $form_values
 */
function spamicide_admin_add_form_id_validate($form, $form_values) {
  if (!file_check_directory(file_directory_path())) {
    $err_link = l("Site Configuration > File system", "admin/settings/file-system", array());
    form_set_error('spamicide_forms][' . $spamicide_form_id . '][form_field', t("The files directory either doesn't exist or is not writable, please go to " . $err_link . " to fix this and try again"));
  }
}

/**
 * Implementation of hook_form_submit
 * @param $form
 * @param $form_values
 */
function spamicide_admin_add_form_id_submit($form, $form_values) {
  $spamicide_form_id = $form_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_values['spamicide_form_field']);
  _spamicide_set_css_file($form_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($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_values) {
  $spamicide_form_id = $form_values['spamicide_form_id'];
  $delete = $form_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
  return 'admin/settings/spamicide';
}

/**
 * Implementation of hook_form_alter
 * @param $form_id
 * @param $form
 */
function spamicide_form_alter($form_id, &$form) {
  $spamicide_field = _spamicide_get_field($form_id);
  if ($spamicide_field) {
    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');
    }
    $spamicide_description = _spamicide_get_description();
    $form[$spamicide_field] = array(
      '#type' => 'textfield',
      '#size' => 30,
      '#description' => $spamicide_description,
    );
    $form['destination'] = array(
      '#type' => 'value',
      '#value' => drupal_get_destination(),
    );
    $form['#pre_render'][] = 'spamicide_pre_render_place_spamicide';
    $form['spamicide']['#validate']['spamicide_validate'] = array();
  }
  else {
    if (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()),
      );

      // Add pre_render function for placing the Spamicide just above the submit button
      $form['#pre_render'][] = 'spamicide_pre_render_place_spamicide';
    }
    else {
      return;
    }
  }
}

/**
 * Implementation of hook_validate
 * @param $form_values
 * @return none
 */
function spamicide_validate($form_values) {
  $form_id = $form_values['#post']['form_id'];
  $spamicide_field = _spamicide_get_field($form_id);
  if (!$spamicide_field) {
    return;
  }
  else {
    if (empty($form_values['#post'][$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', FALSE)) {
        watchdog('Spamicide', t('%form_id post blocked by Spamicide module: their IP address is "%ipaddress".', array(
          '%form_id' => $form_id,
          '%ipaddress' => $_SERVER['REMOTE_ADDR'],
        )), 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_values['#post']['destination']);
      }
    }
  }
}

/**
 * Place the spamicide field just before the submit button
 * @param $form_id
 * @param $form
 */
function spamicide_pre_render_place_spamicide($form_id, &$form) {

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

    // set the weight of the Spamicide element a tiny bit smaller than the lightest button weight
    // (note that the default resolution of #weight values is 1/1000 (see drupal/includes/form.inc))
    $first_button_weight = min($button_weights);
    $spamicide_field = _spamicide_get_field($form_id);
    if ($spamicide_field) {
      $form[$spamicide_field]['#weight'] = $first_button_weight - 0.5 / 1000.0;
    }
    else {
      $form['spamicide']['#weight'] = $first_button_weight - 0.5 / 1000.0;
    }

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

/**
 * Get the spamicide field name and .css file_name
 * @param $form_id
 * @return the spamicide field name and .css file_name to call or FALSE
 */
function _spamicide_get_field($form_id) {
  $result = db_query("SELECT enabled, form_field FROM {spamicide} WHERE form_id = '%s'", $form_id);
  if ($result) {
    $spamicide = db_fetch_object($result);
    if ($spamicide->enabled) {
      return $spamicide->form_field;
    }
    else {
      return FALSE;
    }
  }
}
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)) {

    // or !is_file or !file_exists or !file_check_location
    $css = "#edit-" . str_replace("_", "-", $form_field) . "-wrapper\n{\n  display: none;\n}\n";
    file_save_data($css, $css_file, FILE_EXISTS_REPLACE);
  }
}

/**
 * Show translation if available
 * @param $lang_code
 * @return translated field description
 */
function _spamicide_get_description($lang_code = NULL) {

  // work to be done
  $default = t('To prevent automated spam submissions leave this field empty.');
  if (module_exists('locale')) {
    if ($lang_code == NULL) {
      global $locale;
      $lang_code = $locale;
    }
    $description = variable_get("spamicide_description_{$lang_code}", $default);
  }
  else {
    $description = variable_get('spamicide_description', $default);
  }
  return $description;
}

Related topics

Functions

Namesort descending Description
spamicide_admin_add_form_id Callback function for adding spamicide functionality to an existing form
spamicide_admin_add_form_id_submit Implementation of hook_form_submit
spamicide_admin_add_form_id_validate Implementation of hook_form_validate
spamicide_admin_form Form for spamicide administration
spamicide_admin_form_submit Implementation of hook_form_submit
spamicide_admin_form_validate
spamicide_delete_confirm Confirm dialog for disabling/deleting a Spamicide
spamicide_delete_confirm_submit submission handler of Spamicide delete confirm_form
spamicide_form_alter Implementation of hook_form_alter
spamicide_help
spamicide_menu @file This module provides yet another tool to eliminate spam.
spamicide_perm
spamicide_pre_render_place_spamicide Place the spamicide field just before the submit button
spamicide_requirements Implementation of hook_requirements().
spamicide_validate Implementation of hook_validate
theme_spamicide_admin_settings_spamicide Custom theme function
_spamicide_get_description Show translation if available
_spamicide_get_field Get the spamicide field name and .css file_name
_spamicide_set_css_file