More tidying.
authorJeff Veit <jeff.veit@gmail.com>
Fri, 23 Jun 2017 00:53:28 +0000 (01:53 +0100)
committerJeff Veit <jeff.veit@gmail.com>
Fri, 23 Jun 2017 00:53:28 +0000 (01:53 +0100)
22 files changed:
web/modules/contrib/views_bootstrap [deleted submodule]
web/modules/contrib/views_bootstrap/.travis.yml [new file with mode: 0644]
web/modules/contrib/views_bootstrap/README.md [new file with mode: 0755]
web/modules/contrib/views_bootstrap/composer.json [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapAccordion.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapCarousel.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapGrid.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapListGroup.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapMediaObject.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapTab.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapTable.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/src/ViewsBootstrap.php [new file with mode: 0644]
web/modules/contrib/views_bootstrap/templates/views-bootstrap-accordion.html.twig [new file with mode: 0644]
web/modules/contrib/views_bootstrap/templates/views-bootstrap-carousel.html.twig [new file with mode: 0644]
web/modules/contrib/views_bootstrap/templates/views-bootstrap-grid.html.twig [new file with mode: 0644]
web/modules/contrib/views_bootstrap/templates/views-bootstrap-list-group.html.twig [new file with mode: 0644]
web/modules/contrib/views_bootstrap/templates/views-bootstrap-media-object.html.twig [new file with mode: 0644]
web/modules/contrib/views_bootstrap/templates/views-bootstrap-tab.html.twig [new file with mode: 0644]
web/modules/contrib/views_bootstrap/templates/views-bootstrap-table.html.twig [new file with mode: 0644]
web/modules/contrib/views_bootstrap/views_bootstrap.info.yml [new file with mode: 0644]
web/modules/contrib/views_bootstrap/views_bootstrap.module [new file with mode: 0644]
web/modules/contrib/views_bootstrap/views_bootstrap.theme.inc [new file with mode: 0644]

diff --git a/web/modules/contrib/views_bootstrap b/web/modules/contrib/views_bootstrap
deleted file mode 160000 (submodule)
index ef95dac..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit ef95dac8dfeb8f2d61b0e5d40075a0a6e23e53ca
diff --git a/web/modules/contrib/views_bootstrap/.travis.yml b/web/modules/contrib/views_bootstrap/.travis.yml
new file mode 100644 (file)
index 0000000..f66ebc0
--- /dev/null
@@ -0,0 +1,51 @@
+# @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
diff --git a/web/modules/contrib/views_bootstrap/README.md b/web/modules/contrib/views_bootstrap/README.md
new file mode 100755 (executable)
index 0000000..57d628a
--- /dev/null
@@ -0,0 +1,24 @@
+[![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)
diff --git a/web/modules/contrib/views_bootstrap/composer.json b/web/modules/contrib/views_bootstrap/composer.json
new file mode 100644 (file)
index 0000000..0a75e75
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "name": "drupal/views_bootstrap",
+  "type": "drupal-module",
+  "description": "Integrate the Bootstrap framework with Views.",
+  "keywords": ["Drupal"],
+  "license": "GPL-2.0+"
+}
diff --git a/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapAccordion.php b/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapAccordion.php
new file mode 100644 (file)
index 0000000..f1bdd28
--- /dev/null
@@ -0,0 +1,67 @@
+<?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.'),
+      ];
+    }
+  }
+
+}
diff --git a/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapCarousel.php b/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapCarousel.php
new file mode 100644 (file)
index 0000000..e7f2f0b
--- /dev/null
@@ -0,0 +1,113 @@
+<?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'],
+    ];
+  }
+
+}
diff --git a/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapGrid.php b/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapGrid.php
new file mode 100644 (file)
index 0000000..0c21cae
--- /dev/null
@@ -0,0 +1,204 @@
+<?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,
+        ],
+      ];
+    }
+  }
+
+}
diff --git a/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapListGroup.php b/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapListGroup.php
new file mode 100644 (file)
index 0000000..b88be0b
--- /dev/null
@@ -0,0 +1,67 @@
+<?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.'),
+    ];
+
+  }
+
+}
diff --git a/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapMediaObject.php b/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapMediaObject.php
new file mode 100644 (file)
index 0000000..6095389
--- /dev/null
@@ -0,0 +1,96 @@
+<?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.'),
+    ];
+
+  }
+
+}
diff --git a/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapTab.php b/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapTab.php
new file mode 100644 (file)
index 0000000..d2cbed9
--- /dev/null
@@ -0,0 +1,87 @@
+<?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
diff --git a/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapTable.php b/web/modules/contrib/views_bootstrap/src/Plugin/views/style/ViewsBootstrapTable.php
new file mode 100644 (file)
index 0000000..0c9a0e1
--- /dev/null
@@ -0,0 +1,61 @@
+<?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'),
+      ],
+    ];
+  }
+
+}
diff --git a/web/modules/contrib/views_bootstrap/src/ViewsBootstrap.php b/web/modules/contrib/views_bootstrap/src/ViewsBootstrap.php
new file mode 100644 (file)
index 0000000..84d46dd
--- /dev/null
@@ -0,0 +1,105 @@
+<?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;
+  }
+
+}
diff --git a/web/modules/contrib/views_bootstrap/templates/views-bootstrap-accordion.html.twig b/web/modules/contrib/views_bootstrap/templates/views-bootstrap-accordion.html.twig
new file mode 100644 (file)
index 0000000..6205aac
--- /dev/null
@@ -0,0 +1,22 @@
+<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>
diff --git a/web/modules/contrib/views_bootstrap/templates/views-bootstrap-carousel.html.twig b/web/modules/contrib/views_bootstrap/templates/views-bootstrap-carousel.html.twig
new file mode 100644 (file)
index 0000000..5895ec0
--- /dev/null
@@ -0,0 +1,65 @@
+{#
+/**
+ * @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>
diff --git a/web/modules/contrib/views_bootstrap/templates/views-bootstrap-grid.html.twig b/web/modules/contrib/views_bootstrap/templates/views-bootstrap-grid.html.twig
new file mode 100644 (file)
index 0000000..364ed12
--- /dev/null
@@ -0,0 +1,59 @@
+{#
+/**
+ * @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>
diff --git a/web/modules/contrib/views_bootstrap/templates/views-bootstrap-list-group.html.twig b/web/modules/contrib/views_bootstrap/templates/views-bootstrap-list-group.html.twig
new file mode 100644 (file)
index 0000000..8f57d6e
--- /dev/null
@@ -0,0 +1,31 @@
+{#
+/**
+ * @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>
diff --git a/web/modules/contrib/views_bootstrap/templates/views-bootstrap-media-object.html.twig b/web/modules/contrib/views_bootstrap/templates/views-bootstrap-media-object.html.twig
new file mode 100644 (file)
index 0000000..fd34b78
--- /dev/null
@@ -0,0 +1,38 @@
+{# @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>
+
diff --git a/web/modules/contrib/views_bootstrap/templates/views-bootstrap-tab.html.twig b/web/modules/contrib/views_bootstrap/templates/views-bootstrap-tab.html.twig
new file mode 100644 (file)
index 0000000..5955b42
--- /dev/null
@@ -0,0 +1,21 @@
+{% 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>
diff --git a/web/modules/contrib/views_bootstrap/templates/views-bootstrap-table.html.twig b/web/modules/contrib/views_bootstrap/templates/views-bootstrap-table.html.twig
new file mode 100644 (file)
index 0000000..119915d
--- /dev/null
@@ -0,0 +1,134 @@
+{#
+/**
+ * @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 %}
diff --git a/web/modules/contrib/views_bootstrap/views_bootstrap.info.yml b/web/modules/contrib/views_bootstrap/views_bootstrap.info.yml
new file mode 100644 (file)
index 0000000..c611996
--- /dev/null
@@ -0,0 +1,7 @@
+name: Views Bootstrap
+type: module
+description: 'Views Twitter Bootstrap Components.'
+package: Views
+core: 8.x
+dependencies:
+  - drupal:views
diff --git a/web/modules/contrib/views_bootstrap/views_bootstrap.module b/web/modules/contrib/views_bootstrap/views_bootstrap.module
new file mode 100644 (file)
index 0000000..ca3cea0
--- /dev/null
@@ -0,0 +1,40 @@
+<?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();
+}
diff --git a/web/modules/contrib/views_bootstrap/views_bootstrap.theme.inc b/web/modules/contrib/views_bootstrap/views_bootstrap.theme.inc
new file mode 100644 (file)
index 0000000..09f1750
--- /dev/null
@@ -0,0 +1,229 @@
+<?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;
+  }
+}
+