+++ /dev/null
-Subproject commit ef95dac8dfeb8f2d61b0e5d40075a0a6e23e53ca
--- /dev/null
+# @file
+# .travis.yml - Drupal for Travis CI Integration
+#
+# Template provided by https://github.com/LionsAd/drupal_ti.
+#
+# Based for simpletest upon:
+# https://github.com/sonnym/travis-ci-drupal-module-example
+
+language: php
+sudo: false
+
+cache:
+ directories:
+ - $HOME/.composer/cache
+
+php:
+ - 5.5
+ - 5.6
+ - 7
+ - hhvm
+
+matrix:
+ fast_finish: true
+
+branches:
+ only:
+ - "8.x-3.x"
+ - "8.x-3.x-dev"
+
+env:
+ global:
+ # add composer's global bin directory to the path
+ # see: https://github.com/drush-ops/drush#install---composer
+ - PATH="$PATH:$HOME/.composer/vendor/bin"
+
+before_install:
+ - composer self-update
+ # Codesniffer and Coder
+ - composer global require "squizlabs/php_codesniffer:2.0.*@dev"
+ - composer global require drupal/coder:8.2.0-beta1
+ - ln -s ~/.composer/vendor/drupal/coder/coder_sniffer/Drupal ~/.composer/vendor/squizlabs/php_codesniffer/CodeSniffer/Standards/
+
+script:
+ - phpcs --report=full --standard=Drupal "$TRAVIS_BUILD_DIR" || true
+ - cd ~/
+ - git clone --depth 1 --branch 8.2.x http://git.drupal.org/project/drupal.git
+ - cd drupal/modules
+ - git clone --depth 1 --branch 8.x-3.x-dev https://github.com/ericpugh/drupal-views-bootstrap.git views_bootstrap
+ - ln -s $TRAVIS_BUILD_DIR
+ - cd ../
+# - ./vendor/bin/phpunit -c core modules/views_bootstrap/tests/src/Unit
--- /dev/null
+[![Build Status](https://travis-ci.org/ericpugh/drupal-views-bootstrap.svg?branch=8.x-3.x)](https://travis-ci.org/ericpugh/drupal-views-bootstrap)
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * Introduction
+ * Requirements
+
+INTRODUCTION
+------------
+
+The Views Bootstrap module adds styles to Views to output the results of a view
+as several common Twitter Bootstrap components.
+
+ * For a full description of the module, visit the project page:
+ https://www.drupal.org/project/views_bootstrap
+
+REQUIREMENTS
+------------
+
+This module requires the following themes/modules:
+
+ * Views (Core)
+ * Bootstrap Theme (https://www.drupal.org/project/bootstrap)
--- /dev/null
+{
+ "name": "drupal/views_bootstrap",
+ "type": "drupal-module",
+ "description": "Integrate the Bootstrap framework with Views.",
+ "keywords": ["Drupal"],
+ "license": "GPL-2.0+"
+}
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap\Plugin\views\style;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\style\StylePluginBase;
+use Drupal\views_bootstrap\ViewsBootstrap;
+
+/**
+ * Style plugin to render each item as a row in a Bootstrap Accordion.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ * id = "views_bootstrap_accordion",
+ * title = @Translation("Bootstrap Accordion"),
+ * help = @Translation("Displays rows in a Bootstrap Accordion."),
+ * theme = "views_bootstrap_accordion",
+ * theme_file = "../views_bootstrap.theme.inc",
+ * display_types = {"normal"}
+ * )
+ */
+class ViewsBootstrapAccordion extends StylePluginBase {
+ /**
+ * Does the style plugin for itself support to add fields to it's output.
+ *
+ * @var bool
+ */
+ protected $usesFields = TRUE;
+
+ /**
+ * Does the style plugin allows to use style plugins.
+ *
+ * @var bool
+ */
+ protected $usesRowPlugin = TRUE;
+
+ /**
+ * Definition.
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['panel_title_field'] = ['default' => NULL];
+
+ return $options;
+ }
+
+ /**
+ * Render the given style.
+ */
+ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ if (isset($form['grouping'])) {
+ unset($form['grouping']);
+
+ $form['panel_title_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Panel title field'),
+ '#options' => $this->displayHandler->getFieldLabels(TRUE),
+ '#required' => TRUE,
+ '#default_value' => $this->options['panel_title_field'],
+ '#description' => $this->t('Select the field that will be used as the accordian panel titles.'),
+ ];
+ }
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap\Plugin\views\style;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\style\StylePluginBase;
+
+/**
+ * Style plugin to render each item as a row in a Bootstrap Carousel.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ * id = "views_bootstrap_carousel",
+ * title = @Translation("Bootstrap Carousel"),
+ * help = @Translation("Displays rows in a Bootstrap Carousel."),
+ * theme = "views_bootstrap_carousel",
+ * theme_file = "../views_bootstrap.theme.inc",
+ * display_types = {"normal"}
+ * )
+ */
+class ViewsBootstrapCarousel extends StylePluginBase {
+ /**
+ * Does the style plugin for itself support to add fields to it's output.
+ *
+ * @var bool
+ */
+ protected $usesFields = TRUE;
+
+ /**
+ * Definition.
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['interval'] = ['default' => 5000];
+ $options['navigation'] = ['default' => TRUE];
+ $options['indicators'] = ['default' => TRUE];
+ $options['pause'] = ['default' => TRUE];
+ $options['wrap'] = ['default' => TRUE];
+
+ $options['image'] = ['default' => ''];
+ $options['title'] = ['default' => ''];
+ $options['description'] = ['default' => ''];
+
+ return $options;
+ }
+
+ /**
+ * Render the given style.
+ */
+ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $fields = ['' => t('<None>')];
+ $fields += $this->displayHandler->getFieldLabels(TRUE);
+
+ $form['interval'] = [
+ '#type' => 'number',
+ '#title' => $this->t('Interval'),
+ '#description' => t('The amount of time to delay between automatically cycling an item. If false, carousel will not automatically cycle.'),
+ '#default_value' => $this->options['interval'],
+ ];
+
+ $form['navigation'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Show navigation'),
+ '#default_value' => $this->options['navigation'],
+ ];
+
+ $form['indicators'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Show indicators'),
+ '#default_value' => $this->options['indicators'],
+ ];
+
+ $form['pause'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Pause on hover'),
+ '#description' => t('Pauses the cycling of the carousel on mouseenter and resumes the cycling of the carousel on mouseleave.'),
+ '#default_value' => $this->options['pause'],
+ ];
+
+ $form['wrap'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Wrap'),
+ '#description' => t('The carousel should cycle continuously or have hard stops.'),
+ '#default_value' => $this->options['wrap'],
+ ];
+
+ $form['image'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Image'),
+ '#options' => $fields,
+ '#default_value' => $this->options['image'],
+ ];
+
+ $form['title'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Title'),
+ '#options' => $fields,
+ '#default_value' => $this->options['title'],
+ ];
+
+ $form['description'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Description'),
+ '#options' => $fields,
+ '#default_value' => $this->options['description'],
+ ];
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap\Plugin\views\style;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\style\StylePluginBase;
+use Drupal\Component\Utility\Html;
+
+/**
+ * Style plugin to render each item in an ordered or unordered list.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ * id = "views_bootstrap_grid",
+ * title = @Translation("Bootstrap Grid"),
+ * help = @Translation("Displays rows in a Bootstrap Grid layout"),
+ * theme = "views_bootstrap_grid",
+ * theme_file = "../views_bootstrap.theme.inc",
+ * display_types = {"normal"}
+ * )
+ */
+class ViewsBootstrapGrid extends StylePluginBase {
+ /**
+ * Overrides \Drupal\views\Plugin\views\style\StylePluginBase::usesRowPlugin.
+ *
+ * @var bool
+ */
+ protected $usesRowPlugin = TRUE;
+
+ /**
+ * Overrides \Drupal\views\Plugin\views\style\StylePluginBase::usesRowClass.
+ *
+ * @var bool
+ */
+ protected $usesRowClass = TRUE;
+
+ /**
+ * Return the token-replaced row or column classes for the specified result.
+ *
+ * @param int $result_index
+ * The delta of the result item to get custom classes for.
+ * @param string $type
+ * The type of custom grid class to return, either "row" or "col".
+ *
+ * @return string
+ * A space-delimited string of classes.
+ */
+ public function getCustomClass($result_index, $type) {
+ if (isset($this->options[$type . '_class_custom'])) {
+ $class = $this->options[$type . '_class_custom'];
+ if ($this->usesFields() && $this->view->field) {
+ $class = strip_tags($this->tokenizeValue($class, $result_index));
+ }
+
+ $classes = explode(' ', $class);
+ foreach ($classes as &$class) {
+ $class = Html::cleanCssIdentifier($class);
+ }
+ return implode(' ', $classes);
+ }
+ }
+
+ /**
+ * Normalize a list of columns.
+ *
+ * Normalize columns based upon the fields that are available. This compares
+ * the fields stored in the style handler
+ * to the list of fields actually in the view, removing fields that
+ * have been removed and adding new fields in their own column.
+ * - Each field must be in a column.
+ * - Each column must be based upon a field, and that field is somewhere in
+ * the column.
+ * - Any fields not currently represented must be added.
+ * - Columns must be re-ordered to match the fields.
+ *
+ * @param array $columns
+ * An array of all fields; the key is the id of the field and the
+ * value is the id of the column the field should be in.
+ * @param array|null $fields
+ * The fields to use for the columns. If not provided, they will
+ * be requested from the current display. The running render should
+ * send the fields through, as they may be different than what the
+ * display has listed due to access control or other changes.
+ *
+ * @return array
+ * An array of all the sanitized columns.
+ */
+ public function sanitizeColumns(array $columns, $fields = NULL) {
+ $sanitized = [];
+ if ($fields === NULL) {
+ $fields = $this->displayHandler->getOption('fields');
+ }
+ // Pre-configure the sanitized array so that the order is retained.
+ foreach ($fields as $field => $info) {
+ // Set to itself so that if it isn't touched, it gets column
+ // status automatically.
+ $sanitized[$field] = $field;
+ }
+
+ if (!empty($columns)) {
+ return $sanitized;
+ }
+
+ foreach ($columns as $field => $column) {
+ // first, make sure the field still exists.
+ if (!isset($sanitized[$field])) {
+ continue;
+ }
+
+ // If the field is the column, mark it so, or the column
+ // it's set to is a column, that's ok.
+ if ($field == $column || $columns[$column] == $column && !empty($sanitized[$column])) {
+ $sanitized[$field] = $column;
+ }
+ // Since we set the field to itself initially, ignoring
+ // the condition is ok; the field will get its column
+ // status back.
+ }
+
+ return $sanitized;
+ }
+
+ /**
+ * Definition.
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['alignment'] = ['default' => 'horizontal'];
+ $options['columns'] = ['default' => '4'];
+ $options['col_xs'] = ['default' => 'col-xs-12'];
+ $options['col_sm'] = ['default' => 'col-sm-12'];
+ $options['col_md'] = ['default' => 'col-md-12'];
+ $options['col_lg'] = ['default' => 'col-lg-12'];
+ $options['automatic_width'] = ['default' => TRUE];
+ $options['col_class_custom'] = ['default' => ''];
+ $options['col_class_default'] = ['default' => TRUE];
+ $options['row_class_custom'] = ['default' => ''];
+ $options['row_class_default'] = ['default' => TRUE];
+ $options['default'] = ['default' => ''];
+ $options['info'] = ['default' => []];
+ $options['override'] = ['default' => TRUE];
+ $options['sticky'] = ['default' => FALSE];
+ $options['order'] = ['default' => 'asc'];
+ $options['caption'] = ['default' => ''];
+ $options['summary'] = ['default' => ''];
+ $options['description'] = ['default' => ''];
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $form['alignment'] = [
+ '#type' => 'radios',
+ '#title' => $this->t('Alignment'),
+ '#options' => [
+ 'horizontal' => $this->t('Horizontal'),
+ 'vertical' => $this->t('Vertical'),
+ ],
+ '#description' => $this->t('Horizontal alignment will place items starting in the upper left and moving right.
+ Vertical alignment will place items starting in the upper left and moving down.'),
+ '#default_value' => $this->options['alignment'],
+ ];
+
+ $form['columns'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Number of columns per row'),
+ '#required' => TRUE,
+ '#default_value' => isset($this->options['columns']) ? $this->options['columns'] : NULL,
+ '#options' => [
+ 1 => 1,
+ 2 => 2,
+ 3 => 3,
+ 4 => 4,
+ 6 => 6,
+ 12 => 12,
+ 999 => $this->t('All'),
+ ],
+ ];
+
+ foreach (['xs', 'sm', 'md', 'lg'] as $size) {
+ $form["col_${size}"] = [
+ '#type' => 'select',
+ '#title' => $this->t("Number of columns (col-${size})"),
+ '#required' => TRUE,
+ '#default_value' => isset($this->options["col_${size}"]) ? $this->options["col_${size}"] : NULL,
+ '#options' => [
+ "col-${size}-12" => 1,
+ "col-${size}-6" => 2,
+ "col-${size}-4" => 3,
+ "col-${size}-3" => 4,
+ "col-${size}-2" => 6,
+ "col-${size}-1" => 12,
+ ],
+ ];
+ }
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap\Plugin\views\style;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\style\StylePluginBase;
+
+/**
+ * Style plugin to render each item in an ordered or unordered list.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ * id = "views_bootstrap_list_group",
+ * title = @Translation("Bootstrap List Group"),
+ * help = @Translation("Displays rows in a Bootstrap List Group."),
+ * theme = "views_bootstrap_list_group",
+ * theme_file = "../views_bootstrap.theme.inc",
+ * display_types = {"normal"}
+ * )
+ */
+class ViewsBootstrapListGroup extends StylePluginBase {
+
+ /**
+ * Overrides \Drupal\views\Plugin\views\style\StylePluginBase::usesRowPlugin.
+ *
+ * @var bool
+ */
+ protected $usesRowPlugin = TRUE;
+
+ /**
+ * Overrides \Drupal\views\Plugin\views\style\StylePluginBase::usesRowClass.
+ *
+ * @var bool
+ */
+ protected $usesRowClass = TRUE;
+
+ /**
+ * Definition.
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['link_field'] = ['default' => []];
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $fields = ['' => $this->t('<None>')];
+ $fields += $this->displayHandler->getFieldLabels(TRUE);
+
+ $form['title_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Title field'),
+ '#options' => $fields,
+ '#required' => FALSE,
+ '#default_value' => $this->options['title_field'],
+ '#description' => $this->t('Select the field that will be used as the title.'),
+ ];
+
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap\Plugin\views\style;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\style\StylePluginBase;
+
+/**
+ * Style plugin to render each item as a row in a Bootstrap Media Object.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ * id = "views_bootstrap_media_object",
+ * title = @Translation("Bootstrap Media Object"),
+ * help = @Translation("Displays rows in a Bootstrap Media Object."),
+ * theme = "views_bootstrap_media_object",
+ * theme_file = "../views_bootstrap.theme.inc",
+ * display_types = {"normal"}
+ * )
+ */
+class ViewsBootstrapMediaObject extends StylePluginBase {
+
+ /**
+ * Does the style plugin for itself support to add fields to it's output.
+ *
+ * @var bool
+ */
+ protected $usesFields = TRUE;
+
+ /**
+ * Definition.
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['image_field'] = ['default' => []];
+ $options['heading_field'] = ['default' => []];
+ $options['body_field'] = ['default' => []];
+ $options['image_class'] = ['default' => 'media-left'];
+
+ return $options;
+ }
+
+ /**
+ * Render the given style.
+ */
+ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $fields = $this->displayHandler->getFieldLabels(TRUE);
+ $optionalFields = ['' => $this->t('<None>')];
+ $optionalFields += $this->displayHandler->getFieldLabels(TRUE);
+
+ $form['heading_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Heading field'),
+ '#options' => $fields,
+ '#required' => TRUE,
+ '#default_value' => $this->options['heading_field'],
+ '#description' => $this->t('Select the field that will be used as the media object heading.'),
+ ];
+
+ $form['image_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Image field'),
+ '#options' => $this->displayHandler->getFieldLabels(TRUE),
+ '#required' => TRUE,
+ '#default_value' => $this->options['image_field'],
+ '#description' => $this->t('Select the field that will be used as the media object image.'),
+ ];
+
+ $form['image_class'] = [
+ '#type' => 'radios',
+ '#title' => $this->t('Image Alignment'),
+ '#options' => [
+ 'media-left' => $this->t('Left'),
+ 'media-right' => $this->t('Right'),
+ 'media-middle' => $this->t('Middle'),
+ ],
+ '#default_value' => $this->options['image_class'],
+ '#description' => t('Align the media object image left or right.'),
+ ];
+
+ $form['body_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Body field'),
+ '#options' => $optionalFields,
+ '#required' => FALSE,
+ '#default_value' => $this->options['body_field'],
+ '#description' => $this->t('Select the field that will be used as the media object body.'),
+ ];
+
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap\Plugin\views\style;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\style\StylePluginBase;
+use Drupal\views_bootstrap\ViewsBootstrap;
+
+/**
+ * Style plugin to render each item in an ordered or unordered list.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ * id = "views_bootstrap_tab",
+ * title = @Translation("Bootstrap Tab"),
+ * help = @Translation("Displays rows in Bootstrap Tabs."),
+ * theme = "views_bootstrap_tab",
+ * theme_file = "../views_bootstrap.theme.inc",
+ * display_types = {"normal"}
+ * )
+ */
+class ViewsBootstrapTab extends StylePluginBase {
+
+ /**
+ * Does the style plugin for itself support to add fields to it's output.
+ *
+ * @var bool
+ */
+ protected $usesFields = TRUE;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $usesOptions = TRUE;
+
+ /**
+ * Definition.
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['tab_field'] = ['default' => NULL];
+ $options['tab_type'] = ['default' => 'tabs'];
+ $options['justified'] = ['default' => FALSE];
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ if (isset($form['grouping'])) {
+ unset($form['grouping']);
+
+ $form['tab_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Tab field'),
+ '#options' => $this->displayHandler->getFieldLabels(TRUE),
+ '#required' => TRUE,
+ '#default_value' => $this->options['tab_field'],
+ '#description' => t('Select the field that will be used as the tab.'),
+ ];
+
+ $form['tab_type'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Tab Type'),
+ '#options' => [
+ 'tabs' => $this->t('Tabs'),
+ 'pills' => $this->t('Pills'),
+ 'list' => $this->t('List'),
+ ],
+ '#required' => TRUE,
+ '#default_value' => $this->options['tab_type'],
+ ];
+
+ $form['justified'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Justified'),
+ '#default_value' => $this->options['justified'],
+ '#description' => $this->t('Make tabs equal widths of their parent'),
+ ];
+ }
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap\Plugin\views\style;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\style\Table;
+
+/**
+ * Style plugin to render each item as a row in a Bootstrap table.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ * id = "views_bootstrap_table",
+ * title = @Translation("Bootstrap Table"),
+ * help = @Translation("Displays rows in a Bootstrap table."),
+ * theme = "views_bootstrap_table",
+ * theme_file = "../views_bootstrap.theme.inc",
+ * display_types = {"normal"}
+ * )
+ */
+class ViewsBootstrapTable extends Table {
+
+ /**
+ * Definition.
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['bootstrap_styles'] = ['default' => []];
+ $options['responsive'] = ['default' => FALSE];
+
+ return $options;
+ }
+
+ /**
+ * Render the given style.
+ */
+ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $form['responsive'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Responsive'),
+ '#default_value' => $this->options['responsive'],
+ '#description' => $this->t('To make a table scroll horizontally on small devices.'),
+ ];
+
+ $form['bootstrap_styles'] = [
+ '#title' => $this->t('Bootstrap styles'),
+ '#type' => 'checkboxes',
+ '#default_value' => $this->options['bootstrap_styles'],
+ '#options' => [
+ 'striped' => $this->t('Striped'),
+ 'bordered' => $this->t('Bordered'),
+ 'hover' => $this->t('Hover'),
+ 'condensed' => $this->t('Condensed'),
+ ],
+ ];
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Drupal\views_bootstrap;
+
+use Drupal\Component\Utility\Html;
+use Drupal\views\ViewExecutable;
+
+/**
+ * The primary class for the Views Bootstrap module.
+ *
+ * Provides many helper methods.
+ *
+ * @ingroup utility
+ */
+class ViewsBootstrap {
+
+ /**
+ * Returns the theme hook definition information.
+ */
+ public static function getThemeHooks() {
+ $hooks['views_bootstrap_accordion'] = [
+ 'preprocess functions' => [
+ 'template_preprocess_views_bootstrap_accordion',
+ 'template_preprocess_views_view_accordion',
+ ],
+ 'file' => 'views_bootstrap.theme.inc',
+ ];
+ $hooks['views_bootstrap_carousel'] = [
+ 'preprocess functions' => [
+ 'template_preprocess_views_bootstrap_carousel',
+ 'template_preprocess_views_view_carousel',
+ ],
+ 'file' => 'views_bootstrap.theme.inc',
+ ];
+ $hooks['views_bootstrap_grid'] = [
+ 'preprocess functions' => [
+ 'template_preprocess_views_bootstrap_grid',
+ 'template_preprocess_views_view_grid',
+ ],
+ 'file' => 'views_bootstrap.theme.inc',
+ ];
+ $hooks['views_bootstrap_list_group'] = [
+ 'preprocess functions' => [
+ 'template_preprocess_views_bootstrap_list_group',
+ 'template_preprocess_views_view_list_group',
+ ],
+ 'file' => 'views_bootstrap.theme.inc',
+ ];
+ $hooks['views_bootstrap_media_object'] = [
+ 'preprocess functions' => [
+ 'template_preprocess_views_bootstrap_media_object',
+ 'template_preprocess_views_view_media_object',
+ ],
+ 'file' => 'views_bootstrap.theme.inc',
+ ];
+ $hooks['views_bootstrap_tab'] = [
+ 'preprocess functions' => [
+ 'template_preprocess_views_bootstrap_tab',
+ 'template_preprocess_views_view_tab',
+ ],
+ 'file' => 'views_bootstrap.theme.inc',
+ ];
+ $hooks['views_bootstrap_table'] = [
+ 'preprocess functions' => [
+ 'template_preprocess_views_bootstrap_table',
+ 'template_preprocess_views_view_table',
+ ],
+ 'file' => 'views_bootstrap.theme.inc',
+ ];
+
+ return $hooks;
+ }
+
+ /**
+ * Get unique element id.
+ *
+ * @param \Drupal\views\ViewExecutable $view
+ * A ViewExecutable object.
+ *
+ * @return string
+ * A unique id for an HTML element.
+ */
+ public static function getUniqueId(ViewExecutable $view) {
+ $id = $view->storage->id() . '-' . $view->current_display;
+ return Html::getUniqueId('views-bootstrap-' . $id);
+ }
+
+ /**
+ * Get the number of items from the column class string.
+ *
+ * @param string $size
+ * Bootstrap grid size xs|sm|md|lg.
+ *
+ * @return int|false
+ * Number of columns in a 12 column grid or false.
+ */
+ public static function getColSize($size) {
+ if (preg_match('~col-[a-z]{2}-([0-9]*)~', $size, $matches)) {
+ return 12 / $matches[1];
+ }
+
+ return FALSE;
+ }
+
+}
--- /dev/null
+<div id="{{ id }}" {{ attributes.addClass(classes) }}>
+ {% for key, row in rows -%}
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h4 class="panel-title">
+ <a class="accordion-toggle"
+ data-toggle="collapse"
+ data-parent="#{{ id }}"
+ href="#{{ id }}-collapse-{{ key }}">
+ {{row.title}}
+ </a>
+ </h4>
+ </div>
+
+ <div id="{{ id }}-collapse-{{ key }}" class="panel-collapse collapse">
+ <div class="panel-body">
+ {{row.content}}
+ </div>
+ </div>
+ </div>
+ {%- endfor %}
+</div>
--- /dev/null
+{#
+/**
+ * @file
+ * Default theme implementation for displaying a view as a bootstrap carousel.
+ *
+ * Available variables:
+ * - view: The view object.
+ * - rows: A list of the view's row items.
+ * - id: A valid HTML ID and guaranteed to be unique.
+ * - interval: The amount of time to delay between automatically cycling a
+ * slide item. If false, carousel will not automatically cycle.
+ * - pause: Pauses the cycling of the carousel on mouseenter and
+ * resumes the cycling of the carousel on mouseleave.
+ * - wrap: Whether the carousel should cycle continuously or have
+ * hard stops.
+ *
+ * @see template_preprocess_views_bootstrap_carousel()
+ *
+ * @ingroup themeable
+ */
+#}
+<div id="{{ id }}" class="carousel slide" data-ride="carousel" data-interval="{{ interval }}" data-pause="{{ pause }}" data-wrap="{{ wrap }}">
+
+ {# Carousel indicators #}
+ {% if indicators %}
+ <ol class="carousel-indicators">
+ {% for key, row in rows %}
+ {% set indicator_classes = [loop.first ? 'active'] %}
+ <li class="{{ indicator_classes|join(' ') }}" data-target="#{{ id }}" data-slide-to="{{ key }}"></li>
+ {% endfor %}
+ </ol>
+ {% endif %}
+
+ {# Carousel rows #}
+ <div class="carousel-inner" role="listbox">
+ {% for row in rows %}
+ {% set row_classes = ['item', loop.first ? 'active'] %}
+ <div class="{{ row_classes|join(' ') }}">
+ {{ row.image }}
+ {% if row.title or row.description %}
+ <div class="carousel-caption">
+ {% if row.title %}
+ <h3>{{ row.title }}</h3>
+ {% endif %}
+ {% if row.description %}
+ <p>{{ row.description }}</p>
+ {% endif %}
+ </div>
+ {% endif %}
+ </div>
+ {% endfor %}
+ </div>
+
+ {# Carousel navigation #}
+ {% if navigation %}
+ <a class="left carousel-control" href="#{{ id }}" role="button" data-slide="prev">
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
+ <span class="sr-only">{{ 'Previous'|t }}</span>
+ </a>
+ <a class="right carousel-control" href="#{{ id }}" role="button" data-slide="next">
+ <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
+ <span class="sr-only">{{ 'Next'|t }}</span>
+ </a>
+ {% endif %}
+</div>
--- /dev/null
+{#
+/**
+ * @file views-bootstrap-grid.html.twig
+ * Default simple view template to display Bootstrap Grids.
+ *
+ *
+ * - columns: Contains rows grouped by columns.
+ * - rows: Contains a nested array of rows. Each row contains an array of
+ * columns.
+ * - column_type: Contains a number (default Bootstrap grid system column type).
+ *
+ * @ingroup views_templates
+ */
+#}
+{%
+ set classes = [
+ 'views-view-grid',
+ options.alignment,
+ ]
+%}
+<div id="{{ id }}" {{ attributes.addClass(classes) }}>
+ {% if options.alignment == 'horizontal' %}
+ {% for row in items %}
+ <div class="row">
+ {% for column in row.content %}
+ <div class="col {{ col_xs }} {{ col_sm }} {{ col_md }} {{ col_lg }} {{ options.row_class }}">
+ {{ column.content }}
+ </div>
+ {% endfor %}
+ </div>
+ {% endfor %}
+ {% else %}
+ <div class="row">
+ {% for column in items %}
+ <div class="col {{ col_xs }} {{ col_sm }} {{ col_md }} {{ col_lg }} {{ options.row_class }}">
+ {% for row in column.content %}
+ {{ row.content }}
+ {% endfor %}
+ </div>
+
+ {% if loop.index is divisible by (sizes.xs) %}
+ <div class="clearfix visible-xs-block"></div>
+ {% endif %}
+
+ {% if loop.index is divisible by (sizes.sm) %}
+ <div class="clearfix visible-sm-block"></div>
+ {% endif %}
+
+ {% if loop.index is divisible by (sizes.md) %}
+ <div class="clearfix visible-md-block"></div>
+ {% endif %}
+
+ {% if loop.index is divisible by (sizes.lg) %}
+ <div class="clearfix visible-lg-block"></div>
+ {% endif %}
+ {% endfor %}
+ </div>
+ {% endif %}
+</div>
--- /dev/null
+{#
+/**
+ * @file views-bootstrap-list-group.html.twig
+ * Default simple view template to display Bootstrap List Group.
+ *
+ * Available variables:
+ * - view: The view object.
+ * - rows: A list of the view's row items.
+ * - id: A valid HTML ID and guaranteed to be unique.
+ *
+ * @see template_preprocess_views_bootstrap_list_group()
+ *
+ * @ingroup views_templates
+ */
+#}
+{%
+ set classes = [
+ 'views-view-list-group',
+ ]
+%}
+
+<ul id="{{ id }}" {{ attributes.addClass(classes) }}>
+ {% for key, row in rows %}
+ <li class="list-group-item">
+ {% if row.title %}
+ <h4 class="list-group-item-heading">{{ row.title }}</h4>
+ {% endif %}
+ <p class="list-group-item-text">{{ row.content }}</p>
+ </li>
+ {% endfor %}
+</ul>
--- /dev/null
+{# @TODO: How to remove bootstrap theme's default .img-responsive class on image fields, which doesn't work with .media-left, etc. For now, I'm using .pull-left as a fix. #}
+<div id="{{ id }}" {{ attributes.addClass(classes) }}>
+ {% for key, row in rows -%}
+ <div class="media">
+
+ {% if row.image_class == "media-left" %}
+ <div class="pull-left">
+ {{ row.image }}
+ </div>
+ {% endif %}
+
+ {% if row.image_class == "media-middle" %}
+ <div class="media-middle">
+ {{ row.image }}
+ </div>
+ {% endif %}
+
+ <div class="media-body">
+ {% if row.heading %}
+ <h4 class="media-heading">
+ {{ row.heading }}
+ </h4>
+ {% endif %}
+ {% if row.body %}
+ {{ row.body }}
+ {% endif %}
+
+ {% if row.image_class == "media-right" %}
+ <div class="pull-right">
+ {{ row.image }}
+ </div>
+ {% endif %}
+
+ </div>
+ </div>
+ {%- endfor %}
+</div>
+
--- /dev/null
+{% if title is not empty %}
+ <h3>{{ title }}</h3>
+{% endif %}
+<div id="views-bootstrap-tab-{{ id }}" {{ attributes.addClass(classes) }}>
+ <ul class="nav nav-{{ tab_type }} {% if justified %}nav-justified{% endif %}">
+ {% for key, tab in tabs %}
+ {% set tab_classes = [loop.first ? 'active'] %}
+ <li class="{{ tab_classes|join(' ') }}">
+ <a href="{{ "#tab-#{ id }-#{ key }" }}" data-toggle="tab">{{ tab }}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ <div class="tab-content">
+ {% for key, row in rows %}
+ {% set row_classes = ['tab-pane', loop.first ? 'active'] %}
+ <div class="{{ row_classes|join(' ') }}" id="tab-{{ id }}-{{ key }}" {{ row.attributes }}>
+ {{ row.content }}
+ </div>
+ {% endfor %}
+ </div>
+</div>
--- /dev/null
+{#
+/**
+ * @file
+ * Default theme implementation for displaying a view as a bootstrap table.
+ *
+ * Available variables:
+ * - attributes: Remaining HTML attributes for the element.
+ * - class: HTML classes that can be used to style contextually through CSS.
+ * - title : The title of this group of rows.
+ * - header: The table header columns.
+ * - attributes: Remaining HTML attributes for the element.
+ * - content: HTML classes to apply to each header cell, indexed by
+ * the header's key.
+ * - default_classes: A flag indicating whether default classes should be
+ * used.
+ * - caption_needed: Is the caption tag needed.
+ * - caption: The caption for this table.
+ * - accessibility_description: Extended description for the table details.
+ * - accessibility_summary: Summary for the table details.
+ * - rows: Table row items. Rows are keyed by row number.
+ * - attributes: HTML classes to apply to each row.
+ * - columns: Row column items. Columns are keyed by column number.
+ * - attributes: HTML classes to apply to each column.
+ * - content: The column content.
+ * - default_classes: A flag indicating whether default classes should be
+ * used.
+ * - responsive: A flag indicating whether table is responsive.
+ * - sticky: A flag indicating whether table header is sticky.
+ *
+ * @see template_preprocess_views_bootstrap_table()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+ set classes = [
+ 'cols-' ~ header|length,
+ sticky ? 'sticky-enabled',
+ ]
+%}
+
+{% if responsive %}
+ <div class="table-responsive">
+{% endif %}
+
+<table{{ attributes.addClass(classes) }}>
+ {% if caption_needed %}
+ <caption>
+ {% if caption %}
+ {{ caption }}
+ {% else %}
+ {{ title }}
+ {% endif %}
+ {% if (summary is not empty) or (description is not empty) %}
+ <details>
+ {% if summary is not empty %}
+ <summary>{{ summary }}</summary>
+ {% endif %}
+ {% if description is not empty %}
+ {{ description }}
+ {% endif %}
+ </details>
+ {% endif %}
+ </caption>
+ {% endif %}
+ {% if header %}
+ <thead>
+ <tr>
+ {% for key, column in header %}
+ {% if column.default_classes %}
+ {%
+ set column_classes = [
+ 'views-field',
+ 'views-field-' ~ fields[key],
+ ]
+ %}
+ {% endif %}
+ <th{{ column.attributes.addClass(column_classes).setAttribute('scope', 'col') }}>
+ {%- if column.wrapper_element -%}
+ <{{ column.wrapper_element }}>
+ {%- if column.url -%}
+ <a href="{{ column.url }}" title="{{ column.title }}">{{ column.content }}{{ column.sort_indicator }}</a>
+ {%- else -%}
+ {{ column.content }}{{ column.sort_indicator }}
+ {%- endif -%}
+ </{{ column.wrapper_element }}>
+ {%- else -%}
+ {%- if column.url -%}
+ <a href="{{ column.url }}" title="{{ column.title }}">{{ column.content }}{{ column.sort_indicator }}</a>
+ {%- else -%}
+ {{- column.content }}{{ column.sort_indicator }}
+ {%- endif -%}
+ {%- endif -%}
+ </th>
+ {% endfor %}
+ </tr>
+ </thead>
+ {% endif %}
+ <tbody>
+ {% for row in rows %}
+ <tr{{ row.attributes }}>
+ {% for key, column in row.columns %}
+ {% if column.default_classes %}
+ {%
+ set column_classes = [
+ 'views-field'
+ ]
+ %}
+ {% for field in column.fields %}
+ {% set column_classes = column_classes|merge(['views-field-' ~ field]) %}
+ {% endfor %}
+ {% endif %}
+ <td{{ column.attributes.addClass(column_classes) }}>
+ {%- if column.wrapper_element -%}
+ <{{ column.wrapper_element }}>
+ {% for content in column.content %}
+ {{ content.separator }}{{ content.field_output }}
+ {% endfor %}
+ </{{ column.wrapper_element }}>
+ {%- else -%}
+ {% for content in column.content %}
+ {{- content.separator }}{{ content.field_output -}}
+ {% endfor %}
+ {%- endif %}
+ </td>
+ {% endfor %}
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+
+{% if responsive %}
+ </div>
+{% endif %}
--- /dev/null
+name: Views Bootstrap
+type: module
+description: 'Views Twitter Bootstrap Components.'
+package: Views
+core: 8.x
+dependencies:
+ - drupal:views
--- /dev/null
+<?php
+
+/**
+ * @file
+ * Custom functions for Views Bootstrap.
+ */
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\views_bootstrap\ViewsBootstrap;
+
+/**
+ * Implements hook_help().
+ */
+function views_bootstrap_help($route_name, RouteMatchInterface $route_match) {
+ switch ($route_name) {
+ case 'help.page.views_bootstrap':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The <a href="https://www.drupal.org/project/views_bootstrap">Views Bootstrap module</a> adds styles to Views to output the results of a view as several common <a href="http://getbootstrap.com/components/">Twitter Bootstrap</a> components.') . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<p>' . t('<a href="http://moduledev.dev/admin/structure/views/add">Create a view</a> using one of the following styles:') . '</p>';
+ $output .= '<ul>';
+ $output .= '<li>' . t('<a href="http://getbootstrap.com/css/#grid-example-basic">Grid</a>') . '</li>';
+ $output .= '<li>' . t('<a href="http://getbootstrap.com/css/#tables">Tables</a>') . '</li>';
+ $output .= '<li>' . t('<a href="http://getbootstrap.com/components/#media">Media object</a>') . '</li>';
+ $output .= '<li>' . t('<a href="http://getbootstrap.com/javascript/#collapse-example-accordion">Accordion</a>') . '</li>';
+ $output .= '<li>' . t('<a href="http://getbootstrap.com/javascript/#carousel">Carousel</a>') . '</li>';
+ $output .= '<li>' . t('<a href="http://getbootstrap.com/components/#list-group">List group</a>') . '</li>';
+ $output .= '<ul>';
+
+ return $output;
+ }
+}
+
+/**
+ * {@inheritdoc}
+ */
+function views_bootstrap_theme() {
+ return ViewsBootstrap::getThemeHooks();
+}
--- /dev/null
+<?php
+
+/**
+ * @file
+ * Preprocessors and helper functions to make theming easier.
+ */
+
+use Drupal\views_bootstrap\ViewsBootstrap;
+use Drupal\Core\Template\Attribute;
+
+/**
+ * Prepares variables for views accordion templates.
+ *
+ * Default template: views-bootstrap-accordion.html.twig.
+ *
+ * @param array $vars
+ * An associative array containing:
+ * - view: A ViewExecutable object.
+ * - rows: The raw row data.
+ */
+function template_preprocess_views_bootstrap_accordion(array &$vars) {
+ $view = $vars['view'];
+ $vars['id'] = ViewsBootstrap::getUniqueId($view);
+ $panel_title_field = $view->style_plugin->options['panel_title_field'];
+ $vars['attributes']['class'][] = 'panel-group';
+ if ($panel_title_field) {
+ foreach ($vars['rows'] as $id => $row) {
+ $vars['rows'][$id] = [];
+ $vars['rows'][$id]['content'] = $row;
+ $vars['rows'][$id]['title'] = $view->style_plugin->getFieldValue($id, $panel_title_field);
+ }
+ }
+ else {
+ // @TODO: This would be better as valdiation errors on the style plugin options form.
+ drupal_set_message(t('@style style will not display without the "@field" setting.',
+ [
+ '@style' => $view->style_plugin->definition['title'],
+ '@field' => 'Panel title',
+ ]
+ ), 'warning');
+ }
+ // @TODO: Make sure that $vars['rows'] is rendered array.
+ // @SEE: Have a look template_preprocess_views_view_unformatted()
+ // and views-view-unformatted.html.twig
+}
+
+/**
+ * Prepares variables for views carousel template.
+ *
+ * Default template: views-bootstrap-carousel.html.twig.
+ *
+ * @param array $vars
+ * An associative array containing:
+ * - view: A ViewExecutable object.
+ * - rows: The raw row data.
+ */
+function template_preprocess_views_bootstrap_carousel(array &$vars) {
+ $view = $vars['view'];
+ $vars['id'] = ViewsBootstrap::getUniqueId($view);
+ $vars['attributes']['class'][] = 'views-bootstrap-media-object';
+ $vars['attributes']['class'][] = 'media-list';
+
+ // Carousel options.
+ $vars['interval'] = $view->style_plugin->options['interval'];
+ $vars['navigation'] = $view->style_plugin->options['navigation'];
+ $vars['indicators'] = $view->style_plugin->options['indicators'];
+ $vars['pause'] = $view->style_plugin->options['pause'] ? 'hover' : FALSE;
+ $vars['wrap'] = $view->style_plugin->options['wrap'];
+
+ // Carousel rows.
+ $image = $view->style_plugin->options['image'];
+ $title = $view->style_plugin->options['title'];
+ $description = $view->style_plugin->options['description'];
+ $fieldLabels = $view->display_handler->getFieldLabels(TRUE);
+
+ foreach ($vars['rows'] as $id => $row) {
+ $vars['rows'][$id] = [];
+ $vars['rows'][$id]['image'] = $view->style_plugin->getField($id, $image);
+ $vars['rows'][$id]['title'] = $view->style_plugin->getField($id, $title);
+ $vars['rows'][$id]['description'] = $view->style_plugin->getField($id, $description);
+ // Add any additional fields to result.
+ foreach (array_keys($fieldLabels) as $label) {
+ if (!in_array($label, [$image, $title, $description])) {
+ $vars['rows'][$id][$label] = $view->style_plugin->getField($id, $label);
+ }
+ }
+ }
+
+}
+
+/**
+ * Prepares variables for views grid templates.
+ *
+ * Default template: views-bootstrap-grid.html.twig.
+ *
+ * @param array $vars
+ * An associative array containing:
+ * - view: A ViewExecutable object.
+ * - rows: The raw row data.
+ */
+function template_preprocess_views_bootstrap_grid(array &$vars) {
+ $view = $vars['view'];
+ $vars['id'] = ViewsBootstrap::getUniqueId($view);
+ $vars['attributes']['class'][] = 'grid';
+ $options = $view->style_plugin->options;
+ $options['automatic_width'] = ['default' => TRUE];
+ $horizontal = ($options['alignment'] === 'horizontal');
+
+ foreach (['xs', 'sm', 'md', 'lg'] as $size) {
+ $vars["col_" . $size] = $options["col_" . $size];
+ // Get the value from the size sting.
+ $vars['sizes'][$size] = ViewsBootstrap::getColSize($options["col_" . $size]);
+ }
+ $vars['options'] = $options;
+
+}
+
+/**
+ * Prepares variables for views list group templates.
+ *
+ * Default template: views-bootstrap-list-group.html.twig.
+ *
+ * @param array $vars
+ * An associative array containing:
+ * - view: A ViewExecutable object.
+ * - rows: The raw row data.
+ */
+function template_preprocess_views_bootstrap_list_group(array &$vars) {
+ $view = $vars['view'];
+ $options = $view->style_plugin->options;
+ $vars['id'] = ViewsBootstrap::getUniqueId($view);
+ $vars['attributes']['class'][] = 'views-bootstrap-list-group';
+ foreach ($vars['rows'] as $id => $row) {
+ $vars['rows'][$id] = [];
+ $vars['rows'][$id]['content'] = $row;
+ $vars['rows'][$id]['title'] = $vars['view']->style_plugin->getField($id, $options['title_field']);
+ }
+
+}
+
+/**
+ * Prepares variables for views media object templates.
+ *
+ * Default template: views-bootstrap-media-object.html.twig.
+ *
+ * @param array $vars
+ * An associative array containing:
+ * - view: A ViewExecutable object.
+ * - rows: The raw row data.
+ */
+function template_preprocess_views_bootstrap_media_object(array &$vars) {
+ $vars['id'] = ViewsBootstrap::getUniqueId($vars['view']);
+ $image_class = $vars['view']->style_plugin->options['image_class'];
+ $image_field = $vars['view']->style_plugin->options['image_field'];
+ $heading_field = $vars['view']->style_plugin->options['heading_field'];
+ $body_field = $vars['view']->style_plugin->options['body_field'];
+
+ foreach ($vars['rows'] as $id => $row) {
+ $vars['rows'][$id] = [];
+ $vars['classes'][$id] .= ' media-object';
+ $vars['rows'][$id]['image_class'] = $image_class;
+ $vars['rows'][$id]['image'] = $vars['view']->style_plugin->getField($id, $image_field);
+ $vars['rows'][$id]['heading'] = $vars['view']->style_plugin->getField($id, $heading_field);
+ $vars['rows'][$id]['body'] = $vars['view']->style_plugin->getField($id, $body_field);
+ }
+}
+
+/**
+ * Prepares variables for views tab templates.
+ *
+ * Default template: views-bootstrap-tab.html.twig.
+ *
+ * @param array $vars
+ * An associative array containing:
+ * - view: A ViewExecutable object.
+ * - rows: The raw row data.
+ */
+function template_preprocess_views_bootstrap_tab(array &$vars) {
+ $vars['id'] = ViewsBootstrap::getUniqueId($vars['view']);
+ $view = $vars['view'];
+ $tab_field = $view->style_plugin->options['tab_field'];
+ $vars['tab_type'] = $view->style_plugin->options['tab_type'];
+ $vars['justified'] = $view->style_plugin->options['justified'];
+
+ // Get tabs.
+ if ($tab_field) {
+ if (isset($view->field[$tab_field])) {
+ foreach (array_keys($vars['rows']) as $key) {
+ $vars['tabs'][$key] = $view->style_plugin->getFieldValue($key, $tab_field);
+ }
+ }
+ foreach ($vars['rows'] as $id => $row) {
+ $vars['rows'][$id] = array();
+ $vars['rows'][$id]['content'] = $row;
+ $vars['rows'][$id]['attributes'] = new Attribute();
+ if ($row_class = $view->style_plugin->getRowClass($id)) {
+ $vars['rows'][$id]['attributes']->addClass($row_class);
+ }
+ }
+ }
+ else {
+ // @TODO: This would be better as valdiation errors on the style plugin options form.
+ drupal_set_message(t('@style style will not display without the "@field" setting.',
+ [
+ '@style' => $view->style_plugin->definition['title'],
+ '@field' => 'Tab title',
+ ]
+ ), 'warning');
+ }
+}
+
+/**
+ * Prepares variables for views table templates.
+ *
+ * Default template: views-bootstrap-table.html.twig.
+ *
+ * @param array $vars
+ * An associative array containing:
+ * - view: A ViewExecutable object.
+ * - rows: The raw row data.
+ */
+function template_preprocess_views_bootstrap_table(array &$vars) {
+ $vars['responsive'] = $vars['view']->style_plugin->options['responsive'];
+ $vars['attributes']['class'][] = 'table';
+ foreach (array_filter($vars['view']->style_plugin->options['bootstrap_styles']) as $style) {
+ $vars['attributes']['class'][] = 'table-' . $style;
+ }
+}
+