diff --git a/core/core.services.yml b/core/core.services.yml index 9f13030..e144728 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1282,6 +1282,10 @@ services: class: Drupal\Core\StreamWrapper\ProfileStream tags: - { name: stream_wrapper, scheme: profile } + stream_wrapper.library: + class: Drupal\Core\StreamWrapper\LibraryStream + tags: + - { name: stream_wrapper, scheme: library } kernel_destruct_subscriber: class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber tags: diff --git a/core/lib/Drupal/Core/Extension/LibraryDiscovery.php b/core/lib/Drupal/Core/Extension/LibraryDiscovery.php new file mode 100644 index 0000000..c811b9c --- /dev/null +++ b/core/lib/Drupal/Core/Extension/LibraryDiscovery.php @@ -0,0 +1,102 @@ +root). + $dir_prefix = ($dir == '' ? '' : "$dir/") . self::LIBRARIES_DIRECTORY . '/'; + $absolute_dir = ($dir == '' ? $this->root : $this->root . "/$dir"); + $absolute_dir .= '/' . self::LIBRARIES_DIRECTORY; + + if (!is_dir($absolute_dir)) { + return $files; + } + // Use Unix paths regardless of platform, skip dot directories, follow + // symlinks (to allow extensions to be linked from elsewhere), and return + // the RecursiveDirectoryIterator instance to have access to getSubPath(), + // since SplFileInfo does not support relative paths. + $flags = \FilesystemIterator::UNIX_PATHS; + $flags |= \FilesystemIterator::SKIP_DOTS; + $flags |= \FilesystemIterator::FOLLOW_SYMLINKS; + $flags |= \FilesystemIterator::CURRENT_AS_SELF; + $directory_iterator = new \RecursiveDirectoryIterator($absolute_dir, $flags); + + /** + * @var string $key + * @var \RecursiveDirectoryIterator $fileinfo + */ + foreach ($directory_iterator as $key => $fileinfo) { + if ($this->fileCache && $cached_extension = $this->fileCache->get($fileinfo->getPathname())) { + $files[$cached_extension->getType()][$key] = $cached_extension; + continue; + } + + if (!$fileinfo->isDir()) { + continue; + } + + $type = self::EXTENSION_TYPE; + $name = $fileinfo->getBasename(); + $pathname = $dir_prefix . $fileinfo->getSubPathname(); + + $extension = new Extension($this->root, $type, $pathname); + + // Track the originating directory for sorting purposes. + $extension->subpath = self::LIBRARIES_DIRECTORY . '/' . $fileinfo->getFilename(); + $extension->origin = $dir; + + $files[$type][$key] = $extension; + + if ($this->fileCache) { + $this->fileCache->set($fileinfo->getPathname(), $extension); + } + } + + return $files; + } + + /** + * {@inheritdoc} + */ + public function scan($type, $include_tests = NULL) { + return parent::scan(self::EXTENSION_TYPE); + } + +} diff --git a/core/lib/Drupal/Core/StreamWrapper/LibraryStream.php b/core/lib/Drupal/Core/StreamWrapper/LibraryStream.php new file mode 100644 index 0000000..3ce3164 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/LibraryStream.php @@ -0,0 +1,89 @@ +getDrupalRoot()); + $files = $library_discovery->scan($name); + if (!isset($files[$name])) { + throw new \InvalidArgumentException("Library $name does not exist"); + } + + return $name; + } + + /** + * {@inheritdoc} + */ + protected function getDirectoryPath() { + $name = parent::getOwnerName(); + $library_discovery = new LibraryDiscovery($this->getDrupalRoot()); + /** @var $files \Drupal\Core\Extension\Extension[] */ + $files = $library_discovery->scan($name); + $name = $this->getOwnerName(); + return $files[$name]->getPathname(); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return $this->t('Library files'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->t('Local files stored under the libraries directory.'); + } + + /** + * Return the path to the Drupal root. + * + * Since stream wrappers don't allow us to pass in __construct() parameters, + * we have to fall back to \Drupal. + * + * @return string + */ + protected function getDrupalRoot() { + if (!isset($this->drupalRoot)) { + $this->drupalRoot = \Drupal::root(); + } + return $this->drupalRoot; + } + + /** + * Set the path to the Drupal root. + * + * @param string $drupalRoot + * The absolute path to the root of the Drupal installation. + */ + public function setDrupalRoot($drupalRoot) { + $this->drupalRoot = $drupalRoot; + } + +}