first commit

This commit is contained in:
2024-07-15 12:33:27 +02:00
commit ce50ae282b
22084 changed files with 2623791 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
<!-- @file Documentation for the @BootstrapAlter annotated plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapAlter
- [Pre-requisite](#prerequisite)
- [Supported alter hooks](#supported)
- [Form alter hooks](#form)
- [Create a plugin](#create)
- [Rebuild the cache](#rebuild)
---
## Pre-requisite {#prerequisite}
Due to the nature of how Drupal alter hooks work, there is no "catch all" alter
hook (like for forms with hook_form_alter). That means for you to use this
plugin, it must be invoked from inside each and every alter hook that lives in
`THEMENAME.theme`.
Luckily you don't have to worry about invoking the plugin directly. Instead,
all you have to do is call the `Bootstrap::alter` helper method and pass the
alter function name and parameters as arguments:
```php
<?php
use Drupal\bootstrap\Bootstrap;
/**
* Implements hook_HOOK_alter().
*/
function hook_some_hook_alter(&$data, &$context1 = NULL, &$context2 = NULL) {
Bootstrap::alter(__FUNCTION__, $data, $context1, $context2);
}
?>
```
## Supported alter hooks {#supported}
This base theme implements several of the most commonly used alter hooks in
themes and are automatically supported out-of-the-box.
Once a base theme has implemented an alter hook, like mentioned above, all
subsequent sub-themes will have the ability to implement a plugin for that
alter hook directly. All you have to do is simply create the plugin file in
`./themes/THEMENAME/src/Plugin/Alter`. No need to implement any code in
`THEMENAME.theme`:
- `hook_bootstrap_colorize_text_alter`
- `hook_bootstrap_iconize_text_alter`
- `hook_element_info_alter`
- `hook_js_settings_alter`
- `hook_library_info_alter`
- `hook_page_attachments_alter`
- `hook_theme_registry_alter`
- `hook_theme_suggestions_alter`
{.alert.alert-info}**Note:** if you do not see an alter hook here that you think
_should_ be here, please
[create an issue](https://www.drupal.org/node/add/project-issue/bootstrap)
## Form alter hooks {#form}
You were probably thinking: "Hey, where's `hook_form_alter`? Didn't you _just_
mention that above?"
As we all know, forms can be a tad more involved than just a simple "alter" and
we figured that we'd give you a little more power behind what you can actually
do with them. So if you're interested in those, please go see:
@link plugins_form @BootstrapForm @endlink
While, yes technically, `hook_form_system_theme_settings_alter` could also fall
under the form plugin, we decided to take those a step further as well, see:
@link plugins_setting @BootstrapSetting @endlink
## Create a plugin {#create}
We'll use `PageAttachments` implemented by this base theme as an example of
how to add a library from your sub-theme to every page request.
Replace all following instances of `THEMENAME` with the actual machine name of
your sub-theme.
Create a file at `./themes/THEMENAME/src/Plugin/Alter/PageAttachments.php` with the
following contents:
```php
<?php
/**
* @file
* Contains \Drupal\THEMENAME\Plugin\Alter\PageAttachments.
*/
namespace Drupal\THEMENAME\Plugin\Alter;
use Drupal\bootstrap\Plugin\Alter\PageAttachments as BootstrapPageAttachements;
/**
* Implements hook_page_attachments_alter().
*
* @ingroup plugins_alter
*
* @BootstrapAlter("page_attachments")
*/
class PageAttachments extends BootstrapPageAttachements {
/**
* {@inheritdoc}
*/
public function alter(&$attachments, &$context1 = NULL, &$context2 = NULL) {
// Call the parent method from the base theme, if applicable (which it is
// in this case because Bootstrap actually implements this alter).
parent::alter($attachments, $context1, $context2);
// Add your custom library.
$attachments['#attached']['library'][] = 'THEMENAME/my_library';
}
}
?>
```
## Rebuild the cache {#rebuild}
Once you have saved, you must rebuild your cache for this new plugin to be
discovered. This must happen anytime you make a change to the actual file name
or the information inside the `@BootstrapAlter` annotation.
To rebuild your cache, navigate to `admin/config/development/performance` and
click the `Clear all caches` button. Or if you prefer, run `drush cr` from the
command line.
Voilà! After this, you should have a fully functional `@BootstrapAlter` plugin!

View File

@@ -0,0 +1,110 @@
<!-- @file Documentation for the @BootstrapForm annotated discovery plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapForm
- [Create a plugin](#create)
- [Rebuild the cache](#rebuild)
---
## Create a plugin {#create}
We'll use `SearchBlockForm` implemented by this base theme as an example of
how to remove `#input_group_button` from `search_block_form`.
Replace all following instances of `THEMENAME` with the actual machine name of
your sub-theme.
Create a file at `./themes/THEMENAME/src/Plugin/Form/SearchBlockForm.php` with the
following contents:
```php
<?php
namespace Drupal\THEMENAME\Plugin\Form;
use Drupal\bootstrap\Plugin\Form\SearchBlockForm as BootstrapSearchBlockForm;
use Drupal\bootstrap\Utility\Element;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_FORM_ID_alter().
*
* @ingroup plugins_form
*
* @BootstrapForm("search_block_form")
*/
class SearchBlockForm extends BootstrapSearchBlockForm {
/**
* {@inheritdoc}
*/
public function alterForm(array &$form, FormStateInterface $form_state, $form_id = NULL) {
// Call the parent method from the base theme, if applicable (which it is
// in this case because Bootstrap actually implements this alter).
parent::alterForm($form, $form_state, $form_id);
// Disable #input_group_button the normal way:
$form['keys']['#input_group_button'] = FALSE;
}
/**
* {@inheritdoc}
*/
public function alterFormElement(Element $form, FormStateInterface $form_state, $form_id = NULL) {
// This method is the same as above, except the the $form argument passed is
// an instance of \Drupal\bootstrap\Utility\Element for easier manipulation.
// Using this method is preferable and considered "Best Practice".
//
// Disable #input_group_button using the $form Element object:
// $form->keys->setProperty('input_group_button', FALSE);.
}
/**
* {@inheritdoc}
*/
public static function submitForm(array &$form, FormStateInterface $form_state) {
// This method is automatically called when the form is submitted.
}
/**
* {@inheritdoc}
*/
public static function submitFormElement(Element $form, FormStateInterface $form_state) {
// This method is the same as above, except the the $form argument passed is
// an instance of \Drupal\bootstrap\Utility\Element for easier manipulation.
// Using this method is preferable and considered "Best Practice".
}
/**
* {@inheritdoc}
*/
public static function validateForm(array &$form, FormStateInterface $form_state) {
// This method is automatically called when the form is validated.
}
/**
* {@inheritdoc}
*/
public static function validateFormElement(Element $form, FormStateInterface $form_state) {
// This method is the same as above, except the the $form argument passed is
// an instance of \Drupal\bootstrap\Utility\Element for easier manipulation.
// Using this method is preferable and considered "Best Practice".
}
}
?>
```
## Rebuild the cache {#rebuild}
Once you have saved, you must rebuild your cache for this new plugin to be
discovered. This must happen anytime you make a change to the actual file name
or the information inside the `@BootstrapForm` annotation.
To rebuild your cache, navigate to `admin/config/development/performance` and
click the `Clear all caches` button. Or if you prefer, run `drush cr` from the
command line.
Voilà! After this, you should have a fully functional `@BootstrapForm` plugin!

View File

@@ -0,0 +1,118 @@
<!-- @file Documentation for the @BootstrapPreprocess annotated plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapPreprocess
- [Create a plugin](#create)
- [Rebuild the cache](#rebuild)
---
## Create a plugin {#create}
We'll use `Page` implemented by this base theme as an example of how to add
custom classes for the `page.html.twig` template that should only be added
under certain conditions.
Replace all following instances of `THEMENAME` with the actual machine name of
your sub-theme.
Create a file at `./themes/THEMENAME/src/Plugin/Preprocess/Page.php` with the
following contents:
```php
<?php
namespace Drupal\THEMENAME\Plugin\Preprocess;
use Drupal\bootstrap\Plugin\Preprocess\Page as BootstrapPage;
use Drupal\bootstrap\Utility\Element;
use Drupal\bootstrap\Utility\Variables;
/**
* Pre-processes variables for the "page" theme hook.
*
* @ingroup plugins_preprocess
*
* @BootstrapPreprocess("page")
*/
class Page extends BootstrapPage {
/*
* It should be noted that you do not need all three methods here.
* This is to just show you the different examples of how this plugin
* works and how they can be tailored to your needs.
*/
/**
* {@inheritdoc}
*/
public function preprocess(array &$variables, $hook, array $info) {
$value = isset($variables['element']['child']['#value']) ? $variables['element']['child']['#value'] : FALSE;
if (_some_module_condition($value)) {
$variables['attributes']['class'][] = 'my-theme-class';
$variables['attributes']['class'][] = 'another-theme-class';
$key = array_search('page', $variables['attributes']['class']);
if ($key !== FALSE) {
unset($variables['attributes']['class'][$key]);
}
}
// If you are extending and overriding a preprocess method from the base
// theme, it is imperative that you also call the parent (base theme) method
// at some point in the process, typically after you have finished with your
// preprocessing.
parent::preprocess($variables, $hook, $info);
}
/**
* {@inheritdoc}
*/
public function preprocessVariables(Variables $variables) {
// This method is almost identical to the one above, but it introduces the
// Variables utility class in the base theme. This class has a plethora of
// helpful methods to quickly modify common tasks when you're in a
// preprocess function. It also acts like the normal $variables array when
// you need it to in instances of accessing nested content or in loop
// structures like foreach.
$value = isset($variables['element']['child']['#value']) ? $variables['element']['child']['#value'] : FALSE;
if (_some_module_condition($value)) {
$variables->addClass(['my-theme-class', 'another-theme-class'])->removeClass('page');
}
parent::preprocessVariables($variables);
}
/**
* {@inheritdoc}
*/
protected function preprocessElement(Element $element, Variables $variables) {
// This method is only ever invoked if either $variables['element'] or
// $variables['elements'] exists. These keys are usually only found in forms
// or render arrays when there is a #type being used. This introduces the
// Element utility class in the base theme. It too has a bucket-load of
// features, specific to the unique characteristics of render arrays with
// their "properties" (keys starting with #). This will quickly allow you to
// access some of the nested element data and reduce the overhead required
// for commonly used logic.
$value = $element->child->getProperty('value', FALSE);
if (_some_module_condition($value)) {
$variables->addClass(['my-theme-class', 'another-theme-class'])->removeClass('page');
}
parent::preprocessElement($element, $variables);
}
}
?>
```
## Rebuild the cache {#rebuild}
Once you have saved, you must rebuild your cache for this new plugin to be
discovered. This must happen anytime you make a change to the actual file name
or the information inside the `@BootstrapPreprocess` annotation.
To rebuild your cache, navigate to `admin/config/development/performance` and
click the `Clear all caches` button. Or if you prefer, run `drush cr` from the
command line.
Voilà! After this, you should have a fully functional `@BootstrapPreprocess`
plugin!

View File

@@ -0,0 +1,119 @@
<!-- @file Documentation for the @BootstrapPrerender annotated plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapPrerender
- [Create a plugin](#create)
- [Rebuild the cache](#rebuild)
---
## Create a plugin {#create}
We'll use `Link` implemented by this base theme as an example of how to add
custom classes and an icon to make the link look like a Bootstrap button. This
example will only work if the link is passed some sort of `#context` when the
render array is built, like the following:
```php
<?php
$build['my_button'] = [
'#type' => 'link',
'#title' => t('Download'),
'#url' => Url::fromUserInput('/download', [
'query' => ['item' => '1234'],
]),
'#context' => [
'downloadButton' => TRUE,
],
];
?>
```
Replace all following instances of `THEMENAME` with the actual machine name of
your sub-theme.
Create a file at `./themes/THEMENAME/src/Plugin/Prerender/Link.php` with the
following contents:
```php
<?php
namespace Drupal\THEMENAME\Plugin\Prerender;
use Drupal\bootstrap\Plugin\Prerender\Link as BootstrapLink;
use Drupal\bootstrap\Bootstrap;
use Drupal\bootstrap\Utility\Element;
/**
* Pre-render callback for the "link" element type.
*
* @ingroup plugins_prerender
*
* @BootstrapPrerender("link",
* action = @BootstrapConstant(
* "\Drupal\bootstrap\Bootstrap::CALLBACK_PREPEND"
* )
* )
*
* @see \Drupal\Core\Render\Element\Link::preRenderLink()
*/
class Link extends BootstrapLink {
/*
* It should be noted that you do not need both methods here.
* This is to just show you the different examples of how this plugin
* works and how it can be tailored to your needs.
*/
/**
* {@inheritdoc}
*/
public static function preRender(array $element) {
$context = isset($element['#context']) ? $element['#context'] : [];
// Make downloadButton links into buttons.
if (!empty($context['downloadButton'])) {
$element['#icon'] = Bootstrap::glyphicon('download-alt');
$element['#attributes']['class'][] = 'btn';
$element['#attributes']['class'][] = 'btn-primary';
$element['#attributes']['class'][] = 'btn-lg';
}
// You must always return the element in this method, as well as call the
// parent method when sub-classing this method as it is used to invoke
// static::preRenderElement().
return parent::preRender($element);
}
/**
* {@inheritdoc}
*/
public static function preRenderElement(Element $element) {
// Make downloadButton links into buttons.
// Same as above, just a little cleaner with the Element utility class.
if ($element->getContext('downloadButton')) {
$element->addClass(['btn', 'btn-primary', 'btn-lg'])->setIcon(Bootstrap::glyphicon('download-alt'));
}
// You don't always have to call the parent method when sub-classing, but
// it is generally recommended that you do (otherwise the icon that was
// just added wouldn't work).
parent::preRenderElement($element);
}
}
?>
```
## Rebuild the cache {#rebuild}
Once you have saved, you must rebuild your cache for this new plugin to be
discovered. This must happen anytime you make a change to the actual file name
or the information inside the `@BootstrapPrerender` annotation.
To rebuild your cache, navigate to `admin/config/development/performance` and
click the `Clear all caches` button. Or if you prefer, run `drush cr` from the
command line.
Voilà! After this, you should have a fully functional `@BootstrapPrerender`
plugin!

View File

@@ -0,0 +1,99 @@
<!-- @file Documentation for the @BootstrapProcess annotated plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapProcess
- [Create a plugin](#create)
- [Rebuild the cache](#rebuild)
---
## Create a plugin {#create}
{.alert.alert-warning}**Note:** This plugin is _not_ a re-implementation of the
D7 `hook_process_HOOK` for theme hooks in anyway. That layer was removed from
the theme system in D8 and for good reason (see:
[Remove the process layer](https://www.drupal.org/node/1843650)). This plugin
is about automatically adding a `#process` callback for a form element `#type`.
This is especially useful when dealing with core elements that have implemented
their own callbacks; either to alter their output or remove entirely.
We'll use `TextFormat` implemented by this base theme as an example of how to
override the class entirely and remove this base theme's over-simplification
for the "format tips" section.
Replace all following instances of `THEMENAME` with the actual machine name of
your sub-theme.
Create a file at `./themes/THEMENAME/src/Plugin/Process/TextFormat.php` with the
following contents:
```php
<?php
namespace Drupal\THEMENAME\Plugin\Process;
use Drupal\bootstrap\Plugin\Process\TextFormat as BootstrapTextFormat;
use Drupal\bootstrap\Utility\Element;
use Drupal\Core\Form\FormStateInterface;
/**
* Processes the "text_format" element.
*
* @ingroup plugins_process
*
* @BootstrapProcess("text_format")
*
* @see \Drupal\filter\Element\TextFormat::processFormat()
*/
class TextFormat extends BootstrapTextFormat {
/*
* It should be noted that you do not need both methods here.
* This is to just show you the different examples of how this plugin
* works and how it can be tailored to your needs.
*/
/**
* {@inheritdoc}
*/
public static function process(array $element, FormStateInterface $form_state, array &$complete_form) {
// You must return the element immediately if this is TRUE.
if (!empty($element['#bootstrap_ignore_process'])) {
return $element;
}
// Technically this isn't the method that we need to achieve our goal.
// But showing it just for example sake.
//
// You must always return the element in this method, as well as call the
// parent method when sub-classing this method as it is used to invoke
// static::processElement();
return parent::process($element, $form_state, $complete_form);
}
/**
* {@inheritdoc}
*/
public static function processElement(Element $element, FormStateInterface $form_state, array &$complete_form) {
// Normally, we'd call the parent method here. But this is actually an
// instance where we know we don't want to use the alterations made by
// the base theme. So we just comment it out and leave the method empty.
// parent::processElement($element, $form_state, $complete_form);.
}
}
?>
```
## Rebuild the cache {#rebuild}
Once you have saved, you must rebuild your cache for this new plugin to be
discovered. This must happen anytime you make a change to the actual file name
or the information inside the `@BootstrapProcess` annotation.
To rebuild your cache, navigate to `admin/config/development/performance` and
click the `Clear all caches` button. Or if you prefer, run `drush cr` from the
command line.
Voilà! After this, you should have a fully functional `@BootstrapProcess`
plugin!

View File

@@ -0,0 +1,90 @@
<!-- @file Documentation for the @BootstrapProvider annotated plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapProvider
- [Create a plugin](#create)
- [Rebuild the cache](#rebuild)
---
## Create a plugin {#create}
We'll use the `\Drupal\bootstrap\Plugin\Provider\JsDelivr` CDN Provider as an
example of how to create a quick custom CDN provider using its API URLs.
Replace all following instances of `THEMENAME` with the actual machine name of
your sub-theme.
You may also feel free to replace the provided URLs with your own. Most of the
popular CDN API output can be easily parsed, however you may need to provide
addition parsing in your custom CDN Provider if you're not getting the desired
results.
If you're truly interested in implementing a CDN Provider, it is highly
recommended that you read the accompanying PHP based documentation on the
classes and methods responsible for actually retrieving, parsing and caching
the data from the CDN's API.
Create a file at `./themes/THEMENAME/src/Plugin/Provider/MyCdn.php` with the
following contents:
```php
<?php
namespace Drupal\THEMENAME\Plugin\Provider;
use Drupal\bootstrap\Plugin\Provider\ApiProviderBase;
/**
* The "mycdn" CDN Provider plugin.
*
* @ingroup plugins_provider
*
* @BootstrapProvider(
* id = "mycdn",
* label = @Translation("My CDN"),
* description = @Translation("My CDN (jsDelivr)"),
* weight = -1
* )
*/
class JsDelivr extends ApiProviderBase {
/**
* {@inheritdoc}
*/
protected function getApiAssetsUrlTemplate() {
return 'https://data.jsdelivr.com/v1/package/npm/@library@@version/flat';
}
/**
* {@inheritdoc}
*/
protected function getApiVersionsUrlTemplate() {
return 'https://data.jsdelivr.com/v1/package/npm/@library';
}
/**
* {@inheritdoc}
*/
protected function getCdnUrlTemplate() {
return 'https://cdn.jsdelivr.net/npm/@library@@version/@file';
}
}
?>
```
## Rebuild the cache {#rebuild}
Once you have saved, you must rebuild your cache for this new plugin to be
discovered. This must happen anytime you make a change to the actual file name
or the information inside the `@BootstrapProvider` annotation.
To rebuild your cache, navigate to `admin/config/development/performance` and
click the `Clear all caches` button. Or if you prefer, run `drush cr` from the
command line.
Voilà! After this, you should have a fully functional `@BootstrapProvider`
plugin!

View File

@@ -0,0 +1,70 @@
<!-- @file Documents the Plugin System for the Drupal Bootstrap base theme. -->
<!-- @defgroup -->
<!-- @ingroup -->
# Plugin System
- [Overview](#overview)
- [Helpful Tips](#helpful-tips)
---
## Overview {#overview}
The [Drupal Bootstrap] base theme handles some very complex theme registry
alterations and annotated plugin discoveries to assist with the organization
and maintenance of its source code.
By leveraging OOP (object oriented programming) with PHP namespacing and
Drupal's autoloading, we garner the ability to include files only when a
theme hook is actually invoked. This allows the base theme to reduce its per
page PHP memory footprint as much as possible. It also allows for easier
maintenance and organization with as much customization this base theme
implements.
The data and display logic of the [Drupal Bootstrap] base theme has been
divided into what we call the "Plugin System". It's nearly identical to the
other plugin system(s) found through out Drupal, with the exception that these
plugins are not bound to the container in any way.
This is, in part, due to the fact that themes are not allowed to participate in
container construction since a theme could vary from page to page (in theory).
So, instead, this base theme implements its own annotated discovery plugins
to leverage the powerful inheritance capabilities of PHP class instances.
All of these plugins can be found in the following directories and are
discussed, in length, below in their respective sub-topics:
- `./themes/bootstrap/src/Plugin/Alter`
- `./themes/bootstrap/src/Plugin/Form`
- `./themes/bootstrap/src/Plugin/Preprocess`
- `./themes/bootstrap/src/Plugin/Prerender`
- `./themes/bootstrap/src/Plugin/Process`
- `./themes/bootstrap/src/Plugin/Provider`
- `./themes/bootstrap/src/Plugin/Setting`
- `./themes/bootstrap/src/Plugin/Update`
While sub-themes are not required to do so, they can easily emulate this same
type of file structure/workflow and take advantage of this base theme's unique
ability and power. All you have to do is make sure you extend from this base
theme's implementation, if it exists.
Rest assured though, there is no need to structure your sub-theme this way. If
you feel more comfortable storing everything in your sub-theme's
`THEMENAME.theme` file and invoking the "normal" Drupal hooks, please feel free
to do so. It will not impact your sub-theme one way or the other.
It is, however, highly recommended that you at least read through this a bit to
gain some understanding on how this base theme structures its PHP and template
components. This will allow you to more easily copy stuff over to your
sub-theme, should the need arise.
## Helpful tips {#helpful-tips}
All plugins, except those that only have static methods, have the active Theme
object available to them: e.g. `$this->theme`. This will allow you to do things
like get a theme setting very, very easily: e.g.
`$this->theme->getSetting('button_size')`.
A helpful primer on Annotation-based plugins can be found at:
https://www.drupal.org/node/1882526
[Drupal Bootstrap]: https://www.drupal.org/project/bootstrap

View File

@@ -0,0 +1,162 @@
<!-- @file Documentation for the @BootstrapSetting annotated plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapSetting
- [Create a plugin](#create)
- [Rebuild the cache](#rebuild)
- [Public Methods](#methods)
## Create a plugin {#create}
We will use `SkipLink` as our first `@BootstrapSetting` plugin to create. In
this example we want our sub-theme to specify a different skip link anchor id
to change in the Theme Settings interface altering the default of
`#main-content`.
Replace all of the following instances of `THEMENAME` with the actual machine
name of your sub-theme.
Create a file at
`./themes/THEMENAME/src/Plugin/Setting/THEMENAME/Accessibility/SkipLink.php`
with the following contents:
```php
<?php
namespace Drupal\THEMENAME\Plugin\Setting\THEMENAME\Accessibility;
use Drupal\bootstrap\Plugin\Setting\SettingBase;
/**
* The "THEMENAME_skip_link_id" theme setting.
*
* @ingroup plugins_setting
*
* @BootstrapSetting(
* id = "THEMENAME_skip_link_id",
* type = "textfield",
* title = @Translation("Anchor ID for the ""skip link"""),
* defaultValue = "main-content",
* description = @Translation("Specify the HTML ID of the element that the accessible-but-hidden ""skip link"" should link to. (<a href="":link"" target=""_blank"">Read more about skip links</a>.)",
* arguments = { ":link" = "https://www.drupal.org/node/467976" }),
* groups = {
* "THEMENAME" = "THEMETITLE",
* "accessibility" = @Translation("Accessibility"),
* },
* )
*/
class SkipLink extends SettingBase {}
?>
```
Helpfully Bootstrap adds a global `theme` variable added to every template
in `Bootstrap::preprocess()`.
This variable can now simply be called in the `html.html.twig` file with the
following contents:
```twig
<a href="#{{ theme.settings.THEMENAME_skip_link_id }}"
class="visually-hidden focusable skip-link">
{{ 'Skip to main content'|t }}
</a>
```
In addition, the `page.html.twig` file will also need to be adjusted for this to
work properly with the new anchor id.
```twig
<a id="{{ theme.settings.THEMENAME_skip_link_id }}"></a>
```
## Rebuild the cache {#rebuild}
Once you have saved, you must rebuild your cache for this new plugin to be
discovered. This must happen anytime you make a change to the actual file name
or the information inside the `@BootstrapSetting` annotation.
To rebuild your cache, navigate to `admin/config/development/performance` and
click the `Clear all caches` button. Or if you prefer, run `drush cr` from the
command line.
Voilà! After this, you should have a fully functional `@BootstrapSetting`
plugin!
## Public Methods {#methods}
Now that we covered how to create a basic `@BootstrapSetting` plugin, we can
discuss how to customize a setting to fulfill a range of requirements.
The `@BootstrapSetting` is implemented through the base class `SettingBase`
which provides a variety of public methods to assist in the customization of
a plugin.
#### SettingBase::alterForm(array &$form, FormStateInterface $form_state, $form_id = NULL)
#### SettingBase::alterFormElement(Element $form, FormStateInterface $form_state, $form_id = NULL)
Both of these methods provide a way for you to alter the setting's form render
array element as well as the form state object.
The first method is similar to any standard `hook_form_alter`.
However, the second method passes the `$form` argument as an instance of the
`Element` utility helper class. This will allow easier manipulation of all the
elements in this method. Using this method is preferable and considered
"Best Practice".
Two useful examples to study:
- CDNProvider::alterFormElement
- RegionWells::alterFormElement
#### SettingBase::drupalSettings()
This method provides a way for you to determine whether a theme setting should
be added to the `drupalSettings` JavaScript variable. Please note that by
default this is set to `FALSE` to prevent any potentially sensitive information
from being leaked.
#### SettingBase::getCacheTags()
This method provides a way for you to add cache tags that when the instantiated
class is modified the associated cache tags will be invalidated. This is
incredibly useful for example with CDNCustomCss::getCacheTags() which returns an
array of `library_info`. So when a CdnProvider::getCacheTags() instantiated
plugin changes the `library_info` cache tag will be invalidated automatically.
It is important to note that the invalidation occurs because the base theme
loads external resources using libraries by altering the libraries it defines
based on settings in LibraryInfo::alter().
#### SettingBase::getGroupElement(Element $form, FormStateInterface $form_state)
This method provides a way for you to retrieve the last group (fieldset /
details form element) the setting is nested in; based on the plugin definition.
#### SettingBase::getGroups()
This method retrieves the associative array of groups; based on the plugin
definition. It's keyed by the group machine name and its value is the
translatable label.
#### SettingBase::getSettingElement(Element $form, FormStateInterface $form_state)
This method provides a way for you to retrieve the form element that was
automatically generated by the base theme for the setting; based on the plugin
definition.
#### SettingBase::submitForm(array &$form, FormStateInterface $form_state)
#### SettingBase::submitFormElement(Element $form, FormStateInterface $form_state)
Both of these methods provide a way for you to alter the submitted values
stored in the form state object before the setting's value is ultimately stored
in configuration by the base theme, which is performed automatically for you.
Two useful example to study:
- RegionWells::submitFormElement
#### SettingBase::validateForm(array &$form, FormStateInterface $form_state)
#### SettingBase::validateFormElement(Element $form, FormStateInterface $form_state)
Both of these methods provide a way for you to validate the setting's form.

View File

@@ -0,0 +1,9 @@
<!-- @file Documentation for the @BootstrapUpdate annotated plugin. -->
<!-- @defgroup -->
<!-- @ingroup -->
# @BootstrapUpdate
This plugin is a little too complex to explain (for now). If you would like to
help expand this documentation, please [create an issue](https://www.drupal.org/node/add/project-issue/bootstrap).
See the existing classes below on examples of how to implement your own.