summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormarkcarver2018-12-17 23:26:02 (GMT)
committerMark Carver2018-12-17 23:26:02 (GMT)
commitfdc012705655004c92c4b543f508d468222cd575 (patch)
tree0ef3491430b36f384e3b1ee8a760ef417b1a31ac
parentd33fe477180da110fc07c111d5b02b91172b810c (diff)
Issue #2657138 by markcarver: Refactor providers to utilize new data.jsdelivr.com API
-rw-r--r--src/Plugin/Provider/JsDelivr.php87
-rw-r--r--src/Plugin/Provider/ProviderBase.php67
2 files changed, 121 insertions, 33 deletions
diff --git a/src/Plugin/Provider/JsDelivr.php b/src/Plugin/Provider/JsDelivr.php
index 2c0e7a5..9c288e9 100644
--- a/src/Plugin/Provider/JsDelivr.php
+++ b/src/Plugin/Provider/JsDelivr.php
@@ -13,7 +13,6 @@ use Drupal\Component\Utility\NestedArray;
* @BootstrapProvider(
* id = "jsdelivr",
* label = @Translation("jsDelivr"),
- * api = "https://api.jsdelivr.com/v1/bootstrap/libraries",
* themes = { },
* versions = { },
* )
@@ -66,10 +65,10 @@ class JsDelivr extends ProviderBase {
$themes[$theme]['title'] = $title;
if ($min) {
- $themes[$theme]['min'][$filetype][] = "$base_url/$path/bootstrap{$matches[2]}$min.$filetype";
+ $themes[$theme]['min'][$filetype][] = "$base_url/" . ltrim($file, '/');
}
else {
- $themes[$theme][$filetype][] = "$base_url/$path/bootstrap{$matches[2]}$min.$filetype";
+ $themes[$theme][$filetype][] = "$base_url/" . ltrim($file, '/');
}
}
}
@@ -92,6 +91,61 @@ class JsDelivr extends ProviderBase {
/**
* {@inheritdoc}
+ *
+ * Due to the complex nature of how the existing Provider APIs work and the
+ * changes made to the JsDelivr API, a new method was needed to extract the
+ * appropriate JSON and URLs to convert the new API data structure into the
+ * previous API data structure.
+ *
+ * @see https://www.drupal.org/project/bootstrap/issues/2657138
+ */
+ public function processDefinition(array &$definition, $plugin_id) {
+ $json = [];
+ foreach (['bootstrap', 'bootswatch'] as $package) {
+ $data = ['name' => $package, 'assets' => []];
+ $latest = '0.0.0';
+ $versions = [];
+ $packageJson = $this->requestJson("https://data.jsdelivr.com/v1/package/npm/$package") + ['versions' => []];
+ foreach ($packageJson['versions'] as $key => $version) {
+ // Skip irrelevant versions.
+ if (!preg_match('/^' . substr(Bootstrap::FRAMEWORK_VERSION, 0, 1) . '\.\d+\.\d+$/', $version)) {
+ continue;
+ }
+ $versionJson = $this->requestJson("https://data.jsdelivr.com/v1/package/npm/$package@$version/flat");
+
+ // Skip empty files.
+ if (empty($versionJson['files'])) {
+ continue;
+ }
+
+ $versions[] = $version;
+ if (version_compare($latest, $version) === -1) {
+ $latest = $version;
+ }
+
+ $asset = ['files' => [], 'version' => $version];
+ foreach ($versionJson['files'] as $file) {
+ // Skip old bootswatch file structure.
+ if ($package === 'bootswatch' && preg_match('`^/2|/bower_components`', $file['name'], $matches)) {
+ continue;
+ }
+ preg_match('`([^/]*)/bootstrap(-theme)?(\.min)?\.(js|css)$`', $file['name'], $matches);
+ if (!empty($matches[1]) && !empty($matches[4])) {
+ $asset['files'][] = $file['name'];
+ }
+ }
+ $data['assets'][] = $asset;
+ }
+ $data['lastversion'] = $latest;
+ $data['versions'] = $versions;
+ $json[] = $data;
+ }
+
+ $this->processApi($json, $definition);
+ }
+
+ /**
+ * {@inheritdoc}
*/
public function processApi(array $json, array &$definition) {
$definition['description'] = t('<p><a href=":jsdelivr" target="_blank">jsDelivr</a> is a free multi-CDN infrastructure that uses <a href=":maxcdn" target="_blank">MaxCDN</a>, <a href=":cloudflare" target="_blank">Cloudflare</a> and many others to combine their powers for the good of the open source community... <a href=":jsdelivr_about" target="_blank">read more</a></p>', [
@@ -101,18 +155,11 @@ class JsDelivr extends ProviderBase {
':cloudflare' => 'https://www.cloudflare.com',
]);
- // Expected library names from jsDelivr API v1. Must use "twitter-bootstrap"
- // instead of "bootstrap" (which is just a directory alias).
- // @see https://www.drupal.org/node/2504343
- // @see https://github.com/jsdelivr/api/issues/94
- $bootstrap = 'twitter-bootstrap';
- $bootswatch = 'bootswatch';
-
// Extract the raw asset files from the JSON data for each framework.
$libraries = [];
if ($json) {
foreach ($json as $data) {
- if ($data['name'] === $bootstrap || $data['name'] === $bootswatch) {
+ if ($data['name'] === 'bootstrap' || $data['name'] === 'bootswatch') {
foreach ($data['assets'] as $asset) {
if (preg_match('/^' . substr(Bootstrap::FRAMEWORK_VERSION, 0, 1) . '\.\d\.\d$/', $asset['version'])) {
$libraries[$data['name']][$asset['version']] = $asset['files'];
@@ -123,17 +170,17 @@ class JsDelivr extends ProviderBase {
}
// If the main bootstrap library could not be found, then provide defaults.
- if (!isset($libraries[$bootstrap])) {
+ if (!isset($libraries['bootstrap'])) {
$definition['error'] = TRUE;
$definition['versions'][Bootstrap::FRAMEWORK_VERSION] = Bootstrap::FRAMEWORK_VERSION;
$definition['themes'][Bootstrap::FRAMEWORK_VERSION] = [
'bootstrap' => [
'title' => (string) t('Bootstrap'),
- 'css' => ['//cdn.jsdelivr.net/bootstrap/' . Bootstrap::FRAMEWORK_VERSION . '/css/bootstrap.css'],
- 'js' => ['//cdn.jsdelivr.net/bootstrap/' . Bootstrap::FRAMEWORK_VERSION . '/js/bootstrap.js'],
+ 'css' => ['https://cdn.jsdelivr.net/npm/bootstrap@' . Bootstrap::FRAMEWORK_VERSION . '/dist/css/bootstrap.css'],
+ 'js' => ['https://cdn.jsdelivr.net/npm/bootstrap@' . Bootstrap::FRAMEWORK_VERSION . '/dist/js/bootstrap.js'],
'min' => [
- 'css' => ['//cdn.jsdelivr.net/bootstrap/' . Bootstrap::FRAMEWORK_VERSION . '/css/bootstrap.min.css'],
- 'js' => ['//cdn.jsdelivr.net/bootstrap/' . Bootstrap::FRAMEWORK_VERSION . '/js/bootstrap.min.js'],
+ 'css' => ['https://cdn.jsdelivr.net/npm/bootstrap@' . Bootstrap::FRAMEWORK_VERSION . '/dist/css/bootstrap.min.css'],
+ 'js' => ['https://cdn.jsdelivr.net/npm/bootstrap@' . Bootstrap::FRAMEWORK_VERSION . '/dist/js/bootstrap.min.js'],
],
],
];
@@ -141,7 +188,7 @@ class JsDelivr extends ProviderBase {
}
// Populate the provider array with the versions and themes available.
- foreach (array_keys($libraries[$bootstrap]) as $version) {
+ foreach (array_keys($libraries['bootstrap']) as $version) {
$definition['versions'][$version] = $version;
if (!isset($definition['themes'][$version])) {
@@ -149,11 +196,11 @@ class JsDelivr extends ProviderBase {
}
// Extract Bootstrap themes.
- $definition['themes'][$version] = NestedArray::mergeDeep($definition['themes'][$version], $this->extractThemes($libraries[$bootstrap][$version], "//cdn.jsdelivr.net/bootstrap/$version"));
+ $definition['themes'][$version] = NestedArray::mergeDeep($definition['themes'][$version], $this->extractThemes($libraries['bootstrap'][$version], "https://cdn.jsdelivr.net/npm/bootstrap@$version"));
// Extract Bootswatch themes.
- if (isset($libraries[$bootswatch][$version])) {
- $definition['themes'][$version] = NestedArray::mergeDeep($definition['themes'][$version], $this->extractThemes($libraries[$bootswatch][$version], "//cdn.jsdelivr.net/bootswatch/$version"));
+ if (isset($libraries['bootswatch'][$version])) {
+ $definition['themes'][$version] = NestedArray::mergeDeep($definition['themes'][$version], $this->extractThemes($libraries['bootswatch'][$version], "https://cdn.jsdelivr.net/npm/bootswatch@$version"));
}
}
diff --git a/src/Plugin/Provider/ProviderBase.php b/src/Plugin/Provider/ProviderBase.php
index c8b3032..10ddeb3 100644
--- a/src/Plugin/Provider/ProviderBase.php
+++ b/src/Plugin/Provider/ProviderBase.php
@@ -5,9 +5,8 @@ namespace Drupal\bootstrap\Plugin\Provider;
use Drupal\bootstrap\Plugin\PluginBase;
use Drupal\bootstrap\Plugin\ProviderManager;
use Drupal\Component\Serialization\Json;
-use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
-use GuzzleHttp\Psr7\Response;
/**
* CDN provider base class.
@@ -138,22 +137,24 @@ class ProviderBase extends PluginBase implements ProviderInterface {
// Use manually imported API data, if it exists.
if (file_exists("$provider_path/$plugin_id.json") && ($imported_data = file_get_contents("$provider_path/$plugin_id.json"))) {
$definition['imported'] = TRUE;
- $response = new Response(200, [], $imported_data);
+ try {
+ $json = Json::decode($imported_data);
+ }
+ catch (\Exception $e) {
+ // Intentionally left blank.
+ }
}
// Otherwise, attempt to request API data if the provider has specified
// an "api" URL to use.
else {
- $client = \Drupal::httpClient();
- $request = new Request('GET', $api);
- try {
- $response = $client->send($request);
- }
- catch (RequestException $e) {
- $response = new Response(400);
- }
+ $json = $this->requestJson($api);
+ }
+
+ if (!isset($json)) {
+ $json = [];
+ $definition['error'] = TRUE;
}
- $contents = $response->getBody(TRUE)->getContents();
- $json = Json::decode($contents) ?: [];
+
$this->processApi($json, $definition);
}
}
@@ -163,4 +164,44 @@ class ProviderBase extends PluginBase implements ProviderInterface {
*/
public function processApi(array $json, array &$definition) {}
+ /**
+ * 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.
+ *
+ * @return array|null
+ * The requested JSON array or NULL if an error occurred.
+ */
+ protected function requestJson($uri, array $options = []) {
+ $json = NULL;
+
+ $options += [
+ 'method' => 'GET',
+ 'headers' => [
+ 'User-Agent' => 'Drupal Bootstrap (https://www.drupal.org/project/bootstrap)',
+ ],
+ ];
+
+ /** @var \GuzzleHttp\Client $client */
+ $client = \Drupal::service('http_client_factory')->fromOptions($options);
+ $request = new Request($options['method'], $uri);
+ try {
+ $response = $client->send($request, $options);
+ if ($response->getStatusCode() == 200) {
+ $contents = $response->getBody(TRUE)->getContents();
+ $json = Json::decode($contents);
+ }
+ }
+ catch (GuzzleException $e) {
+ // Intentionally left blank.
+ }
+ catch (\Exception $e) {
+ // Intentionally left blank.
+ }
+ return $json;
+ }
+
}