/** * Manages theme alter hooks as classes and allows sub-themes to sub-class. * * @param string $function * The procedural function name of the alter (e.g. __FUNCTION__). * @param mixed $data * The variable that was passed to the hook_TYPE_alter() implementation to * be altered. The type of this variable depends on the value of the $type * argument. For example, when altering a 'form', $data will be a structured * array. When altering a 'profile', $data will be an object. * @param mixed $context1 * (optional) An additional variable that is passed by reference. * @param mixed $context2 * (optional) An additional variable that is passed by reference. If more * context needs to be provided to implementations, then this should be an * associative array as described above. */ public static function alter($function, &$data, &$context1 = NULL, &$context2 = NULL) { static $theme; if (!isset($theme)) { $theme = self::getTheme(); } // Immediately return if the active theme is not Bootstrap based. if (!$theme->subthemeOf('materialize')) { return; } // Extract the alter hook name. $hook = Unicode::extractHook($function, 'alter'); // Handle form alters separately. if (strpos($hook, 'form') === 0) { $form_id = $context2; if (!$form_id) { $form_id = Unicode::extractHook($function, 'alter', 'form'); } // Due to a core bug that affects admin themes, we should not double // process the "system_theme_settings" form twice in the global // hook_form_alter() invocation. // @see https://drupal.org/node/943212 if ($context2 === 'system_theme_settings') { return; } // Retrieve a list of form definitions. $form_manager = new FormManager($theme); /** @var \Drupal\bootstrap\Plugin\Form\FormInterface $form */ if ($form_manager->hasDefinition($form_id) && ($form = $form_manager->createInstance($form_id, ['theme' => $theme]))) { $data['#submit'][] = [get_class($form), 'submitForm']; $data['#validate'][] = [get_class($form), 'validateForm']; $form->alterForm($data, $context1, $context2); } } else { // Retrieve a list of alter definitions. $alter_manager = new AlterManager($theme); /** @var \Drupal\bootstrap\Plugin\Alter\AlterInterface $class */ if ($alter_manager->hasDefinition($hook) && ($class = $alter_manager->createInstance($hook, ['theme' => $theme]))) { $class->alter($data, $context1, $context2); } } }
/** * Converts an element description into a tooltip based on certain criteria. * * @param array|\Drupal\materialize\Utility\Element|NULL $target_element * The target element render array the tooltip is to be attached to, passed * by reference or an existing Element object. If not set, it will default * this Element instance. * @param bool $input_only * Toggle determining whether or not to only convert input elements. * @param int $length * The length of characters to determine if description is "simple". * * @return $this */ public function smartDescription(&$target_element = NULL, $input_only = TRUE, $length = NULL) { static $theme; if (!isset($theme)) { $theme = Materialize::getTheme(); } // Determine if tooltips are enabled. static $enabled; if (!isset($enabled)) { $enabled = $theme->getSetting('tooltip_enabled') && $theme->getSetting('forms_smart_descriptions'); } // Immediately return if tooltip descriptions are not enabled. if (!$enabled) { return $this; } // Allow a different element to attach the tooltip. /** @var Element $target */ if (is_object($target_element) && $target_element instanceof self) { $target = $target_element; } elseif (isset($target_element) && is_array($target_element)) { $target = new self($target_element, $this->formState); } else { $target = $this; } // Retrieve the length limit for smart descriptions. if (!isset($length)) { // Disable length checking by setting it to FALSE if empty. $length = (int) $theme->getSetting('forms_smart_descriptions_limit') ?: FALSE; } // Retrieve the allowed tags for smart descriptions. This is primarily used // for display purposes only (i.e. non-UI/UX related elements that wouldn't // require a user to "click", like a link). Disable length checking by // setting it to FALSE if empty. static $allowed_tags; if (!isset($allowed_tags)) { $allowed_tags = array_filter(array_unique(array_map('trim', explode(',', $theme->getSetting('forms_smart_descriptions_allowed_tags') . '')))) ?: FALSE; } // Return if element or target shouldn't have "simple" tooltip descriptions. $html = FALSE; if ($input_only && !$target->hasProperty('input') || !$this->getProperty('smart_description', TRUE) || !$target->getProperty('smart_description', TRUE) || !$this->hasProperty('description') || $target->hasAttribute('data-toggle') || !Unicode::isSimple($this->getProperty('description'), $length, $allowed_tags, $html)) { return $this; } // Default attributes type. $type = DrupalAttributes::ATTRIBUTES; // Use #label_attributes for 'checkbox' and 'radio' elements. if ($this->isType(['checkbox', 'radio'])) { $type = DrupalAttributes::LABEL; } elseif ($this->isType(['checkboxes', 'radios'])) { $type = DrupalAttributes::WRAPPER; } // Retrieve the proper attributes array. $attributes = $target->getAttributes($type); // Set the tooltip attributes. $attributes['title'] = $allowed_tags !== FALSE ? Xss::filter((string) $this->getProperty('description'), $allowed_tags) : $this->getProperty('description'); $attributes['data-toggle'] = 'tooltip'; if ($html || $allowed_tags === FALSE) { $attributes['data-html'] = 'true'; } // Remove the element description so it isn't (re-)rendered later. $this->unsetProperty('description'); return $this; }