diff --git a/core/includes/common.inc b/core/includes/common.inc index df5677f..486c6ce 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -913,6 +913,10 @@ function drupal_set_time_limit($time_limit) { /** * Returns the path to a system item (module, theme, etc.). * + * This function should only be used when including a file containing PHP code; + * the 'module://', 'profile://' and 'theme://' stream wrappers should be used + * for other use cases. + * * @param $type * The type of the item; one of 'core', 'profile', 'module', 'theme', or * 'theme_engine'. diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php index 937953f..dbc7fcb 100644 --- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php @@ -87,10 +87,17 @@ protected function getTarget($uri = NULL) { $uri = $this->uri; } - list(, $target) = explode('://', $uri, 2); + $uri_parts = explode('://', $uri, 2); + if (count($uri_parts) === 1) { + // The delimiter ('://') was not found in $uri, malformed $uri passed. + throw new \InvalidArgumentException('Malformed $uri parameter passed: %s', $uri); + } + else { + list(, $target) = $uri_parts; - // Remove erroneous leading or trailing, forward-slashes and backslashes. - return trim($target, '\/'); + // Remove erroneous leading or trailing, forward-slashes and backslashes. + return trim($target, '\/'); + } } /** diff --git a/core/lib/Drupal/Core/StreamWrapper/ModuleStream.php b/core/lib/Drupal/Core/StreamWrapper/ModuleStream.php new file mode 100644 index 0000000..9021ae3 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/ModuleStream.php @@ -0,0 +1,46 @@ +moduleExists($name) ? $name : FALSE; + } + + /** + * Gets the module's directory path. + * + * @param string $uri + * Optional URI. + * + * @return string + * String specifying the path. + */ + public function getDirectoryPath($uri = NULL) { + return drupal_get_path('module', $this->getOwnerName($uri)); + } +} diff --git a/core/lib/Drupal/Core/StreamWrapper/ProfileStream.php b/core/lib/Drupal/Core/StreamWrapper/ProfileStream.php new file mode 100644 index 0000000..bf2cdfc --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/ProfileStream.php @@ -0,0 +1,49 @@ +getOwnerName($uri)); + } +} diff --git a/core/lib/Drupal/Core/StreamWrapper/SystemStream.php b/core/lib/Drupal/Core/StreamWrapper/SystemStream.php new file mode 100644 index 0000000..57e3d60 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/SystemStream.php @@ -0,0 +1,89 @@ +uri; + } + + $uri_parts = explode('://', $uri, 2); + if (count($uri_parts) === 1) { + // The delimiter ('://') was not found in $uri, malformed $uri passed. + throw new \InvalidArgumentException(sprintf('Malformed uri parameter passed: %s', $uri)); + } + else { + list($scheme, $target) = $uri_parts; + } + // Remove the trailing filename from the path. + $length = strpos($target, '/'); + return ($length === FALSE) ? $target : substr($target, 0, $length); + } + + /** + * {@inheritdoc} + */ + public function getTarget($uri = NULL) { + $target = $this->extractTarget($uri); + return file_exists($this->getDirectoryPath($uri) . '/' . $target) ? $target : NULL; + } + + /** + * Returns the local target of the resource, regardless of whether it exists. + * + * @param string $uri + * Optional URI. + * + * @return bool|string + * A path to the local target. + */ + protected function extractTarget($uri = NULL) { + // If the owner doesn't exist at all, we don't extract anything. + if ($this->getOwnerName($uri) === FALSE) { + return FALSE; + } + $target = parent::getTarget($uri); + // Remove the preceding owner name including slash from the path. + $start = strpos($target, '/'); + $target = ($start === FALSE) ? '' : substr($target, $start + 1); + return $target; + } + + /** + * {@inheritdoc} + */ + public function getExternalUrl($uri = NULL) { + $dir = $this->getDirectoryPath($uri); + if (empty($dir)) { + return FALSE; + } + + $target = $this->extractTarget($uri); + $path = $target != '' ? '/' . UrlHelper::encodePath(str_replace('\\', '/', $target)) : ''; + return \Drupal::request()->getUri() . $dir . $path; + } +} diff --git a/core/lib/Drupal/Core/StreamWrapper/ThemeStream.php b/core/lib/Drupal/Core/StreamWrapper/ThemeStream.php new file mode 100644 index 0000000..e59efd0 --- /dev/null +++ b/core/lib/Drupal/Core/StreamWrapper/ThemeStream.php @@ -0,0 +1,62 @@ +getActiveTheme(); + break; + case 'default': + $name = \Drupal::config('system.theme')->get('default'); + break; + case 'admin': + $name = \Drupal::config('system.theme')->get('admin'); + break; + } + // Return name only for enabled themes. + return array_key_exists($name, \Drupal::service('theme_handler')->listInfo()) ? $name : FALSE; + } + + /** + * Gets the theme's directory path. + * + * @param string $uri + * Optional URI. + * + * @return string + * String specifying the path. + */ + public function getDirectoryPath($uri = NULL) { + return drupal_get_path('theme', $this->getOwnerName($uri)); + } + + /** + * Gets the currently active theme. + */ + protected function getActiveTheme() { + return \Drupal::service('theme.negotiator')->determineActiveTheme(\Drupal::service('current_route_match')); + } +} diff --git a/core/modules/block/tests/modules/block_test/block_test.module b/core/modules/block/tests/modules/block_test/block_test.module index a527ae1..0583b86 100644 --- a/core/modules/block/tests/modules/block_test/block_test.module +++ b/core/modules/block/tests/modules/block_test/block_test.module @@ -8,6 +8,14 @@ use Drupal\block\BlockPluginInterface; /** + * Implements hook_system_theme_info(). + */ +function block_test_system_theme_info() { + $themes['block_test_theme'] = "module://block_test/themes/block_test_theme/block_test_theme.info.yml"; + return $themes; +} + +/** * Implements hook_block_alter(). */ function block_test_block_alter(&$block_info) { diff --git a/core/modules/breakpoint/tests/breakpoint_theme_test.module b/core/modules/breakpoint/tests/breakpoint_theme_test.module new file mode 100644 index 0000000..57e8888 --- /dev/null +++ b/core/modules/breakpoint/tests/breakpoint_theme_test.module @@ -0,0 +1,13 @@ + $string) { diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 616d064..f60ab48 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -61,6 +61,7 @@ private $moduleFiles; private $themeFiles; + private $themeData; /** * The configuration directories for this test run. @@ -101,6 +102,7 @@ protected function beforePrepareEnvironment() { if (!isset($this->moduleFiles)) { $this->moduleFiles = \Drupal::state()->get('system.module.files') ?: array(); $this->themeFiles = \Drupal::state()->get('system.theme.files') ?: array(); + $this->themeData = \Drupal::state()->get('system.theme.data') ?: array(); } } @@ -170,6 +172,7 @@ protected function setUp() { $this->container->get('state')->set('system.module.files', $this->moduleFiles); $this->container->get('state')->set('system.theme.files', $this->themeFiles); + $this->container->get('state')->set('system.theme.data', $this->themeData); // Create a minimal core.extension configuration object so that the list of // enabled modules can be maintained allowing diff --git a/core/modules/system/src/Tests/File/SystemStreamUnitTest.php b/core/modules/system/src/Tests/File/SystemStreamUnitTest.php new file mode 100644 index 0000000..691f309 --- /dev/null +++ b/core/modules/system/src/Tests/File/SystemStreamUnitTest.php @@ -0,0 +1,278 @@ + 'System Stream wrappers', + 'description' => 'Unit tests system stream wrapper functions.', + 'group' => 'File API', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + drupal_static_reset('file_get_stream_wrappers'); + + // Add default profile for the purposes of this test + new Settings(Settings::getAll() + array( + 'install_profile' => 'standard' + )); + } + + /** + * {@inheritdoc} + */ + public function tearDown() { + parent::tearDown(); + } + + /** + * Test Invalid stream uri. + */ + public function testInvalidStreamUriException() { + $bad_uris = array( + 'invalid/uri', + 'invalid_uri', + 'module/invalid/uri', + 'module/invalid_uri', + 'module:invalid_uri', + 'module::/invalid/uri', + 'module::/invalid_uri', + 'module//:invalid/uri', + 'module//invalid_uri', + 'module//invalid/uri', + ); + + /** @var \Drupal\Core\StreamWrapper\ModuleStream $instance */ + $instance = file_stream_wrapper_get_instance_by_scheme('module'); + + foreach ($bad_uris as $bad_uri) { + try { + $instance->getOwnerName($bad_uri); + $this->fail(format_string('Invalid uri %uri not detected.', array('%uri' => $bad_uri))); + } + catch (\InvalidArgumentException $e) { + $this->pass(format_string('Throw exception on invalid uri %uri supplied.', array('%uri' => $bad_uri))); + } + } + } + + /** + * Test the Module stream wrapper functions. + */ + public function testModuleStream() { + // Generate a module stream wrapper instance. + $uri1 = 'module://system'; + $uri2 = 'module://system/css/system.admin.css'; + $uri3 = 'module://file_test/file_test.dummy.inc'; + $uri4 = 'module://file_test/includes/file_test.dummy.inc'; + $uri5 = 'module://ckeditor/ckeditor.info.yml'; + $uri6 = 'module://foo_bar/foo.bar.js'; + + /** @var \Drupal\Core\StreamWrapper\ModuleStream $instance */ + $instance = file_stream_wrapper_get_instance_by_scheme('module'); + + // getOwnerName() + $this->assertEqual($instance->getOwnerName($uri1), 'system', 'Extract module name from a partial URI.'); + $this->assertEqual($instance->getOwnerName($uri2), 'system', 'Extract module name for a resource located in a subdirectory.'); + $this->assertEqual($instance->getOwnerName($uri3), 'file_test', 'Extract module name for a module located in a subdirectory.'); + $this->assertEqual($instance->getOwnerName($uri4), 'file_test', 'Extract module name even for a non-existing resource, as long as the module exists.'); + $this->assertFalse($instance->getOwnerName($uri5), "Fail returning a disabled module's name."); + $this->assertFalse($instance->getOwnerName($uri6), "Fail returning a non-existing module's name."); + + // getTarget() + $this->assertEqual($instance->getTarget($uri1), '', 'Return empty target from a partial URI.'); + $this->assertEqual($instance->getTarget($uri2), 'css/system.admin.css', 'Extract target for a resource located in a subdirectory.'); + $this->assertEqual($instance->getTarget($uri3), 'file_test.dummy.inc', 'Extract target for a module in a non-standard location.'); + $this->assertFalse($instance->getTarget($uri4), 'Fail returning a target for a non-existing resource.'); + $this->assertFalse($instance->getTarget($uri5), "Fail returning a target within a disabled module."); + $this->assertFalse($instance->getTarget($uri6), "Fail returning a target within a non-existing module."); + + // getDirectoryPath() + $this->assertEqual($instance->getDirectoryPath($uri1), 'core/modules/system', "Lookup module's directory path for a partial URI."); + $this->assertEqual($instance->getDirectoryPath($uri2), 'core/modules/system', "Lookup module's directory path for a resource located in a subdirectory."); + $this->assertEqual($instance->getDirectoryPath($uri3), 'core/modules/file/tests/file_test', "Lookup module's directory path for a module located in a subdirectory."); + $this->assertEqual($instance->getDirectoryPath($uri4), 'core/modules/file/tests/file_test', "Lookup module's directory path even for a non-existing resource, as long as the module exists."); + $this->assertFalse($instance->getDirectoryPath($uri5), "Fail returning a disabled module's directory path"); + $this->assertFalse($instance->getDirectoryPath($uri6), "Fail returning a non-existing module's directory path."); + + // getExternalUrl() + $base_url = \Drupal::request()->getUri(); + $this->assertEqual($instance->getExternalUrl($uri1), $base_url . 'core/modules/system', "Return the external url for the module directory path."); + $this->assertEqual($instance->getExternalUrl($uri2), $base_url . 'core/modules/system/css/system.admin.css', "Return the external url of a resource located in a subdirectory."); + $this->assertEqual($instance->getExternalUrl($uri3), $base_url . 'core/modules/file/tests/file_test/file_test.dummy.inc', "Return the external url of an include file located in a subdirectory."); + $this->assertEqual($instance->getExternalUrl($uri4), $base_url . 'core/modules/file/tests/file_test/includes/file_test.dummy.inc', "Return the external url even for a non-existing resource, as long as the module exists."); + $this->assertFalse($instance->getExternalUrl($uri5), "Fail returning the external uri for resources in a disabled module."); + $this->assertFalse($instance->getExternalUrl($uri6), "Fail returning the external uri for resources in a non-existing module."); + } + + /** + * Test the Profile stream wrapper functions. + */ + public function testProfileStream() { + $uri1 = 'profile://minimal'; + $uri2 = 'profile://minimal/config/install/block.block.stark_login.yml'; + $uri3 = 'profile://minimal/config/install/node.type.article.yml'; + $uri4 = 'profile://foo_bar/'; + $uri5 = 'profile://current'; + $uri6 = 'profile://current/standard.info.yml'; + + /** @var \Drupal\Core\StreamWrapper\ProfileStream $instance */ + $instance = file_stream_wrapper_get_instance_by_scheme('profile'); + + // getOwnerName() + $this->assertEqual($instance->getOwnerName($uri1), 'minimal', "Extract profile's name from a partial URI."); + $this->assertEqual($instance->getOwnerName($uri2), 'minimal', "Extract profile's name for a resource located in a subdirectory."); + $this->assertEqual($instance->getOwnerName($uri3), 'minimal', "Extract profile's name even for a non-existing resource, as long as the profile exists."); + $this->assertFalse($instance->getOwnerName($uri4), "Fail returning a non-existing profile's name."); + $this->assertEqual($instance->getOwnerName($uri5), 'standard', format_string('Lookup real name of %current for a partial URI.', array('%current' => 'profile://current'))); + $this->assertEqual($instance->getOwnerName($uri6), 'standard', format_string('Lookup real name of %current for a resource.', array('%current' => 'profile://current'))); + + // getTarget() + $this->assertEqual($instance->getTarget($uri1), '', 'Return empty target for a partial URI giving only the profile.'); + $this->assertEqual($instance->getTarget($uri2), 'config/install/block.block.stark_login.yml', 'Extract target for a resource located in a subdirectory.'); + $this->assertFalse($instance->getTarget($uri3), 'Fail returning a target for a non-existing resource.'); + $this->assertFalse($instance->getTarget($uri4), 'Fail returning a target within a non-existing profile.'); + $this->assertEqual($instance->getTarget($uri5), '', format_string('Return empty target for a partial URI giving only %current.', array('%current' => 'profile://current'))); + $this->assertEqual($instance->getTarget($uri6), 'standard.info.yml', format_string("Extract target from a resource within %current.", array('%current' => 'profile://current'))); + + // getDirectoryPath() + $this->assertEqual($instance->getDirectoryPath($uri1), 'core/profiles/minimal', "Lookup profile's directory path for a partial URI."); + $this->assertEqual($instance->getDirectoryPath($uri2), 'core/profiles/minimal', "Lookup profile's directory path for a resource located in a subdirectory."); + $this->assertEqual($instance->getDirectoryPath($uri3), 'core/profiles/minimal', "Lookup profile's directory path even for a non-existing resource, as long as the profile exists."); + $this->assertFalse($instance->getDirectoryPath($uri4), "Fail returning a non-existing profile's directory path."); + $this->assertEqual($instance->getDirectoryPath($uri5), 'core/profiles/standard', format_string('Lookup real directory path of %current for a partial URI.', array('%current' => 'profile://current'))); + $this->assertEqual($instance->getDirectoryPath($uri6), 'core/profiles/standard', format_string('Lookup real directory path of %current for a resource.', array('%current' => 'profile://current'))); + + // getExternalUrl() + $base_url = \Drupal::request()->getUri(); + $this->assertEqual($instance->getExternalUrl($uri1), $base_url . 'core/profiles/minimal', "Return the external url for the profile directory path."); + $this->assertEqual($instance->getExternalUrl($uri2), $base_url . 'core/profiles/minimal/config/install/block.block.stark_login.yml', "Return the external url of a file located in a subdirectory."); + $this->assertEqual($instance->getExternalUrl($uri3), $base_url . 'core/profiles/minimal/config/install/node.type.article.yml', "Return the external url of a non-existing file located in a subdirectory."); + $this->assertFalse($instance->getExternalUrl($uri4), "Fail returning the external uri for a disabled profile."); + $this->assertEqual($instance->getExternalUrl($uri5), $base_url . 'core/profiles/standard', format_string('Lookup the external url of %current for a partial URI.', array('%current' => 'profile://current'))); + $this->assertEqual($instance->getExternalUrl($uri6), $base_url . 'core/profiles/standard/standard.info.yml', format_string('Lookup the external url of %current for a resource.', array('%current' => 'profile://current'))); + } + + /** + * Test the Theme stream wrapper functions. + */ + public function testThemeStream() { + /** @var \Drupal\Core\Extension\ThemeHandler $theme_handler */ + $theme_handler = \Drupal::service('theme_handler'); + + // Enable Stark, Bartik and Seven themes. + $theme_handler->enable(array('bartik', 'seven', 'stark')); + + // Set admin theme to Seven. + $system_theme = \Drupal::config('system.theme'); + $this->assertNull($system_theme->get('admin'), 'No admin theme was set.'); + $system_theme->set('admin', 'seven')->save(); + $this->assertEqual($system_theme->get('admin'), 'seven', format_string('Make %seven the admin theme.', array('%seven' => 'Seven'))); + + // Set default theme to Bartik. + $theme_handler->setDefault('bartik'); + $this->assertEqual($theme_handler->getDefault(), 'bartik', format_string('Make %bartik the default theme.', array('%bartik' => 'Bartik'))); + + // Disable Stark theme. + $theme_handler->disable(array('stark')); + + $uri1 = 'theme://seven'; + $uri2 = 'theme://seven/style.css'; + $uri3 = 'theme://bartik/color/preview.js'; + $uri4 = 'theme://fifteen/screenshot.png'; + $uri5 = 'theme://current'; + $uri6 = 'theme://current/logo.png'; + $uri7 = 'theme://default'; + $uri8 = 'theme://default/bartik.info.yml'; + $uri9 = 'theme://admin'; + $uri10 = 'theme://admin/fake.info.yml'; + $uri11 = 'theme://stark/stark.info.yml'; + + /** @var \Drupal\Core\StreamWrapper\ThemeStream $instance */ + $instance = file_stream_wrapper_get_instance_by_scheme('theme'); + + // getOwnerName() + $this->assertEqual($instance->getOwnerName($uri1), 'seven', "Extract theme's name from a partial URI."); + $this->assertEqual($instance->getOwnerName($uri2), 'seven', "Extract theme's name from a full URI."); + $this->assertEqual($instance->getOwnerName($uri3), 'bartik', "Extract theme's name from a full URI with subdirectory."); + $this->assertFalse($instance->getOwnerName($uri4), "Fail returning a non-existing theme's name."); + $this->assertEqual($instance->getOwnerName($uri5), 'bartik', format_string('Lookup real name of %current for a partial URI.', array('%current' => 'theme://current')) . $instance->getOwnerName($uri5)); + $this->assertEqual($instance->getOwnerName($uri6), 'bartik', format_string('Lookup real name of %current for a resource.', array('%current' => 'theme://current')) . $instance->getOwnerName($uri6)); + $this->assertEqual($instance->getOwnerName($uri7), 'bartik', format_string('Lookup real name of %default for a partial URI.', array('%default' => 'theme://default'))); + $this->assertEqual($instance->getOwnerName($uri8), 'bartik', format_string('Lookup real name of %default for a resource.', array('%default' => 'theme://default'))); + $this->assertEqual($instance->getOwnerName($uri9), 'seven', format_string('Lookup real name of %admin for a partial URI.', array('%admin' => 'theme://admin'))); + $this->assertEqual($instance->getOwnerName($uri10), 'seven', format_string('Lookup real name of %admin even for a non-existing resource.', array('%admin' => 'theme://admin'))); + $this->assertFalse($instance->getOwnerName($uri11), "Fail returning a disabled theme's name."); + + // getTarget() + $this->assertEqual($instance->getTarget($uri1), '', 'Return empty target for a partial URI giving only the theme.'); + $this->assertFalse($instance->getTarget($uri2), 'Fail returning a target for a non-existent resource.'); + $this->assertEqual($instance->getTarget($uri3), 'color/preview.js', 'Extract target for a resource located in a subdirectory.'); + $this->assertFalse($instance->getTarget($uri4), 'Fail returning a target within a non-existing theme.'); + $this->assertEqual($instance->getTarget($uri5), '', format_string('Return empty target for a partial URI giving only %current.', array('%current' => 'theme://current'))); + $this->assertEqual($instance->getTarget($uri6), 'logo.png', format_string("Extract target from a resource within %current.", array('%current' => 'theme://current'))); + $this->assertEqual($instance->getTarget($uri7), '', format_string('Return empty target for a partial URI giving only %default.', array('%default' => 'theme://default'))); + $this->assertEqual($instance->getTarget($uri8), 'bartik.info.yml', format_string("Extract target from a resource located in a subdirectory of %default.", array('%default' => 'theme://default')) . $instance->getTarget($uri8)); + $this->assertEqual($instance->getTarget($uri9), '', format_string('Return empty target for a partial URI giving only %admin.', array('%admin' => 'theme://admin'))); + $this->assertFalse($instance->getTarget($uri10), format_string("Fail returning target for a non-existing resource within %admin.", array('%admin' => 'theme://admin'))); + $this->assertFalse($instance->getTarget($uri11), "Fail returning a target for a disabled theme."); + + // getDirectoryPath() + $this->assertEqual($instance->getDirectoryPath($uri1), 'core/themes/seven', "Lookup theme's directory path for a partial URI."); + $this->assertEqual($instance->getDirectoryPath($uri2), 'core/themes/seven', "Lookup theme's directory path for a resource located in a subdirectory."); + $this->assertEqual($instance->getDirectoryPath($uri3), 'core/themes/bartik', "Lookup theme's directory path for a resource."); + $this->assertFalse($instance->getDirectoryPath($uri4), "Fail returning a non-existing theme's directory path."); + $this->assertEqual($instance->getDirectoryPath($uri5), 'core/themes/bartik', format_string('Lookup real directory path of %current for a partial URI.', array('%current' => 'theme://current'))); + $this->assertEqual($instance->getDirectoryPath($uri6), 'core/themes/bartik', format_string('Lookup real directory path of %current for a resource.', array('%current' => 'theme://current'))); + $this->assertEqual($instance->getDirectoryPath($uri7), 'core/themes/bartik', format_string('Lookup real directory path of %default for a partial URI.', array('%default' => 'theme://default'))); + $this->assertEqual($instance->getDirectoryPath($uri8), 'core/themes/bartik', format_string('Lookup real directory path of %default for a resource.', array('%default' => 'theme://default'))); + $this->assertEqual($instance->getDirectoryPath($uri9), 'core/themes/seven', format_string('Lookup real directory path of %admin for a partial URI.', array('%admin' => 'theme://admin'))); + $this->assertEqual($instance->getDirectoryPath($uri10), 'core/themes/seven', format_string('Lookup real directory path of %admin for a resource.', array('%admin' => 'theme://admin'))); + $this->assertFalse($instance->getDirectoryPath($uri11), "Fail returning a disabled theme's directory path."); + + // getExternalUrl() + $base_url = \Drupal::request()->getUri(); + $this->assertEqual($instance->getExternalUrl($uri1), $base_url . 'core/themes/seven', "Lookup theme's external url for a partial URI."); + $this->assertEqual($instance->getExternalUrl($uri2), $base_url . 'core/themes/seven/style.css', "Lookup theme's external url for a resource located in a subdirectory."); + $this->assertEqual($instance->getExternalUrl($uri3), $base_url . 'core/themes/bartik/color/preview.js', "Lookup theme's external url for a resource."); + $this->assertFalse($instance->getExternalUrl($uri4), "Fail returning a non-existing theme's external url."); + $this->assertEqual($instance->getExternalUrl($uri5), $base_url . 'core/themes/bartik', format_string('Lookup real external url of %current for a partial URI.', array('%current' => 'theme://current'))); + $this->assertEqual($instance->getExternalUrl($uri6), $base_url . 'core/themes/bartik/logo.png', format_string('Lookup external url of %current for a resource.', array('%current' => 'theme://current'))); + $this->assertEqual($instance->getExternalUrl($uri7), $base_url . 'core/themes/bartik', format_string('Lookup external url of %default for a partial URI.', array('%default' => 'theme://default'))); + $this->assertEqual($instance->getExternalUrl($uri8), $base_url . 'core/themes/bartik/bartik.info.yml', format_string('Lookup external url of %default for a resource.', array('%default' => 'theme://default'))); + $this->assertEqual($instance->getExternalUrl($uri9), $base_url . 'core/themes/seven', format_string('Lookup external url of %admin for a partial URI.', array('%admin' => 'theme://admin'))); + $this->assertEqual($instance->getExternalUrl($uri10), $base_url . 'core/themes/seven/fake.info.yml', format_string('Lookup external url of %admin for a resource.', array('%admin' => 'theme://admin'))); + $this->assertFalse($instance->getExternalUrl($uri11), "Fail returning a disabled theme's external url."); + + } +} diff --git a/core/modules/system/system.module b/core/modules/system/system.module index f421e6b..50407b5 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -737,6 +737,24 @@ function system_stream_wrappers() { 'description' => new TranslationWrapper('Temporary local files for upload and previews.'), 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN, ), + 'module' => array( + 'name' => t('Module files'), + 'class' => 'Drupal\Core\StreamWrapper\ModuleStream', + 'description' => t('Local module files.'), + 'type' => STREAM_WRAPPERS_READ, + ), + 'theme' => array( + 'name' => t('Theme files'), + 'class' => 'Drupal\Core\StreamWrapper\ThemeStream', + 'description' => t('Local theme files.'), + 'type' => STREAM_WRAPPERS_READ, + ), + 'profile' => array( + 'name' => t('Profile files'), + 'class' => 'Drupal\Core\StreamWrapper\ProfileStream', + 'description' => t('Local profile files.'), + 'type' => STREAM_WRAPPERS_READ, + ), ); // Only register the private file stream wrapper if a file path has been set. diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.module b/core/modules/system/tests/modules/ajax_test/ajax_test.module index eee24f3..e427ca7 100644 --- a/core/modules/system/tests/modules/ajax_test/ajax_test.module +++ b/core/modules/system/tests/modules/ajax_test/ajax_test.module @@ -13,6 +13,14 @@ use Drupal\Core\Ajax\HtmlCommand; /** + * Implements hook_system_theme_info(). + */ +function ajax_test_system_theme_info() { + $themes['test_theme'] = "module://system/tests/themes/test_theme/test_theme.info.yml"; + return $themes; +} + +/** * Menu callback: Returns an element suitable for use by * \Drupal\Core\Ajax\AjaxResponse::ajaxRender(). * diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module index b6f13c8..b8d3c2a 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -49,13 +49,21 @@ function theme_test_theme($existing, $type, $theme, $path) { $items['theme_test_function_template_override'] = array( 'variables' => array(), ); - $info['test_theme_not_existing_function'] = array( - 'function' => 'test_theme_not_existing_function', - ); return $items; } /** + * Implements hook_system_theme_info(). + */ +function theme_test_system_theme_info() { + $themes['test_theme'] = "module://system/tests/themes/test_theme/test_theme.info.yml"; + $themes['test_basetheme'] = "module://system/tests/themes/test_basetheme/test_basetheme.info.yml"; + $themes['test_subtheme'] = "module://system/tests/themes/test_subtheme/test_subtheme.info.yml"; + $themes['test_theme_phptemplate'] = "module://system/tests/themes/test_theme_phptemplate/test_theme_phptemplate.info.yml"; + return $themes; +} + +/** * Implements hook_preprocess_HOOK() for HTML document templates. */ function theme_test_preprocess_html(&$variables) { diff --git a/core/modules/update/tests/modules/update_test/update_test.module b/core/modules/update/tests/modules/update_test/update_test.module index 536d330..b99bf9e 100644 --- a/core/modules/update/tests/modules/update_test/update_test.module +++ b/core/modules/update/tests/modules/update_test/update_test.module @@ -10,6 +10,15 @@ */ /** + * Implements hook_system_theme_info(). + */ +function update_test_system_theme_info() { + $themes['update_test_basetheme'] = "module://update/tests/themes/update_test_basetheme/update_test_basetheme.info.yml"; + $themes['update_test_subtheme'] = "module://update/tests/themes/update_test_subtheme/update_test_subtheme.info.yml"; + return $themes; +} + +/** * Implements hook_system_info_alter(). * * Checks the 'update_test.settings:system_info' configuration and sees if we