示例#1
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);
 }
示例#2
0
 /**
  * {@inheritdoc}
  */
 public function preprocessElement(Element $element, Variables $variables)
 {
     $link = $element->getProperty('link');
     $link += ['localized_options' => []];
     $link['localized_options']['set_active_class'] = TRUE;
     $icon = Bootstrap::glyphiconFromString($link['title']);
     $options = isset($link['localized_options']) ? $link['localized_options'] : [];
     if (isset($link['url'])) {
         // Turn link into a mini-button and colorize based on title.
         $class = Bootstrap::cssClassFromString($link['title'], 'default');
         if (!isset($options['attributes']['class'])) {
             $options['attributes']['class'] = [];
         }
         $string = is_string($options['attributes']['class']);
         if ($string) {
             $options['attributes']['class'] = explode(' ', $options['attributes']['class']);
         }
         $options['attributes']['class'][] = 'btn';
         $options['attributes']['class'][] = 'btn-xs';
         $options['attributes']['class'][] = 'btn-' . $class;
         if ($string) {
             $options['attributes']['class'] = implode(' ', $options['attributes']['class']);
         }
         $variables['link'] = ['#type' => 'link', '#title' => SafeMarkup::format(\Drupal::service('renderer')->render($icon) . '@text', ['@text' => $link['title']]), '#options' => $options, '#url' => $link['url']];
     } else {
         $variables['link'] = ['#type' => 'link', '#title' => $link['title'], '#options' => $options, '#url' => $link['url']];
     }
 }
示例#3
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}");
     }
 }
示例#4
0
文件: Input.php 项目: 318io/318-io
 /**
  * {@inheritdoc}
  */
 public function preprocessElement(Variables $variables, $hook, array $info)
 {
     $variables->element->map(['id', 'name', 'value', 'type']);
     // Autocomplete.
     if ($route = $variables->element->getProperty('autocomplete_route_name')) {
         $variables['autocomplete'] = TRUE;
         // Use an icon for autocomplete "throbber".
         $icon = Bootstrap::glyphicon('refresh', ['#type' => 'html_tag', '#tag' => 'span', '#attributes' => ['class' => ['ajax-progress', 'ajax-progress-throbber', 'invisible']], 'throbber' => ['#type' => 'html_tag', '#tag' => 'span', '#attributes' => ['class' => ['throbber']]]]);
         $variables->element->setProperty('input_group', TRUE);
         $variables->element->setProperty('field_suffix', $icon);
     }
     // Create variables for #input_group and #input_group_button flags.
     $variables['input_group'] = $variables->element->getProperty('input_group') || $variables->element->getProperty('input_group_button');
     if ($variables['input_group']) {
         $input_group_attributes = ['class' => ['input-group-' . ($variables->element->getProperty('input_group_button') ? 'btn' : 'addon')]];
         if ($prefix = $variables->element->getProperty('field_prefix')) {
             $variables->element->setProperty('field_prefix', ['#type' => 'html_tag', '#tag' => 'span', '#attributes' => $input_group_attributes, '#value' => Element::create($prefix)->render(), '#weight' => -1]);
         }
         if ($suffix = $variables->element->getProperty('field_suffix')) {
             $variables->element->setProperty('field_suffix', ['#type' => 'html_tag', '#tag' => 'span', '#attributes' => $input_group_attributes, '#value' => Element::create($suffix)->render(), '#weight' => 1]);
         }
     }
     // Map the element properties.
     $variables->map(['attributes' => 'attributes', 'icon' => 'icon', 'field_prefix' => 'prefix', 'field_suffix' => 'suffix', 'type' => 'type']);
     // Ensure attributes are proper objects.
     $this->preprocessAttributes($variables, $hook, $info);
 }
示例#5
0
 /**
  * {@inheritdoc}
  */
 protected function preprocessVariables(Variables $variables, $hook, array $info)
 {
     // Retrieve the ID, generating one if needed.
     $id = $variables->getAttribute('id', Html::getUniqueId($variables->offsetGet('id', 'bootstrap-carousel')));
     unset($variables['id']);
     // Build slides.
     foreach ($variables->slides as $key => &$slide) {
         if (!isset($slide['attributes'])) {
             $slide['attributes'] = [];
         }
         $slide['attributes'] = new Attribute($slide['attributes']);
     }
     // Build controls.
     if ($variables->controls) {
         $left_icon = Bootstrap::glyphicon('chevron-left');
         $right_icon = Bootstrap::glyphicon('chevron-right');
         $url = Url::fromUserInput("#{$id}");
         $variables->controls = ['left' => ['#type' => 'link', '#title' => new FormattableMarkup(Element::create($left_icon)->render() . '<span class="sr-only">@text</span>', ['@text' => t('Previous')]), '#url' => $url, '#attributes' => ['class' => ['left', 'carousel-control'], 'role' => 'button', 'data-slide' => 'prev']], 'right' => ['#type' => 'link', '#title' => new FormattableMarkup(Element::create($right_icon)->render() . '<span class="sr-only">@text</span>', ['@text' => t('Next')]), '#url' => $url, '#attributes' => ['class' => ['right', 'carousel-control'], 'role' => 'button', 'data-slide' => 'next']]];
     }
     // Build indicators.
     if ($variables->indicators) {
         $variables->indicators = ['#theme' => 'item_list__bootstrap_carousel_indicators', '#list_type' => 'ol', '#items' => array_keys($variables->slides), '#target' => "#{$id}", '#start_index' => $variables->start_index];
     }
     // Ensure all attributes are proper objects.
     $this->preprocessAttributes($variables, $hook, $info);
 }
示例#6
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);
 }
示例#7
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;
 }
示例#8
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();
 }
示例#9
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');
         }
     }
 }
示例#10
0
 /**
  * {@inheritdoc}
  */
 public function alter(&$types, &$context1 = NULL, &$context2 = NULL)
 {
     // Sort the types for easier debugging.
     ksort($types, SORT_NATURAL);
     $process_manager = new ProcessManager($this->theme);
     $pre_render_manager = new PrerenderManager($this->theme);
     foreach (array_keys($types) as $type) {
         $element =& $types[$type];
         // Ensure elements that have a base type with the #input set match.
         if (isset($element['#base_type']) && isset($types[$element['#base_type']]) && isset($types[$element['#base_type']]['#input'])) {
             $element['#input'] = $types[$element['#base_type']]['#input'];
         }
         // Core does not actually use the "description_display" property on the
         // "details" or "fieldset" element types because the positioning of the
         // description is never used in core templates. However, the form builder
         // automatically applies the value of "after", thus making it impossible
         // to detect a valid value later in the rendering process. It looks better
         // for the "details" and "fieldset" element types to display as "before".
         // @see \Drupal\Core\Form\FormBuilder::doBuildForm()
         if ($type === 'details' || $type === 'fieldset') {
             $element['#description_display'] = 'before';
             $element['#panel_type'] = 'default';
         }
         // Add extra variables to all elements.
         foreach (Bootstrap::extraVariables() as $key => $value) {
             if (!isset($variables["#{$key}"])) {
                 $variables["#{$key}"] = $value;
             }
         }
         // Only continue if the type isn't "form" (as it messes up AJAX).
         if ($type !== 'form') {
             $regex = "/^{$type}/";
             // Add necessary #process callbacks.
             $element['#process'][] = [get_class($process_manager), 'process'];
             $definitions = $process_manager->getDefinitionsLike($regex);
             foreach ($definitions as $definition) {
                 Bootstrap::addCallback($element['#process'], [$definition['class'], 'process'], $definition['replace'], $definition['action']);
             }
             // Add necessary #pre_render callbacks.
             $element['#pre_render'][] = [get_class($pre_render_manager), 'preRender'];
             foreach ($pre_render_manager->getDefinitionsLike($regex) as $definition) {
                 Bootstrap::addCallback($element['#pre_render'], [$definition['class'], 'preRender'], $definition['replace'], $definition['action']);
             }
         }
     }
 }
示例#11
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;
 }
示例#12
0
 /**
  * {@inheritdoc}
  */
 public function preprocessVariables(Variables $variables, $hook, array $info)
 {
     if (!empty($variables['description'])) {
         $variables['description'] = FieldFilteredMarkup::create($variables['description']);
     }
     $descriptions = [];
     $cardinality = $variables['cardinality'];
     if (isset($cardinality)) {
         if ($cardinality == -1) {
             $descriptions[] = t('Unlimited number of files can be uploaded to this field.');
         } else {
             $descriptions[] = \Drupal::translation()->formatPlural($cardinality, 'One file only.', 'Maximum @count files.');
         }
     }
     $upload_validators = $variables['upload_validators'];
     if (isset($upload_validators['file_validate_size'])) {
         $descriptions[] = t('@size limit.', ['@size' => format_size($upload_validators['file_validate_size'][0])]);
     }
     if (isset($upload_validators['file_validate_extensions'])) {
         $extensions = new FormattableMarkup('<code>@extensions</code>', ['@extensions' => implode(', ', explode(' ', $upload_validators['file_validate_extensions'][0]))]);
         $descriptions[] = t('Allowed types: @extensions.', ['@extensions' => $extensions]);
     }
     if (isset($upload_validators['file_validate_image_resolution'])) {
         $max = $upload_validators['file_validate_image_resolution'][0];
         $min = $upload_validators['file_validate_image_resolution'][1];
         if ($min && $max && $min == $max) {
             $descriptions[] = t('Images must be exactly <strong>@size</strong> pixels.', ['@size' => $max]);
         } elseif ($min && $max) {
             $descriptions[] = t('Images must be larger than <strong>@min</strong> pixels. Images larger than <strong>@max</strong> pixels will be resized.', ['@min' => $min, '@max' => $max]);
         } elseif ($min) {
             $descriptions[] = t('Images must be larger than <strong>@min</strong> pixels.', ['@min' => $min]);
         } elseif ($max) {
             $descriptions[] = t('Images larger than <strong>@max</strong> pixels will be resized.', ['@max' => $max]);
         }
     }
     $variables['descriptions'] = $descriptions;
     if ($descriptions) {
         $build = array();
         $id = Html::getUniqueId('upload-instructions');
         $build['toggle'] = ['#type' => 'link', '#title' => t('Upload requirements'), '#url' => Url::fromUserInput("#{$id}"), '#icon' => Bootstrap::glyphicon('question-sign'), '#attributes' => ['class' => ['icon-before'], 'data-toggle' => 'popover', 'data-html' => 'true', 'data-placement' => 'bottom', 'data-title' => t('Upload requirements')]];
         $build['requirements'] = ['#type' => 'container', '#theme_wrappers' => ['container__file_upload_help'], '#attributes' => ['id' => $id, 'class' => ['hidden', 'help-block'], 'aria-hidden' => 'true']];
         $build['requirements']['descriptions'] = ['#theme' => 'item_list__file_upload_help', '#items' => $descriptions];
         $variables['popover'] = $build;
     }
 }
示例#13
0
文件: Update.php 项目: frankcr/sftw8
 /**
  * {@inheritdoc}
  */
 public function alterForm(array &$form, FormStateInterface $form_state, $form_id = NULL)
 {
     parent::alterForm($form, $form_state, $form_id);
     $update_manager = new UpdateManager($this->theme);
     $pending = $update_manager->getPendingUpdates();
     if ($pending) {
         $form['update'] = ['#type' => 'details', '#title' => \Drupal::translation()->formatPlural(count($pending), 'Pending Update', 'Pending Updates'), '#panel_type' => 'primary', '#weight' => -20];
         $rows = [];
         foreach ($pending as $version => $update) {
             $row = [];
             $row[] = $version;
             $row[] = new FormattableMarkup('<strong>@title</strong><p class="help-block">@description</p>', ['@title' => $update->getTitle(), '@description' => $update->getDescription()]);
             $rows[] = ['class' => [$update->getLevel() ?: 'default'], 'data' => $row];
         }
         $form['update']['table'] = ['#type' => 'table', '#header' => [t('Update'), t('Description')], '#rows' => $rows];
         $form['update']['update'] = ['#type' => 'submit', '#value' => t('Update @theme', ['@theme' => $this->theme->getTitle()]), '#icon' => Bootstrap::glyphicon('open'), '#attributes' => ['class' => ['btn-primary']], '#submit' => [[get_class($this), 'updateTheme']]];
     }
 }
示例#14
0
 /**
  * {@inheritdoc}
  */
 public function preprocessVariables(Variables $variables)
 {
     $region = $variables['elements']['#region'];
     $variables['region'] = $region;
     $variables['content'] = $variables['elements']['#children'];
     // Help region.
     if ($region === 'help' && !empty($variables['content'])) {
         $variables['content'] = ['icon' => Bootstrap::glyphicon('question-sign'), 'content' => ['#markup' => $variables['content']]];
         $variables->addClass(['alert', 'alert-info', 'messages', 'info']);
     }
     // Support for "well" classes in regions.
     static $region_wells;
     if (!isset($region_wells)) {
         $region_wells = $this->theme->getSetting('region_wells');
     }
     if (!empty($region_wells[$region])) {
         $variables->addClass($region_wells[$region]);
     }
 }
 /**
  * {@inheritdoc}
  */
 public function alterForm(array &$form, FormStateInterface $form_state, $form_id = NULL)
 {
     $e = Element::create($form, $form_state);
     $e->addClass(['form-inline', 'bg-info', 'text-center', 'clearfix']);
     // Backlink.
     $options = $e->backlink->getProperty('options', []);
     $e->backlink->addClass(isset($options['attributes']['class']) ? $options['attributes']['class'] : []);
     $e->backlink->addClass(['btn', 'btn-info', 'pull-left']);
     $e->backlink->setButtonSize();
     $e->backlink->setIcon(Bootstrap::glyphicon('chevron-left'));
     // Ensure the UUID is set.
     if ($uuid = $e->uuid->getProperty('value')) {
         $options['query'] = ['uuid' => $uuid];
     }
     // Override the options attributes.
     $options['attributes'] = $e->backlink->getAttributes()->getArrayCopy();
     $e->backlink->setProperty('options', $options);
     // View mode.
     $e->view_mode->addClass('pull-right', $e::WRAPPER);
 }
示例#16
0
 /**
  * {@inheritdoc}
  */
 public function preprocessVariables(Variables $variables, $hook, array $info)
 {
     $options = [];
     $file = $variables['file'] instanceof File ? $variables['file'] : File::load($variables['file']->fid);
     $url = file_create_url($file->getFileUri());
     $file_size = $file->getSize();
     $mime_type = $file->getMimeType();
     // Set options as per anchor format described at
     // http://microformats.org/wiki/file-format-examples
     $options['attributes']['type'] = "{$mime_type}; length={$file_size}";
     // Use the description as the link text if available.
     if (empty($variables['description'])) {
         $link_text = $file->getFilename();
     } else {
         $link_text = $variables['description'];
         $options['attributes']['title'] = $file->getFilename();
     }
     // Retrieve the generic mime type from core (mislabeled as "icon_class").
     $generic_mime_type = file_icon_class($mime_type);
     // Map the generic mime types to an icon and state.
     $mime_map = ['application-x-executable' => ['label' => t('binary file'), 'icon' => 'console'], 'audio' => ['label' => t('audio file'), 'icon' => 'headphones'], 'image' => ['label' => t('image'), 'icon' => 'picture'], 'package-x-generic' => ['label' => t('archive'), 'icon' => 'compressed'], 'text' => ['label' => t('document'), 'icon' => 'file'], 'video' => ['label' => t('video'), 'icon' => 'film']];
     // Retrieve the mime map array.
     $mime = isset($mime_map[$generic_mime_type]) ? $mime_map[$generic_mime_type] : ['label' => t('file'), 'icon' => 'file', 'state' => 'primary'];
     // Classes to add to the file field for icons.
     $variables->addClass(['file', 'file--mime-' . strtr($mime_type, ['/' => '-', '.' => '-']), 'file--' . $generic_mime_type]);
     // Set the icon for the mime type.
     $icon = Bootstrap::glyphicon($mime['icon']);
     $variables->icon = Element::create($icon)->addClass('text-primary')->getArray();
     $options['attributes']['title'] = t('Open @mime in new window', ['@mime' => $mime['label']]);
     if ($this->theme->getSetting('tooltip_enabled')) {
         $options['attributes']['data-toggle'] = 'tooltip';
         $options['attributes']['data-placement'] = 'bottom';
     }
     $variables['link'] = Link::fromTextAndUrl($link_text, Url::fromUri($url, $options));
     // Add the file size as a variable.
     $variables->file_size = format_size($file_size);
     // Preprocess attributes.
     $this->preprocessAttributes($variables, $hook, $info);
 }
示例#17
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');
     }
 }
示例#18
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'))];
 }
示例#19
0
 /**
  * Processes elements with AJAX properties.
  *
  * @param \Drupal\bootstrap\Utility\Element $element
  *   The element object.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param array $complete_form
  *   The complete form structure.
  */
 public static function processAjax(Element $element, FormStateInterface $form_state, array &$complete_form)
 {
     $ajax = $element->getProperty('ajax');
     // Show throbber AJAX requests in an input button group.
     if (!isset($ajax['progress']['type']) || $ajax['progress']['type'] === 'throbber') {
         // Use an icon for autocomplete "throbber".
         $icon = Bootstrap::glyphicon('refresh');
         $element->appendProperty('field_suffix', Element::create($icon)->addClass(['ajax-progress', 'ajax-progress-throbber']));
         $element->setProperty('input_group', TRUE);
     }
 }
示例#20
0
 /**
  * {@inheritdoc}
  */
 public function alter(&$cache, &$context1 = NULL, &$context2 = NULL)
 {
     // Sort the registry alphabetically (for easier debugging).
     ksort($cache);
     // Ensure paths to templates are set properly. This allows templates to
     // be moved around in a theme without having to constantly ensuring that
     // the theme's hook_theme() definitions have the correct static "path" set.
     foreach ($this->currentTheme->getAncestry() as $ancestor) {
         $current_theme = $ancestor->getName() === $this->currentTheme->getName();
         $theme_path = $ancestor->getPath();
         foreach ($ancestor->fileScan('/\\.html\\.twig$/', 'templates') as $file) {
             $hook = str_replace('-', '_', str_replace('.html.twig', '', $file->filename));
             $path = dirname($file->uri);
             $incomplete = !isset($cache[$hook]) || strrpos($hook, '__');
             if (!isset($cache[$hook])) {
                 $cache[$hook] = [];
             }
             $cache[$hook]['path'] = $path;
             $cache[$hook]['type'] = $current_theme ? 'theme' : 'base_theme';
             $cache[$hook]['theme path'] = $theme_path;
             if ($incomplete) {
                 $cache[$hook]['incomplete preprocess functions'] = TRUE;
             }
         }
     }
     // Discover all the theme's preprocess plugins.
     $preprocess_manager = new PreprocessManager($this->currentTheme);
     $plugins = $preprocess_manager->getDefinitions();
     ksort($plugins, SORT_NATURAL);
     // Iterate over the preprocess plugins.
     foreach ($plugins as $plugin_id => $definition) {
         $incomplete = !isset($cache[$plugin_id]) || strrpos($plugin_id, '__');
         if (!isset($cache[$plugin_id])) {
             $cache[$plugin_id] = [];
         }
         array_walk($cache, function (&$info, $hook) use($plugin_id, $definition) {
             if ($hook === $plugin_id || strpos($hook, $plugin_id . '__') === 0) {
                 if (!isset($info['preprocess functions'])) {
                     $info['preprocess functions'] = [];
                 }
                 // Due to a limitation in \Drupal\Core\Theme\ThemeManager::render,
                 // callbacks must be functions and not classes. We always specify
                 // "bootstrap_preprocess" here and then assign the plugin ID to a
                 // separate property that we can later intercept and properly invoke.
                 // @todo Revisit if/when preprocess callbacks can be any callable.
                 Bootstrap::addCallback($info['preprocess functions'], 'bootstrap_preprocess', $definition['replace'], $definition['action']);
                 $info['preprocess functions'] = array_unique($info['preprocess functions']);
                 $info['bootstrap preprocess'] = $plugin_id;
             }
         });
         if ($incomplete) {
             $cache[$plugin_id]['incomplete preprocess functions'] = TRUE;
         }
     }
     // Allow core to post process.
     $this->postProcessExtension($cache, $this->theme);
 }
示例#21
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);
}
示例#22
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;
 }
示例#23
0
 /**
  * {@inheritdoc}
  */
 public function getTheme()
 {
     return Bootstrap::getTheme($this->pluginDefinition['provider']);
 }
示例#24
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;
 }
示例#25
0
 /**
  * Preprocess theme hook variables.
  *
  * @param array $variables
  *   The variables array, passed by reference.
  * @param string $hook
  *   The name of the theme hook.
  * @param array $info
  *   The theme hook info.
  */
 public static function preprocess(array &$variables, $hook, array $info)
 {
     static $theme;
     if (!isset($theme)) {
         $theme = self::getTheme();
     }
     static $preprocess_manager;
     if (!isset($preprocess_manager)) {
         $preprocess_manager = new PreprocessManager($theme);
     }
     // Ensure that any default theme hook variables exist. Due to how theme
     // hook suggestion alters work, the variables provided are from the
     // original theme hook, not the suggestion.
     if (isset($info['variables'])) {
         $variables = NestedArray::mergeDeepArray([$info['variables'], $variables], TRUE);
     }
     // Add extra variables to all theme hooks.
     foreach (Bootstrap::extraVariables() as $key => $value) {
         if (!isset($variables[$key])) {
             $variables[$key] = $value;
         }
     }
     // Add active theme context.
     // @see https://www.drupal.org/node/2630870
     if (!isset($variables['theme'])) {
         $variables['theme'] = $theme->getInfo();
         $variables['theme']['name'] = $theme->getName();
         $variables['theme']['path'] = $theme->getPath();
         $variables['theme']['title'] = $theme->getTitle();
         $variables['theme']['settings'] = $theme->settings()->get();
         $variables['theme']['has_glyphicons'] = $theme->hasGlyphicons();
         $variables['theme']['query_string'] = \Drupal::getContainer()->get('state')->get('system.css_js_query_string') ?: '0';
     }
     // Invoke necessary preprocess plugin.
     if (isset($info['bootstrap preprocess'])) {
         if ($preprocess_manager->hasDefinition($info['bootstrap preprocess'])) {
             $class = $preprocess_manager->createInstance($info['bootstrap preprocess'], ['theme' => $theme]);
             /** @var \Drupal\bootstrap\Plugin\Preprocess\PreprocessInterface $class */
             $class->preprocess($variables, $hook, $info);
         }
     }
 }