diff --git a/core/MAINTAINERS.txt b/core/MAINTAINERS.txt index e93c71b10e..55020fc2e3 100644 --- a/core/MAINTAINERS.txt +++ b/core/MAINTAINERS.txt @@ -322,6 +322,9 @@ Page Cache Path - Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch +Path Alias +- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch + Plugin - Kris Vanderwater 'EclipseGc' https://www.drupal.org/u/eclipseGc - Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia diff --git a/core/composer.json b/core/composer.json index 2573c591d2..970d15ebfc 100644 --- a/core/composer.json +++ b/core/composer.json @@ -138,6 +138,7 @@ "drupal/options": "self.version", "drupal/page_cache": "self.version", "drupal/path": "self.version", + "drupal/path_alias": "self.version", "drupal/quickedit": "self.version", "drupal/rdf": "self.version", "drupal/responsive_image": "self.version", diff --git a/core/core.api.php b/core/core.api.php index cfb8800728..f54be27586 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -762,8 +762,8 @@ * A typical service definition in a *.services.yml file looks like this: * @code * path.alias_manager: - * class: Drupal\Core\Path\AliasManager - * arguments: ['@path.crud', '@path.alias_whitelist', '@language_manager'] + * class: Drupal\path_alias\AliasManager + * arguments: ['@path.crud', '@path_alias.whitelist', '@language_manager'] * @endcode * Some services use other services as factories; a typical service definition * is: diff --git a/core/core.services.yml b/core/core.services.yml index 5cdcc3efe5..9da94f6298 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -461,14 +461,18 @@ services: queue.database: class: Drupal\Core\Queue\QueueDatabaseFactory arguments: ['@database'] + # @deprecated in Drupal 8.8.x and will be removed before 9.0.0. Use the + # "path_alias.whitelist" service instead. + # See https://www.drupal.org/node/3092086 path.alias_whitelist: - class: Drupal\Core\Path\AliasWhitelist - tags: - - { name: needs_destruction } - arguments: [path_alias_whitelist, '@cache.bootstrap', '@lock', '@state', '@path.alias_repository'] + alias: path_alias.whitelist + deprecated: 'The "%alias_id%" service alias is deprecated. Use "path_alias.whitelist" instead. See https://drupal.org/node/3092086' + # @deprecated in Drupal 8.8.x and will be removed before 9.0.0. Use the + # "path_alias.manager" service instead. + # See https://www.drupal.org/node/3092086 path.alias_manager: class: Drupal\Core\Path\AliasManager - arguments: ['@path.alias_repository', '@path.alias_whitelist', '@language_manager', '@cache.data'] + arguments: ['@path_alias.repository', '@path_alias.whitelist', '@language_manager', '@cache.data'] path.current: class: Drupal\Core\Path\CurrentPathStack arguments: ['@request_stack'] @@ -960,17 +964,12 @@ services: arguments: ['@lock', '@plugin.manager.menu.link', '@database', '@database.replica_kill_switch'] tags: - { name: event_subscriber } - path.alias_repository: - class: Drupal\Core\Path\AliasRepository - arguments: ['@database'] - tags: - - { name: backend_overridable } path.alias_storage: class: Drupal\Core\Path\AliasStorage arguments: ['@database', '@module_handler', '@entity_type.manager'] tags: - { name: backend_overridable } - deprecated: The "%service_id%" service is deprecated. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865 + deprecated: The "%service_id%" service is deprecated. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865 path.matcher: class: Drupal\Core\Path\PathMatcher arguments: ['@config.factory', '@current_route_match'] @@ -1222,11 +1221,6 @@ services: arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user', '@bare_html_page_renderer', '@messenger'] tags: - { name: event_subscriber } - path_subscriber: - class: Drupal\Core\EventSubscriber\PathSubscriber - tags: - - { name: event_subscriber } - arguments: ['@path.alias_manager', '@path.current'] route_access_response_subscriber: class: Drupal\Core\EventSubscriber\RouteAccessResponseSubscriber tags: @@ -1357,12 +1351,6 @@ services: arguments: ['@current_route_match'] tags: - { name: route_processor_outbound, priority: 200 } - path_processor_alias: - class: Drupal\Core\PathProcessor\PathProcessorAlias - tags: - - { name: path_processor_inbound, priority: 100 } - - { name: path_processor_outbound, priority: 300 } - arguments: ['@path.alias_manager'] route_processor_csrf: class: Drupal\Core\Access\RouteProcessorCsrf tags: @@ -1755,3 +1743,34 @@ services: pager.parameters: class: Drupal\Core\Pager\PagerParameters arguments: ['@request_stack'] + + # Path Alias services, defined here to be available even when "path_alias" is + # not enabled yet. These will replace the legacy core services, which are now + # deprecated and will be removed in Drupal 9. + # See https://www.drupal.org/node/3092086 + # @todo Move these to "path_alias" once core fully supports it as an optional + # module. See https://www.drupal.org/node/3092090. + path_alias.subscriber: + class: Drupal\Core\EventSubscriber\PathSubscriber + tags: + - { name: event_subscriber } + arguments: ['@path.alias_manager', '@path.current'] + path_alias.path_processor: + class: Drupal\Core\PathProcessor\PathProcessorAlias + tags: + - { name: path_processor_inbound, priority: 100 } + - { name: path_processor_outbound, priority: 300 } + arguments: ['@path.alias_manager'] + path_alias.manager: + class: Drupal\Core\Path\AliasManager + arguments: ['@path_alias.repository', '@path_alias.whitelist', '@language_manager', '@cache.data'] + path_alias.repository: + class: Drupal\Core\Path\AliasRepository + arguments: ['@database'] + tags: + - { name: backend_overridable } + path_alias.whitelist: + class: Drupal\Core\Path\AliasWhitelist + tags: + - { name: needs_destruction } + arguments: [path_alias_whitelist, '@cache.bootstrap', '@lock', '@state', '@path_alias.repository'] diff --git a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php index 819fdf94b7..f25bc49b4a 100644 --- a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php @@ -11,6 +11,11 @@ /** * Provides a path subscriber that converts path aliases. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\EventSubscriber\PathAliasSubscriber. + * + * @see https://www.drupal.org/node/3092086 */ class PathSubscriber implements EventSubscriberInterface { @@ -39,6 +44,13 @@ class PathSubscriber implements EventSubscriberInterface { public function __construct(AliasManagerInterface $alias_manager, CurrentPathStack $current_path) { $this->aliasManager = $alias_manager; $this->currentPath = $current_path; + + // This is used as base class by the new class, so we do not trigger + // deprecation notices when that or any child class is instantiated. + $new_class = 'Drupal\path_alias\EventSubscriber\PathAliasSubscriber'; + if (!is_a($this, $new_class) && class_exists($new_class)) { + @trigger_error('The \\' . __CLASS__ . ' class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \\' . $new_class . '. See https://drupal.org/node/3092086', E_USER_DEPRECATED); + } } /** diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php index 59694bb2f8..77aaf8274b 100644 --- a/core/lib/Drupal/Core/Path/AliasManager.php +++ b/core/lib/Drupal/Core/Path/AliasManager.php @@ -7,9 +7,15 @@ use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; /** * The default alias manager implementation. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\AliasManager. + * + * @see https://www.drupal.org/node/3092086 */ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { @@ -115,12 +121,37 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { public function __construct($alias_repository, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) { if (!$alias_repository instanceof AliasRepositoryInterface) { @trigger_error('Passing the path.alias_storage service to AliasManager::__construct() is deprecated in drupal:8.8.0 and will be removed before drupal:9.0.0. Pass the new dependencies instead. See https://www.drupal.org/node/3013865.', E_USER_DEPRECATED); - $alias_repository = \Drupal::service('path.alias_repository'); + $alias_repository = \Drupal::service('path_alias.repository'); } $this->pathAliasRepository = $alias_repository; $this->languageManager = $language_manager; $this->whitelist = $whitelist; $this->cache = $cache; + + // This is used as base class by the new class, so we do not trigger + // deprecation notices when that or any child class is instantiated. + $new_class = 'Drupal\path_alias\AliasManager'; + if (!is_a($this, $new_class) && class_exists($new_class)) { + @trigger_error('The \\' . __CLASS__ . ' class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \\' . $new_class . '. See https://drupal.org/node/3092086', E_USER_DEPRECATED); + + // Despite being two different services, hence two different class + // instances, both the new and the legacy alias managers need to share the + // same internal state to keep the path/alias lookup optimizations + // working. + try { + $alias_manager = \Drupal::service('path_alias.manager'); + if ($alias_manager instanceof $new_class) { + $synced_properties = ['cacheKey', 'langcodePreloaded', 'lookupMap', 'noAlias', 'noPath', 'preloadedPathLookups']; + foreach ($synced_properties as $property) { + $this->{$property} = &$alias_manager->{$property}; + } + } + } + catch (ServiceCircularReferenceException $e) { + // This may happen during installation when "path_alias" has not swapped + // the alias manager class yet. Nothing to do in this case. + } + } } /** diff --git a/core/lib/Drupal/Core/Path/AliasManagerInterface.php b/core/lib/Drupal/Core/Path/AliasManagerInterface.php index 6ede000e41..0684223176 100644 --- a/core/lib/Drupal/Core/Path/AliasManagerInterface.php +++ b/core/lib/Drupal/Core/Path/AliasManagerInterface.php @@ -4,6 +4,11 @@ /** * Find an alias for a path and vice versa. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\AliasManagerInterface. + * + * @see https://www.drupal.org/node/3092086 */ interface AliasManagerInterface { diff --git a/core/lib/Drupal/Core/Path/AliasRepository.php b/core/lib/Drupal/Core/Path/AliasRepository.php index dbb14d800c..7c28161fdf 100644 --- a/core/lib/Drupal/Core/Path/AliasRepository.php +++ b/core/lib/Drupal/Core/Path/AliasRepository.php @@ -9,6 +9,11 @@ /** * Provides the default path alias lookup operations. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\AliasRepository. + * + * @see https://www.drupal.org/node/3092086 */ class AliasRepository implements AliasRepositoryInterface { @@ -27,6 +32,13 @@ class AliasRepository implements AliasRepositoryInterface { */ public function __construct(Connection $connection) { $this->connection = $connection; + + // This is used as base class by the new class, so we do not trigger + // deprecation notices when that or any child class is instantiated. + $new_class = 'Drupal\path_alias\AliasRepository'; + if (!is_a($this, $new_class) && class_exists($new_class)) { + @trigger_error('The \\' . __CLASS__ . ' class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \\' . $new_class . '. See https://drupal.org/node/3092086', E_USER_DEPRECATED); + } } /** diff --git a/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php b/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php index 15febd495b..2c8947d6c3 100644 --- a/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php +++ b/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php @@ -5,15 +5,10 @@ /** * Provides an interface for path alias lookup operations. * - * The path alias repository service is only used internally in order to - * optimize alias lookup queries needed in the critical path of each request. - * However, it is not marked as an internal service because alternative storage - * backends still need to override it if they provide a different storage class - * for the PathAlias entity type. + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\AliasRepositoryInterface. * - * Whenever you need to determine whether an alias exists for a system path, or - * whether a system path has an alias, the 'path.alias_manager' service should - * be used instead. + * @see https://www.drupal.org/node/3092086 */ interface AliasRepositoryInterface { diff --git a/core/lib/Drupal/Core/Path/AliasStorage.php b/core/lib/Drupal/Core/Path/AliasStorage.php index b0dfb5bc9b..d61860e9b9 100644 --- a/core/lib/Drupal/Core/Path/AliasStorage.php +++ b/core/lib/Drupal/Core/Path/AliasStorage.php @@ -9,7 +9,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageInterface; -@trigger_error('\Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865.', E_USER_DEPRECATED); +@trigger_error('\Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865.', E_USER_DEPRECATED); /** * Provides a class for CRUD operations on path aliases. @@ -19,7 +19,7 @@ * the same, and will both refer to the same internal system path. * * @deprecated \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and - * is removed from drupal:9.0.0. Use the "path.alias_repository" service + * is removed from drupal:9.0.0. Use the "path_alias.repository" service * instead, or the entity storage handler for the "path_alias" entity type * for CRUD methods. * @@ -82,7 +82,7 @@ public function save($source, $alias, $langcode = LanguageInterface::LANGCODE_NO } if ($pid) { - /** @var \Drupal\Core\Path\PathAliasInterface $path_alias */ + /** @var \Drupal\path_alias\PathAliasInterface $path_alias */ $path_alias = $this->getPathAliasEntityStorage()->load($pid); $original_values = [ 'source' => $path_alias->getPath(), @@ -142,7 +142,7 @@ public function load($conditions) { ->execute(); $entities = $this->getPathAliasEntityStorage()->loadMultiple($result); - /** @var \Drupal\Core\Path\PathAliasInterface $path_alias */ + /** @var \Drupal\path_alias\PathAliasInterface $path_alias */ $path_alias = reset($entities); if ($path_alias) { return [ @@ -326,6 +326,10 @@ public function getAliasesForAdminListing($header, $keys = NULL) { * {@inheritdoc} */ public function pathHasMatchingAlias($initial_substring) { + if (!$this->moduleHandler->moduleExists('path_alias')) { + return FALSE; + } + $query = $this->getBaseQuery(); $query->addExpression(1); diff --git a/core/lib/Drupal/Core/Path/AliasStorageInterface.php b/core/lib/Drupal/Core/Path/AliasStorageInterface.php index 24a7c627b1..3191faef4d 100644 --- a/core/lib/Drupal/Core/Path/AliasStorageInterface.php +++ b/core/lib/Drupal/Core/Path/AliasStorageInterface.php @@ -8,7 +8,7 @@ * Provides a class for CRUD operations on path aliases. * * @deprecated \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and - * is removed from drupal:9.0.0. Use the "path.alias_repository" service + * is removed from drupal:9.0.0. Use the "path_alias.repository" service * instead, or the entity storage handler for the "path_alias" entity type * for CRUD methods. * diff --git a/core/lib/Drupal/Core/Path/AliasWhitelist.php b/core/lib/Drupal/Core/Path/AliasWhitelist.php index 48b736280c..ff6c193f67 100644 --- a/core/lib/Drupal/Core/Path/AliasWhitelist.php +++ b/core/lib/Drupal/Core/Path/AliasWhitelist.php @@ -10,6 +10,11 @@ /** * Extends CacheCollector to build the path alias whitelist over time. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\AliasWhitelist. + * + * @see https://www.drupal.org/node/3092086 */ class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface { @@ -54,9 +59,16 @@ public function __construct($cid, CacheBackendInterface $cache, LockBackendInter if (!$alias_repository instanceof AliasRepositoryInterface) { @trigger_error('Passing the path.alias_storage service to AliasWhitelist::__construct() is deprecated in drupal:8.8.0 and will be removed before drupal:9.0.0. Pass the new dependencies instead. See https://www.drupal.org/node/3013865.', E_USER_DEPRECATED); - $alias_repository = \Drupal::service('path.alias_repository'); + $alias_repository = \Drupal::service('path_alias.repository'); } $this->pathAliasRepository = $alias_repository; + + // This is used as base class by the new class, so we do not trigger + // deprecation notices when that or any child class is instantiated. + $new_class = 'Drupal\path_alias\AliasWhitelist'; + if (!is_a($this, $new_class) && class_exists($new_class)) { + @trigger_error('The \\' . __CLASS__ . ' class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \\' . $new_class . '. See https://drupal.org/node/3092086', E_USER_DEPRECATED); + } } /** diff --git a/core/lib/Drupal/Core/Path/AliasWhitelistInterface.php b/core/lib/Drupal/Core/Path/AliasWhitelistInterface.php index ca2cf6300e..76ed7c36ce 100644 --- a/core/lib/Drupal/Core/Path/AliasWhitelistInterface.php +++ b/core/lib/Drupal/Core/Path/AliasWhitelistInterface.php @@ -7,9 +7,9 @@ /** * Cache the alias whitelist. * - * The whitelist contains the first element of the router paths of all - * aliases. For example, if /node/12345 has an alias then "node" is added to - * the whitelist. This optimization allows skipping the lookup for every - * /user/{user} path if "user" is not in the whitelist. + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\AliasWhitelistInterface. + * + * @see https://www.drupal.org/node/3092086 */ interface AliasWhitelistInterface extends CacheCollectorInterface {} diff --git a/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraintValidator.php b/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraintValidator.php index 2e95cc8653..ba4a785eeb 100644 --- a/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraintValidator.php +++ b/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraintValidator.php @@ -43,7 +43,7 @@ public static function create(ContainerInterface $container) { * {@inheritdoc} */ public function validate($entity, Constraint $constraint) { - /** @var \Drupal\Core\Path\PathAliasInterface $entity */ + /** @var \Drupal\path_alias\PathAliasInterface $entity */ $path = $entity->getPath(); $alias = $entity->getAlias(); $langcode = $entity->language()->getId(); diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php index d0690fee20..de7f29c6a7 100644 --- a/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php +++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php @@ -8,6 +8,11 @@ /** * Processes the inbound path using path alias lookups. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. + * Use \Drupal\path_alias\PathProcessor\AliasPathProcessor. + * + * @see https://www.drupal.org/node/3092086 */ class PathProcessorAlias implements InboundPathProcessorInterface, OutboundPathProcessorInterface { @@ -26,6 +31,13 @@ class PathProcessorAlias implements InboundPathProcessorInterface, OutboundPathP */ public function __construct(AliasManagerInterface $alias_manager) { $this->aliasManager = $alias_manager; + + // This is used as base class by the new class, so we do not trigger + // deprecation notices when that or any child class is instantiated. + $new_class = 'Drupal\path_alias\PathProcessor\AliasPathProcessor'; + if (!is_a($this, $new_class) && class_exists($new_class)) { + @trigger_error('The \\' . __CLASS__ . ' class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \\' . $new_class . '. See https://drupal.org/node/3092086', E_USER_DEPRECATED); + } } /** diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php index c635550812..627aa506cd 100644 --- a/core/lib/Drupal/Core/Routing/RouteProvider.php +++ b/core/lib/Drupal/Core/Routing/RouteProvider.php @@ -490,8 +490,8 @@ protected function getRouteCollectionCacheId(Request $request) { */ protected function getCurrentLanguageCacheIdPart() { // This must be in sync with the language logic in - // \Drupal\Core\PathProcessor\PathProcessorAlias::processInbound() and - // \Drupal\Core\Path\AliasManager::getPathByAlias(). + // \Drupal\path_alias\PathProcessor\AliasPathProcessor::processInbound() and + // \Drupal\path_alias\AliasManager::getPathByAlias(). // @todo Update this if necessary in https://www.drupal.org/node/1125428. return $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(); } diff --git a/core/lib/Drupal/Core/Update/UpdateServiceProvider.php b/core/lib/Drupal/Core/Update/UpdateServiceProvider.php index e26b019470..c663e9ae64 100644 --- a/core/lib/Drupal/Core/Update/UpdateServiceProvider.php +++ b/core/lib/Drupal/Core/Update/UpdateServiceProvider.php @@ -41,8 +41,8 @@ public function alter(ContainerBuilder $container) { // manager. We do this by removing the tags that the compiler pass looks // for. This means that the URL generator can safely be used during the // database update process. - if ($container->hasDefinition('path_processor_alias')) { - $container->getDefinition('path_processor_alias') + if ($container->hasDefinition('path_alias.path_processor')) { + $container->getDefinition('path_alias.path_processor') ->clearTag('path_processor_inbound') ->clearTag('path_processor_outbound'); } diff --git a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php index db255d79f8..be66745fdb 100644 --- a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php +++ b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php @@ -24,6 +24,7 @@ class MigrateBlockContentTranslationTest extends MigrateDrupal6TestBase { 'block_content', 'content_translation', 'language', + 'path_alias', 'statistics', 'taxonomy', // Required for translation migrations. diff --git a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php index 6ea998b0c9..2e3e55ee37 100644 --- a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php +++ b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php @@ -26,6 +26,7 @@ class MigrateBlockTest extends MigrateDrupal6TestBase { 'aggregator', 'book', 'forum', + 'path_alias', 'statistics', ]; diff --git a/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockContentTranslationTest.php b/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockContentTranslationTest.php index 3201d02b16..cb24a69ab3 100644 --- a/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockContentTranslationTest.php +++ b/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockContentTranslationTest.php @@ -28,6 +28,7 @@ class MigrateBlockContentTranslationTest extends MigrateDrupal7TestBase { 'config_translation', 'content_translation', 'language', + 'path_alias', 'statistics', 'taxonomy', // Required for translation migrations. diff --git a/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockTest.php b/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockTest.php index 929854165a..fca92af331 100644 --- a/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockTest.php +++ b/core/modules/block/tests/src/Kernel/Migrate/d7/MigrateBlockTest.php @@ -25,6 +25,7 @@ class MigrateBlockTest extends MigrateDrupal7TestBase { 'node', 'text', 'filter', + 'path_alias', 'user', ]; diff --git a/core/modules/config/tests/src/Functional/ConfigImportAllTest.php b/core/modules/config/tests/src/Functional/ConfigImportAllTest.php index 07d00def17..548a27c4c0 100644 --- a/core/modules/config/tests/src/Functional/ConfigImportAllTest.php +++ b/core/modules/config/tests/src/Functional/ConfigImportAllTest.php @@ -90,7 +90,7 @@ public function testInstallUninstall() { // Ensure that only core required modules and the install profile can not be uninstalled. $validation_reasons = \Drupal::service('module_installer')->validateUninstall(array_keys($all_modules)); - $this->assertEquals(['system', 'user', 'standard'], array_keys($validation_reasons)); + $this->assertEquals(['path_alias', 'system', 'user', 'standard'], array_keys($validation_reasons)); $modules_to_uninstall = array_filter($all_modules, function ($module) use ($validation_reasons) { // Filter required and not enabled modules. diff --git a/core/modules/jsonapi/tests/src/Functional/PathAliasTest.php b/core/modules/jsonapi/tests/src/Functional/PathAliasTest.php index 745f85f840..3b80c5f069 100644 --- a/core/modules/jsonapi/tests/src/Functional/PathAliasTest.php +++ b/core/modules/jsonapi/tests/src/Functional/PathAliasTest.php @@ -2,7 +2,7 @@ namespace Drupal\Tests\jsonapi\Functional; -use Drupal\Core\Path\Entity\PathAlias; +use Drupal\path_alias\Entity\PathAlias; use Drupal\Core\Url; /** diff --git a/core/modules/menu_link_content/menu_link_content.module b/core/modules/menu_link_content/menu_link_content.module index 48dafd76d3..def0ed9db0 100644 --- a/core/modules/menu_link_content/menu_link_content.module +++ b/core/modules/menu_link_content/menu_link_content.module @@ -7,7 +7,7 @@ use Drupal\Core\Url; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Path\PathAliasInterface; +use Drupal\path_alias\PathAliasInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\system\MenuInterface; diff --git a/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php b/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php index a00bbd1838..7980de9765 100644 --- a/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php +++ b/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php @@ -21,7 +21,7 @@ class PathAliasMenuLinkContentTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['menu_link_content', 'system', 'link', 'test_page_test', 'user']; + public static $modules = ['menu_link_content', 'system', 'link', 'path_alias', 'test_page_test', 'user']; /** * {@inheritdoc} @@ -44,7 +44,7 @@ protected function setUp() { public function register(ContainerBuilder $container) { parent::register($container); - $definition = $container->getDefinition('path_processor_alias'); + $definition = $container->getDefinition('path_alias.path_processor'); $definition ->addTag('path_processor_inbound', ['priority' => 100]); } diff --git a/core/modules/path/config/optional/language.content_settings.path_alias.path_alias.yml b/core/modules/path/config/optional/language.content_settings.path_alias.path_alias.yml index e7c75a72a8..daacf92e07 100644 --- a/core/modules/path/config/optional/language.content_settings.path_alias.path_alias.yml +++ b/core/modules/path/config/optional/language.content_settings.path_alias.path_alias.yml @@ -1,6 +1,8 @@ langcode: en status: true -dependencies: { } +dependencies: + module: + - path_alias id: path_alias.path_alias target_entity_type_id: path_alias target_bundle: path_alias diff --git a/core/modules/path/path.info.yml b/core/modules/path/path.info.yml index 2c3f23fcb5..f0b3aec706 100644 --- a/core/modules/path/path.info.yml +++ b/core/modules/path/path.info.yml @@ -4,3 +4,5 @@ description: 'Allows users to rename URLs.' package: Core version: VERSION configure: entity.path_alias.collection +dependencies: + - drupal:path_alias diff --git a/core/modules/path/path.module b/core/modules/path/path.module index 0e7ce166a3..40323b0d21 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -47,15 +47,19 @@ function path_help($route_name, RouteMatchInterface $route_match) { * Implements hook_entity_type_alter(). */ function path_entity_type_alter(array &$entity_types) { + // @todo Remove the conditional once core fully supports "path_alias" as an + // optional module. See https://drupal.org/node/3092090. /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ - $entity_types['path_alias']->setFormClass('default', PathAliasForm::class); - $entity_types['path_alias']->setFormClass('delete', ContentEntityDeleteForm::class); - $entity_types['path_alias']->setHandlerClass('route_provider', ['html' => AdminHtmlRouteProvider::class]); - $entity_types['path_alias']->setListBuilderClass(PathAliasListBuilder::class); - $entity_types['path_alias']->setLinkTemplate('collection', '/admin/config/search/path'); - $entity_types['path_alias']->setLinkTemplate('add-form', '/admin/config/search/path/add'); - $entity_types['path_alias']->setLinkTemplate('edit-form', '/admin/config/search/path/edit/{path_alias}'); - $entity_types['path_alias']->setLinkTemplate('delete-form', '/admin/config/search/path/delete/{path_alias}'); + if (isset($entity_types['path_alias'])) { + $entity_types['path_alias']->setFormClass('default', PathAliasForm::class); + $entity_types['path_alias']->setFormClass('delete', ContentEntityDeleteForm::class); + $entity_types['path_alias']->setHandlerClass('route_provider', ['html' => AdminHtmlRouteProvider::class]); + $entity_types['path_alias']->setListBuilderClass(PathAliasListBuilder::class); + $entity_types['path_alias']->setLinkTemplate('collection', '/admin/config/search/path'); + $entity_types['path_alias']->setLinkTemplate('add-form', '/admin/config/search/path/add'); + $entity_types['path_alias']->setLinkTemplate('edit-form', '/admin/config/search/path/edit/{path_alias}'); + $entity_types['path_alias']->setLinkTemplate('delete-form', '/admin/config/search/path/delete/{path_alias}'); + } } /** diff --git a/core/modules/path/src/PathAliasForm.php b/core/modules/path/src/PathAliasForm.php index 6a44309110..3ce2da6307 100644 --- a/core/modules/path/src/PathAliasForm.php +++ b/core/modules/path/src/PathAliasForm.php @@ -15,7 +15,7 @@ class PathAliasForm extends ContentEntityForm { /** * The path_alias entity. * - * @var \Drupal\Core\Path\PathAliasInterface + * @var \Drupal\path_alias\PathAliasInterface */ protected $entity; diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php b/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php index bdc20e60aa..a791d7afeb 100644 --- a/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php +++ b/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php @@ -27,7 +27,7 @@ protected function computeValue() { $entity = $this->getEntity(); if (!$entity->isNew()) { /** @var \Drupal\Core\Path\AliasRepositoryInterface $path_alias_repository */ - $path_alias_repository = \Drupal::service('path.alias_repository'); + $path_alias_repository = \Drupal::service('path_alias.repository'); if ($path_alias = $path_alias_repository->lookupBySystemPath('/' . $entity->toUrl()->getInternalPath(), $this->getLangcode())) { $value = [ diff --git a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php index 3962175b26..5ddc00902d 100644 --- a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php +++ b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php @@ -87,7 +87,7 @@ public static function validateFormElement(array &$element, FormStateInterface $ if (!empty($alias)) { $form_state->setValueForElement($element['alias'], $alias); - /** @var \Drupal\Core\Path\PathAliasInterface $path_alias */ + /** @var \Drupal\path_alias\PathAliasInterface $path_alias */ $path_alias = \Drupal::entityTypeManager()->getStorage('path_alias')->create([ 'path' => $element['source']['#value'], 'alias' => $alias, diff --git a/core/modules/path/tests/src/Functional/PathAliasTest.php b/core/modules/path/tests/src/Functional/PathAliasTest.php index 71e5783b9a..295430480c 100644 --- a/core/modules/path/tests/src/Functional/PathAliasTest.php +++ b/core/modules/path/tests/src/Functional/PathAliasTest.php @@ -326,7 +326,7 @@ public function testNodeAlias() { // Delete the node and check that the path alias is also deleted. $node5->delete(); - $path_alias = \Drupal::service('path.alias_repository')->lookUpBySystemPath('/node/' . $node5->id(), $node5->language()->getId()); + $path_alias = \Drupal::service('path_alias.repository')->lookUpBySystemPath('/node/' . $node5->id(), $node5->language()->getId()); $this->assertFalse($path_alias, 'Alias was successfully deleted when the referenced node was deleted.'); // Create sixth test node. diff --git a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php index c38f3d1f89..617fe73f0e 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php @@ -2,7 +2,7 @@ namespace Drupal\Tests\path\Kernel\Migrate\d6; -use Drupal\Core\Path\PathAliasInterface; +use Drupal\path_alias\PathAliasInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\Core\Database\Database; use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase; @@ -24,6 +24,7 @@ class MigrateUrlAliasTest extends MigrateDrupal6TestBase { 'language', 'content_translation', 'path', + 'path_alias', 'menu_ui', // Required for translation migrations. 'migrate_drupal_multilingual', @@ -57,7 +58,7 @@ protected function setUp() { * The path alias ID. * @param array $conditions * The path conditions. - * @param \Drupal\Core\Path\PathAliasInterface $path_alias + * @param \Drupal\path_alias\PathAliasInterface $path_alias * The path alias. */ private function assertPath($pid, $conditions, PathAliasInterface $path_alias) { diff --git a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php index 307e5172f6..e776a31f2d 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php @@ -13,6 +13,7 @@ class MigrateUrlAliasTest extends MigrateUrlAliasTestBase { * {@inheritdoc} */ public static $modules = [ + 'path_alias', 'content_translation', 'migrate_drupal_multilingual', ]; diff --git a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php index 5d9ca426b5..86a11fe71d 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php @@ -22,6 +22,7 @@ 'menu_ui', 'node', 'path', + 'path_alias', 'text', ]; diff --git a/core/modules/path/tests/src/Kernel/PathItemTest.php b/core/modules/path/tests/src/Kernel/PathItemTest.php index 5a39d698b3..3398116d45 100644 --- a/core/modules/path/tests/src/Kernel/PathItemTest.php +++ b/core/modules/path/tests/src/Kernel/PathItemTest.php @@ -19,7 +19,7 @@ class PathItemTest extends KernelTestBase { * * @var array */ - public static $modules = ['path', 'node', 'user', 'system', 'language', 'content_translation']; + public static $modules = ['path', 'path_alias', 'node', 'user', 'system', 'language', 'content_translation']; /** * {@inheritdoc} @@ -45,7 +45,7 @@ protected function setUp() { */ public function testPathItem() { /** @var \Drupal\Core\Path\AliasRepositoryInterface $alias_repository */ - $alias_repository = \Drupal::service('path.alias_repository'); + $alias_repository = \Drupal::service('path_alias.repository'); $node_storage = \Drupal::entityTypeManager()->getStorage('node'); diff --git a/core/modules/path/tests/src/Kernel/PathLegacyRoutesKernelTest.php b/core/modules/path/tests/src/Kernel/PathLegacyRoutesKernelTest.php index f83ad0bcb4..3cd8832b72 100644 --- a/core/modules/path/tests/src/Kernel/PathLegacyRoutesKernelTest.php +++ b/core/modules/path/tests/src/Kernel/PathLegacyRoutesKernelTest.php @@ -16,7 +16,7 @@ class PathLegacyRoutesKernelTest extends KernelTestBase { /** * {@inheritdoc} */ - protected static $modules = ['path']; + protected static $modules = ['path', 'path_alias']; /** * @expectedDeprecation The 'path.admin_add' route is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the 'entity.path_alias.add_form' route instead. See https://www.drupal.org/node/3013865 diff --git a/core/modules/path_alias/path_alias.info.yml b/core/modules/path_alias/path_alias.info.yml new file mode 100644 index 0000000000..76bdd35a48 --- /dev/null +++ b/core/modules/path_alias/path_alias.info.yml @@ -0,0 +1,7 @@ +name: Path alias +type: module +description: 'Provides the API allowing to rename URLs.' +package: Core +version: VERSION +required: true +hidden: true diff --git a/core/modules/path_alias/src/AliasManager.php b/core/modules/path_alias/src/AliasManager.php new file mode 100644 index 0000000000..a169b402ba --- /dev/null +++ b/core/modules/path_alias/src/AliasManager.php @@ -0,0 +1,10 @@ + [ + 'class' => PathAliasSubscriber::class, + 'core_class' => PathSubscriber::class, + 'new_service' => 'path_alias.subscriber', + ], + 'path_processor_alias' => [ + 'class' => AliasPathProcessor::class, + 'core_class' => PathProcessorAlias::class, + 'new_service' => 'path_alias.path_processor', + ], + 'path.alias_manager' => [ + 'class' => AliasManager::class, + 'core_class' => CoreAliasManager::class, + 'new_service' => 'path_alias.manager', + ], + 'path.alias_whitelist' => [ + 'class' => AliasWhitelist::class, + 'core_class' => CoreAliasWhitelist::class, + 'new_service' => 'path_alias.whitelist', + ], + 'path_alias.repository' => [ + 'class' => AliasRepository::class, + 'core_class' => CoreAliasRepository::class, + 'new_service' => 'path_alias.repository', + ], + ]; + + // Replace services only if core classes are implementing them to avoid + // overriding customizations not relying on decoration. + foreach ($service_map as $id => $info) { + // Mark legacy services as "deprecated". + $definition = $id !== $info['new_service'] && $container->hasDefinition($id) ? $container->getDefinition($id) : NULL; + if ($definition && $definition->getClass() === $info['core_class']) { + $definition->setDeprecated(TRUE, 'The "%service_id%" service is deprecated. Use "' . $info['new_service'] . '" instead. See https://drupal.org/node/3092086'); + } + // Also the new service's class is initially set to the legacy one, to + // avoid errors when the "path_alias" module is not enabled yet. Here we + // need to replace that as well. + $definition = $container->getDefinition($info['new_service']); + if ($definition && $definition->getClass() === $info['core_class']) { + $definition->setClass($info['class']); + } + } + } + +} diff --git a/core/lib/Drupal/Core/Path/PathAliasStorage.php b/core/modules/path_alias/src/PathAliasStorage.php similarity index 98% rename from core/lib/Drupal/Core/Path/PathAliasStorage.php rename to core/modules/path_alias/src/PathAliasStorage.php index ed765accbc..e3a936ed79 100644 --- a/core/lib/Drupal/Core/Path/PathAliasStorage.php +++ b/core/modules/path_alias/src/PathAliasStorage.php @@ -1,6 +1,6 @@ aliasManager = $alias_manager; + } + + /** + * {@inheritdoc} + */ + public function getPathByAlias($alias, $langcode = NULL) { + $this->aliasManager->getPathByAlias($alias, $langcode); + } + + /** + * {@inheritdoc} + */ + public function getAliasByPath($path, $langcode = NULL) { + return $this->aliasManager->getAliasByPath($path, $langcode); + } + + /** + * {@inheritdoc} + */ + public function cacheClear($source = NULL) { + $this->aliasManager->cacheClear($source); + } + +} diff --git a/core/modules/path_alias/tests/modules/path_alias_deprecated_test/src/NewAliasManager.php b/core/modules/path_alias/tests/modules/path_alias_deprecated_test/src/NewAliasManager.php new file mode 100644 index 0000000000..3adcb0ee9e --- /dev/null +++ b/core/modules/path_alias/tests/modules/path_alias_deprecated_test/src/NewAliasManager.php @@ -0,0 +1,30 @@ +getDefinition('path.alias_manager'); + $definition->setClass(static::$newClass); + } + + if (!static::$useDecorator) { + $decorator_id = 'path_alias_deprecated_test.path.alias_manager'; + if ($container->hasDefinition($decorator_id)) { + $container->removeDefinition($decorator_id); + } + } + } + +} diff --git a/core/tests/Drupal/FunctionalTests/Hal/PathAliasHalJsonAnonTest.php b/core/modules/path_alias/tests/src/Functional/Hal/PathAliasHalJsonAnonTest.php similarity index 85% rename from core/tests/Drupal/FunctionalTests/Hal/PathAliasHalJsonAnonTest.php rename to core/modules/path_alias/tests/src/Functional/Hal/PathAliasHalJsonAnonTest.php index 5c8c16c6a8..22e3f441af 100644 --- a/core/tests/Drupal/FunctionalTests/Hal/PathAliasHalJsonAnonTest.php +++ b/core/modules/path_alias/tests/src/Functional/Hal/PathAliasHalJsonAnonTest.php @@ -1,11 +1,12 @@ createPathAlias('/test-source-Case', '/test-alias'); - $path_alias_repository = $this->container->get('path.alias_repository'); + $path_alias_repository = $this->container->get('path_alias.repository'); $this->assertEquals('/test-alias', $path_alias_repository->lookupBySystemPath('/test-source-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['alias']); $this->assertEquals('/test-alias', $path_alias_repository->lookupBySystemPath('/test-source-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['alias']); } @@ -50,7 +55,7 @@ public function testLookupBySystemPath() { public function testLookupByAlias() { $this->createPathAlias('/test-source', '/test-alias-Case'); - $path_alias_repository = $this->container->get('path.alias_repository'); + $path_alias_repository = $this->container->get('path_alias.repository'); $this->assertEquals('/test-source', $path_alias_repository->lookupByAlias('/test-alias-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['path']); $this->assertEquals('/test-source', $path_alias_repository->lookupByAlias('/test-alias-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['path']); } @@ -112,8 +117,8 @@ public function testWhitelist() { $memoryCounterBackend = new MemoryCounterBackend(); // Create AliasManager and Path object. - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); - $aliasManager = new AliasManager($this->container->get('path.alias_repository'), $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path_alias.repository')); + $aliasManager = new AliasManager($this->container->get('path_alias.repository'), $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); // No alias for user and admin yet, so should be NULL. $this->assertNull($whitelist->get('user')); @@ -153,7 +158,7 @@ public function testWhitelist() { // Re-initialize the whitelist using the same cache backend, should load // from cache. - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path_alias.repository')); $this->assertNull($whitelist->get('user')); $this->assertTrue($whitelist->get('admin')); $this->assertNull($whitelist->get($this->randomMachineName())); @@ -173,8 +178,8 @@ public function testWhitelistCacheDeletionMidRequest() { $memoryCounterBackend = new MemoryCounterBackend(); // Create AliasManager and Path object. - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); - $aliasManager = new AliasManager($this->container->get('path.alias_repository'), $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path_alias.repository')); + $aliasManager = new AliasManager($this->container->get('path_alias.repository'), $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); // Whitelist cache should not exist at all yet. $this->assertFalse($memoryCounterBackend->get('path_alias_whitelist')); @@ -201,7 +206,7 @@ public function testWhitelistCacheDeletionMidRequest() { // Whitelist should load data from its cache, see that it hasn't done a // check for 'user' yet, perform the check, then mark the result to be // persisted to cache. - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path_alias.repository')); $this->assertTrue($whitelist->get('user')); // Delete the whitelist cache. This could happen from an outside process, diff --git a/core/modules/path_alias/tests/src/Kernel/DeprecatedServicesTest.php b/core/modules/path_alias/tests/src/Kernel/DeprecatedServicesTest.php new file mode 100644 index 0000000000..9e88d8d6a7 --- /dev/null +++ b/core/modules/path_alias/tests/src/Kernel/DeprecatedServicesTest.php @@ -0,0 +1,186 @@ +installEntitySchema('path_alias'); + } + + /** + * @expectedDeprecation The "path.alias_manager" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086 + */ + public function testAliasManagerDeprecation() { + $this->container->get('path.alias_manager'); + } + + /** + * @expectedDeprecation The "path.alias_manager" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086 + * @expectedDeprecation The \Drupal\Core\Path\AliasManager class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasManager. See https://drupal.org/node/3092086 + */ + public function testOverriddenServiceImplementation() { + $class = $this->setServiceClass(OverriddenAliasManager::class); + $this->assertServiceClass('path.alias_manager', $class); + $this->assertServiceClass('path_alias.manager', AliasManager::class); + } + + /** + * @expectedDeprecation The "path.alias_manager" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086 + */ + public function testNewServiceImplementation() { + $class = $this->setServiceClass(NewAliasManager::class); + $this->assertServiceClass('path.alias_manager', $class); + $this->assertServiceClass('path_alias.manager', AliasManager::class); + } + + /** + * @expectedDeprecation The "path_alias_deprecated_test.path.alias_manager.inner" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086 + * @expectedDeprecation The \Drupal\Core\Path\AliasManager class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasManager. See https://drupal.org/node/3092086 + */ + public function testDecoratorForOverriddenServiceImplementation() { + $this->setServiceClass(OverriddenAliasManager::class, TRUE); + $this->assertServiceClass('path.alias_manager', AliasManagerDecorator::class); + $this->assertServiceClass('path_alias.manager', AliasManager::class); + } + + /** + * @expectedDeprecation The "path_alias_deprecated_test.path.alias_manager.inner" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086 + */ + public function testDecoratorForNewServiceImplementation() { + $this->setServiceClass(NewAliasManager::class, TRUE); + $this->assertServiceClass('path.alias_manager', AliasManagerDecorator::class); + $this->assertServiceClass('path_alias.manager', AliasManager::class); + } + + /** + * @expectedDeprecation The "path.alias_manager" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086 + */ + public function testDefaultImplementations() { + $this->assertServiceClass('path.alias_manager', CoreAliasManager::class); + $this->assertServiceClass('path_alias.manager', AliasManager::class); + } + + /** + * No deprecation message expected. + */ + public function testRegularImplementation() { + $this->assertServiceClass('path_alias.manager', AliasManager::class); + } + + /** + * Test that the new alias manager and the legacy ones share the same state. + * + * @expectedDeprecation The \Drupal\Core\Path\AliasManager class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasManager. See https://drupal.org/node/3092086 + */ + public function testAliasManagerSharedState() { + /** @var \Drupal\Core\Path\AliasManager $legacy_alias_manager */ + $legacy_alias_manager = $this->container->get('path.alias_manager'); + /** @var \Drupal\path_alias\AliasManager $alias_manager */ + $alias_manager = $this->container->get('path_alias.manager'); + + $cache_key = $this->randomMachineName(); + $alias_manager->setCacheKey($cache_key); + $this->assertSharedProperty('preload-paths:' . $cache_key, $legacy_alias_manager, 'cacheKey'); + + $invalid_alias = '/' . $this->randomMachineName(); + $alias_manager->getPathByAlias($invalid_alias); + $this->assertSharedProperty(['en' => [$invalid_alias => TRUE]], $legacy_alias_manager, 'noPath'); + + $this->assertSharedProperty(FALSE, $legacy_alias_manager, 'preloadedPathLookups'); + + /** @var \Drupal\path_alias\Entity\PathAlias $alias */ + $alias = PathAlias::create([ + 'path' => '/' . $this->randomMachineName(), + 'alias' => $invalid_alias . '2', + ]); + $alias->save(); + + $this->assertSharedProperty([], $legacy_alias_manager, 'preloadedPathLookups'); + + /** @var \Drupal\Core\State\StateInterface $state */ + $state = $this->container->get('state'); + $state->set('router.path_roots', [ltrim($alias->getPath(), '/')]); + + $alias_manager->getAliasByPath($alias->getPath()); + $this->assertSharedProperty(['en' => [$alias->getPath() => $alias->getAlias()]], $legacy_alias_manager, 'lookupMap'); + + $invalid_path = $alias->getPath() . '/' . $this->randomMachineName(); + $alias_manager->getAliasByPath($invalid_path); + $this->assertSharedProperty(['en' => [$invalid_path => TRUE]], $legacy_alias_manager, 'noAlias'); + } + + /** + * Asserts that a shared property has the expected value. + * + * @param mixed $expected + * The property expected value. + * @param \Drupal\Core\Path\AliasManager $legacy_alias_manager + * An instance of the legacy alias manager. + * @param string $property + * The property name. + */ + protected function assertSharedProperty($expected, CoreAliasManager $legacy_alias_manager, $property) { + $reflector = new \ReflectionProperty(get_class($legacy_alias_manager), $property); + $reflector->setAccessible(TRUE); + $this->assertSame($expected, $reflector->getValue($legacy_alias_manager)); + } + + /** + * Asserts that the specified service is implemented by the expected class. + * + * @param string $service_id + * A service ID. + * @param string $expected_class + * The name of the expected class. + */ + protected function assertServiceClass($service_id, $expected_class) { + $service = $this->container->get($service_id); + $this->assertSame(get_class($service), $expected_class); + } + + /** + * Sets the specified implementation for the service being tested. + * + * @param string $class + * The name of the implementation class. + * @param bool $use_decorator + * (optional) Whether using a decorator service to wrap the specified class. + * Defaults to no decorator. + * + * @return string + * The specified class name. + */ + protected function setServiceClass($class, $use_decorator = FALSE) { + PathAliasDeprecatedTestServiceProvider::$newClass = $class; + PathAliasDeprecatedTestServiceProvider::$useDecorator = $use_decorator; + $this->container->get('kernel')->rebuildContainer(); + return $class; + } + +} diff --git a/core/modules/system/tests/src/Kernel/PathHooksTest.php b/core/modules/path_alias/tests/src/Kernel/PathHooksTest.php similarity index 88% rename from core/modules/system/tests/src/Kernel/PathHooksTest.php rename to core/modules/path_alias/tests/src/Kernel/PathHooksTest.php index cef1211928..2a7b867431 100644 --- a/core/modules/system/tests/src/Kernel/PathHooksTest.php +++ b/core/modules/path_alias/tests/src/Kernel/PathHooksTest.php @@ -1,23 +1,23 @@ aliasRepository = $this->createMock(AliasRepositoryInterface::class); - $this->aliasWhitelist = $this->createMock('Drupal\Core\Path\AliasWhitelistInterface'); + $this->aliasWhitelist = $this->createMock('Drupal\path_alias\AliasWhitelistInterface'); $this->languageManager = $this->createMock('Drupal\Core\Language\LanguageManagerInterface'); $this->cache = $this->createMock('Drupal\Core\Cache\CacheBackendInterface'); diff --git a/core/modules/path_alias/tests/src/Unit/DeprecatedClassesTest.php b/core/modules/path_alias/tests/src/Unit/DeprecatedClassesTest.php new file mode 100644 index 0000000000..8ac7df49d3 --- /dev/null +++ b/core/modules/path_alias/tests/src/Unit/DeprecatedClassesTest.php @@ -0,0 +1,170 @@ +aliasManager = $this->prophesize(AliasManagerInterface::class) + ->reveal(); + $this->aliasRepository = $this->prophesize(AliasRepositoryInterface::class) + ->reveal(); + $this->aliasWhitelist = $this->prophesize(AliasWhitelistInterface::class) + ->reveal(); + $this->cache = $this->prophesize(CacheBackendInterface::class) + ->reveal(); + $this->currentPathStack = $this->prophesize(CurrentPathStack::class) + ->reveal(); + $this->languageManager = $this->prophesize(LanguageManagerInterface::class) + ->reveal(); + $this->lock = $this->prophesize(LockBackendInterface::class) + ->reveal(); + $this->state = $this->prophesize(StateInterface::class) + ->reveal(); + + /** @var \Prophecy\Prophecy\ObjectProphecy $container */ + $container = $this->prophesize(ContainerBuilder::class); + $container->get('path_alias.manager') + ->willReturn($this->aliasManager); + \Drupal::setContainer($container->reveal()); + } + + /** + * @covers \Drupal\Core\EventSubscriber\PathSubscriber::__construct + * + * @expectedDeprecation The \Drupal\Core\EventSubscriber\PathSubscriber class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\EventSubscriber\PathAliasSubscriber. See https://drupal.org/node/3092086 + */ + public function testPathSubscriber() { + new PathSubscriber($this->aliasManager, $this->currentPathStack); + } + + /** + * @covers \Drupal\path_alias\EventSubscriber\PathAliasSubscriber::__construct + */ + public function testPathAliasSubscriber() { + $object = new PathAliasSubscriber($this->aliasManager, $this->currentPathStack); + $this->assertInstanceOf(PathSubscriber::class, $object); + } + + /** + * @covers \Drupal\Core\PathProcessor\PathProcessorAlias::__construct + * + * @expectedDeprecation The \Drupal\Core\PathProcessor\PathProcessorAlias class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\PathProcessor\AliasPathProcessor. See https://drupal.org/node/3092086 + */ + public function testPathProcessorAlias() { + new PathProcessorAlias($this->aliasManager); + } + + /** + * @covers \Drupal\path_alias\PathProcessor\AliasPathProcessor::__construct + */ + public function testAliasPathProcessor() { + $object = new AliasPathProcessor($this->aliasManager); + $this->assertInstanceOf(PathProcessorAlias::class, $object); + } + + /** + * @covers \Drupal\Core\Path\AliasManager::__construct + * + * @expectedDeprecation The \Drupal\Core\Path\AliasManager class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasManager. See https://drupal.org/node/3092086 + */ + public function testCoreAliasManager() { + new CoreAliasManager($this->aliasRepository, $this->aliasWhitelist, $this->languageManager, $this->cache); + } + + /** + * @covers \Drupal\path_alias\AliasManager::__construct + */ + public function testAliasManager() { + $object = new AliasManager($this->aliasRepository, $this->aliasWhitelist, $this->languageManager, $this->cache); + $this->assertInstanceOf(CoreAliasManager::class, $object); + } + + /** + * @covers \Drupal\Core\Path\AliasWhitelist::__construct + * + * @expectedDeprecation The \Drupal\Core\Path\AliasWhitelist class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasWhitelist. See https://drupal.org/node/3092086 + */ + public function testCoreAliasWhitelist() { + new CoreAliasWhitelist('path_alias_whitelist', $this->cache, $this->lock, $this->state, $this->aliasRepository); + } + + /** + * @covers \Drupal\path_alias\AliasWhitelist::__construct + */ + public function testAliasWhitelist() { + $object = new AliasWhitelist('path_alias_whitelist', $this->cache, $this->lock, $this->state, $this->aliasRepository); + $this->assertInstanceOf(CoreAliasWhitelist::class, $object); + } + +} diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorAliasTest.php b/core/modules/path_alias/tests/src/Unit/PathProcessor/AliasPathProcessorTest.php similarity index 75% rename from core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorAliasTest.php rename to core/modules/path_alias/tests/src/Unit/PathProcessor/AliasPathProcessorTest.php index 8a2a17ecd6..4f28f64953 100644 --- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorAliasTest.php +++ b/core/modules/path_alias/tests/src/Unit/PathProcessor/AliasPathProcessorTest.php @@ -1,42 +1,43 @@ aliasManager = $this->createMock('Drupal\Core\Path\AliasManagerInterface'); - $this->pathProcessor = new PathProcessorAlias($this->aliasManager); + $this->aliasManager = $this->createMock('Drupal\path_alias\AliasManagerInterface'); + $this->pathProcessor = new AliasPathProcessor($this->aliasManager); } /** * Tests the processInbound method. * - * @see \Drupal\Core\PathProcessor\PathProcessorAlias::processInbound + * @see \Drupal\path_alias\PathProcessor\AliasPathProcessor::processInbound */ public function testProcessInbound() { $this->aliasManager->expects($this->exactly(2)) diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index b212bc5961..1464c02305 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -385,12 +385,12 @@ public function containerBuild(ContainerBuilder $container) { ->addArgument(new Reference('keyvalue')); } - if ($container->hasDefinition('path_processor_alias')) { + if ($container->hasDefinition('path_alias.path_processor')) { // The alias-based processor requires the path_alias entity schema to be // installed, so we prevent it from being registered to the path processor // manager. We do this by removing the tags that the compiler pass looks // for. This means that the URL generator can safely be used within tests. - $definition = $container->getDefinition('path_processor_alias'); + $definition = $container->getDefinition('path_alias.path_processor'); $definition->clearTag('path_processor_inbound')->clearTag('path_processor_outbound'); } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 82fb06aef9..c0793611b6 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -21,9 +21,9 @@ use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Path\Entity\PathAlias; -use Drupal\Core\Path\PathAliasStorage; -use Drupal\Core\Path\PathAliasStorageSchema; +use Drupal\path_alias\Entity\PathAlias; +use Drupal\path_alias\PathAliasStorage; +use Drupal\path_alias\PathAliasStorageSchema; use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\PrivateStream; use Drupal\Core\StreamWrapper\PublicStream; @@ -2395,6 +2395,11 @@ function system_update_8802() { * Install the 'path_alias' entity type. */ function system_update_8803() { + // Enable the Path alias module if needed. + if (!\Drupal::moduleHandler()->moduleExists('path_alias')) { + \Drupal::service('module_installer')->install(['path_alias'], FALSE); + } + $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); if (!$entity_definition_update_manager->getEntityType('path_alias')) { $entity_type = new ContentEntityType([ @@ -2550,3 +2555,39 @@ function system_update_8804(&$sandbox = NULL) { return t('Path aliases have been converted to entities.'); } } + +/** + * Change the provider of the 'path_alias' entity type and its base fields. + */ +function system_update_8805() { + // If the path alias module is not installed, it means that + // system_update_8803() ran as part of Drupal 8.8.0-alpha1, in which case we + // need to enable the module and change the provider of the 'path_alias' + // entity type. + if (!\Drupal::moduleHandler()->moduleExists('path_alias')) { + \Drupal::service('module_installer')->install(['path_alias'], FALSE); + + /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */ + $last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository'); + $entity_type = $last_installed_schema_repository->getLastInstalledDefinition('path_alias'); + + // Set the new class for the entity type. + $entity_type->setClass(PathAlias::class); + + // Set the new provider for the entity type. + $reflection = new ReflectionClass($entity_type); + $property = $reflection->getProperty('provider'); + $property->setAccessible(TRUE); + $property->setValue($entity_type, 'path_alias'); + + $last_installed_schema_repository->setLastInstalledDefinition($entity_type); + + $field_storage_definitions = $last_installed_schema_repository->getLastInstalledFieldStorageDefinitions('path_alias'); + foreach ($field_storage_definitions as $field_storage_definition) { + if ($field_storage_definition->isBaseField()) { + $field_storage_definition->setProvider('path_alias'); + $last_installed_schema_repository->setLastInstalledFieldStorageDefinition($field_storage_definition); + } + } + } +} diff --git a/core/modules/system/tests/src/Functional/Update/PathAliasToEntityUpdateTest.php b/core/modules/system/tests/src/Functional/Update/PathAliasToEntityUpdateTest.php index e688faab80..0d0a1401b9 100644 --- a/core/modules/system/tests/src/Functional/Update/PathAliasToEntityUpdateTest.php +++ b/core/modules/system/tests/src/Functional/Update/PathAliasToEntityUpdateTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\system\Functional\Update; use Drupal\FunctionalTests\Update\UpdatePathTestBase; +use Drupal\path_alias\Entity\PathAlias; /** * Tests the conversion of path aliases to entities. @@ -52,6 +53,23 @@ public function testConversionToEntities() { $this->runUpdates(); + /** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */ + $module_handler = $this->container->get('module_handler'); + $this->assertTrue($module_handler->moduleExists('path_alias')); + + $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('path_alias'); + $this->assertEquals('path_alias', $entity_type->getProvider()); + $this->assertEquals(PathAlias::class, $entity_type->getClass()); + + $field_storage_definitions = \Drupal::service('entity.last_installed_schema.repository')->getLastInstalledFieldStorageDefinitions('path_alias'); + $this->assertEquals('path_alias', $field_storage_definitions['id']->getProvider()); + $this->assertEquals('path_alias', $field_storage_definitions['revision_id']->getProvider()); + $this->assertEquals('path_alias', $field_storage_definitions['langcode']->getProvider()); + $this->assertEquals('path_alias', $field_storage_definitions['uuid']->getProvider()); + $this->assertEquals('path_alias', $field_storage_definitions['status']->getProvider()); + $this->assertEquals('path_alias', $field_storage_definitions['path']->getProvider()); + $this->assertEquals('path_alias', $field_storage_definitions['alias']->getProvider()); + // Check that the 'path_alias' entity tables have been created and the // 'url_alias' table has been deleted. $this->assertTrue($schema->tableExists('path_alias')); diff --git a/core/modules/system/tests/src/Kernel/DeprecatedPathHooksTest.php b/core/modules/system/tests/src/Kernel/DeprecatedPathHooksTest.php index 75f3ae4a7f..b896913785 100644 --- a/core/modules/system/tests/src/Kernel/DeprecatedPathHooksTest.php +++ b/core/modules/system/tests/src/Kernel/DeprecatedPathHooksTest.php @@ -16,7 +16,7 @@ class DeprecatedPathHooksTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['system', 'path_deprecated_test']; + public static $modules = ['system', 'path_alias', 'path_deprecated_test']; /** * {@inheritdoc} diff --git a/core/modules/user/tests/src/Kernel/Views/ArgumentValidateTest.php b/core/modules/user/tests/src/Kernel/Views/ArgumentValidateTest.php index 76bfad7ce4..62b49e6ec6 100644 --- a/core/modules/user/tests/src/Kernel/Views/ArgumentValidateTest.php +++ b/core/modules/user/tests/src/Kernel/Views/ArgumentValidateTest.php @@ -23,6 +23,7 @@ class ArgumentValidateTest extends ViewsKernelTestBase { */ public static $modules = [ 'node', + 'path_alias', 'user_test_views', ]; diff --git a/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php b/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php index 6d1ee400dd..3f2cf3bae6 100644 --- a/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php +++ b/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php @@ -28,7 +28,7 @@ /** * {@inheritdoc} */ - public static $modules = ['system', 'views', 'views_test_config', 'views_test_data', 'user']; + public static $modules = ['path_alias', 'system', 'views', 'views_test_config', 'views_test_data', 'user']; /** * {@inheritdoc} diff --git a/core/modules/workspaces/src/WorkspacesAliasRepository.php b/core/modules/workspaces/src/WorkspacesAliasRepository.php index e1a1e82834..6230f04f4d 100644 --- a/core/modules/workspaces/src/WorkspacesAliasRepository.php +++ b/core/modules/workspaces/src/WorkspacesAliasRepository.php @@ -2,7 +2,7 @@ namespace Drupal\workspaces; -use Drupal\Core\Path\AliasRepository; +use Drupal\path_alias\AliasRepository; /** * Provides workspace-specific path alias lookup queries. diff --git a/core/modules/workspaces/src/WorkspacesServiceProvider.php b/core/modules/workspaces/src/WorkspacesServiceProvider.php index 1ed61747aa..712af5f368 100644 --- a/core/modules/workspaces/src/WorkspacesServiceProvider.php +++ b/core/modules/workspaces/src/WorkspacesServiceProvider.php @@ -20,8 +20,8 @@ public function alter(ContainerBuilder $container) { $renderer_config['required_cache_contexts'][] = 'workspace'; $container->setParameter('renderer.config', $renderer_config); - // Replace the class of the 'path.alias_repository' service. - $container->getDefinition('path.alias_repository') + // Replace the class of the 'path_alias.repository' service. + $container->getDefinition('path_alias.repository') ->setClass(WorkspacesAliasRepository::class) ->addMethodCall('setWorkspacesManager', [new Reference('workspaces.manager')]); } diff --git a/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php b/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php index af0ffd89ed..ad2c6eaede 100644 --- a/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php +++ b/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php @@ -86,7 +86,7 @@ class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['system', 'user']; + public static $modules = ['system', 'user', 'path_alias']; /** * {@inheritdoc} @@ -124,6 +124,7 @@ protected function setUp() { $this->allModules = array_keys($all_modules); $this->allModules[] = 'system'; $this->allModules[] = 'user'; + $this->allModules[] = 'path_alias'; sort($this->allModules); $this->container->get('module_installer')->install($this->allModules); diff --git a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php index f0f430e2c8..f5f8456848 100644 --- a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php +++ b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php @@ -25,7 +25,7 @@ class DbDumpTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['system', 'config', 'dblog', 'menu_link_content', 'link', 'block_content', 'file', 'user']; + public static $modules = ['system', 'config', 'dblog', 'menu_link_content', 'link', 'block_content', 'file', 'path_alias', 'user']; /** * Test data to write into config. diff --git a/core/tests/Drupal/KernelTests/Core/Config/CacheabilityMetadataConfigOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Config/CacheabilityMetadataConfigOverrideTest.php index 7f3e44bb40..ed9147f09f 100644 --- a/core/tests/Drupal/KernelTests/Core/Config/CacheabilityMetadataConfigOverrideTest.php +++ b/core/tests/Drupal/KernelTests/Core/Config/CacheabilityMetadataConfigOverrideTest.php @@ -20,6 +20,7 @@ class CacheabilityMetadataConfigOverrideTest extends KernelTestBase { 'block_content', 'config', 'config_override_test', + 'path_alias', 'system', 'user', ]; diff --git a/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php b/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php index 0efc69ef14..f4eeec9c05 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php @@ -25,7 +25,7 @@ class CreateSampleEntityTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['system', 'field', 'filter', 'text', 'file', 'user', 'node', 'comment', 'taxonomy']; + public static $modules = ['path_alias', 'system', 'field', 'filter', 'text', 'file', 'user', 'node', 'comment', 'taxonomy']; /** * {@inheritdoc} diff --git a/core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php b/core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php index dc15422796..aff96fa383 100644 --- a/core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php +++ b/core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php @@ -15,7 +15,7 @@ class LegacyAliasStorageTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['system']; + public static $modules = ['system', 'path_alias']; /** * @var \Drupal\Core\Path\AliasStorage @@ -34,7 +34,7 @@ protected function setUp() { /** * @covers ::load - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testLoad() { $this->storage->save('/test-source-Case', '/test-alias-Case'); @@ -56,7 +56,7 @@ public function testLoad() { * @covers ::load * @covers ::save * @covers ::delete - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testCRUD() { $entity_storage = \Drupal::entityTypeManager()->getStorage('path_alias'); @@ -148,7 +148,7 @@ protected function sampleUrlAliases() { /** * @covers ::preloadPathAlias - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testPreLoadPathAlias() { $this->storage->save('/test-source-Case', '/test-alias'); @@ -158,7 +158,7 @@ public function testPreLoadPathAlias() { /** * @covers ::lookupPathAlias - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testLookupPathAlias() { $this->storage->save('/test-source-Case', '/test-alias'); @@ -169,7 +169,7 @@ public function testLookupPathAlias() { /** * @covers ::lookupPathSource - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testLookupPathSource() { $this->storage->save('/test-source', '/test-alias-Case'); @@ -180,7 +180,7 @@ public function testLookupPathSource() { /** * @covers ::aliasExists - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testAliasExists() { $this->storage->save('/test-source-Case', '/test-alias-Case'); @@ -191,7 +191,7 @@ public function testAliasExists() { /** * @covers ::languageAliasExists - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testLanguageAliasExists() { $this->assertFalse($this->storage->languageAliasExists()); @@ -202,7 +202,7 @@ public function testLanguageAliasExists() { /** * @covers ::getAliasesForAdminListing - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testGetAliasesForAdminListing() { $this->storage->save('/test-source-Case', '/test-alias-Case'); @@ -226,7 +226,7 @@ public function testGetAliasesForAdminListing() { /** * @covers ::pathHasMatchingAlias - * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. */ public function testPathHasMatchingAlias() { $this->storage->save('/test-source-Case', '/test-alias-Case'); diff --git a/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php b/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php index a393a1ceeb..d05170deb9 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php @@ -20,7 +20,7 @@ class ContentNegotiationRoutingTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['conneg_test']; + public static $modules = ['conneg_test', 'path_alias']; /** * {@inheritdoc} @@ -39,8 +39,8 @@ public function register(ContainerBuilder $container) { // \Drupal\KernelTests\KernelTestBase::register() removes the alias path // processor. - if ($container->hasDefinition('path_processor_alias')) { - $definition = $container->getDefinition('path_processor_alias'); + if ($container->hasDefinition('path_alias.path_processor')) { + $definition = $container->getDefinition('path_alias.path_processor'); $definition->addTag('path_processor_inbound', ['priority' => 100])->addTag('path_processor_outbound', ['priority' => 300]); } } diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php index bb1380faa7..57a8aa06dc 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php @@ -38,7 +38,7 @@ class RouteProviderTest extends KernelTestBase { /** * Modules to enable. */ - public static $modules = ['url_alter_test', 'system', 'language']; + public static $modules = ['url_alter_test', 'system', 'language', 'path_alias']; /** * A collection of shared fixture data for tests. @@ -100,8 +100,8 @@ public function register(ContainerBuilder $container) { parent::register($container); // Read the incoming path alias for these tests. - if ($container->hasDefinition('path_processor_alias')) { - $definition = $container->getDefinition('path_processor_alias'); + if ($container->hasDefinition('path_alias.path_processor')) { + $definition = $container->getDefinition('path_alias.path_processor'); $definition->addTag('path_processor_inbound'); } } diff --git a/core/tests/Drupal/KernelTests/Core/Theme/StableLibraryOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Theme/StableLibraryOverrideTest.php index 795ea33466..ec11d3dab6 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/StableLibraryOverrideTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/StableLibraryOverrideTest.php @@ -49,7 +49,7 @@ class StableLibraryOverrideTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['system', 'user']; + public static $modules = ['system', 'user', 'path_alias']; /** * {@inheritdoc} @@ -72,6 +72,7 @@ protected function setUp() { $this->allModules = array_keys($all_modules); $this->allModules[] = 'system'; $this->allModules[] = 'user'; + $this->allModules[] = 'path_alias'; sort($this->allModules); $this->container->get('module_installer')->install($this->allModules); diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index dfda4a9fc0..0d13d9d2d3 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -538,12 +538,12 @@ public function register(ContainerBuilder $container) { ->addTag('event_subscriber'); } - if ($container->hasDefinition('path_processor_alias')) { + if ($container->hasDefinition('path_alias.path_processor')) { // The alias-based processor requires the path_alias entity schema to be // installed, so we prevent it from being registered to the path processor // manager. We do this by removing the tags that the compiler pass looks // for. This means that the URL generator can safely be used within tests. - $container->getDefinition('path_processor_alias') + $container->getDefinition('path_alias.path_processor') ->clearTag('path_processor_inbound') ->clearTag('path_processor_outbound'); } diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php index b669a36258..de82e65839 100644 --- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php +++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php @@ -192,6 +192,15 @@ public static function getSkippedDeprecations() { // The following deprecation is listed for Twig 2 compatibility when unit // testing using \Symfony\Component\ErrorHandler\DebugClassLoader. 'The "Twig\Environment::getTemplateClass()" method is considered internal. It may change without further notice. You should not extend it from "Drupal\Core\Template\TwigEnvironment".', + // These deprecations are triggered when instantiating path alias + // services. The new versions live in the "path_alias" module. + // @todo Remove in https://www.drupal.org/node/3092090. + 'The "path.alias_manager" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086', + 'The \Drupal\Core\EventSubscriber\PathSubscriber class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\EventSubscriber\PathAliasSubscriber. See https://drupal.org/node/3092086', + 'The \Drupal\Core\PathProcessor\PathProcessorAlias class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\PathProcessor\AliasPathProcessor. See https://drupal.org/node/3092086', + 'The \Drupal\Core\Path\AliasManager class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasManager. See https://drupal.org/node/3092086', + 'The \Drupal\Core\Path\AliasRepository class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasRepository. See https://drupal.org/node/3092086', + 'The \Drupal\Core\Path\AliasWhitelist class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \Drupal\path_alias\AliasWhitelist. See https://drupal.org/node/3092086', ]; } diff --git a/core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php b/core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php index 2f12e4c399..24153b3ba2 100644 --- a/core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php +++ b/core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php @@ -22,11 +22,11 @@ * (optional) A language code for the path alias. Defaults to * \Drupal\Core\Language\LanguageInterface::LANGCODE_NOT_SPECIFIED. * - * @return \Drupal\Core\Path\PathAliasInterface + * @return \Drupal\path_alias\PathAliasInterface * A path alias entity. */ protected function createPathAlias($path, $alias, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) { - /** @var \Drupal\Core\Path\PathAliasInterface $path_alias */ + /** @var \Drupal\path_alias\PathAliasInterface $path_alias */ $path_alias = \Drupal::entityTypeManager()->getStorage('path_alias')->create([ 'path' => $path, 'alias' => $alias, @@ -43,7 +43,7 @@ protected function createPathAlias($path, $alias, $langcode = LanguageInterface: * @param array $conditions * An array of query conditions. * - * @return \Drupal\Core\Path\PathAliasInterface|null + * @return \Drupal\path_alias\PathAliasInterface|null * A path alias entity or NULL. */ protected function loadPathAliasByConditions($conditions) { diff --git a/core/tests/fixtures/config_install/multilingual.tar.gz b/core/tests/fixtures/config_install/multilingual.tar.gz index e4961cc7de..e60ae04ec2 100644 --- a/core/tests/fixtures/config_install/multilingual.tar.gz +++ b/core/tests/fixtures/config_install/multilingual.tar.gz @@ -1,47 +1,40 @@ -J,]ms<~ -7W!!wZ>չgE-SϜ~hmv_{3- ;_V;?4 9ޯq,iFe#YU15 ˢIG;l}\}۷ԦSTq , 1*-~Gnjw"Z8 Ns?'OǸGrtN/Z@.X!qo+v.Q>_>mJ#w<1؏ df\>_$o/-[zA¡SÓnݤP$/\$j7>9cO|roڎ'siN>X漩Li&nF_Ό):7$ax|D{~f.| BXDfg(j2'dy9cyWN6"T؄m*z;Yov =~m5U؊pCg eb]#DXeIEGz0B>)-Y.^ÇR[@ޅ+ -vwz7"-~ت" zi`8F3tBK/I8? KAYҙ́X"+H8fӠIX{a|~->.Y9^N\ʽo<_}ͦa|‹nXW}lp[J}-Ttp$Gq>wutwUsFX4oFg#!7?"/QѵP)>UV/Om*IV?XPeȐPCOclm?#N9]m\zca+MgI'6*EK\IDg0~h7rq̞uxa;ÑQ:#/o,[HH\b\*eBc?Xg[iApD^X=%[ T 薁E 62*X9&nBUt{6HbFa=rap SjU|;=Eo2SDd k $-%.:`@Mf -0;&7>taS\ uOy֟kw?Cct/,%X#( IC۪==uuB;=6r%NI Ȟvc?LHbKVmY%݊T,j 2TIߛHΓb]x4c}]; Af3cON|7qaJ%e;(b_x *Pl+McTl}-]پOF ' ." Uy8YSBigK}qa..‽r˿eOVQJt@ncq$1L6dkJbEus/tngCnuA )Lx=! -i9xer!wHo{OjO[0:^ښ [F4>ߟ_}2}y\IA#^ |ngaW3B0Ys?5Sm*&^~v/Y?[2 P6 @'mQ±F>3ϧX>k}vK{S *Rs|kH#l V~I&ϵ>fg_U?K7x4Wu KE8`QrƐl zl`5}:Ex"%<"3)^ ؆dNx8&{ťGFM\wz}.th:)M?>E篖XC&%ˡpd)"w]Dt8vk8/ -OxD~ =`#J9{vnZ "zs;Lfw)_21Ԍ O(ϸ;Y4L89jup-mP#fS@a Ym{UթZOV=/()? 9~*9P`N{S;kڍT ]BzFqܷ=Od*:ʦEB7IhxC)\TU)^;R؝ݲTDo}侟5ftlC(cuhQjldc)|_u= \h#qn6Ό຃*դT E36{$P'+ތ6`9wBxQ.dIhuYDp$S'O%!u^1~yԄv -}]z7{WvjI H &^Z YO'S%7^ߑ3*s]Co~N LLu -.*bs_jIj%gFRYv!@z_ Y8D@3 ,[F)QU -6~"= 5lGNdьBG&$`g]jB\F/'Wx@ ?w57ٖNZ([:=W6 ݕ:gvr?_7wHL15ocK6XLHWx@e"K@4+k%(57;W͡/VGhӋ -Igw -k-e6q,f34# hD@Qd5xd>' Vm{yL[/2B]EZ4 2 xV XJ,Ó '_oGhKNbuܵfLbћ.NQ2N2)ڤb7q@y 뿢<+$%ZuQ"2{Vh>_4] [GLZ ?:xpx :/6?;a|2&!Bx!tg `8޹0qw.脍WQ}<^9U!.*Z׬EBr6־5.-gZ{c Ђa~_`<7(O%Yi_O{0LkNސ<@K1Y}r> 3%$߀K72Gׂ$Ka~tK%{IѤ(ZBBr)'?Qz'WzryL`My4Mo_NQO$jcuI+ G]26ZäD7EWJ΄=R>}<nT P6=\rwCzl突$ -G/3R t~*+QSm/?SE@߻$z΃&.:x'n8D8r#<.iJS7zܓ7Ƿ'%0 /f}6Ϯ?HD)ӍV?f>qMRVkxX[w,%" fb]V a"UhM<ՕEJG~dм4}>hÀmzEB8mujBިs"O~g_lw?'yw& :W!P׍WŶow%xQDq`WڥEύ?cy~%:a<;?<1i@dD!GϐIGMc_?3A7LY`濠@tLYiCSAs3[.6*J<ꕙK -#/#n&g.AA -S9h"ILr \^36iA ŸiCWkxEey&_{7iG+GfwLV8iYbo@fA{u#nj00Ջj؜x2rЋtmZh>O^ Lanp<)忘g"3@gh@dEC-?4V;fz[ņ7%ݺ/nʳ^=6n-^ ܁m85Qc_]y.OF1CcH3dd@Of[tΰrs8EoMݸ &Oӵ8#Pؤ&;gZY'Fuj8i?)!MV2L @4WL8m@g(Zٲg t% ggQ{nb JGӵ(&/[ -9᳖?)8yex/_l51tȀ 9@OP,1A.L l =`&B5)tiL?<R?Q8t}k/Kvx>3)6JJL -9>|4AN3;3B1+]ҊH.0[((xh o*\-sc)'٭8>I8M:N)-\mtNEJo?BJ)ؓǚ鍑[}Bw]QLkޗ+lU2f#/zߋ-|73\ y6FK8>3yOVeU Gr֕E@5A/o$ |?#M??_3mwZNGj.4:nr1C?V'pnM -"B^Jiqg4N+//n897!tP'O}|g3Θ݈hȓNt}˒T9/ t)Z=q@t7H L2zh䣫m~7io'-rge/44 (2o2L*AV@wnw{ˎ\޲ӮKmB܁F&Of -4%/b0,q~7] ?/Jyo&X2N"a -dN& !]6$VxMKo0,:3Ҹ_MBۭv/iT( ә26Ic؅/&I?$$%8X(deM.ɶ 721mr/2Zjo -+5;̍־C9pj !RI&Y?K{B R&@ڠਟWhVf((-kX[fh#Z*\(_M;sV>Odf @'yMdCܕ2( PZH-xN{?eD1U,+'pҟ |`]3XX PiܹO翾s|y_F|0>4o ~@sZDu;"⁃0~Hؒ}zJn2|[^5Γ |] -m T ql95b|;W%e( _G;5Mr;]phTY멇O=M>ɧ jT]Tkޝ`; i`yXhw*!7I?4gqޚA.yx[G]nt܎}.<$gEL9oȔyɐE鼪󼠗KrT˦ XI4h'#f({Ih{jt]) "y?}O=wbGhSd p7ds3HºJj:)fo9y9eԶgyq!|&'K–"3? -_.f9fF#vԧkK3*Ho!Z探6!R80CUͷt(ȌBlY'֒_-Li5\TA`UoB9JWXAZgƝquuKPL^okI?b|Kd$Nغo\kv֢(Q73Nah7#(+-[R`G,C8Fݙj#4*U2tAlMzqP\\E i{ˤ~ sOE6\{.-lxZM+ -bg6*-=~uWTN NV5j:B1D_4s+O yH.; r8 p*Y$t4з9{?>~w*4+ iS#o\ 9b\6džI|w^ ,dݶ^҃Tpༀ=/UXΊ8ŭ#XBOf'Z'Y\Z`SQ\oEs4AAhi6"}ڂf׌;8wڄBm}<0IH&sCEHuc鐖4e)5 mcpF7E|DiR<`7!{N!e)Ov3:/IǏ9Ba񃎠wIc.B/ȅHqJ<$,0I#we -w35tI2ftty_n -# -..-p #$q3H&ǽe> cDɧ9|fVMqpx Rgư[ 8)=&#M /Sϸt9Px' "arO!2<\/)n`>5XO3Vz-=i $ǵG*I#=SAc"X^(U%U͋1:?n3GM]2G^ -! BI"#Gc[ -2p wz1~&{N|ե_6J(ާd$-q/?2gtv#e j_<_7R|/܏%qx&b}6{Ly<&pldBbx؜ӕ;wOvmTbA )^U/뺮!IEY<ͶѨ֜thXEҠ_|e<4KV7$ǻ; b0Y$;> -+ bx;P./m&'^7u1zàߒYb?I R^7xJ$dT0u>/+&Reе!\N[X{ݩ߸-]\MzTWysVn<{WP'?f -M'sާD]/o72oX!}e`oSv/ئ d:}ǭ_m1_nba $龃q9J s/R%AXQxڐ0Xi4ŷBCQyjitvKzǀ 0` 0`_ \ No newline at end of file +Ӹ]}r8fSp:oK%oͲ|$AE\Mbn[>dD n%@fB]}t#c9 4M[,iWM“)bN<ߝC9D}Q§&ScǕ( -rϤ(j> +d +B^p7Vu~NMlD ߔwCM4cжU W? Ŀl+u/'-9>ڞ|,1Xr$pXX XK,? $_DpO4eYV,/њ!.ߡc0YirY-.S1SB,?kcCWC׏ AQW~MzYYg4i k]jy]*&ERcvn7JHl4'C'-?ބ +uGQ/r6Y儜Y ̳< x@ёt@iw4 P(BXvAS rJΥ5s|[ v|9kC0~W9ks*mғ2?"{! Fa; Yd42@gi -jڥ o$]<)kV穪7=ǒN&7X1 4'gZbzߦ.V~?ErgӢ[}D&9kfgãSG߰[Nfr*ZVqa<:NzK)olU@;wCT|hO+8`ѱ.qfEl@Y+М Z3FUoglVl 0;\ -Q +`)1m|d{`Yۃ;gm Z0@`G5BK?grg<#@ l Ձ +w]{a Ѥ{abR15/Ur ZRis}]+;aۏQy_%?t]_1/IEF#(! y<+:tdh4lj}Xh!.(25H3ɬv90I0P]¬n\3rڗ3H5] 3|A f?$7u6L f$M 14R1F~/zn Gx^\ĩ>;lO@! Bp +q({ _;캢\M/.*4͠t;iCrq1Z<3xF\?/li ~ ڗ0tNxIÝc^$m>KOw}jOcT(?tYQ%K\Ge,Ұ[eҜ~lbi1#$'A,ri@.&˂)}[i:l3 w#D}TYٚ}A2!uDYFU.) /J_&HMY#4N@cQS8lHؽ|5Z ~n>yWL/ΚX@WmsG}}Z~"8m2A/2XS/,(L'ilLʢؼSy%rCg]+=i\uV\HŅpߢ gtб@eȔ,ǖ/ (X7mjvUz݆ =mu>KD@w@RNc9}(l G{k |7?MZx$ 1! һ~ՅQwKBcKO,Urx.Osvs2U{P?\a ?9 9@D@I"?=/1~$5]B{c4Bwz?ϥHw!o3Ap_``_yq264]a0n\U CW^/R-{u0/j)-ʠV[q]n^DYr{CD HCь@`$']>ts'6SrBܴWvHq:{$y~:lVpBۂAKC,a, *l[cb>I?*`/M;*93t +3:j>zFC]\hϙb:1ԌD'=R }2h˱jޗIv@#ʓ׫j8@}Uwc?[&+]%!&)c+Lʷُdi2T].'vq r?b;Gfrg/eU>PkapnFt(.+h`]]K]ŢɽqH3s%{Gq_,\c_|?2bEI'ISL0$q+/%qzZO6/'\[o$=\_ + O "'(+vL6]S+/.PznG7,&)ЇN`jr\1ұΓEn%| aFN +sw .T.3-c f`aۀX]9+u˨KE-(tK-|e9Txcvlre% +)j0p<}Z/[*5GTwSR;6F#k1k\BG>iox {Ax/;_(i/ݷ?gIo??vKPOvv_&. οcݼ)Br;?_ $63A>}x׵s1}X 9z7?2|@D!)篐ñ9ٿ6Q ws?,pQA^hN\j'HM ~slLހ~exioCgvgɔ@,0FA9W`BAHVAz \c+Q=Ac7C9hΨ +u4-J{M=C}jqLE(.WcN?(a|ng KOr43I/ { . `0);:avSk)zQ.zP :f-jש=_[" 8(NJ껡~_ě9@ ]Ƀ>xTn䓤힏LkF~ot1Y}zlq3%Τ/7dڴ 'jJ̏.uZ b5j_Xؚj rC˗~+>iW[Udh+ʫ7E]ۅd)/ZUK2Ӹ]RA:F˓d ͫCrBjv)X.gBn]g7*??vS\w;,\nGl]kܫV6$^\1^2A'Kz}墦HElr\~'0Y.rg%3oSG<5|4vq= ␅#LD|wS5U幧? +OQMIKlAz߾wfgߏ mWo+};|I4IlɊͣn.yyv@[5 7OƞR#%5Wh\>4[a6݊"ZE4Ry\V5~_WE|'̎E& |߹[>^Nջ.) 3':Z..k7^6pG n,#o&H/s~8L^,A@T34yEHHuoZsάxѤIw{5QTh0D^_NWJ-U&x^OH9@`dAk^d O MxEG.URTC]/{t?p:n q9}Y,O:v +Z{ pwOb hD&YQa@ +x[>5Y*[޸ukǻ]vH*ݹn\ wTAdm,fة+p%GϐG7m> %0eW)N:E^0eQ Co 4Tr{q_ytn^ Az-lKNO5]W8N\8S>  P<1@9HdBc?*LӸ 2%My75۽ߐ[]W +/?r8gvX6iH$C2耗D J,(?3WG̭Q] 3{4g]9])&k5@{~283AEb™`HXϳg4 !CWV+8U.Ip_ҭRA]B;ߺ:Ak̹>.` DΧ)ݵiDnt^KKGq8v8)glXSn8]U~q~9juԃԼn/%^2aL|!? +ϲn (v9aNXNƘLӻ>>ҊH0-fVw8F7,Tؾ~̕EHOS?ͳR ?`nŴ $q$L9h:hw=)-}DzſbMQDc7j:<N?6-Ժd+s¶y/,c:RusX2O?;Gexls-7,;-{L:cx9uIUV00p(Wa]]i>)~HOgs_I'ovNU H<ۅFlWV$9@,<-L=2Kӝhp7|N? Rn{x3A<;2m>zFpV2 LK`Lb̢c&BH?y^?V2VՐ7T"R4=aM}ܗ5k\ҋ,ʋZ^ܺkH n/ryL0FAޝd!#GNhɲkȧ6e"*mPHuϨ:#jF7lB2$U. ]iV-O[bIkv79ρ%љ3| u(1 , ,YuS[U-d JN>Е~GhQn6FyiJڽw×"{!L{Y(|D$q&pW?I{?&H i@! xe bc?ޥJZ7jN:FC,+3 64 +w,KȎK7b0/Q26o. ~/"f44!p P s4!1/}+,gQ1ҋlUmWj|I떥BYHRmދX/6O:zÉ{eE%,CVƖh@<uѤw ^?hk g7"͛Cy^Qz`r"}-rD5cE$KBJ6O?egi/AQH4Y 1.Ja2c14b g?/}q<#wux<9s{}d}C_,s-ס+WKw 毦=U[jhjh~"+1nG$o cv{O_}#11~Ew/?FO-"[R5tgཟ(Dg4憎_) (n)l+5+~ʓj7sz2|ǫBhxLuI=5lоƖx6/&|afŇu ~Q?={d}ىشNOp?j$ac!ʐ8|Ϭn۾j/zs1Vǜ +Ax[i8 /ROJn?A3E)9 +h]EքK'UL84 KggfpLw'aEv%Os>ӭ8)9~?d-Z\0U4I,YYds"gD#hxC-m%]]Ryg,.9O?)SNjmW=؜rm%~Էc-w YcF +:wT'D0$&3IdU,Xun⮑eZzj-=cy$V] +=-G FBuK/?@\\Bq1i??+ߝ|'m̑; h7XU\6f`¨ޛ%҂_Ӄ$8 Pb/o&XexcFN439GhIRXTX<ٓ\m2ŻŽ- H/!ufڮ1bv?yLmyj:'dgj9;=8b{y8[ONϬ.=Ӓc5Ө!Zt۪nkC?_$>b1O1.6N{o! n[[D*q\]oN +c@X +)wת< rL5Ha޾c j:@>H-e8Õ(@3DƊ1Sn9?W|ԷvLK*pQWܮ=F+H3Av4_l^w7N ̈x%85ȓ&3Cogysח}qvur\˷N#WA/HrJO*! +Y|*"Wslzt!\oſwB-䌪olmq!I|#{ǟ:^ZN,yjn2? O4%ե5WOT4"o;lM>dXa,(s _oyKbY,M~ykARUgG꾳{𽏑^eIxcy|W15cQWf\3Jy&MqLH.ʙ@?D\dg5Q`9(4m^X%/ +jݸ)>_Zu2Aor_eˏWӻYil ˒8iFb Cmc+{+?|&6 ];t) ?< xQ̚:`DDh }ǖj,>><*APnD F[S{ܻN)kY\ 1?C^LjYϩUzaĻ;fㆸ$ܞ0ZmM+,:;պ`]ՁWF=wSr95pS:$}1 +B̯cF"OA觞6񅿣^Si!|Og= *Ak|ȑ#G9rȑ#G9rȑ#!k \ No newline at end of file diff --git a/core/tests/fixtures/config_install/testing_config_install.tar.gz b/core/tests/fixtures/config_install/testing_config_install.tar.gz index 8e2ecd3d44..8bbe945125 100644 --- a/core/tests/fixtures/config_install/testing_config_install.tar.gz +++ b/core/tests/fixtures/config_install/testing_config_install.tar.gz @@ -1,53 +1,37 @@ -,][s⺲_ۼlEq !@ ԮrI ܲ#_H3bNUbd6vVKjSw&?h;H019~;HB42Fۿ盬UMҴoEhWo],~D#mJcu,l$E܍戄bTƗdxP]4r[ָUZ/F ɲ|L}ɱ8%i;A\Ӆf|K7/g&%^y90?Y,rX?;aH\(ߜ%UM g.YNP;">ܩY$$6jF|$k: y!?u"qWz˟ci 4Fh=_H"_2Vׄ)$,3tUb2B)@X'i<ʆ[Us!YD= ):7E?6#NE}g=3/KMU֫E~'neLw)76p7>vi(*?+?/6kAQ>5U+8N")TDz:PC -0 -,! [ڠT{FQm݇ܯb1W~yiǴD8r@wGl` bZ!3#NCÝk( -%` % D - -6;g@(=w;}6;p6v `];0Χ&!pX -W2}g-#rH">޻m$s*+˿Ð om$BR %@*0P)0dR4M(H%-kڳfceV.{V\= /]ZXzc^ލA#~ت&ܫ'05X`/v3X2@?$Hf!ۊ UN__Ϣ=+3op9Z*{~%߀S7 -Np:MoJ;㿚',O5(q]*F6 Tr[P(㿉rCXUnUAojDn8|c8ҫ8FQf5_-MW6螘4HCs)x`_jxPI$+щ4U4PXaL"Ubz(^}-w,?BmVzOGA EQK[K5ntcݍ|: 1XʮkF1¬\jcdQ ;# -";R`?P@)mY)A@1ka1TU9cWv]Ǻ M\Vg)\vjV)}]Yx%p?H?$0( %@ÖQU܀DxHF7dZ ->:+Wc4wnȊ5{w}1ԏFG3?rO")%6D+ _0<1MlFG-6t[ZcZ$({Ќ9cDzWpD,/S#nu@5Ͱ, sa|!7aIOF5VP"3z>+?qs|#rO T,UAl``.#e -齹 bX+jw`e<[/&v%~x'$py'ſ?a(ZR %K:?gd 緕gDVk75tvٹ[ !gр,B@>?msO")!_ŖFF%ƭc3=4:|mΰorf.Lq?@vw\O2',(d:@Vd @knhN$qT_RL`<|[jh\^3Et?|E1)jI.X+U?=n9sTm{/B8@MEV TCDݦGkdI -l돦uڼVXu={]Nܫ -P#(T)w,@ZS4oQ;d%Dz<Ȁ(TEP5:x!g^G%qWP\`;atJR% `z &D6J?2bIs@a2DI>lxp}3h\ tо&ýzrg.}/vY3|1cami$J&I -@!CpYm&QiOcϿ UaKF.Y 'Ũ11n77{%q?tM*{ne(|$!5Id0\sXk3'.Z/kY$a?>m?!Alwv$2݊Mףy$v',0%K/&t`*nqEkv!]0^zpp2ZrRJ~%?I[tOc~&}IFM5eԓhcϹYb97)(0w!}j,3K/JL/?P9`sQ~*H^ւpmU;kMF"=B?Or>wE٤$b#3th&R.?)RȹcZ?pri}\s)7t若~tjO/e{<X#J<'iP%$1KCW~q~Lron3 mS1>ON wwf&PD7&YX; UK'o$O@~e#B |fЗ Q@%KD1yjƃ S3ȗƄM=3'}qon=_,pfq ys+ &[%dE/8,%ngBinJy 鯸RXȄsr/2ݡqo&=T߿iN}]֍|WZ˨japIC?ſ38?c=˲S'G -pd-L5PkL(ZF9&Ks~;jեTampۀw iRiOKQ7R!UI7$}0td9%ɚǝ}ɮͻw -Io}xRzyQ™Γ|z{Pcesz2 HJ{r9do *JD^ެd~`S$ GӥZn5vTiZ wPOjףU) gs~G"߉"O"Q i(Nj*N.P*?x] QٻƑ$=)mxXڼBgtXB2IQeo/LuJn[n3~TId*MEFDF|qE3v+X;Hr^]$dg hLDDS `19yE5Y+VK`$sv>aUUle-UEw{6ѳ9?ƯRaya{Q~N~;:# UAx,*C $Z Y#Yѫ?rBj -қHAP0V$.?%ſ}o8#'Hg'(9[%I$Pn^GX]O ,6dvpr.@+~pN+'΋$B-F*ں5up4] 'qF x*翇?,#O~] U)=#A?!l,(=..L U&}#@'0^mV {Tǭ!Zawo0KTr[΀lON)8,,cÎT,)Er 5oM|)̚ -d9ffp[$S8py}=8aGz;R5rfn3W>){?{?G˱xɑ~+oٺilq`~ - I; ~6zGƃ,'ؑ@VpHfX9h^9Iџ{\|G^4d> |uRBYi΃W?ikuugZȅG,$gX?W޵`oDCqXie7%$gBU=@dJJf]mq}+6ڦ\ -Eݯma%%R{_E.fEyl>,v߬uG%r'&m7K뇯 -ˏ- %#qO䓛bAݬYd}弦Q< R -E E+PTޒ/b=J.xݝ T-_;́՝NO?1{|&C.D/ ?0l:J$#oI$4((HŔhX0 仞 --Z XԪ&NA't\A <s@k9#_bϳHs -/R*2JG p"f5UyS7N{0Vu;vJw+nVZ)u]YyjsNo%y15lD%ILvsI(BYf) 0JҠp*d$GoeQ0hTZL~iP´kIBE E!gkF^3R?߲JCH  -9"55D󝯭-S_Ͻ8wpm]:݂N{ȋT;|@ C=/:EV) ū'` QӬABLm6끯UW.^=h/ʚŝpn@YE.ф@<NGX!")YV$cHPBVZ/=/JCybVVeٻoj^̧f~t=Ƌ|i>ja7/_GaO#)+9(#r,2 7Nj{72t^PfΚ߇Z4_떷2rgցMC8XzZᔙu4g5?[5 Ō,Lơk:K- -Lrikla8n_2/žRZT|ᦶ`'X(r687,u>;cw:G.aRN<&7L3\j:,δٙ3dod?H q̙yv07 -הkE4e!.!wfUW;? ߾+#?a韓cq2S7({M-+֍zU~Z -$UEcГ`ZGr4ޝi? #;LglERbr譨p@Df=]Vhp vT8ʪ}{vb o{W=kI27ϓ7#N?g$ax?/ǎ9`(fgGCH"Q<0FѲbYu?K+ke,*VZj7t|I|&DeDR:}%^acdzbG'2H1P`eRe RcJF|d Rd U6;3sn(2.McDlGOVal07*?i ⟜O_# IY (PP -¬)Xz#kθVS9_Rj -\&27 -FnqD K ?9Ȏ-2UZCI"/#G?dH1X&'\ρK8{A{f_8DkT"?0d2|n؊UCf~ˉnϼ/  )ңPETMmR?S9g"k!ERGiM -%@@Hh!)( i@6.Ơ1s1lKOjrMt o::H4i.~O^高s^E1OI_h U:I]t'Nh'z_moPQ!:גt-';kvw+& ʾqKtTy/Gߒg eǬmf؏ڥe@\Q~1bCܼwT"^P$jɍsw3r lj-yyoD)DJG*do]e+Na'XLSnKz%zO39fhJRЬrc0|WveIW!5VQ u˔&Y%؏+ĐĦ C,'pFH\mh>&oޑ-%D+ř*c%@tTp\p,B;įtwPoyMN~*c@ZJW=cU9T[{n~hiyɝgo__duuM@>v uqWJ>:~kWۗew -518:) r)bCD~(0]O+ps TY$d Z IPDZ,#'زI+,l4߫7rEE_*_a!Lg -&p]1L[zeP ln鸶?7Qӭ}QMz o#V+_ws??R%:-gXDvDclllAĢq<}u0CztA/żM@A_4SZlRR4]X1)nݺ~on̻xx:;i6#VԄ4̖.CŁ;*jM YDtœtE>7 LroMm*K셊t” -8*XN=&񏴾JF>L-B&#HOMz3䜗ݕ.N!\,6N̳ 1aET+4l?-Pzqh ȭ#`|}7V@_3 -(P@ -(P@`h \ No newline at end of file +3ո]}{sJONg"N~8N^Ƙ熢$@ൽg m$m>4>sZnTJR2U&Wj7?Qn¶r*(J_I _fYQfE =c0n8 )ggh,E2J俦#5H,N*Y 97SIwگd;/MojeQz.pfj#f*2aĽ+~[u-٩${*W%"{O]k-/XVW/H k~U@d 8|-jd +N"_`m3fY(*!`x P9B4K74 =G%4C J?;|6??#j4i29( jL ͱH’!-д&?5|[/B1S#]oz{6su= K +JQHb+2 25,2αX2#=j$)S{ȕp{MD2OS4KU!YDDaXJBQfϕ~Eצ5D{nOnrömlmܗݡ?u4ƿ:d +wg0%F%BՐ$Y>b5*vg5w͵¨* 'Pge +6r5RӍ۳3L6R/?D*H*d]Ejx1SL0z\yx[(QD7Ѹ Z2j;񀟍m:{ S<+tYZ  FyZz)3V8[_ Z\LwlOr=\[9?SH5qBY7C 30ROMVԗէH]A|3Ϩe#L.yޟGPf! e c:32c%$MgɃ_m4 &:rчf,pfݹ5S r)^ |O"do-[cfBw&J4$Q4C EV`dgX^%'^pHTF2ԓ1J_:VGHY6&ĺ/Ɣ|bKrO$d@?~xZ;8*RE +DDɗzOC(ş+:t›; {m5WAs>a5&%w#a?<1"_Ad'jh*,2~,7roϺx w7dDC2yٜC +A f͎|w;]~pma7Rsap Ð*+RF1O5XC<в|.( Sbd3rC-z?;YoW5vw/,ZhE!ƠtQE GEC #$+\n|gG6Zt˅NpcOs%ifgsI-6s$oU1:dJ%(~[b<}i%؁7JkEIWJdގje'vL96៧3,#(2IL8z%6SrD:օVHQq:gg$x~z26p@˄ArDKC̤prDҥِDïb<0K:V< J]Z- ֆ(TjȮ[Ii'gA1ЌD'LgS#?@؈3G.vA+fcTPXRv? FUinw'YwgXfI o?ge?X֠K\Q/U +'ruyR ??[; y"+* KVp'T'*,3hdUDr|5yo82"*tJ>%~/Λ'p`W 㾮0w_*i4l$-CϳL,61ƿn'2QcEF>14< >@bwyI`tKdA"%G7YTI,5Y4EouzAfkHkOD>: A+8XC^1, o Rfȏ]krjsتDV 6We~H*ۥ`A|rotv?Z_TOoƁ;U)7*8Ln4:VSL_dcާx~ϕWNHA2la~ 0q[!#X6zSGۄ׽ K< 6%XzW? +(X0/_лȃ֕c˫_n=^șO4g\n+^_upFCyqXL!Ie6[^](>M5*+%ߺώ%j 'iT&^Ԧ@jK93"6q4jb[wA$o4np/8]4yy^L) pc z-DiTC8n7fB+Z9]$D2IQ 5ABm̖MQ<^,ۣjh7l@U&ϊ&QIl6YQӻxFdq!p^oNJ<((M"?jQ4Cq?hp^ƭכLޠ.(XtڟSRZ%UޔH9/cly(kH x`*UNh*DſUrLC\}闚g `U mgoPw O, @e9p$UY3" +z,:/fӫn8njmowC50G|Pꕟo}`)XՕW6neng4NOi;fߋM3,K*QlH?(x ISsNG< Fz>7 |/Br귔5VGݤyͧk8IV`XOe@ xO(xpxklQDx& "A1fP} ǎ5jaWÉ՟!j;5& {8c_|v { mvpxC3T6R0DA@f2QLI?E/rϚMZ򂷝.sJ1k]Ym>n p`K ?Iĉ 40w4>$ʆ,U?p, * $X4JQ 4nԇ?[~y[ӵ(+pr}'/c% >$; kѐuBp?2R?y!Mh@bC6 +G;SAV˦,LmF7dK^!/BwۡPoRw}>$/K47r%yEk{C u +#\'?`*(A,B +c͠GM;D%W{Hbﳆq |j +~V^Ǵ\\ia/+dg!(id`OcYc.Vl fX~ sH{듴,P$p> 7Lf}4uY-&Y(<}"h~ HԩA&7~<'S? ,Y(Wpd2٥6ZڪteRI7{n]/eMXYrƽ/Ύw(L{^>$DI LdP! +m_mZƝl b%c܄2񠮣4ǶhYo!CdXawg45(J838 H"VyAyp+O`}_cTȫrPj58pоsSIJ 5Zly(,ch@Y'2i`x] '50}GpW%%Xʗ利XZt͓?̕hD϶Q[H\#D8XUPLR>Iac߆ğЂx?#w5<å' B8Rs;=aoW_<9ïC:~7:5_ kU_U+Z_u+ /&IĈ놿p5b}BB R4oEP篡Z蟸n~I +TQ]}|U3BH;c! ]knz"ȌOx74%WBqLeq32CV1vSQu42/F<|z^|^ S-Ap+L姪Ӟv¨}qp*?N{5\yfHgYȀSFl[mbG%sBOa1IT5ȅf"Γ[m%B .~, +w_VcnZٷ,%oq4-c/kajYv?CcdB&!X-k"XP_ޏ9=4[4sDFm+ɿ5V?W_/o 6f+((Lj'3+;ů "S#ceӥ!HBC"7!e2^UY!ofGD$V9?ܐdK%[Ȇ". +7-[!m??R ~GO'7oN|f^w7'uFD,$'yda&ysM^Gg绾g(U0ZJ8 2erUk$Q'X ]_R5k<+]f"Rٴ'UOr Wc\r $>{ǟ:NE M'Wn<3/17%ͥuo )B>X{>wzH7Øo$>qൢpĪ?Yr*U+oIM}˿6J@OydvqXcu|SJoY׻O}f6 k߶;㏆/9}i?xr~~QBˈ ˀ H