diff --git a/core/core.services.yml b/core/core.services.yml
index 2b27f69..7db26e1 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1050,6 +1050,12 @@ services:
asset.js.optimizer:
class: Drupal\Core\Asset\JsOptimizer
public: false
+ asset.js.optimizer_license_web_labels_annotator:
+ class: Drupal\Core\Asset\JsLicenseWebLabelsAnnotator
+ arguments: [ '@url_generator' ]
+ asset.js.collection_optimizer_license_web_labels_annotator:
+ class: Drupal\Core\Asset\JsCollectionOptimizer
+ arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer_license_web_labels_annotator', '@asset.js.dumper', '@state', 'system.js_license_web_labels_files' ]
asset.js.collection_grouper:
class: Drupal\Core\Asset\JsCollectionGrouper
public: false
diff --git a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
index 68626ec..f781b1e 100644
--- a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
+++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
@@ -8,7 +8,6 @@
use Drupal\Core\State\StateInterface;
-
/**
* Optimizes JavaScript assets.
*/
@@ -43,6 +42,13 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface {
protected $state;
/**
+ * The name for the JavaScript asset collections optimized by this instance.
+ *
+ * @var string
+ */
+ protected $name;
+
+ /**
* Constructs a JsCollectionOptimizer.
*
* @param \Drupal\Core\Asset\AssetCollectionGrouperInterface
@@ -53,12 +59,16 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface {
* The dumper for optimized JS assets.
* @param \Drupal\Core\State\StateInterface
* The state key/value store.
+ * @param string
+ * (optional) The name for the JavaScript asset collections optimized by
+ * this instance.
*/
- public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state) {
+ public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state, $name = 'system.js_cache_files') {
$this->grouper = $grouper;
$this->optimizer = $optimizer;
$this->dumper = $dumper;
$this->state = $state;
+ $this->name = $name;
}
/**
@@ -85,7 +95,7 @@ public function optimize(array $js_assets) {
// Drupal contrib can override this default JS aggregator to keep the same
// grouping, optimizing and dumping, but change the strategy that is used to
// determine when the aggregate should be rebuilt (e.g. mtime, HTTPS …).
- $map = $this->state->get('system.js_cache_files') ?: array();
+ $map = $this->state->get($this->name) ?: array();
$js_assets = array();
foreach ($js_groups as $order => $js_group) {
// We have to return a single asset, not a group of assets. It is now up
@@ -109,7 +119,7 @@ public function optimize(array $js_assets) {
$uri = $map[$key];
}
if (empty($uri) || !file_exists($uri)) {
- // Concatenate each asset within the group.
+ // Concatenate and optimize each asset within the group.
$data = '';
foreach ($js_group['items'] as $js_asset) {
// Optimize this JS file, but only if it's not yet minified.
@@ -129,7 +139,7 @@ public function optimize(array $js_assets) {
$js_assets[$order]['data'] = $uri;
// Persist the URI for this aggregate file.
$map[$key] = $uri;
- $this->state->set('system.js_cache_files', $map);
+ $this->state->set($this->name, $map);
}
else {
// Use the persisted URI for the optimized JS file.
@@ -174,14 +184,14 @@ protected function generateHash(array $js_group) {
* {@inheritdoc}
*/
public function getAll() {
- return $this->state->get('system.js_cache_files');
+ return $this->state->get($this->name);
}
/**
* {@inheritdoc}
*/
public function deleteAll() {
- $this->state->delete('system.js_cache_files');
+ $this->state->delete($this->name);
$delete_stale = function($uri) {
// Default stale file threshold is 30 days.
if (REQUEST_TIME - filemtime($uri) > \Drupal::config('system.performance')->get('stale_file_threshold')) {
diff --git a/core/lib/Drupal/Core/Asset/JsLicenseWebLabelsAnnotator.php b/core/lib/Drupal/Core/Asset/JsLicenseWebLabelsAnnotator.php
new file mode 100644
index 0000000..a487b27
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/JsLicenseWebLabelsAnnotator.php
@@ -0,0 +1,53 @@
+urlGenerator = $url_generator;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function optimize(array $js_asset) {
+ if ($js_asset['type'] !== 'file') {
+ throw new \Exception('Only file JavaScript assets can be optimized.');
+ }
+ if ($js_asset['type'] === 'file' && !$js_asset['preprocess']) {
+ throw new \Exception('Only file JavaScript assets with preprocessing enabled can be optimized.');
+ }
+
+ // Generate a prefix to be prepended to the "optimized" asset (no actual
+ // optimizations are made; this is a no-op optimizer) that deep-links to the
+ // license information on the JavaScript License Web Labels page.
+ $url = $this->urlGenerator->generateFromRoute('system.javascript_license_web_labels', array(), array('fragment' => drupal_clean_css_identifier($js_asset['data'])));
+ $prefix = "/** JavaScript asset: " . $js_asset['data'] . '; for license information, see ' . $url . " **/\n\n";
+ return $prefix . file_get_contents($js_asset['data']);
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/Scripts.php b/core/lib/Drupal/Core/Render/Element/Scripts.php
index cc04ede..8d070d7 100644
--- a/core/lib/Drupal/Core/Render/Element/Scripts.php
+++ b/core/lib/Drupal/Core/Render/Element/Scripts.php
@@ -59,7 +59,14 @@ public static function preRenderScripts($element) {
// Aggregate the JavaScript if necessary, but only during normal site
// operation.
if (!defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('js.preprocess')) {
- $js_assets = \Drupal::service('asset.js.collection_optimizer')->optimize($js_assets);
+ $optimized_js_assets = \Drupal::service('asset.js.collection_optimizer')->optimize($js_assets);
+ // Also create the alternative version of the aggregate that is
+ // unoptimized, but annotated with deep links to the license information
+ // on the JavaScript License Web Labels page.
+ // @see \Drupal\system\Controller\AssetLicenseInfoController
+ \Drupal::service('asset.js.collection_optimizer_license_web_labels_annotator')->optimize($js_assets);
+ // Now the optimized JavaScript assets are the ones to be rendered.
+ $js_assets = $optimized_js_assets;
}
return \Drupal::service('asset.js.collection_renderer')->render($js_assets);
}
diff --git a/core/modules/system/src/Controller/AssetLicenseInfoController.php b/core/modules/system/src/Controller/AssetLicenseInfoController.php
new file mode 100644
index 0000000..0abeba9
--- /dev/null
+++ b/core/modules/system/src/Controller/AssetLicenseInfoController.php
@@ -0,0 +1,182 @@
+get('library.discovery'),
+ $container->get('asset.js.collection_optimizer'),
+ $container->get('asset.js.collection_optimizer_license_web_labels_annotator')
+ );
+ }
+
+ /**
+ * Constructs a AssetLicenseInfoController object.
+ *
+ * @param \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery
+ * The asset library discovery service.
+ * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $js_collection_optimizer
+ * The JavaScript asset collection optimizer service.
+ * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $js_collection_optimizer_license_annotator
+ * The JavaScript asset collection optimizer license annotator service.
+ */
+ public function __construct(LibraryDiscoveryInterface $library_discovery, AssetCollectionOptimizerInterface $js_collection_optimizer, AssetCollectionOptimizerInterface $js_collection_optimizer_license_annotator) {
+ $this->libraryDiscovery = $library_discovery;
+ $this->jsCollectionOptimizer = $js_collection_optimizer;
+ $this->jsCollectionOptimizerLicenseAnnotator = $js_collection_optimizer_license_annotator;
+ }
+
+ /**
+ * Returns the JavaScript License Web Labels page for all JavaScript assets.
+ *
+ * @see http://www.gnu.org/licenses/javascript-labels.html
+ *
+ * @return array()
+ * A renderable array.
+ */
+ public function jslicense() {
+ $rows = array();
+
+ // List all extensions' JavaScript assets.
+ $module_list = $this->moduleHandler()->getModuleList();
+ $extensions = array_merge(array('core'), array_keys($module_list));
+ foreach ($extensions as $extension) {
+ $libraries = $this->libraryDiscovery->getLibrariesByExtension($extension);
+ $rows_for_extension = array();
+ foreach ($libraries as $library) {
+ $license_column = l($library['license']['name'], $library['license']['url']);
+ if (!isset($library['js'])) {
+ continue;
+ }
+ foreach ($library['js'] as $js_asset) {
+ // This is core's special "drupalSettings" JavaScript asset.
+ if ($js_asset['type'] === 'setting') {
+ continue;
+ }
+ else if ($js_asset['type'] === 'external') {
+ $url = $js_asset['data'];
+ }
+ else {
+ $url = file_create_url($js_asset['data']);
+ }
+ $js_asset_basename = basename($js_asset['data']);
+ $js_file_column = l('' . $js_asset_basename . '
', $url, array('html' => TRUE));
+ $source_column = l('' . $js_asset_basename . '
', $url, array('html' => TRUE));
+ $rows_for_extension[] = array(
+ 'data' => array($js_file_column, $license_column, $source_column),
+ 'id' => drupal_clean_css_identifier($js_asset['data']),
+ );
+ }
+ }
+
+ // Add header for the current extension; add all rows for this extension.
+ if (count($rows_for_extension)) {
+ if ($extension !== 'core') {
+ $rows[] = array(
+ array(
+ 'data' => $this->t('JavaScript files of the @extension-name
@extension-type', array(
+ '@extension-name' => $module_list[$extension]->getName(),
+ '@extension-type' => $module_list[$extension]->getType(),
+ )),
+ 'header' => TRUE,
+ 'colspan' => 3,
+ ),
+ );
+ }
+ foreach ($rows_for_extension as $row) {
+ $rows[] = $row;
+ }
+ }
+ }
+
+ // List all aggregated (and optimized) JavaScript assets.
+ $rows[] = array(
+ array(
+ 'data' => $this->t('Aggregated & optimized (minified) JavaScript files'),
+ 'header' => TRUE,
+ 'colspan' => 3,
+ ),
+ );
+
+ $aggregated_js_assets = $this->jsCollectionOptimizer->getAll();
+ if (count($aggregated_js_assets)) {
+ $unoptimized_aggregated_js_assets = $this->jsCollectionOptimizerLicenseAnnotator->getAll();
+ $license_column = $this->t('Combination');
+ foreach (array_keys($aggregated_js_assets) as $hash) {
+ $optimized_js = $aggregated_js_assets[$hash];
+ $unoptimized_js = $unoptimized_aggregated_js_assets[$hash];
+ $js_file_column = l('' . basename($optimized_js) . '
', file_create_url($optimized_js), array('html' => TRUE));
+ $source_column = l('' . basename($unoptimized_js) . '
', file_create_url($unoptimized_js), array('html' => TRUE));
+ $rows[] = array($js_file_column, $license_column, $source_column);
+ }
+ }
+
+ $table = array(
+ '#type' => 'table',
+ '#header' => array(
+ 'javascript_asset' => array(
+ 'data' => $this->t('JavaScript file'),
+ ),
+ 'license' => array(
+ 'data' => $this->t('License'),
+ ),
+ 'source' => array(
+ 'data' => $this->t('Source code'),
+ 'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
+ ),
+
+ ),
+ '#sticky' => TRUE,
+ '#responsive' => TRUE,
+ '#rows' => $rows,
+ '#attributes' => array(
+ // This ID is required by the JavaScript License Web Labels standard;
+ // see http://www.gnu.org/licenses/javascript-labels.html.
+ 'id' => 'jslicense-labels1',
+ ),
+ );
+
+ return $table;
+ }
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 52903cf..9bbdd43 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -626,6 +626,12 @@ function system_page_attachments(array &$page) {
)
);
}
+
+ // Add the JavaScript Web License Labels link to all pages.
+ $page['#attached']['html_head_link'][][] = array(
+ 'rel' => 'jslicense',
+ 'href' => \Drupal::urlGenerator()->generateFromRoute('system.javascript_license_web_labels'),
+ );
}
/**
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index fc05a95..b5e50b3 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -456,3 +456,11 @@ system.admin_content:
_title: 'Content'
requirements:
_permission: 'access administration pages'
+
+system.javascript_license_web_labels:
+ path: '/system/jslicense'
+ defaults:
+ _content: '\Drupal\system\Controller\AssetLicenseInfoController::jslicense'
+ _title: 'JavaScript license information'
+ requirements:
+ _access: 'TRUE'