class Captcha in CAPTCHA 8
Defines the CAPTCHA form element with default properties.
Plugin annotation
@FormElement("captcha");
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\captcha\Element\Captcha implements ContainerFactoryPluginInterface
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of Captcha
1 file declares its use of Captcha
- captcha.module in ./
captcha.module - This module enables basic CAPTCHA functionality.
3 #type uses of Captcha
- CaptchaExamplesForm::buildChallenge in src/
Form/ CaptchaExamplesForm.php - Returns a renderable array for a given CAPTCHA challenge.
- captcha_form_alter in ./
captcha.module - Implements hook_form_alter().
- ImageCaptchaSettingsForm::buildForm in image_captcha/
src/ Form/ ImageCaptchaSettingsForm.php - Form constructor.
File
- src/
Element/ Captcha.php, line 17
Namespace
Drupal\captcha\ElementView source
class Captcha extends FormElement implements ContainerFactoryPluginInterface {
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Captcha constructor.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The configuration factory.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $configFactory) {
$this->configFactory = $configFactory;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('config.factory'));
}
/**
* {@inheritdoc}
*/
public function getInfo() {
$captcha_element = [
'#input' => TRUE,
'#process' => [
[
static::class,
'processCaptchaElement',
],
],
// The type of challenge: e.g. 'default', 'captcha/Math', etc.
'#captcha_type' => 'default',
'#default_value' => '',
// CAPTCHA in admin mode: presolve the CAPTCHA and always show
// it (despite previous successful responses).
'#captcha_admin_mode' => FALSE,
// The default CAPTCHA validation function.
// TODO: should this be a single string or an array of strings?
'#captcha_validate' => 'captcha_validate_strict_equality',
];
// Override the default CAPTCHA validation function for case
// insensitive validation.
// TODO: shouldn't this be done somewhere else, e.g. in form_alter?
if (CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE == $this->configFactory
->get('captcha.settings')
->get('default_validation')) {
$captcha_element['#captcha_validate'] = 'captcha_validate_case_insensitive_equality';
}
return $captcha_element;
}
/**
* Process callback for CAPTCHA form element.
*/
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;
}
/**
* Pre-render callback for additional processing of a CAPTCHA form element.
*
* This encompasses tasks that should happen after the general FAPI processing
* (building, submission and validation) but before rendering
* (e.g. storing the solution).
*
* @param array $element
* The CAPTCHA form element.
*
* @return array
* The manipulated element.
*/
public static function preRenderProcess(array $element) {
module_load_include('inc', 'captcha');
// Get form and CAPTCHA information.
$captcha_info = $element['#captcha_info'];
$form_id = $captcha_info['form_id'];
$captcha_sid = (int) $captcha_info['captcha_sid'];
// Check if CAPTCHA is still required.
// This check is done in a first phase during the element processing
// (@see captcha_process), but it is also done here for better support
// of multi-page forms. Take previewing a node submission for example:
// when the challenge is solved correctely on preview, the form is still
// not completely submitted, but the CAPTCHA can be skipped.
if (_captcha_required_for_user($captcha_sid, $form_id) || $element['#captcha_admin_mode']) {
// Update captcha_sessions table: store the solution
// of the generated CAPTCHA.
_captcha_update_captcha_session($captcha_sid, $captcha_info['solution']);
// Handle the response field if it is available and if it is a textfield.
if (isset($element['captcha_widgets']['captcha_response']['#type']) && $element['captcha_widgets']['captcha_response']['#type'] == 'textfield') {
// Before rendering: presolve an admin mode challenge or
// empty the value of the captcha_response form item.
$value = $element['#captcha_admin_mode'] ? $captcha_info['solution'] : '';
$element['captcha_widgets']['captcha_response']['#value'] = $value;
}
}
else {
// Remove CAPTCHA widgets from form.
unset($element['captcha_widgets']);
}
return $element;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Captcha:: |
protected | property | The config factory. | |
Captcha:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
|
Captcha:: |
public | function |
Returns the element properties for this element. Overrides ElementInterface:: |
|
Captcha:: |
public static | function | Pre-render callback for additional processing of a CAPTCHA form element. | |
Captcha:: |
public static | function | Process callback for CAPTCHA form element. | |
Captcha:: |
public | function |
Captcha constructor. Overrides PluginBase:: |
|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
FormElement:: |
public static | function | Adds autocomplete functionality to elements. | |
FormElement:: |
public static | function | #process callback for #pattern form element property. | |
FormElement:: |
public static | function | #element_validate callback for #pattern form element property. | |
FormElement:: |
public static | function |
Determines how user input is mapped to an element's #value property. Overrides FormElementInterface:: |
15 |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
RenderElement:: |
public static | function | Adds Ajax information about an element to communicate with JavaScript. | |
RenderElement:: |
public static | function | Adds members of this group as actual elements for rendering. | |
RenderElement:: |
public static | function | Form element processing handler for the #ajax form property. | 1 |
RenderElement:: |
public static | function | Arranges elements into groups. | |
RenderElement:: |
public static | function |
Sets a form element's class attribute. Overrides ElementInterface:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |