public static function Captcha::processCaptchaElement in CAPTCHA 8
Process callback for CAPTCHA form element.
File
- src/
Element/ Captcha.php, line 86
Class
- Captcha
- Defines the CAPTCHA form element with default properties.
Namespace
Drupal\captcha\ElementCode
public static function processCaptchaElement(&$element, FormStateInterface $form_state, &$complete_form) {
// Add captcha.inc file.
module_load_include('inc', 'captcha');
// Add JavaScript for general CAPTCHA functionality.
$element['#attached']['library'][] = 'captcha/base';
if ($form_state
->getTriggeringElement() && isset($form_state
->getTriggeringElement()['#limit_validation_errors']) && is_array($form_state
->getTriggeringElement()['#limit_validation_errors'])) {
// This is a partial (ajax) submission with limited validation. Do not
// change anything about the captcha element, assume that it will not
// update the captcha element, do not generate anything, which keeps the
// current token intact for the real submission.
return $element;
}
// 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 = isset($complete_form['form_id']['#value']) ? preg_replace("/[^a-z0-9_-]/", "", (string) $complete_form['form_id']['#value']) : NULL;
// 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);
$captcha_token = Crypt::randomBytesBase64();
\Drupal::database()
->update('captcha_sessions')
->fields([
'token' => $captcha_token,
])
->condition('csid', $captcha_sid)
->execute();
}
// 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'] = [
'#type' => 'hidden',
'#value' => $captcha_sid,
];
// Store CAPTCHA token as hidden field.
$captcha_token = \Drupal::database()
->select('captcha_sessions', 'cs')
->fields('cs', [
'token',
])
->condition('csid', $captcha_sid)
->execute()
->fetchField();
$element['captcha_token'] = [
'#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->get('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).
// Added a new access attribute,
// by default it will be true if access attribute
// not defined in a custom form.
$form_state
->set('captcha_info', [
'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,
'access' => isset($element['#access']) ? $element['#access'] : CAPTCHA_FIELD_DEFAULT_ACCESS,
]);
$element['#captcha_info'] = [
'form_id' => $this_form_id,
'captcha_sid' => $captcha_sid,
];
if (_captcha_required_for_user($captcha_sid, $this_form_id) || $element['#captcha_admin_mode']) {
// Generate a CAPTCHA and its solution
// (note that the CAPTCHA session ID is given as third argument).
$captcha = \Drupal::moduleHandler()
->invoke($captcha_type_module, 'captcha', [
'generate',
$captcha_type_challenge,
$captcha_sid,
]);
// @todo Isn't this moment a bit late to figure out
// that we don't need CAPTCHA?
if (!isset($captcha)) {
return $element;
}
if (!isset($captcha['form']) || !isset($captcha['solution'])) {
// The selected module did not return what we expected:
// log about it and quit.
\Drupal::logger('CAPTCHA')
->error('CAPTCHA problem: unexpected result from hook_captcha() of module %module when trying to retrieve challenge type %type for form %form_id.', [
'%type' => $captcha_type_challenge,
'%module' => $captcha_type_module,
'%form_id' => $this_form_id,
]);
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'] = [
'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'] = [];
}
$element['#pre_render'][] = [
Captcha::class,
'preRenderProcess',
];
// 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'])) {
// This is only used to avoid the re-use message.
$element['captcha_cacheable'] = [
'#type' => 'hidden',
'#value' => 1,
];
}
// Make sure we can use a top level form value
// $form_state->getValue('captcha_response'),
// even if the form has #tree=true.
$element['#tree'] = FALSE;
}
return $element;
}