Projet

Général

Profil

Paste
Télécharger (9,51 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / themes / bootstrap / includes / cdn / JsDelivr.php @ b6b9edaa

1
<?php
2

    
3
namespace Drupal\bootstrap\Backport\Plugin\Provider;
4

    
5
/**
6
 * The "jsdelivr" CDN provider plugin.
7
 *
8
 * Note: this class is a backport from the 8.x-3.x code base.
9
 *
10
 * @see https://drupal-bootstrap.org/api/bootstrap/namespace/Drupal%21bootstrap%21Plugin%21Provider/8
11
 *
12
 * @ingroup plugins_provider
13
 */
14
class JsDelivr extends ProviderBase {
15

    
16
  protected $pluginId = 'jsdelivr';
17

    
18
  /**
19
   * The base API URL.
20
   *
21
   * @var string
22
   */
23
  const BASE_API_URL = 'https://data.jsdelivr.com/v1/package/npm';
24

    
25
  /**
26
   * The base CDN URL.
27
   *
28
   * @var string
29
   */
30
  const BASE_CDN_URL = 'https://cdn.jsdelivr.net/npm';
31

    
32
  /**
33
   * A list of latest versions, keyed by NPM package name.
34
   *
35
   * @var string[]
36
   */
37
  protected $latestVersion = array();
38

    
39
  /**
40
   * A list of themes, keyed by NPM package name.
41
   *
42
   * @var array[]
43
   */
44
  protected $themes = array();
45

    
46
  /**
47
   * A list of versions, keyed by NPM package name.
48
   *
49
   * @var array[]
50
   */
51
  protected $versions = array();
52

    
53
  /**
54
   * {@inheritdoc}
55
   */
56
  public function getDescription() {
57
    return 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>', array(
58
      '!jsdelivr' => 'https://www.jsdelivr.com',
59
      '!jsdelivr_about' => 'https://www.jsdelivr.com/about',
60
      '!maxcdn' => 'https://www.maxcdn.com',
61
      '!cloudflare' => 'https://www.cloudflare.com',
62
    ));
63
  }
64

    
65
  /**
66
   * {@inheritdoc}
67
   */
68
  public function getLabel() {
69
    return t('jsDelivr');
70
  }
71

    
72
  /**
73
   * {@inheritdoc}
74
   */
75
  protected function discoverCdnAssets($version, $theme = 'bootstrap') {
76
    $themes = $this->getCdnThemes($version);
77
    return isset($themes[$theme]) ? $themes[$theme] : array();
78
  }
79

    
80
  /**
81
   * {@inheritdoc}
82
   */
83
  public function getCdnThemes($version = NULL) {
84
    if (!isset($version)) {
85
      $version = $this->getCdnVersion();
86
    }
87
    if (!isset($this->themes[$version])) {
88
      $instance = $this;
89
      $this->themes[$version] = $this->cacheGet('themes.' . static::escapeDelimiter($version), array(), function ($themes) use ($version, $instance) {
90
        foreach (array('bootstrap', 'bootswatch') as $package) {
91
          $mappedVersion = $instance->mapVersion($version, $package);
92
          $files = $instance->requestApiV1($package, $mappedVersion);
93
          $themes = $instance->parseThemes($files, $package, $mappedVersion, $themes);
94
        }
95
        return $themes;
96
      });
97
    }
98
    return $this->themes[$version];
99
  }
100

    
101
  /**
102
   * {@inheritdoc}
103
   */
104
  public function getCdnVersions($package = 'bootstrap') {
105
    if (!isset($this->versions[$package])) {
106
      $instance = $this;
107
      $this->versions[$package] = $this->cacheGet("versions.$package", array(), function ($versions) use ($package, $instance) {
108
        $json = $instance->requestApiV1($package) + array('versions' => array());
109
        foreach ($json['versions'] as $version) {
110
          // Skip irrelevant versions.
111
          if (!preg_match('/^' . substr(BOOTSTRAP_VERSION, 0, 1) . '\.\d+\.\d+$/', $version)) {
112
            continue;
113
          }
114
          $versions[$version] = $version;
115
        }
116
        return $versions;
117
      });
118
    }
119
    return $this->versions[$package];
120
  }
121

    
122
  /**
123
   * {@inheritdoc}
124
   */
125
  protected function mapVersion($version, $package = NULL) {
126
    // While the Bootswatch project attempts to maintain version parity with
127
    // Bootstrap, it doesn't always happen. This causes issues when the system
128
    // expects a 1:1 version match between Bootstrap and Bootswatch.
129
    // @see https://github.com/thomaspark/bootswatch/issues/892#ref-issue-410070082
130
    if ($package === 'bootswatch') {
131
      switch ($version) {
132
        // This version is "broken" because of jsDelivr's API limit.
133
        case '3.4.1':
134
          $version = '3.4.0';
135
          break;
136

    
137
        // This version doesn't exist.
138
        case '3.1.1':
139
          $version = '3.2.0';
140
          break;
141
      }
142
    }
143
    return $version;
144
  }
145

    
146
  /**
147
   * Parses JSON from the API and retrieves valid files.
148
   *
149
   * @param array $json
150
   *   The JSON data to parse.
151
   *
152
   * @return array
153
   *   An array of files parsed from provided JSON data.
154
   */
155
  protected function parseFiles(array $json) {
156
    // Immediately return if malformed.
157
    if (!isset($json['files']) || !is_array($json['files'])) {
158
      return array();
159
    }
160

    
161
    $files = array();
162
    foreach ($json['files'] as $file) {
163
      // Skip old bootswatch file structure.
164
      if (preg_match('`^/2|/bower_components`', $file['name'], $matches)) {
165
        continue;
166
      }
167
      preg_match('`([^/]*)/bootstrap(-theme)?(\.min)?\.(js|css)$`', $file['name'], $matches);
168
      if (!empty($matches[1]) && !empty($matches[4])) {
169
        $files[] = $file['name'];
170
      }
171
    }
172
    return $files;
173
  }
174

    
175
  /**
176
   * Extracts assets from files provided by the jsDelivr API.
177
   *
178
   * This will place the raw files into proper "css", "js" and "min" arrays
179
   * (if they exist) and prepends them with a base URL provided.
180
   *
181
   * @param array $files
182
   *   An array of files to process.
183
   * @param string $package
184
   *   The base URL each one of the $files are relative to, this usually
185
   *   should also include the version path prefix as well.
186
   * @param string $version
187
   *   A specific version to use.
188
   * @param array $themes
189
   *   An existing array of themes. This is primarily used when building a
190
   *   complete list of themes.
191
   *
192
   * @return array
193
   *   An associative array containing the following keys, if there were
194
   *   matching files found:
195
   *   - css
196
   *   - js
197
   *   - min:
198
   *     - css
199
   *     - js
200
   */
201
  protected function parseThemes(array $files, $package, $version, array $themes = array()) {
202
    $baseUrl = static::BASE_CDN_URL . "/$package@$version";
203
    foreach ($files as $file) {
204
      preg_match('`([^/]*)/bootstrap(-theme)?(\.min)?\.(js|css)$`', $file, $matches);
205
      if (!empty($matches[1]) && !empty($matches[4])) {
206
        $path = $matches[1];
207
        $min = $matches[3];
208
        $filetype = $matches[4];
209

    
210
        // Determine the "theme" name.
211
        if ($path === 'css' || $path === 'js') {
212
          $theme = 'bootstrap';
213
          $title = (string) t('Bootstrap');
214
        }
215
        else {
216
          $theme = $path;
217
          $title = ucfirst($path);
218
        }
219
        if ($matches[2]) {
220
          $theme = 'bootstrap_theme';
221
          $title = (string) t('Bootstrap Theme');
222
        }
223

    
224
        $themes[$theme]['title'] = $title;
225
        if ($min) {
226
          $themes[$theme]['min'][$filetype][] = "$baseUrl/" . ltrim($file, '/');
227
        }
228
        else {
229
          $themes[$theme][$filetype][] = "$baseUrl/" . ltrim($file, '/');
230
        }
231
      }
232
    }
233

    
234
    // Post process the themes to fill in any missing assets.
235
    foreach (array_keys($themes) as $theme) {
236
      // Some themes do not have a non-minified version, clone them to the
237
      // "normal" css/js arrays to ensure that the theme still loads if
238
      // aggregation (minification) is disabled.
239
      foreach (array('css', 'js') as $type) {
240
        if (!isset($themes[$theme][$type]) && isset($themes[$theme]['min'][$type])) {
241
          $themes[$theme][$type] = $themes[$theme]['min'][$type];
242
        }
243
      }
244

    
245
      // Prepend the main Bootstrap styles before the Bootstrap theme.
246
      if ($theme === 'bootstrap_theme') {
247
        if (isset($themes['bootstrap']['css'])) {
248
          $themes[$theme]['css'] = array_unique(array_merge($themes['bootstrap']['css'], isset($themes[$theme]['css']) ? $themes[$theme]['css'] : array()));
249
        }
250
        if (isset($themes['bootstrap']['min']['css'])) {
251
          $themes[$theme]['min']['css'] = array_unique(array_merge($themes['bootstrap']['min']['css'], isset($themes[$theme]['min']['css']) ? $themes[$theme]['min']['css'] : array()));
252
        }
253
      }
254

    
255
      // Populate missing JavaScript.
256
      if (!isset($themes[$theme]['js']) && isset($themes['bootstrap']['js'])) {
257
        $themes[$theme]['js'] = $themes['bootstrap']['js'];
258
      }
259
      if (!isset($themes[$theme]['min']['js']) && isset($themes['bootstrap']['min']['js'])) {
260
        $themes[$theme]['min']['js'] = $themes['bootstrap']['min']['js'];
261
      }
262
    }
263

    
264
    return $themes;
265
  }
266

    
267
  /**
268
   * Requests JSON from jsDelivr's API V1.
269
   *
270
   * @param string $package
271
   *   The NPM package being requested.
272
   * @param string $version
273
   *   A specific version of $package to request. If not provided, a list of
274
   *   available versions will be returned.
275
   *
276
   * @return array
277
   *   The JSON data from the API.
278
   */
279
  protected function requestApiV1($package, $version = NULL) {
280
    $url = static::BASE_API_URL . "/$package";
281

    
282
    // If no version was passed, then all versions are returned.
283
    if (!$version) {
284
      return $this->requestJson($url);
285
    }
286

    
287
    $json = $this->requestJson("$url@$version/flat");
288

    
289
    // If bootstrap JSON could not be returned, provide defaults.
290
    if (!$json && $package === 'bootstrap') {
291
      $version = BOOTSTRAP_VERSION;
292
      return array(
293
        'css' => array(static::BASE_CDN_URL . "/$package@$version/dist/css/bootstrap.css"),
294
        'js' => array(static::BASE_CDN_URL . "/$package@$version/dist/js/bootstrap.js"),
295
        'min' => array(
296
          'css' => array(static::BASE_CDN_URL . "/$package@$version/dist/css/bootstrap.min.css"),
297
          'js' => array(static::BASE_CDN_URL . "/$package@$version/dist/js/bootstrap.min.js"),
298
        ),
299
      );
300
    }
301

    
302
    // Parse the files from JSON.
303
    return $this->parseFiles($json);
304
  }
305

    
306
}