Ejemplo n.º 1
0
 /**
  * {@inheritdoc}
  */
 public static function preRenderElement(Element $element)
 {
     // Injects the icon into the title (the only way this is possible).
     if ($icon =& $element->getProperty('icon')) {
         $title = $element->getProperty('title');
         // Handle #icon_only.
         if ($element->getProperty('icon_only')) {
             if ($attribute_title = $element->getAttribute('title', '')) {
                 $title .= ' - ' . $attribute_title;
             }
             $element->setAttribute('title', $title)->addClass('icon-only')->setProperty('title', $icon);
             if (Bootstrap::getTheme()->getSetting('tooltip_enabled')) {
                 $element->setAttribute('data-toggle', 'tooltip');
             }
             return;
         }
         // Handle #icon_position.
         $position = $element->getProperty('icon_position', 'before');
         // Render #icon and trim it (so it doesn't add underlines in whitespace).
         $rendered_icon = trim(Element::create($icon)->render());
         // Default position is before.
         $markup = "{$rendered_icon}@title";
         if ($position === 'after') {
             $markup = "@title{$rendered_icon}";
         }
         // Replace the title and set an icon position class.
         $element->setProperty('title', new FormattableMarkup($markup, ['@title' => $title]))->addClass("icon-{$position}");
     }
 }
Ejemplo n.º 2
0
 /**
  * Global #process callback for form elements.
  *
  * @param array $element
  *   The element render array.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param array $complete_form
  *   The complete form structure.
  *
  * @return array
  *   The altered element array.
  *
  * @see \Drupal\bootstrap\Plugin\Alter\ElementInfo::alter
  */
 public static function process(array $element, FormStateInterface $form_state, array &$complete_form)
 {
     if (!empty($element['#bootstrap_ignore_process'])) {
         return $element;
     }
     static $theme;
     if (!isset($theme)) {
         $theme = Bootstrap::getTheme();
     }
     $e = Element::create($element, $form_state);
     // Process AJAX.
     if ($e->getProperty('ajax') && !$e->isButton() || $e->getProperty('autocomplete_route_name')) {
         static::processAjax($e, $form_state, $complete_form);
     }
     // Add "form-inline" class.
     if ($e->hasClass('container-inline')) {
         $e->replaceClass('container-inline', 'form-inline');
     }
     if ($e->isType(['color', 'date', 'number', 'range', 'tel', 'weight'])) {
         $e->addClass('form-inline', 'wrapper_attributes');
     }
     // Check for errors and set the "has_error" property flag.
     $errors = $e->getError();
     $e->setProperty('errors', $errors);
     if (isset($errors) || $e->getProperty('required') && $theme->getSetting('forms_required_has_error')) {
         $e->setProperty('has_error', TRUE);
     }
     // Process input groups.
     if ($e->getProperty('input') && ($e->getProperty('input_group') || $e->getProperty('input_group_button'))) {
         static::processInputGroups($e, $form_state, $complete_form);
     }
     return $element;
 }
Ejemplo n.º 3
0
 /**
  * Extracts the hook name from a function name.
  *
  * @param string $string
  *   The function name to extract the hook name from.
  * @param string $suffix
  *   A suffix hook ending (like "alter") to also remove.
  * @param string $prefix
  *   A prefix hook beginning (like "form") to also remove.
  *
  * @return string
  *   The extracted hook name.
  */
 public static function extractHook($string, $suffix = NULL, $prefix = NULL)
 {
     $regex = '^(' . implode('|', array_keys(Bootstrap::getTheme()->getAncestry())) . ')';
     $regex .= $prefix ? '_' . $prefix : '';
     $regex .= $suffix ? '_|_' . $suffix . '$' : '';
     return preg_replace("/{$regex}/", '', $string);
 }
Ejemplo n.º 4
0
 /**
  * {@inheritdoc}
  */
 public function __construct(array $configuration, $plugin_id, $plugin_definition)
 {
     if (!isset($configuration['theme'])) {
         $configuration['theme'] = Bootstrap::getTheme();
     }
     $this->theme = $configuration['theme'];
     parent::__construct($configuration, $plugin_id, $plugin_definition);
 }
Ejemplo n.º 5
0
 /**
  * Retrieves the currently selected theme on the settings form.
  *
  * @param array $form
  *   Nested array of form elements that comprise the form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  *
  * @return \Drupal\bootstrap\Theme|FALSE
  *   The currently selected theme object or FALSE if not a Bootstrap theme.
  */
 public static function getTheme(array &$form, FormStateInterface $form_state)
 {
     $build_info = $form_state->getBuildInfo();
     $theme = isset($build_info['args'][0]) ? Bootstrap::getTheme($build_info['args'][0]) : FALSE;
     // Do not continue if the theme is not Bootstrap specific.
     if (!$theme || !$theme->subthemeOf('bootstrap')) {
         unset($form['#submit'][0]);
         unset($form['#validate'][0]);
     }
     return $theme;
 }
Ejemplo n.º 6
0
 /**
  * {@inheritdoc}
  */
 public function __construct(array $configuration, $plugin_id, $plugin_definition)
 {
     // This is technically a plugin constructor, but because we wish to use the
     // protected methods of the Registry class, we must extend from it. Thus,
     // to properly construct the extended Registry object, we must pass the
     // arguments it would normally get from the service container to "fake" it.
     if (!isset($configuration['theme'])) {
         $configuration['theme'] = Bootstrap::getTheme();
     }
     $this->currentTheme = $configuration['theme'];
     parent::__construct(\Drupal::service('app.root'), \Drupal::service('cache.default'), \Drupal::service('lock'), \Drupal::service('module_handler'), \Drupal::service('theme_handler'), \Drupal::service('theme.initialization'), $this->currentTheme->getName());
     $this->setThemeManager(\Drupal::service('theme.manager'));
     $this->init();
 }
Ejemplo n.º 7
0
 /**
  * {@inheritdoc}
  */
 public static function processElement(Element $element, FormStateInterface $form_state, array &$complete_form)
 {
     if (isset($element->format)) {
         $element->format->addClass('form-inline');
         // Guidelines (removed).
         $element->format->guidelines->setProperty('access', FALSE);
         // Format (select).
         $element->format->format->addClass('input-sm')->setProperty('title_display', 'invisible')->setProperty('weight', -10);
         // Help (link).
         $element->format->help->about->setAttribute('title', t('Opens in new window'))->setProperty('icon', Bootstrap::glyphicon('question-sign'));
         if (Bootstrap::getTheme()->getSetting('tooltip_enabled')) {
             $element->format->help->about->setAttribute('data-toggle', 'tooltip');
         }
     }
 }
Ejemplo n.º 8
0
 /**
  * Global #process callback for form elements.
  *
  * @param array $element
  *   The element render array.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param array $complete_form
  *   The complete form structure.
  *
  * @return array
  *   The altered element array.
  *
  * @see \Drupal\bootstrap\Plugin\Alter\ElementInfo::alter
  */
 public static function process(array $element, FormStateInterface $form_state, array &$complete_form)
 {
     if (!empty($element['#bootstrap_ignore_process'])) {
         return $element;
     }
     static $theme;
     if (!isset($theme)) {
         $theme = Bootstrap::getTheme();
     }
     $e = Element::create($element, $form_state);
     // Add "form-inline" class.
     if ($e->hasClass('container-inline')) {
         $e->replaceClass('container-inline', 'form-inline');
     }
     if ($e->isType(['color', 'date', 'number', 'password', 'password_confirm', 'range', 'tel', 'weight'])) {
         $e->addClass('form-inline', 'wrapper_attributes');
     }
     // Add "form-group" class, don't replace "form-wrapper" as that is needed
     // by some JavaScript for certain functionality to work.
     if ($e->hasClass('form-wrapper')) {
         $e->addClass('form-group');
     }
     // Check for errors and set the "has_error" property flag.
     $errors = $e->getError();
     $e->setProperty('errors', $errors);
     if (isset($errors) || $e->getProperty('required') && $theme->getSetting('forms_required_has_error')) {
         $e->setProperty('has_error', TRUE);
     }
     // Automatically inject the nearest button found after this element if
     // #input_group_button exists.
     if ($e->getProperty('input_group_button')) {
         // Obtain the parent array to limit search.
         $array_parents = $e->getProperty('array_parents', []);
         // Remove the current element from the array.
         array_pop($array_parents);
         // If element is nested, return the referenced parent from the form.
         // Otherwise return the complete form.
         $parent = Element::create($array_parents ? NestedArray::getValue($complete_form, $array_parents) : $complete_form, $form_state);
         // Find the closest button.
         if ($button = self::findButton($parent)) {
             $e->setProperty('field_suffix', $button->setIcon()->getArray());
             $button->setProperty('access', FALSE);
         }
     }
     return $element;
 }
Ejemplo n.º 9
0
 /**
  * AJAX callback for reloading CDN provider elements.
  *
  * @param array $form
  *   Nested array of form elements that comprise the form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  */
 public static function ajaxCallback(array $form, FormStateInterface $form_state)
 {
     return $form['advanced']['cdn'][$form_state->getValue('cdn_provider', Bootstrap::getTheme()->getSetting('cdn_provider'))];
 }
Ejemplo n.º 10
0
/**
 * Retrieves a setting for the current theme or for a given theme.
 *
 * @param string $name
 *   The name of the setting to be retrieved.
 * @param string $theme
 *   The name of a given theme; defaults to the currently active theme.
 * @param string $prefix
 *   The prefix used on the $name of the setting, this will be appended with
 *   "_" automatically if set.
 *
 * @return mixed
 *   The value of the requested setting, NULL if the setting does not exist.
 *
 * @deprecated Will be removed in a future release.
 *
 * @code
 *   // Before ("button_colorize" and "my_subtheme_custom_option").
 *   $colorize = bootstrap_setting('button_colorize', 'my_subtheme');
 *   $custom_option = bootstrap_setting('custom_option', 'my_subtheme', 'my_subtheme');
 *
 *   // After ("button_colorize" and "my_subtheme_custom_option").
 *   use Drupal\bootstrap\Bootstrap;
 *   $my_subtheme = Bootstrap::getTheme('my_subtheme');
 *   $my_subtheme->getSetting('button_colorize');
 *   $my_subtheme->getSetting('my_subtheme_custom_option');
 * @endcode
 *
 * @see \Drupal\bootstrap\Theme::getSetting()
 * @see \Drupal\bootstrap\Bootstrap::getTheme()
 */
function bootstrap_setting($name, $theme = NULL, $prefix = 'bootstrap')
{
    Bootstrap::deprecated();
    $theme = Bootstrap::getTheme($theme);
    $prefix = $prefix !== 'bootstrap' && !empty($prefix) ? $prefix . '_' : '';
    return $theme->getSetting($prefix . $name);
}
Ejemplo n.º 11
0
 /**
  * Retrieves the full base/sub-theme ancestry of a theme.
  *
  * @param bool $reverse
  *   Whether or not to return the array of themes in reverse order, where the
  *   active theme is the first entry.
  *
  * @return \Drupal\bootstrap\Theme[]
  *   An associative array of \Drupal\bootstrap objects (theme), keyed
  *   by machine name.
  */
 public function getAncestry($reverse = FALSE)
 {
     $ancestry = $this->themeHandler->getBaseThemes($this->themes, $this->getName());
     foreach (array_keys($ancestry) as $name) {
         $ancestry[$name] = Bootstrap::getTheme($name, $this->themeHandler);
     }
     $ancestry[$this->getName()] = $this;
     return $reverse ? array_reverse($ancestry) : $ancestry;
 }
Ejemplo n.º 12
0
 /**
  * Converts an element description into a tooltip based on certain criteria.
  *
  * @param array|\Drupal\bootstrap\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 = Bootstrap::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;
 }
Ejemplo n.º 13
0
 /**
  * Matches a Bootstrap Glyphicon based on a string value.
  *
  * @param string $string
  *   The string to match classes against.
  * @param array $default
  *   The default render array to return if no match is found.
  *
  * @return string
  *   The Bootstrap icon matched against the value of $haystack or $default if
  *   no match could be made.
  */
 public static function glyphiconFromString($string, $default = [])
 {
     static $lang;
     if (!isset($lang)) {
         $lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
     }
     $theme = Bootstrap::getTheme();
     $texts = $theme->getCache('glyphiconFromString', [$lang]);
     $string = (string) $string;
     if ($texts->isEmpty()) {
         $data = ['matches' => [], 'contains' => [t('Manage')->render() => 'cog', t('Configure')->render() => 'cog', t('Settings')->render() => 'cog', t('Download')->render() => 'download', t('Export')->render() => 'export', t('Filter')->render() => 'filter', t('Import')->render() => 'import', t('Save')->render() => 'ok', t('Update')->render() => 'ok', t('Edit')->render() => 'pencil', t('Uninstall')->render() => 'trash', t('Install')->render() => 'plus', t('Write')->render() => 'plus', t('Cancel')->render() => 'remove', t('Delete')->render() => 'trash', t('Remove')->render() => 'trash', t('Search')->render() => 'search', t('Upload')->render() => 'upload', t('Preview')->render() => 'eye-open']];
         // Allow sub-themes to alter this array of patterns.
         /** @var \Drupal\Core\Theme\ThemeManager $theme_manager */
         $theme_manager = \Drupal::service('theme.manager');
         $theme_manager->alter('bootstrap_iconize_text', $data);
         $texts->setMultiple($data);
     }
     // Iterate over the array.
     foreach ($texts as $pattern => $strings) {
         foreach ($strings as $value => $icon) {
             switch ($pattern) {
                 case 'matches':
                     if ($string === $value) {
                         return self::glyphicon($icon, $default);
                     }
                     break;
                 case 'contains':
                     if (strpos(Unicode::strtolower($string), Unicode::strtolower($value)) !== FALSE) {
                         return self::glyphicon($icon, $default);
                     }
                     break;
             }
         }
     }
     // Return a default icon if nothing was matched.
     return $default;
 }
Ejemplo n.º 14
0
 /**
  * {@inheritdoc}
  */
 public function getTheme()
 {
     return Bootstrap::getTheme($this->pluginDefinition['provider']);
 }
Ejemplo n.º 15
0
 /**
  * Batch 'finished' callback
  *
  * @param bool $success
  *   A boolean indicating whether the batch has completed successfully.
  * @param array $results
  *   The value(s) set in $context['results'] in
  *   \Drupal\bootstrap\Plugin\Setting\Update::batchProcess().
  * @param $operations
  *   If $success is FALSE, contains the operations that remained unprocessed.
  */
 public static function batchFinished($success, $results, $operations)
 {
     /** @type \Drupal\bootstrap\Theme $theme */
     // Reconstruct the theme object this update is being applied to.
     $theme = Bootstrap::getTheme($results['theme_name']);
     // Save the current state of the installed schemas.
     $theme->setSetting('schemas', $results['schemas']);
     // Show successful updates.
     if (!empty($results['success'])) {
         $build = ['#theme' => 'item_list__theme_update', '#items' => $results['success'], '#context' => ['type' => 'success']];
         drupal_set_message(new FormattableMarkup('@message' . \Drupal::service('renderer')->render($build), ['@message' => t('Successfully completed the following theme updates:')]));
     }
     // Show failed errors.
     if (!empty($results['errors'])) {
         $build = ['#theme' => 'item_list__theme_update', '#items' => $results['errors'], '#context' => ['type' => 'errors']];
         drupal_set_message(new FormattableMarkup('@message' . \Drupal::service('renderer')->render($build), ['@message' => t('The following theme updates could not be completed:')]), 'error');
     }
 }