Commit fd18ce46 authored by markcarver's avatar markcarver

Issue #3031415 by markcarver: Overhaul CDN Providers API

parent 6aa33213
...@@ -69,48 +69,56 @@ function _drush_bootstrap_generate_docs_settings(Theme $bootstrap) { ...@@ -69,48 +69,56 @@ function _drush_bootstrap_generate_docs_settings(Theme $bootstrap) {
$output[] = '```'; $output[] = '```';
// Determine the groups. // Determine the groups.
$groups = []; $groups = [
'general' => [],
'components' => [],
'javascript' => [],
'cdn' => [],
'advanced' => [],
];
foreach ($bootstrap->getSettingPlugin() as $setting) { foreach ($bootstrap->getSettingPlugin() as $setting) {
// Only get the first two groups (we don't need 3rd, or more, levels). // Only get the first two groups (we don't need 3rd, or more, levels).
$_groups = array_slice($setting->getGroups(), 0, 2, FALSE); $_groups = array_filter(array_slice($setting->getGroups(), 0, 2, FALSE));
if (!$_groups) { if (!$_groups) {
continue; continue;
} }
$groups[implode(' > ', $_groups)][] = $setting->getPluginDefinition(); $groups[array_keys($_groups)[0]][implode(' > ', $_groups)][] = $setting->getPluginDefinition();
} }
// Generate a table of each group's settings. // Generate a table of each group's settings.
foreach ($groups as $group => $settings) { foreach ($groups as $subgroups) {
$output[] = ''; foreach ($subgroups as $group => $settings) {
$output[] = '---'; $output[] = '';
$output[] = ''; $output[] = '---';
$output[] = "### $group"; $output[] = '';
$output[] = ''; $output[] = "### $group";
$output[] = '<table class="table table-striped table-responsive">'; $output[] = '';
$output[] = ' <thead>'; $output[] = '<table class="table table-striped table-responsive">';
$output[] = ' <tr>'; $output[] = ' <thead>';
$output[] = ' <th class="col-xs-3">Setting name</th>'; $output[] = ' <tr>';
$output[] = ' <th>Description and default value</th>'; $output[] = ' <th class="col-xs-3">Setting name</th>';
$output[] = ' </tr>'; $output[] = ' <th>Description and default value</th>';
$output[] = ' </thead>'; $output[] = ' </tr>';
$output[] = ' <tbody>'; $output[] = ' </thead>';
foreach ($settings as $definition) { $output[] = ' <tbody>';
$output[] = ' <tr>'; foreach ($settings as $definition) {
$output[] = ' <td class="col-xs-3">'; $output[] = ' <tr>';
$output[] = $definition['id']; $output[] = ' <td class="col-xs-3">';
$output[] = ' </td>'; $output[] = $definition['id'];
$output[] = ' <td>'; $output[] = ' </td>';
$output[] = ' <div class="help-block">'; $output[] = ' <td>';
$output[] = str_replace('&quote;', '"', wordwrap($definition['description'])); $output[] = ' <div class="help-block">';
$output[] = ' </div>'; $output[] = str_replace('&quote;', '"', wordwrap($definition['description']));
$output[] = ' <pre class=" language-yaml"><code>'; $output[] = ' </div>';
$output[] = wordwrap(Yaml::encode([$definition['id'] => $definition['defaultValue']])); $output[] = ' <pre class=" language-yaml"><code>';
$output[] = '</code></pre>'; $output[] = wordwrap(Yaml::encode([$definition['id'] => $definition['defaultValue']]));
$output[] = ' </td>'; $output[] = '</code></pre>';
$output[] = ' </tr>'; $output[] = ' </td>';
$output[] = ' </tr>';
}
$output[] = ' </tbody>';
$output[] = '</table>';
} }
$output[] = ' </tbody>';
$output[] = '</table>';
} }
// Ensure we have link references at the bottom. // Ensure we have link references at the bottom.
......
...@@ -671,8 +671,8 @@ function _bootstrap_remove_class($class, array &$element, $property = 'attribute ...@@ -671,8 +671,8 @@ function _bootstrap_remove_class($class, array &$element, $property = 'attribute
* @endcode * @endcode
* *
* @see \Drupal\bootstrap\Plugin\ProviderManager * @see \Drupal\bootstrap\Plugin\ProviderManager
* @see \Drupal\bootstrap\Theme::getProviders() * @see \Drupal\bootstrap\Theme::getCdnProviders()
* @see \Drupal\bootstrap\Theme::getProvider() * @see \Drupal\bootstrap\Theme::getCdnProvider()
*/ */
function bootstrap_cdn_provider($provider = NULL, $reset = FALSE) { function bootstrap_cdn_provider($provider = NULL, $reset = FALSE) {
Bootstrap::deprecated(); Bootstrap::deprecated();
...@@ -746,7 +746,20 @@ function bootstrap_element_smart_description(array &$element, array &$target = N ...@@ -746,7 +746,20 @@ function bootstrap_element_smart_description(array &$element, array &$target = N
* *
* // After. * // After.
* use Drupal\bootstrap\Plugin\ProviderManager; * use Drupal\bootstrap\Plugin\ProviderManager;
* $assets = ProviderManager::load($theme, $provider)->getAssets($type); * $original_type = $type;
* $config = \Drupal::config('system.performance');
* $cdnAssets = ProviderManager::load($theme, $provider)->getCdnAssets();
* $data = [];
* $types = !isset($type) ? ['css', 'js'] : (array) $type;
* foreach ($types as $type) {
* if ($config->get("$type.preprocess") && !empty($cdnAssets['min'][$type])) {
* $data[$type] = $cdnAssets['min'][$type];
* }
* elseif (!empty($data[$type])) {
* $data[$type] = $cdnAssets[$type];
* }
* }
* $assets = is_string($original_type) ? $data[$original_type] : $data;
* @endcode * @endcode
* *
* @see \Drupal\bootstrap\Plugin\Provider\Custom::getAssets() * @see \Drupal\bootstrap\Plugin\Provider\Custom::getAssets()
...@@ -757,7 +770,20 @@ function bootstrap_element_smart_description(array &$element, array &$target = N ...@@ -757,7 +770,20 @@ function bootstrap_element_smart_description(array &$element, array &$target = N
*/ */
function bootstrap_get_cdn_assets($type = NULL, $provider = NULL, $theme = NULL) { function bootstrap_get_cdn_assets($type = NULL, $provider = NULL, $theme = NULL) {
Bootstrap::deprecated(); Bootstrap::deprecated();
return ProviderManager::load($theme, $provider)->getAssets($type); $original_type = $type;
$assets = [];
$config = \Drupal::config('system.performance');
$cdnAssets = ProviderManager::load($theme, $provider)->getCdnAssets();
$types = !isset($type) ? ['css', 'js'] : (array) $type;
foreach ($types as $type) {
if ($config->get("$type.preprocess") && !empty($cdnAssets['min'][$type])) {
$assets[$type] = $cdnAssets['min'][$type];
}
elseif (!empty($data[$type])) {
$assets[$type] = $cdnAssets[$type];
}
}
return is_string($original_type) ? $assets[$original_type] : $assets;
} }
/** /**
......
...@@ -18,11 +18,24 @@ can override CSS, templates, and theme processing. ...@@ -18,11 +18,24 @@ can override CSS, templates, and theme processing.
#### Choose a Starterkit {#starterkit} #### Choose a Starterkit {#starterkit}
- @link sub_theming_cdn CDN Starterkit @endlink - uses the "out-of-the-box" - @link sub_theming_cdn CDN Starterkit @endlink - uses the "out-of-the-box"
CSS and JavaScript files served by the [jsDelivr CDN]. CSS and JavaScript files served by a CDN Provider (like [jsDelivr]).
- @link sub_theming_less Less Starterkit @endlink - uses the - @link sub_theming_less Less Starterkit @endlink - uses the
[Bootstrap Framework] [Less] source files and a local [Less] preprocessor. [Bootstrap Framework] [Less] source files and a local [Less] preprocessor.
- @link sub_theming_sass Sass Starterkit @endlink - uses the - @link sub_theming_sass Sass Starterkit @endlink - uses the
[Bootstrap Framework] [Sass] source files and a local [Sass] preprocessor. [Bootstrap Framework] [Sass] source files and a local [Sass] preprocessor.
{.alert.alert-info} **Note** Using the "CDN Starterkit" is the preferred method
for loading Bootstrap CSS and JS on simpler sites that do not use a site-wide
CDN. Using a CDN Provider for loading Bootstrap, however, does mean that it
depends on a third-party service. There is no obligation or commitment made by
this project or these third-party CDN services that guarantees up-time or
quality of service. If you need to customize Bootstrap, you must choose one of
the Less or Sass Starterkits, compile the source code locally, and disable the
"CDN Provider" theme setting. Alternatively, you may also choose to enable a
site-wide CDN implementation for performance reasons.
{.alert.alert-warning} **Warning** All locally compiled versions of Bootstrap
will be superseded by any enabled "CDN Provider"; **do not use both**.
Once you've selected one of the above starterkits, here's how to install it: Once you've selected one of the above starterkits, here's how to install it:
...@@ -62,6 +75,6 @@ to customize. ...@@ -62,6 +75,6 @@ to customize.
[Drupal Bootstrap]: https://www.drupal.org/project/bootstrap [Drupal Bootstrap]: https://www.drupal.org/project/bootstrap
[Bootstrap Framework]: https://getbootstrap.com/docs/3.4/ [Bootstrap Framework]: https://getbootstrap.com/docs/3.4/
[jsDelivr CDN]: http://www.jsdelivr.com [jsDelivr]: http://www.jsdelivr.com
[Less]: http://lesscss.org [Less]: http://lesscss.org
[Sass]: http://sass-lang.com [Sass]: http://sass-lang.com
This diff is collapsed.
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
var $context = $(context); var $context = $(context);
// General. // General.
$context.find('#edit-general').drupalSetSummary(function () { $context.find('[data-drupal-selector="edit-general"]').drupalSetSummary(function () {
var summary = []; var summary = [];
// Buttons. // Buttons.
var size = $context.find('select[name="button_size"] :selected'); var size = $context.find('select[name="button_size"] :selected');
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
}); });
// Components. // Components.
$context.find('#edit-components').drupalSetSummary(function () { $context.find('[data-drupal-selector="edit-components"]').drupalSetSummary(function () {
var summary = []; var summary = [];
// Breadcrumbs. // Breadcrumbs.
var breadcrumb = parseInt($context.find('select[name="breadcrumb"]').val(), 10); var breadcrumb = parseInt($context.find('select[name="breadcrumb"]').val(), 10);
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
}); });
}); });
$context.find('#edit-javascript').drupalSetSummary(function () { $context.find('[data-drupal-selector="edit-javascript"]').drupalSetSummary(function () {
var summary = []; var summary = [];
if ($context.find('input[name="modal_enabled"]').is(':checked')) { if ($context.find('input[name="modal_enabled"]').is(':checked')) {
if ($jQueryUiBridge.is(':checked')) { if ($jQueryUiBridge.is(':checked')) {
...@@ -131,13 +131,13 @@ ...@@ -131,13 +131,13 @@
return summary.join(', '); return summary.join(', ');
}); });
// Advanced. // CDN.
$context.find('#edit-advanced').drupalSetSummary(function () { $context.find('[data-drupal-selector="edit-cdn"]').drupalSetSummary(function () {
var summary = []; var summary = [];
var $cdnProvider = $context.find('select[name="cdn_provider"] :selected'); var $cdnProvider = $context.find('select[name="cdn_provider"] :selected');
var cdnProvider = $cdnProvider.val(); var cdnProvider = $cdnProvider.val();
if ($cdnProvider.length && cdnProvider.length) { if ($cdnProvider.length) {
summary.push(Drupal.t('CDN provider: %provider', { '%provider': $cdnProvider.text() })); summary.push(Drupal.t('Provider: %provider', { '%provider': $cdnProvider.text() }));
// jsDelivr CDN. // jsDelivr CDN.
if (cdnProvider === 'jsdelivr') { if (cdnProvider === 'jsdelivr') {
...@@ -153,6 +153,21 @@ ...@@ -153,6 +153,21 @@
} }
return summary.join(', '); return summary.join(', ');
}); });
// Advanced.
$context.find('[data-drupal-selector="edit-advanced"]').drupalSetSummary(function () {
var summary = [];
var deprecations = [];
if ($context.find('input[name="include_deprecated"]').is(':checked')) {
deprecations.push(Drupal.t('Included'));
}
deprecations.push($context.find('input[name="suppress_deprecated_warnings"]').is(':checked') ? Drupal.t('Warnings Suppressed') : Drupal.t('Warnings Shown'));
summary.push(Drupal.t('Deprecations: @value', {
'@value': deprecations.join(', '),
}));
return summary.join(', ');
});
} }
}; };
......
...@@ -11,8 +11,8 @@ use Drupal\Component\Annotation\Plugin; ...@@ -11,8 +11,8 @@ use Drupal\Component\Annotation\Plugin;
* *
* @see \Drupal\bootstrap\Plugin\ProviderInterface * @see \Drupal\bootstrap\Plugin\ProviderInterface
* @see \Drupal\bootstrap\Plugin\ProviderManager * @see \Drupal\bootstrap\Plugin\ProviderManager
* @see \Drupal\bootstrap\Theme::getProviders() * @see \Drupal\bootstrap\Theme::getCdnProviders()
* @see \Drupal\bootstrap\Theme::getProvider() * @see \Drupal\bootstrap\Theme::getCdnProvider()
* @see plugin_api * @see plugin_api
* *
* @Annotation * @Annotation
......
...@@ -5,13 +5,19 @@ namespace Drupal\bootstrap; ...@@ -5,13 +5,19 @@ namespace Drupal\bootstrap;
use Drupal\bootstrap\Plugin\AlterManager; use Drupal\bootstrap\Plugin\AlterManager;
use Drupal\bootstrap\Plugin\FormManager; use Drupal\bootstrap\Plugin\FormManager;
use Drupal\bootstrap\Plugin\PreprocessManager; use Drupal\bootstrap\Plugin\PreprocessManager;
use Drupal\bootstrap\Utility\Crypt;
use Drupal\bootstrap\Utility\Element; use Drupal\bootstrap\Utility\Element;
use Drupal\bootstrap\Utility\Unicode; use Drupal\bootstrap\Utility\Unicode;
use Drupal\Component\Utility\Html; use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Extension\ThemeHandlerInterface; use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup; use Drupal\Core\Render\Markup;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
/** /**
* The primary class for the Drupal Bootstrap base theme. * The primary class for the Drupal Bootstrap base theme.
...@@ -92,6 +98,13 @@ class Bootstrap { ...@@ -92,6 +98,13 @@ class Bootstrap {
*/ */
const PROJECT_DOCUMENTATION = 'https://drupal-bootstrap.org'; const PROJECT_DOCUMENTATION = 'https://drupal-bootstrap.org';
/**
* The project API search URL.
*
* @var string
*/
const PROJECT_API_SEARCH_URL = self::PROJECT_DOCUMENTATION . '/api/bootstrap/' . self::PROJECT_BRANCH . '/search/@query';
/** /**
* The Drupal Bootstrap project page. * The Drupal Bootstrap project page.
* *
...@@ -261,11 +274,13 @@ class Bootstrap { ...@@ -261,11 +274,13 @@ class Bootstrap {
* @param string $query * @param string $query
* The query to search for. * The query to search for.
* *
* @return string * @return \Drupal\Component\Render\FormattableMarkup
* The complete URL to the documentation site. * The complete URL to the documentation site.
*/ */
public static function apiSearchUrl($query = '') { public static function apiSearchUrl($query = '') {
return self::PROJECT_DOCUMENTATION . '/api/bootstrap/' . self::PROJECT_BRANCH . '/search/' . Html::escape($query); return new FormattableMarkup(self::PROJECT_API_SEARCH_URL, [
'@query' => $query,
]);
} }
/** /**
...@@ -362,6 +377,7 @@ class Bootstrap { ...@@ -362,6 +377,7 @@ class Bootstrap {
// Danger class. // Danger class.
t('Delete')->render() => 'danger', t('Delete')->render() => 'danger',
t('Remove')->render() => 'danger', t('Remove')->render() => 'danger',
t('Reset')->render() => 'danger',
t('Uninstall')->render() => 'danger', t('Uninstall')->render() => 'danger',
// Success class. // Success class.
...@@ -417,29 +433,40 @@ class Bootstrap { ...@@ -417,29 +433,40 @@ class Bootstrap {
/** /**
* Logs and displays a warning about a deprecated function/method being used. * Logs and displays a warning about a deprecated function/method being used.
* *
* @param string $caller
* Optional. The function or Class::method that should be shown as
* deprecated. If not set, it will be extrapolated automatically from
* the backtrace. This is primarily used when this method is being invoked
* from inside another method that isn't technically deprecated but has to
* support deprecated functionality.
* @param bool $show_message * @param bool $show_message
* Flag indicating whether to show a message to the user. If TRUE, it will * Flag indicating whether to show a message to the user. If TRUE, it will
* force showing the message. If FALSE, it will only log the message. If * force showing the message. If FALSE, it will only log the message. If
* not set, showing the message will be determined by whether the current * not set, showing the message will be determined by whether the current
* theme has suppressed showing deprecated warnings. * theme has suppressed showing deprecated warnings.
*/ */
public static function deprecated($show_message = NULL) { public static function deprecated($caller = NULL, $show_message = NULL) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// Extrapolate the caller. // Extrapolate the caller.
$caller = $backtrace[1]; if (!isset($caller) && !empty($backtrace[1]) && ($info = $backtrace[1])) {
$class = ''; $caller = (!empty($info['class']) ? $info['class'] . '::' : '') . $info['function'];
if (isset($caller['class'])) { }
$parts = explode('\\', $caller['class']);
$class = array_pop($parts) . '::'; // Remove class namespace.
$method = FALSE;
if (is_string($caller) && strpos($caller, '::') !== FALSE && ($parts = explode('\\', $caller))) {
$method = TRUE;
$caller = array_pop($parts);
} }
$message = t('The following function(s) or method(s) have been deprecated, please check the logs for a more detailed backtrace on where these are being invoked. Click on the function or method link to search the documentation site for a possible replacement or solution: <a href=":url" target="_blank">@title</a>', [ $message = t('The following @type has been deprecated: <a href=":url" target="_blank">@title</a>. Please check the logs for a more detailed backtrace on where it is being invoked.', [
':url' => self::apiSearchUrl($class . $caller['function']), '@type' => $method ? 'method' : 'function',
'@title' => ($class ? $caller['class'] . '::' : '') . $caller['function'] . '()', ':url' => static::apiSearchUrl($caller),
'@title' => $caller,
]); ]);
if ($show_message || (!isset($show_message) && !self::getTheme()->getSetting('suppress_deprecated_warnings', FALSE))) { if ($show_message || (!isset($show_message) && !static::getTheme()->getSetting('suppress_deprecated_warnings', FALSE))) {
drupal_set_message($message, 'warning'); drupal_set_message($message, 'warning');
} }
...@@ -686,6 +713,7 @@ class Bootstrap { ...@@ -686,6 +713,7 @@ class Bootstrap {
t('Cancel')->render() => 'remove', t('Cancel')->render() => 'remove',
t('Delete')->render() => 'trash', t('Delete')->render() => 'trash',
t('Remove')->render() => 'trash', t('Remove')->render() => 'trash',
t('Reset')->render() => 'trash',
t('Search')->render() => 'search', t('Search')->render() => 'search',
t('Upload')->render() => 'upload', t('Upload')->render() => 'upload',
t('Preview')->render() => 'eye-open', t('Preview')->render() => 'eye-open',
...@@ -1184,6 +1212,81 @@ class Bootstrap { ...@@ -1184,6 +1212,81 @@ class Bootstrap {
} }
} }
/**
* Retrieves a response from a URI, using cached response if available.
*
* @param string $uri
* The URI to retrieve JSON from.
* @param array $options
* The options to pass to the HTTP client.
* @param \Exception|null $exception
* The exception thrown if there was an error, passed by reference.
*
* @return \Symfony\Component\HttpFoundation\Response
* A Response object.
*/
public static function cachedRequest($uri, array $options = [], &$exception = NULL) {
$options += [
'method' => 'GET',
'headers' => [
'User-Agent' => 'Drupal Bootstrap ' . static::PROJECT_BRANCH . ' (' . static::PROJECT_PAGE . ')',
],
];
$cache = \Drupal::keyValueExpirable('theme:' . static::getTheme()->getName() . ':http');
$key = 'request-' . Crypt::hashBase64(serialize(['uri' => $uri] + $options));
$response = $cache->get($key);
if (!isset($response)) {
/** @var \GuzzleHttp\Client $client */
$client = \Drupal::service('http_client_factory')->fromOptions($options);
$request = new Request($options['method'], $uri, $options['headers']);
try {
$r = $client->send($request, $options);
// In order to actually cache the response, the contents must be
// extracted from the stream before it's stored in the database.
$response = new Response($r->getBody(TRUE)->getContents(), $r->getStatusCode(), $r->getHeaders());
}
catch (GuzzleException $e) {
$exception = $e;
$response = new Response($e->getCode() ?: 500, [], $e->getMessage());
}
catch (\Exception $e) {
$exception = $e;
$response = new Response($e->getCode() ?: 500, [], $e->getMessage());
}
// Only cache if a maximum age has been detected.
if ($response->getStatusCode() == 200 && ($maxAge = $response->getMaxAge())) {
$cache->setWithExpire($key, $response, $maxAge);
}
}
return $response;
}
/**
* Retrieves JSON from a URI.
*
* @param string $uri
* The URI to retrieve JSON from.
* @param array $options
* The options to pass to the HTTP client.
* @param \Exception|null $exception
* The exception thrown if there was an error, passed by reference.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* A JsonResponse object.
*/
public static function requestJson($uri, array $options = [], &$exception = NULL) {
$r = static::cachedRequest($uri, $options, $exception);
$json = Json::decode($r->getContent() ?: '[]') ?: [];
$response = new JsonResponse($json, $r->getStatusCode(), $r->headers->all());
$response->json = $json;
return $response;
}
/** /**
* Ensures a value is typecast to a string, rendering an array if necessary. * Ensures a value is typecast to a string, rendering an array if necessary.
* *
......
...@@ -36,8 +36,8 @@ class LibraryInfo extends PluginBase implements AlterInterface { ...@@ -36,8 +36,8 @@ class LibraryInfo extends PluginBase implements AlterInterface {
} }
// Alter the framework library based on currently set CDN provider. // Alter the framework library based on currently set CDN provider.
if ($provider = $this->theme->getProvider()) { if ($cdnProvider = $this->theme->getCdnProvider()) {
$provider->alterFrameworkLibrary($libraries['framework']); $cdnProvider->alterFrameworkLibrary($libraries['framework']);
} }
} }
// Core replacements. // Core replacements.
......
...@@ -72,6 +72,7 @@ class SystemThemeSettings extends FormBase implements FormInterface { ...@@ -72,6 +72,7 @@ class SystemThemeSettings extends FormBase implements FormInterface {
'general' => t('General'), 'general' => t('General'),
'components' => t('Components'), 'components' => t('Components'),
'javascript' => t('JavaScript'), 'javascript' => t('JavaScript'),
'cdn' => t('CDN'),
'advanced' => t('Advanced'), 'advanced' => t('Advanced'),
]; ];
foreach ($groups as $group => $title) { foreach ($groups as $group => $title) {
...@@ -80,9 +81,66 @@ class SystemThemeSettings extends FormBase implements FormInterface { ...@@ -80,9 +81,66 @@ class SystemThemeSettings extends FormBase implements FormInterface {
'#title' => $title, '#title' => $title,
'#group' => 'bootstrap', '#group' => 'bootstrap',
]; ];
// Show a button to reset cached HTTP requests.
if ($group === 'advanced') {
$cache = \Drupal::keyValueExpirable('theme:' . $this->theme->getName() . ':http');
$count = count($cache->getAll());
$form[$group]['reset_http_request_cache'] = [
'#type' => 'item',
'#title' => $this->t('Cached HTTP requests: @count', ['@count' => $count]),
'#weight' => 100,
'#smart_description' => FALSE,
'#description' => $this->t('All HTTP requests initiated through the base-theme are cached if there is a "max-age" response header present. These cached requests will persist through cache rebuilds and only expire once the the "max-age" has been reached. If you believe a CDN Provider is not retrieving data properly, you can manually reset this cache here.'),
'#description_display' => 'before',
'#prefix' => '<div id="reset-http-request-cache">',
'#suffix' => '</div>',
];
$form[$group]['reset_http_request_cache']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Reset HTTP Request Cache'),
'#prefix' => '<div>',
'#suffix' => '</div>',
'#submit' => [
[get_class($this), 'submitResetHttpRequestCache'],
],
'#ajax' => [
'callback' => [get_class($this), 'ajaxResetHttpRequestCache'],
'wrapper' => 'reset-http-request-cache',
],
];
}
} }
} }
/**
* Submit callback for resetting the cached HTTP requests.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public static function submitResetHttpRequestCache(array $form, FormStateInterface $form_state) {
$form_state->setRebuild();
$theme = SystemThemeSettings::getTheme(Element::create($form), $form_state);
$cache = \Drupal::keyValueExpirable('theme:' . $theme->getName() . ':http');
$cache->deleteAll();
}
/**
* AJAX callback for reloading the cached HTTP request markup.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public static function ajaxResetHttpRequestCache(array $form, FormStateInterface $form_state) {
return $form['advanced']['reset_http_request_cache'];
}
/** /**
* Retrieves the currently selected theme on the settings form. * Retrieves the currently selected theme on the settings form.
* *
......
...@@ -26,8 +26,8 @@ class Broken extends PluginBase implements ProviderInterface { ...@@ -26,8 +26,8 @@ class Broken extends PluginBase implements ProviderInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getAssets($types = NULL) { public function getCacheTtl() {
return []; return static::CACHE_TTL;
} }
/** /**
...@@ -37,6 +37,20 @@ class Broken extends PluginBase implements ProviderInterface { ...@@ -37,6 +37,20 @@ class Broken extends PluginBase implements ProviderInterface {
return []; return [];
} }
/**
* {@inheritdoc}
*/
public function getCdnExceptions($reset = TRUE) {
return [];
}
/**
* {@inheritdoc}
*/
public function getCdnTheme() {
return NULL;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -44,6 +58,13 @@ class Broken extends PluginBase implements ProviderInterface { ...@@ -44,6 +58,13 @@ class Broken extends PluginBase implements ProviderInterface {
return []; return [];
} }
/**
* {@inheritdoc}
*/
public function getCdnVersion() {
return NULL;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -68,28 +89,40 @@ class Broken extends PluginBase implements ProviderInterface { ...@@ -68,28 +89,40 @@ class Broken extends PluginBase implements ProviderInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getCdnTheme() { public function resetCache() {