You are here

function captcha_element_process in CAPTCHA 7

Process callback for CAPTCHA form element.

1 string reference to 'captcha_element_process'
captcha_element_info in ./captcha.module
Implements of hook_element_info().

File

./captcha.module, line 180
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_element_process($element, &$form_state, $complete_form) {
  module_load_include('inc', 'captcha');

  // Add JavaScript for general CAPTCHA functionality.
  drupal_add_js(drupal_get_path('module', 'captcha') . '/captcha.js');

  // Get the form ID of the form we are currently processing (which is not
  // necessary the same form that is submitted (if any).
  $this_form_id = $complete_form['form_id']['#value'];

  // Get the CAPTCHA session ID.
  // If there is a submitted form: try to retrieve and reuse the
  // CAPTCHA session ID from the posted data.
  list($posted_form_id, $posted_captcha_sid) = _captcha_get_posted_captcha_info($element, $form_state, $this_form_id);
  if ($this_form_id == $posted_form_id && isset($posted_captcha_sid)) {
    $captcha_sid = $posted_captcha_sid;
  }
  else {

    // Generate a new CAPTCHA session if we could not reuse one from a posted form.
    $captcha_sid = _captcha_generate_captcha_session($this_form_id, CAPTCHA_STATUS_UNSOLVED);
  }

  // Store CAPTCHA session ID as hidden field.
  // Strictly speaking, it is not necessary to send the CAPTCHA session id
  // with the form, as the one time CAPTCHA token (see lower) is enough.
  // However, we still send it along, because it can help debugging
  // problems on live sites with only access to the markup.
  $element['captcha_sid'] = array(
    '#type' => 'hidden',
    '#value' => $captcha_sid,
  );
  $captcha_token = db_select('captcha_sessions', 'c')
    ->fields('c', array(
    'token',
  ))
    ->condition('csid', $captcha_sid)
    ->execute()
    ->fetchField();
  if (!isset($captcha_token) && !$form_state['submitted']) {

    // Additional one-time CAPTCHA token: store in database and send with form.
    $captcha_token = md5(mt_rand());
    db_update('captcha_sessions')
      ->fields(array(
      'token' => $captcha_token,
    ))
      ->condition('csid', $captcha_sid)
      ->execute();
  }
  $element['captcha_token'] = array(
    '#type' => 'hidden',
    '#value' => $captcha_token,
  );

  // Get implementing module and challenge for CAPTCHA.
  list($captcha_type_module, $captcha_type_challenge) = _captcha_parse_captcha_type($element['#captcha_type']);

  // Store CAPTCHA information for further processing in
  // - $form_state['captcha_info'], which survives a form rebuild (e.g. node
  //   preview), useful in _captcha_get_posted_captcha_info().
  // - $element['#captcha_info'], for post processing functions that do not
  //   receive a $form_state argument (e.g. the pre_render callback).
  $form_state['captcha_info'] = array(
    'this_form_id' => $this_form_id,
    'posted_form_id' => $posted_form_id,
    'captcha_sid' => $captcha_sid,
    'module' => $captcha_type_module,
    'captcha_type' => $captcha_type_challenge,
  );
  $element['#captcha_info'] = array(
    'form_id' => $this_form_id,
    'captcha_sid' => $captcha_sid,
  );
  if (_captcha_required_for_user($captcha_sid, $this_form_id) || $element['#captcha_admin_mode'] || $element['#captcha_always']) {

    // Generate a CAPTCHA and its solution
    // (note that the CAPTCHA session ID is given as third argument).
    $captcha = module_invoke($captcha_type_module, 'captcha', 'generate', $captcha_type_challenge, $captcha_sid);
    if (!isset($captcha['form']) || !isset($captcha['solution'])) {

      // The selected module did not return what we expected: log about it and quit.
      watchdog('CAPTCHA', 'CAPTCHA problem: unexpected result from hook_captcha() of module %module when trying to retrieve challenge type %type for form %form_id.', array(
        '%type' => $captcha_type_challenge,
        '%module' => $captcha_type_module,
        '%form_id' => $this_form_id,
      ), WATCHDOG_ERROR);
      return $element;
    }

    // Add form elements from challenge as children to CAPTCHA form element.
    $element['captcha_widgets'] = $captcha['form'];

    // Add a validation callback for the CAPTCHA form element (when not in admin mode).
    if (!$element['#captcha_admin_mode']) {
      $element['#element_validate'] = array(
        'captcha_validate',
      );
    }

    // Set a custom CAPTCHA validate function if requested.
    if (isset($captcha['captcha_validate'])) {
      $element['#captcha_validate'] = $captcha['captcha_validate'];
    }

    // Set the theme function.
    $element['#theme'] = 'captcha';

    // Add pre_render callback for additional CAPTCHA processing.
    if (!isset($element['#pre_render'])) {
      $element['#pre_render'] = array();
    }
    $element['#pre_render'][] = 'captcha_pre_render_process';

    // Store the solution in the #captcha_info array.
    $element['#captcha_info']['solution'] = $captcha['solution'];

    // Store if this captcha type is cacheable:
    // A cacheable captcha must not depend on the sid or solution, but be
    // independent - like for example recaptcha.
    $element['#captcha_info']['cacheable'] = !empty($captcha['cacheable']);
    if (!empty($element['#captcha_info']['cacheable']) && drupal_page_is_cacheable()) {

      // This is only used to avoid the re-use message.
      $element['captcha_cacheable'] = array(
        '#type' => 'hidden',
        '#value' => TRUE,
      );
    }

    // Make sure we can use a top level form value $form_state['values']['captcha_response'], even if the form has #tree=true.
    $element['#tree'] = FALSE;
  }
  return $element;
}