You are here

function captcha_form_alter in CAPTCHA 5.3

Same name and namespace in other branches
  1. 8 captcha.module \captcha_form_alter()
  2. 6.2 captcha.module \captcha_form_alter()
  3. 6 captcha.module \captcha_form_alter()
  4. 7 captcha.module \captcha_form_alter()

Implementation of hook_form_alter().

This function adds a CAPTCHA to forms for untrusted users if needed and adds CAPTCHA administration links for site administrators if this option is enabled.

File

./captcha.module, line 476
This module enables basic CAPTCHA functionality: administrators can add a CAPTCHA to desired forms that users without the 'skip CAPTCHA' permission (typically anonymous visitors) have to solve.

Code

function captcha_form_alter($form_id, &$form) {
  if (!user_access('skip CAPTCHA')) {

    // Visitor does not have permission to skip the CAPTCHA
    // Get CAPTCHA type and module for this form. Return if no CAPTCHA was set.
    $result = db_query("SELECT module, type FROM {captcha_points} WHERE form_id = '%s'", $form_id);
    if (!$result) {
      return;
    }
    $captcha_point = db_fetch_object($result);
    if (!$captcha_point || !$captcha_point->type) {
      return;
    }

    // Prevent caching of the page with this CAPTCHA enabled form.
    // This needs to be done even if the CAPTCHA will be skipped (because of
    // persistence): other untrusted users should not get a cached page when
    // the current untrusted user can skip the current CAPTCHA.
    global $conf;
    $conf['cache'] = FALSE;

    // Do not present CAPTCHA if not CAPTCHA-persistent and user has already solved a CAPTCHA for this form
    if (_captcha_persistence_skip($form_id)) {
      return;
    }

    // Generate a CAPTCHA and its solution
    $captcha = module_invoke($captcha_point->module, 'captcha', 'generate', $captcha_point->type);
    if (!$captcha) {

      //The selected module returned nothing, maybe it is disabled or it's wrong, we should watchdog that and then quit.
      watchdog('CAPTCHA', t('CAPTCHA problem: hook_captcha() of module %module returned nothing when trying to retrieve challenge type %type for form %form_id.', array(
        '%type' => $captcha_point->type,
        '%module' => $captcha_point->module,
        '%form_id' => $form_id,
      )), WATCHDOG_ERROR);
      return;
    }

    // Add a CAPTCHA part to the form (depends on value of captcha_description)
    $captcha_description = _captcha_get_description();
    if ($captcha_description) {

      // $captcha_description is not empty: CAPTCHA part is a fieldset with description
      $form['captcha'] = array(
        '#type' => 'fieldset',
        '#title' => t('CAPTCHA'),
        '#description' => $captcha_description,
        '#attributes' => array(
          'class' => 'captcha',
        ),
      );
    }
    else {

      // $captcha_description is empty: CAPTCHA part is an empty markup form element
      $form['captcha'] = array(
        '#type' => 'markup',
        '#prefix' => '<div class="captcha">',
        '#suffix' => '</div>',
      );
    }

    // Add the form elements of the generated CAPTCHA to the form
    $form['captcha'] = array_merge($form['captcha'], $captcha['form']);

    // Store the solution of the generated CAPTCHA as an internal form value.
    // This will be stored later in $_SESSION during the pre_render phase.
    // It can't be saved at this point because hook_form_alter is not only run
    // before form rendering, but also before form validation (which happens
    // in a new (POST) request. Consequently the right CAPTCHA solution would be
    // overwritten just before validation. The pre_render functions are not run
    // before validation and are the right place to store the solution in $_SESSION.
    $form['captcha']['captcha_solution'] = array(
      '#type' => 'value',
      '#value' => $captcha['solution'],
    );

    // The CAPTCHA token is used to differentiate between different instances
    // of the same form. This makes it possible to request the same form a
    // couple of times before submitting them. The solution of the CAPTCHA of
    // each of these form instances will be stored at the pre_render phase in
    // $_SESSION['captcha'][$form_id][$captcha_token]
    $form['captcha']['captcha_token'] = array(
      '#type' => 'hidden',
      '#value' => md5(mt_rand()),
    );

    // other internal values needed for the validation phase
    $form['captcha']['validationdata'] = array(
      '#type' => 'value',
      '#value' => array(
        'form_id' => $form_id,
        'preprocess' => isset($captcha['preprocess']) ? $captcha['preprocess'] : FALSE,
        'module' => $captcha_point->module,
        'type' => $captcha_point->type,
      ),
    );

    // handle the pre_render functions
    $form['#pre_render'][] = 'captcha_pre_render';
    $form['#pre_render'][] = 'captcha_pre_render_place_captcha';

    // Add a validation function for the CAPTCHA part of the form
    $form['captcha']['#validate']['captcha_validate'] = array();
  }
  elseif (user_access('administer CAPTCHA settings') && variable_get('captcha_administration_mode', FALSE) && arg(0) != 'admin') {

    // For administrators: show CAPTCHA info and offer link to configure it
    $result = db_query("SELECT module, type FROM {captcha_points} WHERE form_id = '%s'", $form_id);
    if (!$result) {
      return;
    }
    $captcha_point = db_fetch_object($result);
    $form['captcha'] = array(
      '#type' => 'fieldset',
      '#title' => t('CAPTCHA'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    if ($captcha_point && $captcha_point->type) {
      $form['captcha']['#description'] = t('Untrusted users will see a CAPTCHA here (!settings).', array(
        '!settings' => l(t('general CAPTCHA settings'), 'admin/user/captcha'),
      ));
      $form['captcha']['challenge'] = array(
        '#type' => 'item',
        '#title' => t('Enabled challenge'),
        '#value' => t('"@type" by module "@module" (!change, !disable)', array(
          '@type' => $captcha_point->type,
          '@module' => $captcha_point->module,
          '!change' => l(t('change'), "admin/user/captcha/captcha/captcha_point/{$form_id}", array(), drupal_get_destination()),
          '!disable' => l(t('disable'), "admin/user/captcha/captcha/captcha_point/{$form_id}/disable", array(), drupal_get_destination()),
        )),
      );
    }
    else {
      $form['captcha']['add_captcha'] = array(
        '#value' => l(t('Place a CAPTCHA here for untrusted users.'), "admin/user/captcha/captcha/captcha_point/{$form_id}", array(), drupal_get_destination()),
      );
    }

    // Add pre_render function for placing the CAPTCHA just above the submit button
    $form['#pre_render'] = (array) $form['#pre_render'] + array(
      'captcha_pre_render_place_captcha',
    );
  }
}