diff --git a/core/lib/Drupal/Core/Entity/ContentEntityType.php b/core/lib/Drupal/Core/Entity/ContentEntityType.php index 6d8d619..26c92b7 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityType.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityType.php @@ -8,6 +8,13 @@ class ContentEntityType extends EntityType implements ContentEntityTypeInterface { /** + * An array of entity revision metadata keys. + * + * @var array + */ + protected $revision_metadata_keys = []; + + /** * {@inheritdoc} */ public function __construct($definition) { @@ -41,4 +48,41 @@ protected function checkStorageClass($class) { } } + /** + * {@inheritdoc} + */ + public function getRevisionMetadataKeys($include_backwards_compatibility_field_names = TRUE) { + // Provide backwards compatibility in case the revision metadata keys are + // not defined in the entity annotation. + if (!$this->revision_metadata_keys && $include_backwards_compatibility_field_names) { + $base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->id()); + if ((isset($base_fields['revision_uid']) && $revision_user = 'revision_uid') || (isset($base_fields['revision_user']) && $revision_user = 'revision_user')) { + $this->revision_metadata_keys['revision_user'] = $revision_user; + } + if ((isset($base_fields['revision_timestamp']) && $revision_timestamp = 'revision_timestamp') || (isset($base_fields['revision_created'])) && $revision_timestamp = 'revision_created') { + $this->revision_metadata_keys['revision_created'] = $revision_timestamp; + } + if ((isset($base_fields['revision_log']) && $revision_log = 'revision_log') || (isset($base_fields['revision_log_message']) && $revision_log = 'revision_log_message')) { + $this->revision_metadata_keys['revision_log_message'] = $revision_log; + } + } + return $this->revision_metadata_keys; + } + + /** + * {@inheritdoc} + */ + public function getRevisionMetadataKey($key) { + $keys = $this->getRevisionMetadataKeys(); + return isset($keys[$key]) ? $keys[$key] : FALSE; + } + + /** + * {@inheritdoc} + */ + public function hasRevisionMetadataKey($key) { + $keys = $this->getRevisionMetadataKeys(); + return isset($keys[$key]); + } + } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityTypeInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityTypeInterface.php index f9ef160..1e7ba32 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityTypeInterface.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityTypeInterface.php @@ -6,4 +6,50 @@ * Provides an interface for a content entity type and its metadata. */ interface ContentEntityTypeInterface extends EntityTypeInterface { + + /** + * Gets an array of entity revision metadata keys. + * + * @param bool $include_backwards_compatibility_field_names + * (optional and @deprecated) Whether to provide the revision keys on a + * best-effort basis by looking at the base fields defined by the entity + * type. Note that this parameter will be removed in Drupal 9.0.0. Defaults + * to TRUE. + * + * @return array + * An array describing how the Field API can extract revision metadata + * information of this entity type: + * - revision_log_message: The name of the property that contains description + * of the changes that were made in the current revision. + * - revision_user: The name of the property that contains the user ID of + * the author of the current revision. + * - revision_created: The name of the property that contains the timestamp + * of the current revision. + */ + public function getRevisionMetadataKeys($include_backwards_compatibility_field_names = TRUE); + + /** + * Gets a specific entity revision metadata key. + * + * @param string $key + * The name of the entity revision metadata key to return. + * + * @return string|bool + * The entity revision metadata key, or FALSE if it does not exist. + * + * @see self::getRevisionMetadataKeys() + */ + public function getRevisionMetadataKey($key); + + /** + * Indicates if a given entity revision metadata key exists. + * + * @param string $key + * The name of the entity revision metadata key to check. + * + * @return bool + * TRUE if a given entity revision metadata key exists, FALSE otherwise. + */ + public function hasRevisionMetadataKey($key); + } diff --git a/core/lib/Drupal/Core/Entity/EditorialContentEntityBase.php b/core/lib/Drupal/Core/Entity/EditorialContentEntityBase.php new file mode 100644 index 0000000..ff6abcd --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EditorialContentEntityBase.php @@ -0,0 +1,31 @@ +hasKey('status') ? $entity_type->getKey('status') : 'status'; - return [$key => BaseFieldDefinition::create('boolean') + if (!is_subclass_of($entity_type->getClass(), EntityPublishedInterface::class)) { + throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not implement \Drupal\Core\Entity\EntityPublishedInterface.'); + } + if (!$entity_type->hasKey('published')) { + throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not have a "published" entity key.'); + } + + return [$entity_type->getKey('published') => BaseFieldDefinition::create('boolean') ->setLabel(new TranslatableMarkup('Publishing status')) ->setDescription(new TranslatableMarkup('A boolean indicating the published state.')) ->setRevisionable(TRUE) @@ -30,31 +41,30 @@ public static function publishedBaseFieldDefinitions(EntityTypeInterface $entity } /** - * Returns the published status of the entity. - * - * @return bool - * The published status of the entity. + * {@inheritdoc} */ public function isPublished() { - $status = $this->getEntityKey('status'); - return (bool) (isset($status) ? $status : $this->get('status')->value); + $key = $this->getEntityType()->getKey('published'); + return (bool) $this->get($key)->value; } /** - * Sets the entity as published or not published. - * - * @param bool $published - * A boolean value denoting the published status. - * - * @return \Drupal\Core\Entity\ContentEntityInterface $this - * The Content Entity object. + * {@inheritdoc} + */ + public function publish() { + $key = $this->getEntityType()->getKey('published'); + $this->set($key, TRUE); + + return $this; + } + + /** + * {@inheritdoc} */ - public function setPublished($published) { - /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ - $key = $this->getEntityType()->getKey('status') ?: 'status'; - // @todo: Replace values with constants from EntityPublishedInterface or - // similar when introduced. https://www.drupal.org/node/2811667 - $this->set($key, $published ? 1 : 0); + public function unpublish() { + $key = $this->getEntityType()->getKey('published'); + $this->set($key, FALSE); + return $this; } diff --git a/core/lib/Drupal/Core/Entity/Exception/UnsupportedEntityTypeDefinitionException.php b/core/lib/Drupal/Core/Entity/Exception/UnsupportedEntityTypeDefinitionException.php new file mode 100644 index 0000000..079b422 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Exception/UnsupportedEntityTypeDefinitionException.php @@ -0,0 +1,8 @@ +setLabel(t('Revision create time')) ->setDescription(t('The time that the current revision was created.')) ->setRevisionable(TRUE); - $fields['revision_user'] = BaseFieldDefinition::create('entity_reference') + $fields[static::getRevisionMetadataKey($entity_type, 'revision_user')] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('Revision user')) ->setDescription(t('The user ID of the author of the current revision.')) ->setSetting('target_type', 'user') ->setRevisionable(TRUE); - $fields['revision_log_message'] = BaseFieldDefinition::create('string_long') + $fields[static::getRevisionMetadataKey($entity_type, 'revision_log_message')] = BaseFieldDefinition::create('string_long') ->setLabel(t('Revision log message')) ->setDescription(t('Briefly describe the changes you have made.')) ->setRevisionable(TRUE) @@ -56,14 +56,14 @@ public static function revisionLogBaseFieldDefinitions(EntityTypeInterface $enti * Implements \Drupal\Core\Entity\RevisionLogInterface::getRevisionCreationTime(). */ public function getRevisionCreationTime() { - return $this->revision_created->value; + return $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_created')}->value; } /** * Implements \Drupal\Core\Entity\RevisionLogInterface::setRevisionCreationTime(). */ public function setRevisionCreationTime($timestamp) { - $this->revision_created->value = $timestamp; + $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_created')}->value = $timestamp; return $this; } @@ -71,14 +71,14 @@ public function setRevisionCreationTime($timestamp) { * Implements \Drupal\Core\Entity\RevisionLogInterface::getRevisionUser(). */ public function getRevisionUser() { - return $this->revision_user->entity; + return $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_user')}->entity; } /** * Implements \Drupal\Core\Entity\RevisionLogInterface::setRevisionUser(). */ public function setRevisionUser(UserInterface $account) { - $this->revision_user->entity = $account; + $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_user')}->entity = $account; return $this; } @@ -86,14 +86,14 @@ public function setRevisionUser(UserInterface $account) { * Implements \Drupal\Core\Entity\RevisionLogInterface::getRevisionUserId(). */ public function getRevisionUserId() { - return $this->revision_user->target_id; + return $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_user')}->target_id; } /** * Implements \Drupal\Core\Entity\RevisionLogInterface::setRevisionUserId(). */ public function setRevisionUserId($user_id) { - $this->revision_user->target_id = $user_id; + $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_user')}->target_id = $user_id; return $this; } @@ -101,15 +101,38 @@ public function setRevisionUserId($user_id) { * Implements \Drupal\Core\Entity\RevisionLogInterface::getRevisionLogMessage(). */ public function getRevisionLogMessage() { - return $this->revision_log_message->value; + return $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_log_message')}->value; } /** * Implements \Drupal\Core\Entity\RevisionLogInterface::setRevisionLogMessage(). */ public function setRevisionLogMessage($revision_log_message) { - $this->revision_log_message->value = $revision_log_message; + $this->{static::getRevisionMetadataKey($this->getEntityType(), 'revision_log_message')}->value = $revision_log_message; return $this; } + /** + * Gets the name of a revision metadata field. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * A content entity type definition. + * @param string $key + * The revision metadata key to get, must be one of 'revision_created', + * 'revision_user' or 'revision_log_message'. + * + * @return string + * The name of the field for the specified $key. + */ + protected static function getRevisionMetadataKey(EntityTypeInterface $entity_type, $key) { + /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ + $revision_metadata_keys = $entity_type->getRevisionMetadataKeys(FALSE) + [ + 'revision_created' => 'revision_created', + 'revision_user' => 'revision_user', + 'revision_log_message' => 'revision_log_message', + ]; + + return $revision_metadata_keys[$key]; + } + } diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index 0854220..a6ac273 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -294,17 +294,11 @@ public function getTableMapping(array $storage_definitions = NULL) { // Make sure the key fields come first in the list of fields. $all_fields = array_merge($key_fields, array_diff($all_fields, $key_fields)); - // Nodes have all three of these fields, while custom blocks only have - // log. - // @todo Provide automatic definitions for revision metadata fields in - // https://www.drupal.org/node/2248983. - $revision_metadata_fields = array_intersect(array( - 'revision_timestamp', - 'revision_uid', - 'revision_log', - ), $all_fields); - + // If the entity is revisionable, gather the fields that need to be put + // in the revision table. $revisionable = $this->entityType->isRevisionable(); + $revision_metadata_fields = $revisionable ? array_values($this->entityType->getRevisionMetadataKeys()) : []; + $translatable = $this->entityType->isTranslatable(); if (!$revisionable && !$translatable) { // The base layout stores all the base field values in the base table. diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php index 13af342..494a7fd 100644 --- a/core/modules/block_content/src/Entity/BlockContent.php +++ b/core/modules/block_content/src/Entity/BlockContent.php @@ -2,13 +2,11 @@ namespace Drupal\block_content\Entity; -use Drupal\Core\Entity\ContentEntityBase; -use Drupal\Core\Entity\EntityChangedTrait; +use Drupal\Core\Entity\EditorialContentEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\block_content\BlockContentInterface; -use Drupal\user\UserInterface; /** * Defines the custom block entity class. @@ -50,6 +48,11 @@ * "langcode" = "langcode", * "uuid" = "uuid" * }, + * revision_metadata_keys = { + * "revision_user" = "revision_user", + * "revision_created" = "revision_created", + * "revision_log_message" = "revision_log" + * }, * bundle_entity_type = "block_content_type", * field_ui_base_route = "entity.block_content_type.edit_form", * render_cache = FALSE, @@ -60,9 +63,7 @@ * caching. * See https://www.drupal.org/node/2284917#comment-9132521 for more information. */ -class BlockContent extends ContentEntityBase implements BlockContentInterface { - - use EntityChangedTrait; +class BlockContent extends EditorialContentEntityBase implements BlockContentInterface { /** * The theme the block is being created in. @@ -179,28 +180,12 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDisplayConfigurable('form', TRUE) ->addConstraint('UniqueField', []); - $fields['revision_log'] = BaseFieldDefinition::create('string_long') - ->setLabel(t('Revision log message')) - ->setDescription(t('The log entry explaining the changes in this revision.')) - ->setRevisionable(TRUE); - $fields['changed'] = BaseFieldDefinition::create('changed') ->setLabel(t('Changed')) ->setDescription(t('The time that the custom block was last edited.')) ->setTranslatable(TRUE) ->setRevisionable(TRUE); - $fields['revision_created'] = BaseFieldDefinition::create('created') - ->setLabel(t('Revision create time')) - ->setDescription(t('The time that the current revision was created.')) - ->setRevisionable(TRUE); - - $fields['revision_user'] = BaseFieldDefinition::create('entity_reference') - ->setLabel(t('Revision user')) - ->setDescription(t('The user ID of the author of the current revision.')) - ->setSetting('target_type', 'user') - ->setRevisionable(TRUE); - $fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean') ->setLabel(t('Revision translation affected')) ->setDescription(t('Indicates if the last edit of a translation belongs to current revision.')) @@ -214,13 +199,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { /** * {@inheritdoc} */ - public function getRevisionLog() { - return $this->getRevisionLogMessage(); - } - - /** - * {@inheritdoc} - */ public function setInfo($info) { $this->set('info', $info); return $this; @@ -229,65 +207,15 @@ public function setInfo($info) { /** * {@inheritdoc} */ - public function setRevisionLog($revision_log) { - return $this->setRevisionLogMessage($revision_log); - } - - /** - * {@inheritdoc} - */ - public function getRevisionCreationTime() { - return $this->get('revision_created')->value; - } - - /** - * {@inheritdoc} - */ - public function setRevisionCreationTime($timestamp) { - $this->set('revision_created', $timestamp); - return $this; - } - - /** - * {@inheritdoc} - */ - public function getRevisionUser() { - return $this->get('revision_user')->entity; - } - - public function setRevisionUser(UserInterface $account) { - $this->set('revision_user', $account); - return $this; - } - - /** - * {@inheritdoc} - */ - public function getRevisionUserId() { - return $this->get('revision_user')->entity->id(); - } - - /** - * {@inheritdoc} - */ - public function setRevisionUserId($user_id) { - $this->set('revision_user', $user_id); - return $this; - } - - /** - * {@inheritdoc} - */ - public function getRevisionLogMessage() { - return $this->get('revision_log')->value; + public function getRevisionLog() { + return $this->getRevisionLogMessage(); } /** * {@inheritdoc} */ - public function setRevisionLogMessage($revision_log_message) { - $this->set('revision_log', $revision_log_message); - return $this; + public function setRevisionLog($revision_log) { + return $this->setRevisionLogMessage($revision_log); } /** diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install index 38958aa..895c039 100644 --- a/core/modules/comment/comment.install +++ b/core/modules/comment/comment.install @@ -194,5 +194,17 @@ function comment_update_8300() { } /** + * Set the 'published' entity key. + */ +function comment_update_8301() { + $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $entity_type = $definition_update_manager->getEntityType('comment'); + $keys = $entity_type->getKeys(); + $keys['published'] = 'status'; + $entity_type->set('entity_keys', $keys); + $definition_update_manager->updateEntityType($entity_type); +} + +/** * @} End of "addtogroup updates-8.3.x". */ diff --git a/core/modules/comment/src/CommentInterface.php b/core/modules/comment/src/CommentInterface.php index 28318ee..1f69e9a 100644 --- a/core/modules/comment/src/CommentInterface.php +++ b/core/modules/comment/src/CommentInterface.php @@ -3,13 +3,14 @@ namespace Drupal\comment; use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\user\EntityOwnerInterface; use Drupal\Core\Entity\EntityChangedInterface; /** * Provides an interface defining a comment entity. */ -interface CommentInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface { +interface CommentInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface, EntityPublishedInterface { /** * Comment is awaiting approval. @@ -192,18 +193,13 @@ public function getCreatedTime(); public function setCreatedTime($created); /** - * Checks if the comment is published. - * - * @return bool - * TRUE if the comment is published. - */ - public function isPublished(); - - /** * Returns the comment's status. * * @return int * One of CommentInterface::PUBLISHED or CommentInterface::NOT_PUBLISHED + * + * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. Use + * \Drupal\Core\Entity\EntityPublishedInterface::isPublished() instead. */ public function getStatus(); @@ -215,6 +211,10 @@ public function getStatus(); * * @return \Drupal\comment\CommentInterface * The class instance that this method is called on. + * + * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. Use + * \Drupal\Core\Entity\EntityPublishedInterface::publish() and + * \Drupal\Core\Entity\EntityPublishedInterface::unpublish() instead. */ public function setPublished($status); diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php index a244163..5f96093 100644 --- a/core/modules/comment/src/Entity/Comment.php +++ b/core/modules/comment/src/Entity/Comment.php @@ -8,6 +8,7 @@ use Drupal\comment\CommentInterface; use Drupal\Core\Entity\EntityChangedTrait; use Drupal\Core\Entity\EntityPublishedTrait; +use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; @@ -50,7 +51,8 @@ * "bundle" = "comment_type", * "label" = "subject", * "langcode" = "langcode", - * "uuid" = "uuid" + * "uuid" = "uuid", + * "published" = "status", * }, * links = { * "canonical" = "/comment/{comment}", @@ -81,8 +83,12 @@ public function preSave(EntityStorageInterface $storage) { parent::preSave($storage); if (is_null($this->get('status')->value)) { - $published = \Drupal::currentUser()->hasPermission('skip comment approval') ? CommentInterface::PUBLISHED : CommentInterface::NOT_PUBLISHED; - $this->setPublished($published); + if (\Drupal::currentUser()->hasPermission('skip comment approval')) { + $this->publish(); + } + else { + $this->unpublish(); + } } if ($this->isNew()) { // Add the comment to database. This next section builds the thread field. @@ -480,6 +486,19 @@ public function getStatus() { /** * {@inheritdoc} */ + public function setPublished($status) { + if ($status) { + $this->publish(); + } + else { + $this->unpublish(); + } + return $this; + } + + /** + * {@inheritdoc} + */ public function getThread() { $thread = $this->get('thread'); if (!empty($thread->value)) { diff --git a/core/modules/node/node.install b/core/modules/node/node.install index 720a7e0..2751b2c 100644 --- a/core/modules/node/node.install +++ b/core/modules/node/node.install @@ -222,6 +222,11 @@ function node_update_8003() { } /** + * @addtogroup updates-8.3.x + * @{ + */ + +/** * Change {node_access}.fallback from an int to a tinyint as it is a boolean. */ function node_update_8300() { @@ -234,3 +239,19 @@ function node_update_8300() { 'size' => 'tiny', ]); } + +/** + * Set the 'published' entity key. + */ +function node_update_8301() { + $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $entity_type = $definition_update_manager->getEntityType('node'); + $keys = $entity_type->getKeys(); + $keys['published'] = 'status'; + $entity_type->set('entity_keys', $keys); + $definition_update_manager->updateEntityType($entity_type); +} + +/** + * @} End of "addtogroup updates-8.3.x". + */ diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php index d4e4715..dc398c1 100644 --- a/core/modules/node/src/Entity/Node.php +++ b/core/modules/node/src/Entity/Node.php @@ -2,9 +2,7 @@ namespace Drupal\node\Entity; -use Drupal\Core\Entity\ContentEntityBase; -use Drupal\Core\Entity\EntityChangedTrait; -use Drupal\Core\Entity\EntityPublishedTrait; +use Drupal\Core\Entity\EditorialContentEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; @@ -56,8 +54,14 @@ * "langcode" = "langcode", * "uuid" = "uuid", * "status" = "status", + * "published" = "status", * "uid" = "uid", * }, + * revision_metadata_keys = { + * "revision_user" = "revision_uid", + * "revision_created" = "revision_timestamp", + * "revision_log_message" = "revision_log" + * }, * bundle_entity_type = "node_type", * field_ui_base_route = "entity.node_type.edit_form", * common_reference_target = TRUE, @@ -71,10 +75,7 @@ * } * ) */ -class Node extends ContentEntityBase implements NodeInterface { - - use EntityChangedTrait; - use EntityPublishedTrait; +class Node extends EditorialContentEntityBase implements NodeInterface { /** * Whether the node is being previewed or not. @@ -248,6 +249,19 @@ public function setSticky($sticky) { /** * {@inheritdoc} */ + public function setPublished($status) { + if ($status) { + $this->publish(); + } + else { + $this->unpublish(); + } + return $this; + } + + /** + * {@inheritdoc} + */ public function getOwner() { return $this->get('uid')->entity; } @@ -278,21 +292,6 @@ public function setOwner(UserInterface $account) { /** * {@inheritdoc} */ - public function getRevisionCreationTime() { - return $this->get('revision_timestamp')->value; - } - - /** - * {@inheritdoc} - */ - public function setRevisionCreationTime($timestamp) { - $this->set('revision_timestamp', $timestamp); - return $this; - } - - /** - * {@inheritdoc} - */ public function getRevisionAuthor() { return $this->getRevisionUser(); } @@ -300,13 +299,6 @@ public function getRevisionAuthor() { /** * {@inheritdoc} */ - public function getRevisionUser() { - return $this->get('revision_uid')->entity; - } - - /** - * {@inheritdoc} - */ public function setRevisionAuthorId($uid) { $this->setRevisionUserId($uid); return $this; @@ -315,47 +307,8 @@ public function setRevisionAuthorId($uid) { /** * {@inheritdoc} */ - public function setRevisionUser(UserInterface $user) { - $this->set('revision_uid', $user); - return $this; - } - - /** - * {@inheritdoc} - */ - public function getRevisionUserId() { - return $this->get('revision_uid')->entity->id(); - } - - /** - * {@inheritdoc} - */ - public function setRevisionUserId($user_id) { - $this->set('revision_uid', $user_id); - return $this; - } - - /** - * {@inheritdoc} - */ - public function getRevisionLogMessage() { - return $this->get('revision_log')->value; - } - - /** - * {@inheritdoc} - */ - public function setRevisionLogMessage($revision_log_message) { - $this->set('revision_log', $revision_log_message); - return $this; - } - - /** - * {@inheritdoc} - */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); - $fields += static::publishedBaseFieldDefinitions($entity_type); $fields['title'] = BaseFieldDefinition::create('string') ->setLabel(t('Title')) @@ -447,31 +400,9 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { )) ->setDisplayConfigurable('form', TRUE); - $fields['revision_timestamp'] = BaseFieldDefinition::create('created') - ->setLabel(t('Revision timestamp')) - ->setDescription(t('The time that the current revision was created.')) - ->setQueryable(FALSE) - ->setRevisionable(TRUE); - - $fields['revision_uid'] = BaseFieldDefinition::create('entity_reference') - ->setLabel(t('Revision user ID')) - ->setDescription(t('The user ID of the author of the current revision.')) - ->setSetting('target_type', 'user') - ->setQueryable(FALSE) - ->setRevisionable(TRUE); + $fields['revision_timestamp']->setQueryable(FALSE); - $fields['revision_log'] = BaseFieldDefinition::create('string_long') - ->setLabel(t('Revision log message')) - ->setDescription(t('Briefly describe the changes you have made.')) - ->setRevisionable(TRUE) - ->setDefaultValue('') - ->setDisplayOptions('form', array( - 'type' => 'string_textarea', - 'weight' => 25, - 'settings' => array( - 'rows' => 4, - ), - )); + $fields['revision_uid']->setQueryable(FALSE); $fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean') ->setLabel(t('Revision translation affected')) diff --git a/core/modules/node/src/NodeInterface.php b/core/modules/node/src/NodeInterface.php index 980dacd..0fba8fe 100644 --- a/core/modules/node/src/NodeInterface.php +++ b/core/modules/node/src/NodeInterface.php @@ -2,6 +2,7 @@ namespace Drupal\node; +use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\Core\Entity\RevisionLogInterface; use Drupal\user\EntityOwnerInterface; use Drupal\Core\Entity\EntityChangedInterface; @@ -10,7 +11,7 @@ /** * Provides an interface defining a node entity. */ -interface NodeInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface, RevisionLogInterface { +interface NodeInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface, RevisionLogInterface, EntityPublishedInterface { /** * Gets the node type. @@ -97,23 +98,17 @@ public function isSticky(); public function setSticky($sticky); /** - * Returns the node published status indicator. - * - * Unpublished nodes are only visible to their authors and to administrators. - * - * @return bool - * TRUE if the node is published. - */ - public function isPublished(); - - /** - * Sets the published status of a node.. + * Sets the published status of a node. * * @param bool $published * TRUE to set this node to published, FALSE to set it to unpublished. * * @return \Drupal\node\NodeInterface * The called node entity. + * + * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. Use + * \Drupal\Core\Entity\EntityPublishedInterface::publish() and + * \Drupal\Core\Entity\EntityPublishedInterface::unpublish() instead. */ public function setPublished($published); diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php index 287c2df..4fb84b0 100644 --- a/core/modules/rest/src/Tests/UpdateTest.php +++ b/core/modules/rest/src/Tests/UpdateTest.php @@ -311,7 +311,6 @@ public function testUpdateComment() { $this->pass('Test case 1: PATCH comment using JSON.'); $comment->setSubject('Initial subject')->save(); $read_only_fields = [ - 'status', 'pid', // Extra compared to HAL+JSON. 'entity_id', 'uid', diff --git a/core/modules/system/src/Tests/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php b/core/modules/system/src/Tests/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php new file mode 100644 index 0000000..ee5ec78 --- /dev/null +++ b/core/modules/system/src/Tests/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php @@ -0,0 +1,93 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../tests/fixtures/update/drupal-8.2.1.bare.standard_with_entity_test_enabled.php.gz', + __DIR__ . '/../../../../tests/fixtures/update/drupal-8.entity-data-revision-metadata-fields-2248983.php', + __DIR__ . '/../../../../tests/fixtures/update/drupal-8.views-revision-metadata-fields-2248983.php', + ]; + } + + /** + * Tests that the revision metadata fields are moved correctly. + */ + public function testUpdateHookN() { + $this->runUpdates(); + + foreach (['entity_test_revlog', 'entity_test_mul_revlog'] as $entity_type_id) { + /** @var ContentEntityStorageInterface $storage */ + $storage = \Drupal::entityTypeManager()->getStorage($entity_type_id); + /** @var ContentEntityTypeInterface $entity_type */ + $entity_type = $storage->getEntityType(); + $revision_metadata_field_names = $entity_type->getRevisionMetadataKeys(); + + $database = \Drupal::database(); + $database_schema = $database->schema(); + + $base_table = $entity_type->getBaseTable() ?: $entity_type_id; + $data_table = $entity_type->getDataTable() ?: $entity_type_id . '_field_data'; + $revision_data_table = $entity_type->getRevisionDataTable() ?: $entity_type_id . '_field_revision'; + $revision_table = $entity_type->getRevisionTable() ?: $entity_type_id . '_revision'; + + // Test that the revision metadata fields are present only in the + // revision table. + foreach ($revision_metadata_field_names as $revision_metadata_field_name) { + if ($entity_type->isTranslatable()) { + $this->assertFalse($database_schema->fieldExists($data_table, $revision_metadata_field_name)); + $this->assertFalse($database_schema->fieldExists($revision_data_table, $revision_metadata_field_name)); + } + else { + $this->assertFalse($database_schema->fieldExists($base_table, $revision_metadata_field_name)); + } + $this->assertTrue($database_schema->fieldExists($revision_table, $revision_metadata_field_name)); + } + + // Test that the revision metadata values have been transferred correctly + // and that the moved fields are accessible. + /** @var ContentEntityInterface | RevisionLogInterface $entity_rev_first */ + $entity_rev_first = $storage->loadRevision(1); + $this->assertEqual($entity_rev_first->getRevisionUserId(), '1'); + $this->assertEqual($entity_rev_first->getRevisionLogMessage(), 'first revision'); + $this->assertEqual($entity_rev_first->getRevisionCreationTime(), '1476268517'); + + /** @var ContentEntityInterface | RevisionLogInterface $entity_rev_second */ + $entity_rev_second = $storage->loadRevision(2); + $this->assertEqual($entity_rev_second->getRevisionUserId(), '1'); + $this->assertEqual($entity_rev_second->getRevisionLogMessage(), 'second revision'); + $this->assertEqual($entity_rev_second->getRevisionCreationTime(), '1476268518'); + + + // Test that the views using revision metadata fields are updated + // properly. + $view_config = \Drupal::configFactory()->get('views.view.' . $entity_type_id); + $displays = $view_config->get('display'); + foreach ($displays as $display => $display_data) { + foreach ($display_data['display_options']['fields'] as $property_data) { + if (in_array($property_data['field'], $revision_metadata_field_names)) { + $this->assertEqual($property_data['table'], $revision_table); + } + } + } + } + } + +} diff --git a/core/modules/system/system.install b/core/modules/system/system.install index e8f45eb..c6a6e47 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1768,3 +1768,127 @@ function system_update_8201() { /** * @} End of "addtogroup updates-8.2.0". */ + +/** + * @addtogroup updates-8.3.0 + * @{ + */ + +/** + * Move revision metadata fields to the revision table. + * + * Due to the fields from RevisionLogEntityTrait not being explicitly mentioned + * in the storage they might have been installed wrongly in the base table for + * revisionable untranslatable entities and in the data and revision data + * tables for revisionable and translatable entities. + */ +function system_update_8300(&$sandbox) { + static $revisions = NULL; + $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $database = \Drupal::database(); + $database_schema = $database->schema(); + + foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) { + if (is_subclass_of($entity_type->getClass(), \Drupal\Core\Entity\FieldableEntityInterface::class) && $entity_type->isRevisionable()) { + /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ + $base_fields = $base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($entity_type_id); + $revision_metadata_fields = $entity_type->getRevisionMetadataKeys(); + $fields_to_update = array_intersect_key($base_fields, array_flip($revision_metadata_fields)); + + if (!empty($fields_to_update)) { + $base_table = $entity_type->getBaseTable() ?: $entity_type_id; + $data_table = $entity_type->getDataTable() ?: $entity_type_id . '_field_data'; + $revision_data_table = $entity_type->getRevisionDataTable() ?: $entity_type_id . '_field_revision'; + $revision_table = $entity_type->getRevisionTable() ?: $entity_type_id . '_revision'; + $revision_field = $entity_type->getKey('revision'); + + foreach ($fields_to_update as $revision_metadata_field_name => $definition) { + // If the revision metadata field is present in the data and the + // revision data table install its definition again with the updated + // storage code in order for the field to be installed in the + // revision table, afterwards copy over the field values and remove + // the field from the data and the revision data tables. + if ($database_schema->fieldExists($base_table, $revision_metadata_field_name) || ($database_schema->fieldExists($data_table, $revision_metadata_field_name) && $database_schema->fieldExists($revision_data_table, $revision_metadata_field_name))) { + // Install the field in the revision table. + $entity_definition_update_manager->installFieldStorageDefinition($revision_metadata_field_name, $entity_type_id, $entity_type->getProvider(), $definition); + + if ($entity_type->isTranslatable()) { + // Collect the revision ids only once for an entity type. + $revisions = isset($revisions) ? $revisions : array_keys(\Drupal::entityQuery($entity_type_id) + ->allRevisions() + ->execute()); + + if (!isset($sandbox['progress'])) { + // This must be the first run. Initialize the sandbox. + $sandbox['progress'] = 0; + $sandbox['current'] = 0; + $sandbox['max'] = count($revisions); + } + + // Apply the field value from the revision data table to the + // revision table. + $current = $sandbox['current']; + for ($i = $current; ($i < $current + 100) && ($i < $sandbox['max']); $i++) { + $rev_id = $revisions[$i]; + $fetch_from_table = $entity_type->isTranslatable() ? $revision_data_table : $base_table; + $field_value = $database->select($fetch_from_table, 't') + ->fields('t', [$revision_metadata_field_name]) + ->condition($revision_field, $rev_id) + ->execute() + ->fetchField(); + $database->update($revision_table) + ->condition($revision_field, $rev_id) + ->fields([$revision_metadata_field_name => $field_value]) + ->execute(); + + $sandbox['current']++; + } + } + + $sandbox['#finished'] = $entity_type->isTranslatable() ? $sandbox['current'] == $sandbox['max'] : TRUE; + + if ($sandbox['#finished']) { + // Drop the field from the base, data and revision data tables. + if ($entity_type->isTranslatable()) { + $database_schema->dropField($data_table, $revision_metadata_field_name); + $database_schema->dropField($revision_data_table, $revision_metadata_field_name); + } + else { + $database_schema->dropField($base_table, $revision_metadata_field_name); + } + + unset($sandbox['progress'], $sandbox['#finished']); + } + + foreach (\Drupal::configFactory()->listAll('views.view.') as $view_name) { + $view_config = \Drupal::configFactory()->get($view_name); + $displays = $view_config->get('display'); + $changed = FALSE; + foreach ($displays as $display => &$display_data) { + if (isset($display_data['display_options']['fields'])) { + foreach ($display_data['display_options']['fields'] as $property_name => &$property_data) { + if (isset($property_data['entity_type']) && ($property_data['entity_type'] == $entity_type_id) && isset($property_data['field']) && ($property_data['field'] == $revision_metadata_field_name) && isset($property_data['table']) && ($property_data['table'] != $revision_table)) { + $property_data['table'] = $revision_table; + $changed = TRUE; + } + } + } + } + unset($display_data, $property_data); + if ($changed) { + $view_config = \Drupal::configFactory()->getEditable($view_name); + $view_config->set('display', $displays) + ->save(TRUE); + } + } + } + } + $revisions = NULL; + } + } + } +} + +/** + * @} End of "addtogroup updates-8.3.0". + */ diff --git a/core/modules/system/tests/fixtures/update/drupal-8.2.1.bare.standard_with_entity_test_enabled.php.gz b/core/modules/system/tests/fixtures/update/drupal-8.2.1.bare.standard_with_entity_test_enabled.php.gz new file mode 100644 index 0000000..57d9c07 --- /dev/null +++ b/core/modules/system/tests/fixtures/update/drupal-8.2.1.bare.standard_with_entity_test_enabled.php.gz @@ -0,0 +1,697 @@ +GW]is⺶޿Jݪ>}(ǃ<=&!2$VQ%mܺ6bƤ5l֖,?3llX,\풜aoi##DKyepdt~:GcZ#0LBLXE>u$u;231*mdOs ۷7*s*ܿC1mYD 33zprR'o?fKT]&bdԕi|ig #Qoz k;:mhN֛3&DāQ:dPO8DTAwBO$m)mƪo<' +9aqƢ.<4QCN c纘&u8ynȚSl0B㪭E +x7\nįnr9>퍺O/sQ:yE'_h0iXSUwPԧ9w&Dp2]ON/Stb}NAFK( Pa\a1*?8qZ93%YCZ$R8.Ѣ8ᤒ7| w0qnsGuTI>wIHkR~>S$dK-Q ZPN:)뤬s0aɧxRI'%OS7)礜rN9;ᜡRIi'vE;]&pL'%xRD;)뤬N:b:)礜rN9P=uRIYP(rH9#CV"/}cړV+PMxd(m-w3ͲϪ˙F&Zkz _(~)pXpRN_Ly?53]/Ro$SRr=}B؃(ziͿ)j}(D?hpZ%ڔ'h$fw§H1 K +khA;}͕'%|E ;o]3F{ s/=qB\ ETj؞7=gŶ+ -N+X^t6H'GVc -/!D(jdxCe±AY#b&"< , ^cZ2PI@I8x8FG2:>壹"Fol9Pŗ ͒+ͽCv2m(qР89-xԩuSeQ&֍TY%t!A$Y9 K1PE!G!ĭ]Ч8]In|奤fjpW9rYjm,Cc)53 Vc:,W՜>ULIfzd>xHͣ-Wc0L?`;~ L=hdE$6i Ӆ~"3U #E@B1yhkWG\^nν7 iO)?uy{RqP?jgYY]wg(f㟋LpA$ +E2ېH.*JH?W. wcb +uxgo +%(+^ֶccuR8 B3ȰBzixsKf8T9W\,uU߿: |&1*gxr I6sR.7 `%{?L"ܢ!@G^3RUKcp19NE_J-1el}iuqW!Tl5{t8& ] +^q.^X+ƵOh0Pñ :F6URL 52*8 +L2Py$UxA$f٘;S#n\J7XM>nz@ j/_t;a{3ܢ9RG!ʪ30\Zk7tgEuRGoV1TV +"p* <ډCJzMN0 .t>qEy m,, PL4(ofWՌZ/3=/ 2F5MtW%QtU1:EUdU +sY4h׍p$Q.rr{Z+7ҼuN햯BQyť1z'*M#$pOnK*@">Hqwx*+:hXQT* Y@@k +}S|=]]5]>;;voW*8OrCk xՍ2uɺ_=J Gt'RLX^feF:Q}׮:hK傴Ň[ؾ[>m ti{$bb蠏)]ois4M:=jMSiYB80a ۶44NcH@.e("Vʱb l^f쟺#n\T1j9Z}Gz{xtE=N؝ESƱ לNix}>yǣ=[eQDM%K F +r@x",#ags|6^~JeY_pq[JJ|_C9}`"[z_ Ĭ$3%C 4ADWO&+Q*7ѳqIfZP϶jZ;g&;G73M ƞ4U HF H€eycDQogV&{B?~slےrw]ifE;Ka<ս.F{b7يWuI 'N :UejH>}<ϷywP}Rɵ幜+6Ϊ3]y {+77!W 1//UOpC,1,U#bijXey5!ãXP,]}) 5U/;]4iߡg't<:N[/[G3ԾF"/*! 4I8VYȘG +'`3#׫vw7s kwCu~քLd)>{#$/F\Kw7!9`Ҫ(*:E78H ,Zr4` fg g^np4qZ<G]w?{o >^5q*P`ӛ*@" ҨBœFOJ آworv,,J[էٓ;*:SO({6e7EcuwFɛ=?SJa>?Y$<:'gI#$ǥ()ͩNEQR.>8W5^WuOgi|SƿMuDUZ! .S@vV^cUӳ˶1ni +С;' *2N^^r% z= y7t-`X bCMk!8O)ю{iPy*lraàT8Cw=xSuո*I"a oU"QiIRd62ST!qFMA514VƧ[%):q3k|?-CgZȳ k\ʎ-)l \A!ZTb6Ԣ|kkbϝ{d6+3ch1=?9i594[E`"X͆(Igєօ][AťL-d@fU?i1Fq͸n:o;9E +X^ˢ8Mi3" +d*b +[Ce٦ʩ"ڼӺ?ItqRq\'k:7 v1$ֱ9^,: {|4dhRu[.LY2}!^{WigP2ժ| VBTZۻ] 1F-PM$25fW۷ u&Nc1SBqqL3M5hSd|t:9/wf$cc}ZdY`EE4h^]O珖ĖGGpzm{مƙ0'*ʢIۏؖ`qMUQ[p5oO:v8<E{y +`W  0aYvX'xxbщ+RTQa1.eZaHwU:?&Ipn_ۤ6gQujӕ+ +I_k +20Pc9LRDm$fb J@` @Fy&fa5:l G +Vw?iVA e),;N1˗$ǮF1r rZꯝד\Ѩ_@B*汁E*1YjA}7!bEМ>i}Ѹ7OX?[Q7;\=(D\ƌ70-d͖ևS'0dvi mAj&I|@,5>lgRʁ(!v22d#P]p%.$ @\M2@[$,hD2Wg?P2`0xhݓӺX;'^x"0FDUmlr/e~Ԃ}x XJ:- t}="`|Zt;wQ׋-Mirێ% [l +bKo"%h糏=7S_Y=g= +&Ew iܨWޔ!<S">񔥏cM_\޸vߵ]C7M ǁch6ΩN^i^Sgw(K7v#mHŅ'i_mY6ئ:Du.6!746c\)$fI/{K=<|ӥNL Ωt] LB5PJYo]m1L؄/ }<u|i J B &UClm.fmT@3:++7 mZ՞q-zpq˝- .7)uua!~>I}>>K؈1t$2U`{ 2HMrz S1lqX=i?"mvh?baُHu'q˷QqK]cUQ0b  +"&4| +2ja`,&Kc竧 *ui#Ь ~c99}./V.*Qxa"Oŋ7?"y ckPN'iy'=6C2{{R$UU)/]Db|OXگǙ"haKSBl잌 +g<^U)Ay/٥3-ȗ h$ckDHT7v q&Dh    f}Hz{G_fb㑪vqG>Twac]x|0re'Z޾zÊpiP7pTw }۫.>3Լ`[4 Iٚaq2&Gi(p5('wә|or&ЈW,"5s%Xf 1` °PqycC]uU64YfVXjϯkv{v8m(]D~.WAVb2Q QCkqڧFoVg3P 5>.\@,ALoR % G9sv<^.>y:ǵMdFF1V[)#p[0tnN./{Q KLu&!ҔRF p24Du}1 dcqAGS||ڥݒR@vwFi4v1KL5`T Xg̈lZo#qNloE)wSlVwF׸U8".ne~tzœtX)SP o^Yw}M1Pxo5l_"uJD6N⵺o &kJ[uߗM={A-:e0׸ /y~ZtYm6$n])[wBgdNd'F-IJP?xC?ovIsmCFL#Ju[R B&RCEnRt_LHN߮2(/M{yԩ:qK#= e\ɳVW^|)~KlkT…s:JKl& :ԅpL#au¼Eh#sW,۳Q Z!𛉈{h^>pZ_oثOwj0uߔW)裻. + +Q0~}%To]oS)bG#lM"ЌpKqr~;pv:+s7oQ>wJ'u,MH,flQ8L~u q|H&_,c5`Mkq].}VP%.%ESWuʃf`o+SN BJ@:S8)v3>)p7Վ/l9طW3SZH}`ߨh[+hgncҷB{ʎ4,s,PXRa +"hǔε:e,B񙓰>E5+͈S$^_h3$nA3Ky_2g_,t-hǜ=*1Sr2 *R܅夨z SSJϑ3*P&s]VՠI YDvmrF-Q]r(Euk^*ثvorkH>iԌM)0mYͷ=@-'Uq7ԴDIC JWcWsƙy<[$Jdu s{=ܶs%wrNYV|ЊU=_9kIڅlMI|Vi˞ xQdR) ql9aZwv|h,tŽusvOo>-iEsmKrOŕXQ*uXZSò8I`3f``*9J[C  tl,neK,=UfϗqBNL: =y*o[>q]YS븶+~n YS* S$@pKebHUߏcsyē-5VWDWp  20(PCP'a݉s>5O_+7`69"ӠKNV'IJ +t܄_JeYPx +RXSeSΔ! +rH(AY;6ӕrCmzVFkU甎J٪:X}X=ԳX˅(uz*:W'_[e*,ilKA@ )m סVj}ݹO7ӣGZt@60=[cVb|Y9s57BX +$lsTN =ؖtȯo"h7v .ԫ=5'wY#k5/Ȭܯ/^Z{nJRl{<$/B_b(5תf"S#l~}0a,W[e߼l\YH{Z'_oI^' Wϼ#ִ'|;nde]2B% e1`d٠v{kڿyZQZGzKΏ͓aS.Q:>sR2OEW,%I~êιgrpQ|?{EGo<=wz"aBE>l(wz$lmсxBzDlSE/|\|Ռ=i9ق|W6Iáč/-lg1S7L Tm,z!4$/9d[ wo(H%HOZ~pK +{4qE GI %Jf Q՚0x Ƭzfr=-&韓`9zAf@'ELT@1ķ#)wY%.!z$^Rp'~N]lom̜ӻ,ͻIIKcԛW|_z"WBߑa)!1r)}fG,#f9Lc(1It#Z4ѫhWpۭ'}z<GߦټV:WlO3ܥ{A0ftq*zpn$p&]­R{YH?RücQ?e'^m½R44D8x +ē&\_MMwPh9&j/_^/pRC䐛7Z|;}'Θ؛aՎi)jܕzWe'sAVp,P`kM,@DC\khß$P\KnȉU|2Ԉy*d͂7άN)+pE|ݑtP]2ttP %bffiqRz _n7,םzS8ܲzs3RՉ 볾7^6[ӷt9p(#!@$A1Z}e%>[C4N]qhu$8{kwDfQÂ@2.װfCpA6T G~CjHm>on.K'!-j @aa[":;Dů%> Hvky/^pkuQlZb! P-I3"I=PZ={XcJ}PN? KʼGbRl! +@TV$HlhQErRb$gllS~QA^ߞT^cрݫ^sy+x4{s[YKLo0}%_-6ePAB `Ā,cHuLVg7禀5MjAݚtIp3y6ړ/d=NWB2s%sYkr(=lgcfڍ-CQ|>M~= 4񗹕*<1I}xY@͌"ȡ`8ab?Eȹ /ʡ<3D)7x{$'^O2 ;R!(mI2`L¤-R@S!V iBRQa. چG_eܚua|o C;j.^P +{bZ_nM ElqSyW&0?;/yrEH8x,=Nic J10(^yegכ;()xu$/K.ɳ*^$:\d%5 !!o&t>yA6@6h]"ˏ'-jgNpHaKP\OT:X!Lqh0 Y2Æ!~+^8yYeayQd 1;#VcR?3'#fνsb2'ɣTn /ǁt.V')zN~ 5_q~o8Bx4u}uuǗ-6T +UQ>T::IJJABLG0]8=KfX҄K&%cEGt[RX=aZ;ZWReH6 +e @`% hoZT(Pzq K՞Aw|sGfs<cOG'+e{ *g䥦fq]2L"Hw'Z[ıǴٜYV-"2vn_1A.͈wMqԚjD@$ $;j'o28LFI"9u*gIݎ~DO7Þ3hW۽U~zW]qj +Js6集ߏhZ{``D)%j5:{u|5o֡d:>!*E~)b*̤P[SKQCДd§hHR(td[mmwǥ}Q*>):w=8gQ-md )@- duF}ZH7L;F0kC8b6Ӏaв#6z_ +wy>=b|w#iW׸R%A`[jT몥}!VDM1T#!Ee,Ohr8 jݭ{cy)G|Dt+I6IJ $*@7,M䬐(U寫~}^l7unH+š笎b(T&p[ 0D.(r4q@!.S|z\i{xԼ\7U#젚wt1T8v*Ww+q=xA|[''dž̺ +B!Љ ֈ)TJKQ=bC4헣)fAlj8bT'oe?׎s\AQ)5v'wsW,/e,)m ;uѾrq홀cpsFhd3Q`HҖic[W:<6Y3G/S@T"k&UYmfΌKEaԾ3*[=d( +Ӿmo\Jg)Z:M|[QVK9#K!g:#JVD+ L, 0,:/].B}0mݻk5|P3FJ'{Ofƽ krJUۦ&b<^ +xpo#/o짩[G(4}$ eGw]->xk=;\\"d5 @T |FF S4ݒ?:m+57kԆb{Sh4k\V +M,cYi"0yd0ݼW,e[>g+?[>V!I&BpA;"l4|e^_VDžqoNݰ_hEh)dvβg-lEbĺJ63 B߽8Q8GpgC7aoB@0xAݠCoJ W(\Anx:|hP@GOT> ٻCQD r@˰4jS2 ZҍH^&SHgv!|z7k:,oK(F0[q"D?-<1N;s%PEohn2H.X "QE]%K#6.?6CLJ,%gvlM8Lj)m bNBB[5%֠|~i8{6F"eky.8Ӱj_b>5 +;@3Q,BD$-M16fXѨ]HϲЧ!-G{)vnrQ2_EQt4/qAzչ5wTTaP dd;f;`{^K:lMHdY 'b"tEU@X`ۖnCM ]7߆\|C!7bA۝P*fþp puP{-c?˩D^\pY|)HS-P/[,5!y(1ZdXJOm:oۯWUwwa~vV?౒Y;S\^ S]\Ml`.:SdiS];\gsf6)sfhhCuwS"عH噖Ȉ] ¢vGFdH%T"]NfЌC?2ڜˀed<7Ė:mvʼDoq|'3 ѺgzaLv{ӊZMĔNŦ +&TZd!%9nx%Z1ZCDZomਕR+FR+ VZ 0,>٣RN4{7nԳ݃epjExj-Lb\'-?$(:qJ+:hZV tE~;A~jd ߲72oѕW @bD-ɗ&aj%pVk!V,#yngKJQRj$~`2"IkOŻez2Bn?(AԪ.I*AJ/?n9~͚s,I""8&f&Ml]M(gGO59Ws; (zN{Im~_)A\yytnO/E혿ȒS~z_N^a_\FmL$-)-˖y׭^MG֋{ΆO~\.dYi r~z;]gb :HRU A|>πN@owRGBjR#G²aH@KLI +Dgni$|+V0j(Zσ^|5U)f9vA=eCTMDp5'u6=]_<Fԯ`gCEeTmɀ2-Pe 4$]2g婘wt_)YRO_n1y_e."F3.esS [OEC4NMUab쐶뷇Qo,X2R_(|!!8souAA\+kBVGU)uq 1)c( ׮gU[?-cw]EW|FVrɐ[G#DKXKCӉ\!٭\/~Ui =lܝ|x@Z3 u$kTMa?w|-'6u;,,^AtNaRCSS**A H%/rphhbkQ/N4.dgݰgUX6EdgΠ<{lͭjD6n;>NИ%SMMJ(AӐ|Ob܍XOUJ<yxUe\B:gw ˥K-j"^fgv-Qw%〡jLX;Pf +@6 YlOF6;_R?/*R$?>R1%ȶ +~A GNFGB@_s-2%EQlD9FI$]H?%K|sZ")?A=2 +_ Ar;2ŋ69ۦ2(6'ptIB4n։O?xow}=tہBZƭ߉t; +LθjB2Tagc/u1;F)C0U`7OcWg6ѷ-9k[eBg} H<M$+.^&%GQ ])idY$`*W)dMR +LbTdd#u&!c;|`WӮ.67_LإlaFBhLDLIfeMI )}@{G_;|$ +(Q;Q(亇, MX"#TQZV$6g;eϽ:wdP4_Һݧν_>{w7Nɶca_F#{s06vl:<!6t@R $rY+)j +S>4 ęU .&#WL)佋A..ZV 7vmG9rQ]d XH:W", f:0 ¶tiUiQ⧤FT=r)dOLށ6i*Tf@20 ]Q `n2,ʺegE3Z5h<ꃍv-S^:,(ӫx;-Y[@f˗ߘP4lCr@.nӔΰ< F ;|Sδ Xߛ9nKN2Onz_^)7[dK~*M R3iDDDuVZD|Fl,ApD}YAjvQ +٣Y.Cņ]GKjq3UNn 4'a6z2V=E^yn~ ,q8*p}J^I)i9r踦 -wz_f1J[mtu~ei'k9;/iD.Wȅ!ƒ+Kc߇ɢ"0Ja>R[uiy0W'љ5=k[u|Նs<ʬsM~ 3_(uH|C0[U/ÚEMAaXR'k*ۦQ*kEU-ߕnGŇ#HNjխwjF]ڭZR[)Fˢ XyQ^lBܑӭ=d9WpbSHt C-dSn;TyëQgwÊϟuI~ #sS]_:N12{C&tٚi!dxn M,ĕ0eL|c:<]^MwPkN=R;7Ëm8Sgm?X5 H*Y؃IG% .9ī  +ܼQ bD|Lxq" +UR9Gta4ga8N"iљɿ)=/,4Dd`:Pz4_ +`8a{oDcg獌K|'zK'ޛߜf#0xs OJ_{K_߿T;JP~gM i^=ׂ=I0$KiKJ!iAspKpBUcǤs> ޮ4(ZΨ!|Ȋ`WIOJvq;xGÝ28rVw2@×(I O `)scMBI / +a.1IYVRbiU37{o))OtXmP\Bn;mZY#ۻ4B% 4lW,^ˆ/>욅ia 4cC#8(ZکhQzM)[F:uMw~L L;K^ɂyx0-bŷ!N +nfqrywUg\p=5c~V˳vi +-es/g ^zNnGW糳"7kYԞ_kSbՑ "m#N82 .XqD)9 웥 $ +`,R*wlb" %LNk i}^^1L_yO +.NYTE澶'KTF80n6>s${uTWS4T:yNYq38:-'tVjr^4D{@f%'VkM8 U68Keȋs9@ÓKt4oGYߘ7k콎%L[1Ho2uÕMf\S >txQnu6-SQOW7MPc"Qde :I4%Q%”^܉Nj'p2˥ostӺ*+߇E8]./Jmxufl;#sx9 ݇Y()@K呿Ƚ 0p8fY6nu ]W,nHpS";*"\K>Pn‘Ofϓ1OY <7nclNvNպµ=Q*C/L=~—^ˋzKKZ[ {Ob%/ZЅ4j_)Cш}lmX:C?QLox Hm +̢@a 0T+IЉ$83?FE(aM!K˹Wϖj.h[ӝo%Š" +x&EJ>DjD(=&#t AuJ*HI,n}XAzt}(h`)Z ;."7.fFpvFBSC0TH9:>A+j{]EP"oi5 CƐrBWi+'BinŲMBq: ҍ(:r`o`J]ad +yD0+fp'l^|!:X+TEDF"c%B防@- cZ|@r3o(;^,#vTOC W%4q Ҥx1f;H4eJ/d,"LoG Tp1X`,?RFd(tXpfl"0G$D_PrmSeXyB DWj w&hY[|DE& gbٔ0l}}"[/h`x=BO co+|~\yÝd\+ \0Q[dc(!X؅ 0xEC8yv9M:=+h~ԜnLoxx3!Щr" Nmֿr\~#x%d sNt8NA*O|=f i l1|^5ȇQBG됳;8D 8gD`pm2`R2b|?p̝ W a|A~J?d'ȫIpt873We--w 7/A5`E4 UFLh_ RˬZ KJ4 hwM#m#L/Rf6<}r,9AH% 9oV4񃀀& `|NuHҮdYʃJb8Wd *|ZKs)jV&Y4 +p ]hʻvC Ct d dZ9,0 +jm +.=d`,V5J#M@ϠwAf(HgÌbѻ0Cc$%/]F-P^oFPqȋaw@(x;B7ӝ0jH"rXy|V~w- ),oKOdljL.y(mcdPLPRqKzqp +A3a$1TRs+{KA3EpLc%܉@+A +-CuFuANIiM%l,8~eU[^\gc6#—b +*,8..Ɇ} kbFbbx9/% '6_s79onOb(~H~#ꋵ2kb-峋ϠV ɗ#ǒ]=`ttriOKحS+hχc;p:l9zpqtK-ٞ.kcAb:O6<ǵm|9F^??_ޜco:t_[8UoƷWZںws&_OTZ wܲ +?B^`|({=4)\^IPђmâYհ ߅>燃#ž7aWkZ{<t|BmY5Ax)m4e3|g94])8J,*rgWDu¬l>`SgYR,A{n +5ՈllBy,BV!kvQV,\֔iW{\FOϕtPϛ@aOg7^iӦA|DV|7?'2H`Jd6J}tֻڢ~,^d02 v5E/.OGXma#xF4Fe5!(Q$mY[4hD44EDe~ݛmY_ޓhnJ9k=>\?WeM{rq[*?wzR6H9@֎f~f[ \yШ;<ک QLF~>bPX}%ܳڃN~Do.[0/603F5O6>Z&Zl%0Y>W.L6RDʊ-g 洹 C :%4O%#Y-hpK C?+Lw.mªY{]Lʃ?Kc$7kN"q*~tUD񣫋'GO8|7Ă:=Td^o7gK*wͳc5gkt8oN7+g ~ɾGE<9{Ƒ 2)pim&1c"*()vk(!P4'QI1" >%&"Z_ hm)Ж/h`-n}LW?h}1ڑ~"S|88#,~|'Y@JI4Z8 +ߓ6d~'+}GwiKw|yJ42u|Kv87au +EQy g3M+  \ȓ b +E&gOa|Q6,EV):ݟoo>\y)MX>^r2X2J Exukқ +"*u_ +Sb&EF(`Ĩo#UϤ s£}@k[RDU\!: +H(bm+<>+)MO|z펋4TC^QQK;K.# Qppg%G JI\,]Z|UGtP8k8>P_,y:rFv|t)ŇAt1kptc[2[h 1S\31cCܸYky_sMBbMoh2@. A`,FU*A #"V͂GaA5dKS>꼢ohw(|hXN:6dko(?~HIA qm߈@M"o?}"패e6=3j@]6giHYU)<0.Mu=n$+SJ B9o0RQ6m8G[6lq kS i +;O?;R/ΰt@~AFvd2~i;;%}Gs;[qQ@sw~ *B4Ë=tn3( h{^%}@!dxi4@ԆV2r@- /0v(TqQ W^D݀r*D#psyX!1j2᲻dD䡄;P )6>`lM5> + #MZ7MДpɖ8Y l_89r"ɃQțT"F3!LB %0Xњ4-8jL_}7-);{0傮O;bg/P/ce3ydVO7`B uk6JlyiV>oplV7|SĴj$b0 5׫jYnBdD]?.YJ.~%Z0{lIC2RIlҺQighі>JEخ TO;ee'M+r- v^dl +00Y<(f-#bc_/N^YD88  %^4%6$ +vX ;jt%B?~[4["VX?:9,q&ϣOpm0A\y=:[J-¤+3e#nS\TnͅOts4R]E*t-KdVq Pm5dT_ +Sa>vTo;*]s't.YRJ({{s1{'z vdS%bTtRzzX?RkZ-Mm#vJZSqZth? +j8M@k %Ͼn>]/D'ܩhŰY +٭ gQ㮁!.qh2i.9_fFiA#jeS2(L4y +tB*?!I%0UZ<: .T`6LױIsB +.bc5xpfvcl'[;>a?h0f+jOgJm':ֱRB5"Z~X/vcڢ$O\^6i%}hxhғ@ zd(tp'GF/F~n&8Kazv{9>3nʎ:n՛B^q""ĭ(6i Ph0^/4hV~<Ě;cͣ,H殗ibzedbBp Mf8ϯ_~o?Vޯ6Yyj6_\.:%1.m7h"* X;*KU)\d*"ɧE~RG"W4;S췷YVWMwSR| ,oi  + WWVj"CR=Bſ*eX83*DzR%hCh6-Z$9@HY ])v͒ @;an .{1k::05龾xMG=N:*ڴƣWWklO!N34wlµ>[2 -X=Y`>F &S7mMRO))4 /9ZAp α6q덿~{+U"/r͸;{6օ8ߞ{k>G0f=8uOt)B-&bl5oU8*l7e@&+^,XP>lh=X+\FZ\Tu|K`+.!/L(^CYq>̘:y2J8bp/&~כ/~ty˟sl_=Wœ.I{E8\It +0<؉ff0x+!Byp4" sE s:Z.8{L=rDz Aaw qEꦑ*e" <ȋ@^vttWr 7hgk]vgDGZN5TWLŋ_QgH@xsRH.\Q24L4ݣSY VJot#UEHD^зF>#jMPZsKe)0ibm HM`X![Uᰄ8 % :ĝ)Lhu(+(L?ی`P\F+D3_i{=Z>2^(th[A6 ǞC8x|O*6|䫉waSƸhߣ@A<,ТWcn$K @jȖƾ-Җ@ÀcWt) N "ΌY4E]iJΕ<)+«Vߢ׵r߅k.Z|G%S#:ú~ZWƦ;`  "x'y[Ԧ́+" v`]cde#I|`؋9 [= 8׭vy\]ǫ%񌢐S.hdf8"T}u(Y)E@қ=Q-7pݹИ_ع)75VCV9Dk1I[Xa< +xq6";9QxIHoD&|xOo8[Ͽ  Sb:K~e\['kICDudg\0=\6][Z--I^_i)J0rtū_(,ܹ?7<7/_yBvܦN@W中*yJ,D尢01ԳjҒ $)0mV2jDLB8&I2uq4)jR=]-0 ëgbVyM",1*<jJDqU,O=\DH?#B?d}Wc;;9yn0?RE/闗v)[347m^kEbXE\Ɛk0irΉѾK)0_M|Rp?tEPB25J/𢭍dQ)p;E7b1u#iaUr K\3ޖΞҶ_4\iwHU{aM{}iO:W>۷DQ籕MtNd9(⹛YzB-v;`o__嫿~|+?pj:_WvF}(5]V+X2ʜ!T~WY2R-4{430hhkkl8WH71y/ \=Dk^T|޾_B|Xox6Elxq3 EpVAvgW V|qrEH^@P4l 򕴽x7TW?8W>~WU}{F$c jdi(]5= ~[x%Nٽs*[ 5<`Mprvp[bb?Lxd+,ބ/MCkE-c?.Xϐj/Zhutk~]H+6bpM3$Ͱ4L4Ð ^,{M&Ȅn/-~A?=~7?|3zL~tiߐ`Q`^6&a={ 4j{RF@/FLacÅVim;El:wO$EAXq\; E嚄y ؒ:JPV0xjaVTJCkSkƩkgJN4:] +:;KUY0k&!v&lUYe/>Kv7[NP钊z3‚"vFn-ϱCH,hЖنrˏ%%@<_7~Znj]2Ձr/*b$o.xEVߐ ؠ KL&r_"PP6,@6_KjxPYEJZ)+컨DSպ0FbR]fvibxȒ4-w T`]_xc+EGĪdrU G_ m])D1e+2#=90QaDOA&^ r@?^2q܌Bw$F:ԟijF韋"p8  ԞYXtṔ]!pw8Y66˿|snHիק|&xw ޿b; ޺L;Y{"Mefmo,M6Cu"JGtx2*{G*Pp uv|j:Ƥk33N$.ul삕8l3#ARf_St=Z!nC;(gn&hm5aYҍRۏWeǍѩnsM>b|Y6rKded{A49:1,3q#K1JQCMmDZVxmvQOɒwXQD#HeevzP:yd7C<~qUW5ULiT!U{Nϟ^ɾHB+1f|eQI9ZA$`*,n1ƧpQ9v=vM-?O.e C}sWVθM޷]H;#-gN߯UR!^u^}9dž@nOcR=|97?~z< bW[ zNIa qюLI.bHZuhJ xOLGtpKM +K^\sp&j'ܯB{ jT"qs)LB(Q% (E-U6scmV"hsTvؕL*\Ђ!˛PץHN+&xd \sԜƳ-AY ]ߓ~[10r4nc>RVO?҈~4aO)+lVY&Q62%TJvN6XQO3쥮Uj%D Mb~yNJ + ;%r}`7̍vv_Z^mM3 玗y`vNYuNB& n,Oo#fNrk>{ݏaG.~kWޫ&YU5HFFx|?q:ܙ[pg,2[g6OzGZo+fnRqn8֝C8Vel%}L[$kא Z /W״Ű:UQ$'9cߔP3XN+:CM.xO] ^g GHRsD5r<)/7֠NsHpm!n xVC< y y؈<HS?6s/t{GD ^oIx`o#ޓiPb~Tܻ?yZ@4Nʡ LzT:0rtwMbt?˞uaff]83<8E?~* ]fy')\ET\ [-lQ'&{޽ +f> q_fnirq bB(; +s5E)[RRa[6KVD +P:KJtڅv$2 +_8}!(yJ3>! vBu~nXt~ ڏA1h?0;oҗZ u\V8FK NȎ=%NudJE(b]S@WpKw{ujB% |<.` +bHlSQʮHuV)L>r#]rupmmvb +z&/TōdX9Ck܅D*uLnP׮|*f.:"`TJ;D-X-ۑ,e ,0,M ![ޥqAiOӥpfwkK" z-{DUĀ&/s閩/se՗4|tگ2f;~z,S]EϮkhWKk/i]@|C <5|x4 +5;ʿdXTMnElPWr\u@?8OKՁE] +uPj+n3S7_A"|]m}y厕Z +[ ;_hƈkc O%g3nzv=6]`RٴNpjt{(y7ԈABQy +\ul tɖsώ¹g["kEI8O8 774.2(7oow߭<翑}1{ mceu -p"qa bo>>7/ F z_7Cy:YVaUld;8@wѩW34[qzVp@c{(O&r(i+R}}gJ "BPcPL?IǢ9Gg|HկeU +ީſ@H +KG( 1m +yd+^LMkě6QOʤtM/e"5;XS=i]2,Ĉ]M%&p"Zs^[II{K{R[߀:h^:QtT)G*HRfSOd`xqAL1 F*C$lJb~y8X%;hhxEpG?u3yH,/)] LQ^ zBIa:6 ; K ?/$9x$w`:K/1/}u"GR5_ + Bxz+}6@j$Kkt $šaMu|X* S1)RY%iE]&;9)^!ZԵ\NeLra9<,'M0Iل[i5}n dPVqK1ɬd%1}.Y^D6/nw'͒=Ew> -.Y%N⾴(2'tKemjR&"٥U~T8NCI_Zc<,)]RR0k.=Oq M2ownicZ.=Uzw$_y"Wuߖ +\4{D=) ,YHr;mK_:T\oH0QxsC`%}rݦiS5)xG:tc~fhST' Kس +mZ44-\J6B\ VC^,VMb8: @rU{^Ul+r{t_ϐ52Ⱥ # D=ZJf/7')j"j^ (_fF<]cx , :{1bt^ bY0s|^p$A7O8vBKQ#<._?&WڿO#|}{w&c} Ox.J_`=_3\$} ^p$g blm7nwE\FWC4|B;DO(񡺋^ғe! rmyP З6^RxǽEW +& -/ֻfÜ1!QD0rQdA-EY`U‘% Q2+>`ӂ7rs=oc=k.F]D1lQÞ^E0 SeD]@ :汅 ?f\wA:)}Q ĦES{ Px-8P0xb Ӹ ^q|/F (lOqxqJ!Z$.A=FQy=/Ka%UQ iAep/qT&Y.,\^`jAapR(tz)t=?fBN (HDkofɊ@?F7Cߘ&2|#5B8bOW\=e_KrQ%7P 'jdťDyePm~7:ܫJKt).f]ᆸ6a_.k'Q!>w2lվ\>Xʒ+Y7 P!nEB 6b¡h#B\ KuyAZ-]qK ^- s*$⼭6|jDj;ʑ+\TqCXY +;!g*\0[! 5g.T .` .\z YWKshSq˲z#RiQ)pɨBjSv×âw喡1/]Ö<ϿVÓ 8rik ș~s+f; ـ̏BCK;Bn;œ z vOʷ[ n,+se!J(;|A f{|ǿ6;#1;ٿm:3m|Um|~~g r\GwxP>YAiQq0ߡF.$f"Y}smB?ʇ(6%VWrRz@J"8(G{>̢Y/`Jv&r_Tii1dbbOO<"hs?K6P*[ǀC+'qx3YWj{ ˓6h2h]*9a]P|)"[6Ki zhw]BӋo ._]ɧ$J]s*v+h55Mbo5Uk*tmKj{eU]beVZݮS}A@v煗&:Hth *Yo/ڴVR _~/_ϊ+/lBVG*?BahГ0=/߲'{ES|s6Y*}o?EeUm풛hצ8/rkWtmj6ժk}H{1딪dW߿Wd5jDme@4ZUTvؼAgYU2E/wt }QQf}+ 'D_u\";hՖڲ9kW<eQU RE_\3Qmx2k^rUF^qQv#fx.F`a Dti 34ٟv2']pi%q)#R:F:Œ_Mx ؛V>2]ˑД&ut+..:WM_$_юr4s8ZyT&:i5ۉg[]!IGlain[OB}Qr`'[:)NЮU\ΫH1uΡ!u88m8%ݙ#Qe 6tmC?z, ]gMچذw:>yy-C~Nuu&xXux,s? +Ii`0;a̓,ug|SЗsƋs}denI-O70n%ɋQ{[qhXtM{xzax4L ϋ^r/-h}^oNՑ}q<4bkfdxiv}G|tFeUۗò<Ƀxa0w,ЏyfI&a~m?,e?i`Gq<|3;QC?^EqXAdXu]pM Ran?fvM4JoW"Ц{E`)ss玙[ y`b vb*IGO53yz~`}3Ne݊斺0/NnpagY;@iL**D=|z}Az1D&͞M\WrQ OOњ ɋsHN廇hzoZM%h:/E[M8W> _n?e,oy-_qA p +G#epQyau.ڤ4h.L{Q{Y=*z Cpšyp/~E>f yۆ>JyD)Bl/(?(Wy{%iARJWj7)f-F_!p%i- +fUxځ59iuIBé/M+1\bzF-Ui<-q0#m94b^Lȳ-@S+j[AFP#=дù&PBgQhIEIl;8+88<ILmj{ ?nmRsnI N a=$უصMo1[ڱhn~3(QjFQf YgKIS|'ۖX8$*JǨ?c ӹiڹex$5{8Hp3p>Nf,v`F|+^WVdZL7"p,Nn.شR B/3\ +@Z5];s]+~7>6?r79PIa8t|nfخQv;fG"#رi:ĕzS=4lf`ؽ|C@.Ώ=ʲ0X7Р ȞGkp8Ԯ61;@R?<#wn!|ˢ4RGm}G>Lԟ1Бs-Q'QPojs$e~x](Zp v4ip#pbjU"p NL<{6XGSNjfg~Iԧ[B%>~": W 8~s/pr z`u5u{tQHtn4(U!m𵻐y4vfgxz@fC7s a-TP?ggY~ICp(C9J(J^Ta)^ҠKg JОǑ LPMӘ۹fچWg"_9A{>@i0! ]cí5-K`SЁoAh`Js)貢+3ܶRq8-3};L-vkඞgi&8%C(rM(4s, +kw044zUl +b@VqJvfR##} l@!P:&\;N~ +$iBKmn+8@,t `?N~y8~%׆u]aIj@Q &h Ί>8D!P- 9eFaC)vCO1+4 3݁^j!w ;2N{бEw@+ zk&z*pr66Βyg8?4k}lV~Q*{ņڙn |p&YI:~PG)lHt'Sb&FJMH HĶiNT-yLexGcQ<7kDv=@z"us-pܝ)$CC<6-pO$`JpU2o/'8ܳ"Ȥr}Ʊ0 =D3#Y0>^EE } x[!B{vVYppoRO'Eø bCGN|ÎDW/LsLА8a$=xb%[6(4guaFd无aoOR:Tq:QHi8#7~=_ UNX#0FML/C~cK]̤T6٢?VY!'oؙBBAbǪTl83Ibƹmasgs+3+3ubWJt)sc{GDcp xn6R^؆rvFq #u|Ӝq<{(2]߬W6ύdȟN'Ȭm& 8X׳ލ9 8ъ_ówes|axhj +spzx@+yк 'R:zܦw@DI'++㌹A*C`A}Vn }32,4G!(@>$IE[yzrƶFF +v:?L~aNLhldюJ&So{qA[s߅. >l +E z+CI:ݤEϲ<"HJ=@LہlM[&B+6pD4c#vsdBћ&q.G`:H0nꇆzM),܁ez|֦ e=^ePY +8M+I܍A ;:(N@opR6SzQ܁5 4|7 MGF@`jtInzgfذJ=M)_lVO +8Ncaf)B:QOQO7&Zf0 +X>) Kg.>oYӛ,4*JF .ON2/]'NjzM_lfe0ę[y6X8e8 yH"`sNYEdie^ 8lY˹6C8ݨmM FVq~47r 0S?ʞfnNF]B V~:kֻb8ԕ-3˴alRˍa d^{܇&@I 在 j̽ nQ5Fa" v<շ9!@e{X CZ\Ewf|yݭ6^ 2Fva 4a<7 lmQ?Nx,f& +]bN` 51|%s)s'd.Ħ43 />!?3= 6`! iA ijF4AxNd0g + +i\j]VjB6}ZMmfs/: s3?M, +1^qm!4qC˄ ct-F'^PY &Nޠj71"~ÚY%2y(bbyzQo/AIJ tL;`9#L}1 jҨn`0Lhx*mXVāylX. q-Yj 67&/B;cO͠V+kv(>n(HОgP +J&P]cԘG'O7~>hױQ]n hzI#MMt`ĻC|PgFG ʒ,Vc'qoc`c;т;nãc+SFZ1P h)1n2Z%rώ¹g[9GC*f q77zek0 6]hTx3\1< _ +4ȣN`l̜5r%c_&qx8o,;yaF);<@>;؉&n08$4\'OC7'gKM>&m$6xnYt qK412&#S $̹[`uN}д-P!)5zJH1(W~iNnCE'ļ +V0{{?CdZ8Q;HAUL)XV1$]!]Y)7n8vFe)'n{^Vt4?BS/tB +ѸuPU?ۻ=J]U>yQ̱Q&U?yi4b,HχwLb!TEwN8:#>T?V,ʡ;/% M])=ߡ]z0Ü\&ugks ʺ[ߣl +EpP^yD:*6:d&u$LԂAYA|NG +?fqG-ރ6=yW[ۈO8ow63$/r8U;N]G0l +D%ߡeQ"^ +Lc~?}[ 0VP ]GENy^~m%jUr~V5nW{:{Pk:nr(b{^J]כtE`zm-W +X4W6  +Q\m+Yo@>N&XfaRNǞp/p `<<}}Gn3Զ+h0 \:!A +RV"SpHhxޑĿӰ©䄕t +?HSD̒~ƇtJ x`rq*a + ʆp,j.a& %Z躛Fނt2"Tb_;T̤xğc^·1&8to+q l)B,^n>`~vn|e Ȍe5R3 +U6J`̔+cWR\]OGW(ODFDݮ4!Y@WZ,=M։ӉoYC",UP}*@?Y]e_nJoN49N\3g֐5$iH +Du i6M^TY_XI +`fUSpsX·!F'yOLXWpmV(c2Slt(n.cR* Fx7_I㡷x_[/5~=x:o!׬ВUaih`jU~5z 0N1C,z{=H,&P#3ThóbBᖕa-!Pg!ZZ] _huAvz S+ɹ +\4z +eeG UN7~^mm:RO*9Y& +KkɦCܗ髂󊫦+ +5²2vE֩P&%H*G̨["%S})BIC j8)Uˢ5 zT|Y{s^?w;o,VfqG t[%X:@ TMի?-jA?vǫE R|_UBc'UaГWnϤuR zs`T +^YF'XT*ptaXe*d'[ Ϊ;,ͅ%xiP1Φw߾.^ +PlNg߆U`YYOi KGc}Y`.n#ݡ5teW^֜nyٯ>l ~_nM(&|{}6g<5i9㗎 -]J!pC)og/~4-o@/wo ~s4Ԁ1yA-ϹĦzPLk g[9]beGq랛JQ:n_iT5 v'?dDlӞn";L+Zeܑ-N( 5-}Z͇֯ !p}R̔3'F"W@":e ـXI) ? T7rqXKK%,NJVV}t\m̂e +-e) +J iKfIq ZUnm A=T{Éxzg/ٌĈ +lKqR6ym!&rpd#|@e9S2)2KVqu# 03ݳ3*!o ҅+cjVDT6"<وLsL)|H\+q5 0p/gn|[.e{O}LQZLWṂx.hӹ4(Y(o + Q{Mtr>E:fȺҴ5(_{8(IY0в44@Q'Sp]*vyťD2b{ \8ߕڒkٶڟV s#7zcos8G09s 6hx7+Cg\ic2,J뉷um2XYL˕B޸,BKc*<-<c_k/(k4 yޫ]E@eʉ "f|j +#*;FB΀qۂu9;l]-cw[|x3y8-;NkL)LCk,n}.bvZD@V|ՊVb ۶b#lK0#v*#cK#i +*^~؃ߨ"fi6Iy<$0ÜK~Zp ݥ˻hfq2B"lIHq)U]`#:fv,@;DacrVi3L码&H|qH I'=t@ EdN>A*D'.>Q: +Q;}!B_@.f7y.j_@S,ҹp評ZqO߳WEuȪKϊ"~'\jEcN۬TSNݫ*DR(tZY-Gt_(3 ݑؗ=2z.業:zkkjT:[J9eX"N HI8<Aq rOܒg=hpo#/CEK]pSMI5A)D@%p_u&V]/xA.h s,8؂^/d[?m\݂qGEd_]}A-`s< `AYܯ`-L d!.UEA X0Yg!,}#pÉ +*GbK{$TA3Fœ#˿P]Q.% 1W BDW]0%A߅  ClTb.(1 4s +nق D)B +ޢ_pHƕ8,u+SaŽ > 悄3X @ YpEpGe;W;6%\42s!,8BfSvf-(/| P8͓㔀}*@XÁ-:EcmPaJy NV28C$86W,?YBpseu!/c$ 1nB ,O=1LO A+,  +9Y<+]b 9qTYy98nz8$Hòbr[JKƦ+$DX0ZB5Zn7H-y.Zp6Z8V#Z 8D |Nᩇ H( 7/D2@o6_Ь!JSK$p#WLib,27phtWЁq>_bUxhh("ǡ:t p=nku pukwpQr f.ӡ|h}wY֐žpJ4c/{=0u36mGPRp3ۛf8xS/3(~8gXqOѯ#@[!0жT?i NķE @@3.a[s8i{UJh +z8K-.^ҥ&ĢPQA4)Fizj bm{($Yڂx$ z]z  KV><*$?bo&D 춘VKS(.2JUQǩx/7uPޚ7́ NY$b*2 ˁUT.Dgu&\I r"j)bʇ[|AȈbv~ΌA..ۋ8m1|{y68<3]YXZ \?vrJB%O7,yW_t?~yw7e}7*.۟~}uCire-'a[̖}V+.c%˙+5_qYs ]sPsO]ܹx61I32i36<0Fa?9tȡʤ1a"[8՗qr*sq5{ˊ!aSNRqߵ 5_Z[q7}V"ek+RҌd4!-/uka`BlU\ZgdXIpl4@YtGI|cEp;Js\5o˒ +/[ǻ78GBq\ `EGtCs*;*[:UդʢOuWeE-+U)_W)r4iN5vJ=nSNESk38HgLQ8eX=4Ӛ +VNI_\\(>ZCG VZ%2{*)"y=ə,裈8PlX DG((>RT-MU + mDjm#5r4I-mbvy{^/>S ZY$̲*8]a*_Rnx?B}q}#G;ˆsUllɟV'0J~x!kOęc{< t.e%} OGf\XKyB"c}.e$鮢IIOTy܍;dOyCЃKaJp>?d%#*2rs9Ϊ\Eގh&+ Nt4htPX㩳NWH21H]RO.[O9^TEHZW:h0ɞ ^ղT2[$pڒjƔD$*Ӹ$FDr`q=LK0>U2Dlk)%+h)< 'ۻ/AGSpFtuI8WXl~AFile͠ӣ OvSLXdÖ[d2ftW .4bؾ2R3cq 1wDkٸq\^ K0N4:dF$:йP5ǻ|'wt]SO_\q䔰ą[D{Fs%(Hxն&+~ +y QkUϪ†/DpGQJlU}vYWh: + ^uɼpvDq}W.gxI]r9Oc/}p'ɇ_8D>v{qb=ڹҐŶh]8:PûvGUF0mDA-5(= #וI+DKXh;V(^hBTYjF9MO+ +g3ni@+ +|ch@; (`*SxIp--LJtO8@#D/LNs&DŽ +_k?RӘ\ OnLP_ZjGaQ AN{U +H{*D$j.2_jU +7lTY- z/S[}XyRvK중4juLccoV"R*\8Dgiev[6$\:~??!jpپzxKŽ(? +(ĂA߷jUIUː.VT\sT.JF L[Nnh*-IB?OO(Ldɂul.\D9$⃛xH@]ZL5y[R5ʴ spL$E3.|PKaogp̘L&s Q_3)Sj+5JD w`M]"؃F!ڧ-(5ݥ'qJkxB4mj./ +昃Ё/0B\$nkQvrLpKH,x o&N+10V%)@OR=9Zj!׹sz~ky "+duZq%}5@+XC5f  Q]*H2$$p9ozc]s7+G>hD!4"R{=A}`mjUMpa$X]lk(1U%ߒXl,OPkP8mU\^P,qexvyy/z%ls>+Kp=A"C6}emǮf/ngκCNS`^c)ϸȳ(AEFYaK.-F))$rgn^6)nZP -Xڤfe>mQӞQ*o%GIUVj#h/0Uoݸ 9֬삕$["N%x$ykU4?l׈zY@ _tHnۻl~|7oߓ%K@<+Bl)׈x(煡y 0M39_6@xCA/ߩr0i{l^"tUCKkvޯGOYmn] +mci%@㋑zf.s]2ryR+f񦌼7m牢`yqA[sߍ㹓'<=ca?cDz lnڹvoMij! p.rGdc/If-xzʫ2*]à̬.٧ /;ɬ3pI=1 @AJX=>Qpw% <y3tkB9ķ]8\1]妩`2i0`S4\s5|⪙SAu20ƚW>]ׁ .hԲ]ޅ]5\r4LsZ35B=!m2RP:x f\!ѡjdz/pJ2S 1OȡљHN/J`V@!8@G!A{#x<-2ȏzSU8J#jaYr` DD e8ڈD֘jc 03(RUF#D5joPTs% e3?1N7"1(ZRu`Vw JyS@1v)-p.arC{u-k$:S# GU&ΏwUdeA6i$h+N-XK)zw݁ILƩ +\al}`^ZCbS:V{ +A^ztu&قla(Ug 54tRXķM]W1+ {V{ ~62u8Q A %d¡egz=g&UǑQxğE½,Q/7hY:GQh22DTK_-1۩%X-]\Yix;R"X<r_aI +2`[H!ᗨj<O h)*.}\k[k` Ϡ3 IBtEC\]uId!li'm@B}>l;~ٝ[b?VWl Bv֮mQW5`ȃn 9"X"|#":F\%iVJ3u(HEqk(U48U J?zL?XsB4'DI85QD!0㉘ _g5Nf]4furݬ&h&LrjNt%9)=|WNJgfɚـiu\J{hvfnjÎ.84K[ 5A|P8Nd4AX9|G˗H2/2gN]Oq.4k欬9paoKBA-j a] w/̕G\JhEX|=_s$(p~'/K).t~t5( fhfLCN5dxD!1YaQ4‰g5CG܏~oMx"ES>4R(}T7z=4FҪ[U݊Q߫U=Uz4sqc P T":3ڇ *ԩifUA|%+]5j&ʫZiU(AeyDR*5CGDユT]ήU[UV#d5]I$QͶl\ j @ͶB((3eG<~% +h{q@zFJu^ڧ0GteFDWY'1P K:Z~\ET순Z23jLQ]njk*˪n&bEޒ{ +\ #&=źMށR8sb-zy[#k  ކ?e?`1mJ~qC?XW-!l\0NMp̀%f@(8"kw>͟kօj%X[5_M^u5_Wu'mTם;U#ywߝjo<銽z]x[x[NW*ǫy;$jyjp}{44)TY-Ї {VTStr=,}D,X#_}űەF\>x6iG}o;bW^ҵsT|wOԨo5_5 +d^ӕ|Mͭ| ͝Tw&Ml'18 :Y51vl-I3cnjm)X3c53 J5*jjN+y#ς +t8R͘(3+*QP+NnK;lǬr$oH![x$̊5v% +* +4 +MP(`1%pջ&pϚ41&M\7irw\ ͕\ p%KX8U /3`8 ;4]։Ň`=uMv4ct [BM:cBH\ӬѬ: fW=VvN U]u1쪶~WvvvUCzV+*"T<Өd̴ُ^H5gSYgdhu`G)݇fif` d#5P +]@s9Yj59K49kYZS?,cwaMך|kAɅmMW,4oK4ok8Y-0Q4K.O5K34K34kL>5d4_$4V=Qt`f2[r~! Eۄ`JbCߢƤE_Foɾ %t-V,7p8G)0Mށ".b|sES-k"dIX]Cg6)0v02!f8;4ߑ@63I՜J^}hInV 2uOn ﴀ,]Xp[ ~J/kVk=Ke+ZUMJ(֚qxmqqxq-1]P@ium2 0||RUr7:mv3 +}4P6GiWγ}@;a$~r3 3$m&- cr>)Ky_*Їj{} |DOf .=r?e.jͣ^ rGPg&N:->ޑjxam]+R珒+^=жɮa+W XxU\|ǜE$ uQ2自KK;I%ֹ`I?"/jl$Nݖ*+?c +<)?7rn*'m5~.(Pb LVk7+tC'Dў(SDi/%X*S.wg2ɲO>mt@P0B%ncNk'nL^#SWaAMS]\'ysT3#ŮdkGO\[0㡗vumT:Cri>."zIc 8*Bfw05I8~96qlsxQ +.ʈr4Hi’m>h`<=(=O$ʾjb n=k휃vT n[?aVYS-߰*?bh(Zb!Q*srq +x\=m iic=cEh%~7 sMԥ/ +;T]E.`,.ܬihWe]'CTȣvY.˗TG.k#\" A0O.+tB2fʬ]peڥY4kqi'ڵY67bvm֮ڵ4]{jvѸoX/~72t;ib4>5xtL EY[G蚂1>븋s[_-Pt,8 X(ϳdv,>ޱXg~PD;'BD'>dal?[a-S#3`ܶ`]Yrz݁QŇ?ť^ L +u(""BAA1BB8ܓ!CDM.TDOswd~8̛$I#7^Յ8PUP{#z<1A"فKS`P:Ta+.##!"\ +q|̂2E}`.<*gro\Qjw^ɱ`T/ޒiA24rm,͝ +^ګ]X_tqe`7U[dnO*gڟ}VNU; F: ="7qBّst4.0C":]@ +v.E5S }5j?Q'zkoG-j6_Zv΢mEGq + ' ֹ&EMʜyi:s\tB^3əv~7iS YGȒ) ya4HyV؀CtXGgDn#@* !SR!!:V fЄKph Gl4R 5r<ȮrSǼ|Dg/k6t4ZlT݅B +֩ G3&A^9 rxcW@$GMr$Nj!9omFmFm8꦳]_sdSuչE^D.mWiND4]D^LP=F@t;lH{]"P赡;_8gs6U>6݆ͅ8I Co7Wgi7}Bk7F;LV}΄Qh?:p;və. +hA\rzJș`hY[9#GNT9!G;\CNO8e?8g>ͣwp:\AJGzFP{\Sz{B|_w[( ga*G;h)8tӒAIoS7zbQ a~Pb0/΂9W{MHzM=wb1}NyVoD7a0Wk'd Wi{W2ͩLVywxy}#14]O$PT;f/-Y)͕ڵ䑹ᵹ!gspG09s=an&ȵ%$ HCܤX6ѹh3z KǖP6<|RBv8d)l%2P7MLGAgqƖn/$J}ou!Ͼlg7lv EYV\. @ԮuĭXnjiq3#:tлP*cb"X]UXC fVkg m:AR;jg\;}V]z|f\t6*W@t)KYxș *::qYo;Mvaޫn޷яU~SæXg󤛈SIU>%-ŀ[틡p-q(k bN7oU`i IlL:xW:cyF p?<]\t1M {hO[d;uI_T;;ۃ ғx:zI7wll\L,16fq[X;I-|Pn{3u.~`~dcᑩx˟+Vk aP?}P+W-o*uk9CpSWxVWϪf%ڝ~w(&+ա:Wǁnu<7*|]e[}Al-k\}qϜz=oe~s=h(ms TX;RG_|7 t&1I라rz3 ˛"阩x"We1U1uyr18TbwL.6bړ>tJ5e;!–s~<_}IMx t/ .$", q,[t: j+:55yFlpeGhv\;N- fZ{lBcCE{lLt>.ߢud Kv͆@4z&L #k&to&CCp3ޛhr&Gwk͋~hiBx:Z51!Wmolx{A4^q~Tvdj=CM_΀y`4[e\1^TC-fRHUGՠ7P^|@Q̠?!LnՇ'P;CtROKE-uPՠnsW6_ˏ/A9 R9ay+&kz5b+hvXI2*#cN)t/*s~i7JĀ9aos#ݹ<oVf 8rBVnVty@do @{0VxO`=ww7,ȿL?Wٿ6"#YcDFkȯVxsw%K8D"@wW)K/mUi`|8gܵ8]o=F x[øi/!嗽LWyQG]a+.XxU+gdkn\W਋RQӞOݫ{|"W:}V.tP{]9Cke?jʽ\;i06"}.Fؘ;؉ڋFkzľT4kN5a;ONR3=wjmu\S}m?tQ,A+2=`?nnx!)a5` 6W*&k_Gk`זJ{T/):څaK<Ws;@]/&se@:Ċw~cGCGY8)g[QÕjC^Kg6ԑd:₮- Ln/3\K}/u!Ͼlg7lv½#f- Z=8[e[ӅB/89DGԸU8y6F#gtQXuqrzh7S +1&;8N;w/؉ v/G{H\ Ed"t-T(9@<#'o)*ψkI_;Ii|OCoә| FN* ۅT;7si~DQ/|DJׄӉ_:P{`=>@g)L,LD,.j^^p` I_E+.WNАPk>iY#.OC${g-k7BHcr~|]soϽ'm%ߥ`iM)DkePGUӋ ',"藢."JAC͘>yϾHn 4m:OTSU:XI\dH ,jEW;&Aǎ6bGf\'Cǎ0G*vBExs:\X+>iy #5]qNT$ H:jf~ulMz/˰b˅оF%y_Ֆ5:I堳PV742j;?B"h*`$~ `Ȇ Rf>.yom hU l@Pq;jәj_΀yu !"/>$ڥȪArH}igϣh}&_yWG/Zwc#&E#PevȬ+$w-ƸFx/e)J +|X-N.tx~ƜɞZ.הM;&kR&OYP55~wrjP?]6!&s09"|t +R/cj:bvEDM~?#L+|k +v +|S`t_ēK\)\:*j&!3v )N?pK1W߃3X4!QOlDv7h-;M<5(I/^8ױp$\uf~m%{ِR"sِ|6;2HP},cp}"U]3X:^%ݛu/9XBFK(qJp*"va"x 5 +&"j"&"NQN5qyx; qw͸`[q[jEvRMا +kZE^'- jDd.չ,h]n*%\#vIZҝ:_o`m/9HaA.?|Nl~#̵i°"sy!W]IyfG(>%#)t$tVwtߗPsA-p K`,~QRxp 4M5 @< m+[+L :>d6_ߗx .(WG*8nAmsP^E(iJq +HbJ!QM¨oժ4&YeesY]Wꟁ-걫OilOUcJ'*e-Ff~ګ1᚛u0c)t0.PcsB 1EQ0. dG1"&XIs_( e20yH֭Cb~;2)%*BO* ~]KZ]ǠW{WEG*?T W|;ݧN\<*lt0Mr( ڎwM)v$|/aM~r +B _luL |Cl=ۚ'XlC jGHJ\[ǚsYl Z3ՠnxghmvZLڅŨ5kG@-ROղnkhny;r'*PT|$l2c_TfJ20p6'd&k*:G..XdGSx6ڀZuludYf{oQ}z[GxAn .ł.*b cmR~Qx/;o9 `K 'c;e,BX8a)>#c"f,sFUEcI(@SsÉ +*G`K? qMtr1,?Ė8&;uPuԧK 傠/LS/~&ǑD`#3XamvC16B]ӆ- +VY吝W_3,z:6GWYx|yDH ?CuYp#8mJ8?yf =CG//10` ,ݻfoh O>fٿo<bT O\`; DbxQ1^w/0 m;bFH%KxOъ8cP:ChIx -0u[gsqkbi,v/'V^Ylb]Ž; +[.U+*oeSULو?R0I1?X9HA-OA30+X[v0* + @ߡKZq8} >,:dW45RDCB}ɒί%qն +>YvVMWCI +Aiz*wt:~QOcd׮ʞѾ*́3 +dbӰdgQڶ\c)جƉWuRE,&ygAd{࠵;-XP>QH&vYl_"(M#*x(7$er\OHPh^C 'EnEv_qvy.-oq|]+uvW[b +nI2e@s; \0J(-``CccSA !F8-7"J1(ɑ声E/{b1%Yh1ש8-" 38rJkVQTiGhO`%hph>Y YM,HhRZۓQ +\Hc@XB/{%ȶZ@Xi,y9X{% :z<`~3(aҁR-\%Dg]e^8Nidc"틬SQi %,K|$fE\xpӲϪNϜݔ@39l'ȹ9+'7h?{rn'6v.hAAoPb 5HEOĚ1gAJjX4nUP!郦D Ɩ/AumP,&U#Pz\؋{SQ~z6h'8]! +}ۘ2 +lTh>{֦8lZ֔>D6E"`o.wtYKo&ˊfCR@ &et#{*f2_̋-$ KwZ,cRBn =ԵlA= >_m\)̶̸WAY,Rծhנo0NMcYYTfB*y{z{{#߲pٳٿ6pA$Nx1)<ʸj lE 4hx<{' ވ삹}C0o(Ǟ`o^]2 _{A> f`AO8P=j 7yz*o ,= C|a %ivwy:{S\ʹ`g^n@1,G-bـC@ 7j1J,ËG;]pxu>)&an'G%B$oU@U,w.:lՏo~{jVtJ-UPGPpGD0]NՉjyr\/gdIpt?V`+;-D]rդeiW;l}VOU.QW'iD__t$z*XO_QE wTVKREp Kz??9N_P>rԟ)a- z>]eznG9qʽ>'^9~˜4n r%_rΣv6 ,aj6uݦE4pǽMf$ڥ006*#fW&ŋH09nfUvɂkPD( F9TX0 1\o46 [Q8ϣ8;CǴig˂ 7.]E;0{2"_$bMGoe~VuUݼ?׿a-;0xg,=2 #[If I' !ZJ@x,v}H/@Y#0+Ј%+$+TZBN$jE;ax@M[.E#Q( JwFP5T p`[*RD) m5],!ԓWwӧU + ŵc +WEizN| "v_{8%uWjU˯*IFΣr0ahqPo槿Wo~6| =+H~?÷+QPK+V(-ư;HPVb8vEc؈^[ʝ4t֣T56$*h]pj^ZU%mr)ĻUZ{>Lj\/ԺQLg"LNV? TYrwE٘(nEn.@~z Lp§pkwRчPk.33G\FX}tgM3?Nϥ z.1aܪlJ+ @߻4+g>I!uT~G26+@h%2o#`uU:P؈?}S,aSۜXȓQ5O9fvx&/$/ +MaUX"w!TkI +G{V)1qM[G_R(#CW{O#P\KQ|:4Hΐ分,x!LN\TaϾ/rbmK]Nv1--qghtҤ3#Fggp&)nW\sIy ]6be_Qr{4aι_a"< z\ǀpT,4?f]KoqnEbTp̞UMHךfIHVi!B2nV鞒m07jtL2rnIkŷw~J%Rm6GuQ `=%鄈$:INT8akY|_|W9"&*$_L?yn.InV ][V`9Yˣ${F,X*3z)|D{pj:^+ɰ*^ExO3R3 sgb?A t>}?&})=N.Yiܷ% #!?hd5}N]\\=nPLܮxFrE na/~oD1w:eQ}TDg$sRBZjX\~?H8Mp74'7E٪xC)~,G׼ı֐ +CL ^y fB؍1rIBmF C8"X$!݋GE +j&שnkdd`*AIs!b8\"9)9cqP L.n#ͪ`6r0FΠD dAwX*h4hx3cnc>\e$8g͜~3(Z$Fsp܊[cAͣф: J8dnSek^>7XT:1gDyˉ@WI`PF%80NBPI4kV툫玡 +k-}8ZzAmD~,m3[A=,˗ӓz+hVy\+:\.\av7Bxuw:ds,A:8i]Jũ*/CIxv_4,=PψA|-E?CrL1WGU[5ǵnug](t}>H=~U>Ӈv}]@zBkqA>TV(5ڋxw_E*5 ^ژEggZf/뇪*`ټǩþXR>|^T:_(73D+us̎3e/=9YK[|^(HY^zv=KŠrEZS5z҆"Gnmpp{ca7j0ؿZIQ T 1 EGi"n?FNO^. 71*ۏ0ؑ(4+pgYTNŦc +&#? +['Y$wG.ȭ6O8*LvJN)yjp[~*Dn y?r*SQr;Do퀬o@j!aIC| ٘O7 ~pi%y]Kɺ$ 0(UCGZ89>*멂$ʚk7VnW_Ɠ)#KEHŚ9)F`޶KKKyt\&z'+:(QD>jeP(iܯ^k5@X穜\K+iuZ +lkFuFX5^U}aVG$ &1´Iu5X,s3:nflMܢ-vO񸋓܊bTSt4/$Ak2€v0\"ORi -l v)I#+ eET왴dhv*T kPZ+!Q*U hO6U nF*Z59aju &:VO~r\X:(Dm6:dzʽ2"I2 e>Hp2G =r/G +eY=uvTsG<&?%l (pr@w*Px%űaM5HRKI>Il> )(zpIx+r +->{Uz: +YcɯTtuExĪhՎ؁1@$jҴ`_Op; ;b.hUIj˖ ܽZQݻ`Z9,ARQ,o(jG%[NvoR& h R"qRߜ׷Y>(hO)ǒW;:>ݦ8$S-~:tౚ6H+4kSv, -./!Y@0Xzm8ETKSс &A4!ׯ@)]ZjG^vsz yaۂ Xp6Mٙ G\prxpBN O§162 QTsHRKKz, mBmi*!ȡ*>8L##dp8B +U,BYHް 9B5AMwZt'™ڳ.K.Li;ERLzj{{>M#jz)KEVd fjF6?dR+.bEW`A ƝfRyuP'*"2'I@_;uOf@f,V +z?6 6'SCk5(DY +VSNv\=9 +Eyq+ńxiТY ~, NF:dd.fiH{_ +iѶ+;ܔ2ȤѠ0O4" \,D ÉX3`o2]iҸh (,f i=V!NBޭ-9+pAyx:),v7d(ӧ0ic๕@XO| kK`Nm7YoisՇ4Z!KY + [B[wt[|0nHAy4IeM6k 4)J?CU@s.]}1p3;vsNz~c'MX޴8ۚ&"LABL's +-}_TҢgS7 73~-zkx4fv:y.2 + 8D_~#8LcqV!17q$̈́}`kB.j̳=w6-KOJ,}W'^#e!5(. +~,5O3 ̞*r,zܭPr)#cC^$66jɅO~*_H7Ec Jqs4tpp2)l6zwWI:#L9P-hp,ڣ6:+i"-%C\X*UE Zq&щp㺺ܣBdW2e/3I #;2|B/?77iBb7ul1( äsx%ICտi\ Y, "?#s7wYٿEq8&rNkc> h1}E#ˊ}s$ƌpg?D_~ȸyFyq +t1důL&9GS2 +08lzē'Iꢜ$zMF36fq9 h.J$^b"]ꄹދ(gKd\$E,;˕rHjwI(Nkғ0zֺ.vt-!B&n(:Mc4;lgnV m?6| n YPcȕ6\@ '$~α7v`rp[HL,bzsn* Xt.]E4Džck&Qi׬3i~Ȑ#hSr'Voi9fUXI8 +NcPM.k-n@X;F 2s3g`BnT pX=~78Y+9(qpٵ* {9UYİ_$_{A~-V/V,׍XG\X@-m<lT̗]bUMkN{Bc3[9Pt\ ޵Bo2IE=KJ|جeqke!5):幊 +LJNŴYâ.K.T,V܍tE灒zHo7OnTiYw[_\٤|0U ?8܀}pM$X TЏ+]/iF}5Wn;JѨVvH+grBP8lM>&-2g-(q4gMs&Y3TGd@!#~xn?E߬6. F࿅Rj@L⻪wεGDʊURe'k()NKvR0=P@vQW^Q7\*?(v8Te( $_y!؉#6R+=}ؓz; +jXL/BRTݯ +c 8ԧc JCW|PZbNtCOپ%ͧd+>E8q{Yљ̜cm!ƿKxUI/wu +WJtMtM8r$Lf4j*JkftU5c9V(.2BC4އ2J0~Xj/ KՅdゼ:K5/55T.c=V:ebOgPM8'#Τv]Ofw]8j`SXmv.Ύ4:mYg Ӆ1: cR+$9"?М^ifIJ1cPK=,5giCUy-я[M% fCc[fqn;=6_QI2ztY!$)\;\}graS W}/jlj ax=5S‹Uy[ ]{̰]jЛeGhyEM5.ZFYQloUXny(r5~q WPQ( +׷:}YU H`Mt+Mtѭ| )2 M +ԫԫCVY_⾽ +F=ihZe*W8M0L8JDM}6[(Ȣ'We+ZVN %0cјX75^q +bp{ׅ+uՠ5 ũz[ٟb/39W;]uE\I-dJf1[~\m,(Xw\0tBur 44W`7X!=vźhF#,ċӕʆrǖ#; ؜T?bjT2$K6s)R4P$پeL8 $E]W/JajMx%qjC}A1h 5Zq.NC]Z.t5c댪m*v;[YvL[u"ҪdT82|C\&ĤE>LpmwÔ^E5kFZ!VY"is)LWU YS@wpVs9XWV YȮBm&68uկʖAǶ3-gNcL6;lNvة)9-!+XЪ!P8/u[坬ucC7aMVr3zGymsvG ^i6kǎڅԚ|&uKh ^*k6F{? 4>7k٦۹ҹ4R 7xpoT^]ܷnv +TM.P̚^fL_ڷ+9 ZfMa +fк0k <5֑YSu.-9c9=N;iWwz;(GEDOJ=wthT QӕDׁXfu 48U Q-ն18 )irHԓI#z8Rc$Z(XG*zy*[ݘ.Gn+_OUڕI,Y-jdwr1Ldd`SD`@1Ln`cڿ=a^ILVO\w-֙bg=\ b2hLhSnp l4ߥI6I0;gysYO%Cb'MDgK{ ꕺJ3"HIq?!&LۗQaV2CѲh9ﺿhۋ|Jˡڂ\˕Uʡ΅7&i<'nI7Wum,@P}C\Ďꭘ9,U^2K4 ufQ6ŵ>Lt[fY"eVy>ǿ_|fEY1aZMt4A2/QSe.p\xĨ`L-ϥ|ٶ98KZV}A6= ،`88{`tzMƷV4N-vDrsQϖWBׁv}\fTm8v9F;!arN 6;2 ^3x4 E 7yÛ7zj޸C/Y1Xra͜DA3ymksn?p64g O!xBngK7p݉FғFwbo ؆֫NKZp1b uJbXL9&pHåضiw6֘aZuֶހ [=8*NйHYL ׎qov\=6D|s0N"Buy~r R3|rVaegJ:FDWZ6Lw[A:dho/Q5F H=r;Hj"Mj[z3bFfwr3H+1~PUڀ΀Cŭ@łpӍbF35G snm!(㩮q>렞m +vYxVebjp[ Ω6_r'!kָ^A*/6M:=:|{tJ^miLf;am`ɨڦ^M;Dž]iS擛s`iUkNN/eWON{T#4;S-핋sbGzTo1s5 O#fs^5slcoc5m, +m?L_ijJ0V"%jCPN0ΐ=#Vbp +g1BT ՠRsT?AR'vu|YZ9KGӋ,`C$={Oc = +ZAyTx҅X >qńS ֱ֛N58deD@=u׺t̶g֟AǬy朜Vï?s7?.t~3͎UC<6ՄepLj9?[楎йVYCEt?YދIiu"ǹŕr7nPQaf#Top$X9_8@8 LUq*N?V0M`RL9sL*'쒉D 'Oh W ScDڠoã$P ֗"yrx.*Pu.'VP;pfBa;?\AkQrT ʁjC]^-rήJiFί_g*ۻ$V_M l9(Mb< y െj`52C7T&*̘ ZЫh68Owh>a:M,9W5c_f?߇aNHRZz%su,wy*%Z6XAN/˓ƊԜh CDҎŝyJ87}g +E1iJ6yϵ:nEp,wf *.z"aʭOy;^NFNmGk7nu>d I ۫]--}|yLX̵ϕW$:Q]x:){Q( -oՖe ."kwE^"ɷE!bw=O,MiE=wcSizS~r<-7Cj#εfm_c(*&\3 }?&f8Z;N$_jNLj]o.ރ;S\EJ߃Ŭ3己[>nr-T!Fo:^E +zt1W%|/Qѽ +wTBTg99ͩ:uSuF}u=>:Sum>4YgUK8^p,$D#*GwY)4YllvPKh. tߌƣqߍf}n>w==A^#?ҤPu؍DGZIrS{%٣G"E7cdw઺ yÆTaaC27 ooD-*{K7Pߔ{O ޛW7 znMq]7q^ge8*~eEImՎx7L)1B4DM)=5or1{Hy$ϛ&6 QޕJQɢ7Tf~Ӝ)%< rDVQU2”Q]%+m24O$pA&DZIrCN,fO +8+P\8 W~}|'ƒ\knb +1U)s4ҽ 8i^r&ڣ +q0lsheqP$ȘIʜsRN_+@U7խ2M` cyYS:$m5)󯘾LݾMʖDfiE!=f +q\T)3IcDcˌ^x$I/HzA ^$ Ywnx%H09 %ϯNk =.tanͣ[cDUޝޝ^˝DL|4 ,4%j?FD ˲C$hģG&u9Ȝ.#9CIPN)Ѧ9!}c1oXmrZ:SY[0+Jϳtmɟ$hퟕ {NY6v3RlkS:l!|D#g1C*S+2ƥ%Y狻;ԆsD.%S]}20Т,6{o8i] 9TJb}^Q5I.)Ð7՝nh[ѹ։8\-Myh3Q]Sis3ȴںBtJR öku:DD܌ +**s8JEHz~Rq.5.?oA07@3fwg4vHR= c횬a?.].wFJMJgc{ѭa/#ab tA^i4vsթyb6͍v*n.$ıFHâ.>dBB_Hn9O"#+x; knß1Q 6ߗӡ^x\|mwlpk"ʊ#dҷ#i=b5Zw^MDnzc囮)>Sj%ΟQ-.yWa1T=?)32ȝ?DkL.ȯҴ: ,Lk sSc++A4Ge*{9&p[g3Pͯg԰f= jȗ𼓀-x&0GCȁSCz]r0{&MW?}}UzPiΤV>Pw!.[ qiZchQ/,w\BR?vnzEz2RHYIi<# )!b-Tk31PҡC +TwӬr ͙נ'>'}]Yǔeݼ'za0xm]Y+]/]N7tAHnf`&!ଷk m@b5h17WEGàL*ݯI@G3{\7-v;\[[yݖ>;(o̬-Us;ݠ<-ELi}vjX=m X5S>ۘ-JM8wL\/%lCQ @eYGROX 'a UvX;PG#} +*^Z0-E}no$FNI˚n K MRGࢮzlC38S?{ji$#(uGanCJ tnnsN8=X,}Z/{\Ta+y]u1~heusAokA(ܝ0磌TpPM=w +}n&9. +6R7Xu]E+ 3~Kt!on}ٌn]eOi:;Gf82pbB+ Lt\q +YO ٜ~CAtܚLfL!d@- \؜xJnNZaIrq|̎c\2JKf؟qLbxq1X1Xq`ioϗ1r҇{D3|?wO(^A!JEQ(XP\hhI'yy^O +/fX*Zɛq}8%A2¡p=!T|?[\hfY5PJNB” G-.iRIڦׂu''TT\Y}=> h,cTw>Y4J\/O|y8ӱ|)vx:KǓlArIEbupvMV ,-q0NW;>="&7ϏNmӏ?/y/wcz<zf8Jl$i8Mv:"\NgIMfIZ"ngrH`N-|~qv~et/yzE2 dipwndEdqn6"4I6 2Ƴv5ҳv}o_뗿|m~f_~ }5'4JgtNft,JwUln"΂IF4GQE6ғ|o~r?k/ޅݷc' 8, +h!.V]zd/hƓriZ%IKfZz}=KxOu~vT̿n??|zӓLw6%x`qghF49\܆i2'w8fln~='o^}?y[dˇ>}Ϸ?qLiȷmAyPFyhE-0Þ48q 6"/gQyaA^zZ vGU"gR ILq X?q9?OQ=)I!!s`PMT)09R01CrA{>|2vxJ+$,LLB +aX|w8Sׇ͂6#s L/-.O9%Sj^ T x$;:+!Zc +,!yYX w{1Š?IZBa}dBJKQ]^y–kF 5 lF?;"vA0f!#Yq@#ZyEcE]u F܄^'F"{}oVBqût{cJ.A4\<#C1aaxEuB±_T1aRxNc#ʄ)$\P()”nppD2+mI\CFtѭEl)N"l!![lY~ʪQw'*&hDS6lьd{|* < +7[.xAlPbx)\ax)&xɧx+ +e׉Sh\cTA2T8>%+fx80]b\+Vsa9>ƾzx2Ar²{_{81yG%1s# Es+(dR/lLfjײ1a~pEV2Y4wO?'I14|M3uXh;D⴦ +0Tt^UhI%O-I3, d2Ψ/@8UCeŎR7uaDa(t:ˆ&u4˝Te+%(5OD&,4Hf Xj1Q_:@B!#Ԙ]]JՅ/J)*CڦRRbPTD5Jgzř^fߋ# +Gr'8Ӣu(6(:[:g^!|AyQLf4 +WH!pfV' +7(ΆEY!f%XkfHHlH]1L:!%6L\li|!O3Rb @_RDӀ˟$ KAR;D蠣(@=u\盚HDjxe4EP JF[ fNeq&IIMyO_aь80䪢EPƤ k˒6 &ebxly +-+Xb>IQ%kpK:U~if"6{@V;Ug!Talk9加%T'.ObR-J& }.Չ8v`y\3qE+)VKbuӈXg4n 1"F:[]*Tu,ŵE{*ŠPˋ Lp+bj 4iV*xU F^qVj&K# %}kb1MVk=aêJi% EVp&XI[I gu^2\BͺZ!6Q)TN+ ;-Dn]|ZP#::3P9&#:Gqm,hn#*+7$Z 7K҆ +xYru{aI&r]VXʯf\fz"7TL-5WSH,86^a`_ "#AcqQ2 u 009vPRI%^Ğ8!@q=Dbm\S[a#;U-ʀͯZ_ēZbă!h$@Z$M:&5PѮq>Z5Gc uA%a8Y9)plǧnoy}oo~ +~?῾|&iM}Cck<8xˣk.AsEmZx\$V6(@k6NV6`v;da:]#wߎ/_F??mx[ o2@MVwp +6L0 t$Y<#=r.,o$Va!nۿ~}v;[???/~#f9f2[.d:/0EAz'm(nv:Y2$э/x~_V?wo?1-UIXᙌ"'`:,&$YLY6Y`<].;ȼ){OLs_|/[i9NuBL;;qMv[[a?Ţ8Ito/wrCP*(ZovW7,u881)ڜR#r: J!EzDjbE]Aw:t%bAFQj,8Îdތ#5XIWR.eʚ$JoqJcsv,q&iS8 gD,(v9[(V'sx>9¿8l'OB|fCIz]K&5'P V-j2ܟc}RAД =Z رs`k6ez$5uh_qTC%/ӪF34[^1Z:B%6j+:;zT)I PqEI yH[LDGłu,R{uT +KgzCeEU fy +-F;mA daUIh4\1K(> AEπGɧ'(!<#B +CCBߪ̲23yr(w! RHE +Jq! 'ӏ rA!^,^f +9Y~6Q-6׳HTԵ NnHN[}nNA0/mhhʭ}4)c=I_zz?U֞~yΞ ߲ '()R&8 9PoW +_a[|pа,P4| ϐw1}A +>_huKrv#X*2@L*5U ;*3DpUuJ?E"M_(B˹^VTVo17w §{5`ũ"_ +/a^eVRBPPy >,83-W9z'o1n1H6'BHa#b)yjL4PV1K)(LVo:_>Ln1H\Ny9}E'(Q5\ V>gzEiicɯ|)M}#Kb Rf9 oxR)U~}i]rOKGʢ3E/ѫ/Vr}6 VvGIJOYm*&.Þ]9/'s>J[,cfvԊbl˒z6#EGsy{ޮ=x +)dNU}%EDi?JL.2Rs)T2]qU-sRTXzh~ݢ\VӄRz&ӂ/?28E/eK>=<=o׆Û5 XK +?ID6wD߮ +Q\J&Tm=ՈTEe~u{Cs?qS=ޛDpci%j(9Wß۝c*+)KݶM/uVYC8>"uv9k5UWpZĕ -rYŰAb˸{#Jȵ@Q +6+4eU=15?s_]1ߦH&V<ąv,4_$a!Sɗ_ 8+%f>=VU(k`JCrycEa@VwﲭkEOy6}.ˇs|%P}U/jc|v@)Hv{x3g9 GZr/eL3W]_E@`e#F0h#>v>J&QTSb_ '3MrIV)-.Q\c6sJAqѴF`ţo+>QƝg=%mcm +^b=^kcul( >T%98lh/?Y!mEyH;W>1h3Sշ`o>?nv_?[ϘWxm~s0tkr+477Z+4miuyn^Htn .N#"1]q."y+ +cjQ8 6&-n-*FB^ cn^/xp{L zYЂXn*Z ߓڛW_=#|yDq$hz50”wOv5#ڗ_/צkz^DǢk*ånhx.6b\dpNꑌ8Dngy8b#]s5\B ό0FP8jR`quBzGJHbsp\DG%np|LUzVZR+5 ~5Bl5 +9ĸ=^^֑7RWD¤OHL6Cyk?R?$Coky-ߡQIQ1bɶpENH\pPGNLGO+}$]!O^Ea7r+w9*h,UX z6T2 8E]+UСN\Y_U*:719E0xLdlׇuY gIf9aDZ/`.n58aNPQR<z`cԠ'_ iWR̩wM|*rjIMSʤ !͡.Kq0r]:=.,(əo(Ic I &Kbj ܚ!f8DH",M$zF#I1UģZlz +'V'5!Y4 *W!1:fƦ,-"uEɹ`D3Z/>힏" 4ds//J-SqϰIPWyI !gSgBQgOyR4PR pm a8wy@W/tj$HiAm;N H@Ћl~ֳ +8C"HuS"muz%yo'5r}C/: +>"n7̓mnTȇ<\/~^||A&2.ږ+\~|87}@G5FRBڱ(y~{~R7f(M>l&V̴ UԷ&[f}3"mv!6kkהFpKi +$jkT5N;ej2>Ya~1pb`<k,.륺00А0Xz_fͪBsʬ~ilT{CC2'7m_(Ұ'e p6qeiVJ{+D:,U95JxR%lץ#)J*R9H"ww"U +\j_)$ %)*dRЀLxRPĬF4K խQF䪥&R- hIcubKp,BEn!a*'v 6X䜆NC/Or:,hv+Z?{BꧥMӪq"aq*55 FՒQRZABȪw,lhLo\Y )| "2_|5*WR3,B4V]56N&aN5 +]Y|yGS3U\ #CF6Ťw$t g{b&aBзqѧ1!CDn礏+^MZ'!i~\? +ӧ)!$~?/'nȵ'rp6 R*0&=<?-A]{`? Onw|H> wGC??ޖ?d7ߌ-cpmv; =C<8?&:}ww}_߭ ?,=<!gWO[OtqkUr5#0o(Ho~e[~dq: x`{b^ߑ}OME:n}f>lbe趤?WFMR}K+ҦR12oh"qDQ( ;H8;-׋ 'T\ʒq)?9@~g̀mwOȝs1tih'͇ӤLը(f2Z(ni;D*LV30|ŃxԪ8/2XI ? G 'oXa>\bYmkhk!mm:US\Gmۅ}=XOj֌Y"j:41iªIuUN+([Txxu¸q:Br`,UH1pG&qlv16\5N3;$*LAn$288; A{;f:ߣw#nTBgƐ D3{GFiriV7_7JfdE~LbUvguM&}%zD}[Z8clH8IIMZLϘPyfSFƲd 7*-/! C9ar~z>HHڍ7b+W,f}S #GQe`P}no*oG$ȜJG.p+m{!03ED^Ӓ93ހ-_Zx^'bV"rG6ˏd.{/Ggџ_?y~dfȻ ӄ,Z,"sn[u2ȩ5 Yň=^,cl>]IQⲖ&<ELbśBm Yч}}Xov^CU# s݈FZ~sݯq;TOv?,bs wuxUbfk{z1U% cBfwp5;kZ- 'L_3rvۗ5{k:_4O]BW+$9XB6l( (# l/'$lj*eK`PӒfE]WVH快2EGŌ!C&Q7Iv(n׃ٜwP>{-< +ͨx?ៜ)_5 ?*fBbaJ+uP Fv"e> N]/ݬs+&{~SNE`#QtG*[)viOiپT%ᘤX(ssɪ+zDk.F_s!P=W~|XWt6H@DcjFpo!1Yj}=jC=Ϭs2wӂfRE?9qF_'5=#Ѯ&#&9w\սp o+DV⁋iK.3 +ܿT flIBDU.ejnO'!I^y56[췣lF rD-|kbb$ÇUma +/bWrI4-]%1RbIsaZ b14mv XIY\&U Y#KRਤL +'eal +1E"K 0PYRN*K|ex3/> +(U؇ + ׎jW~ ,_{1oUQ6E +;aTw,@v-".d uKuUK%=6܆$l%o@/-F)DБ-.]a7RB6xTʚc]D& +8 m GϨ<\DR˷+px-H) 7<~ZlHLsJX|Af5߁a=*E1| ڥ{rOU_ rpRlK;>4LUx̀.yh3v#Tr|,e\\R(F- Pv!3 L47P S +47`IU)5|q ){J *1PR `V]s +fe"AJ/`VVSGw|mT 5qm~V)G)j9X'"Y\+q8nŏl:b!nBʵD?pX`t-BD%OY`7xpﭩ-2-hV"u+ +Lsݎ^nBl ;p>ϏJ +dy U¹W%>lH\j/5|G͆|)l=R8ev挾aKSy(gDGRnmX w925P[^AMoc,w Vo88?WuyKSIe1}ޮ6(&-m=|Y+ncܧ7Li@hfF)g+PZ~Wr&9<{ޛo<-^ᛠpW8ɶ/dQJ7l? zYxcoxl?߮ UFB^'URǂNʠW3Bh-O#)W=*3ωs* ?%WOy2F+o.1do8!lɛzqp<>?f{[׏wDoOyݢ4hwϛk|z% d>3E]Vl{*&(52()X/J84a'Q揋5Á<"u + 141w%8hN)"'6m6BnDŢWiwt-H%ߖ/p$1=d߻)IE N/{[ ZN^Fr^qR#g0|*|$>Wnvnx\ [J=lwVM'llAi*cv8,wsfE|+$V#2ɅyײDvL_lCPmd|ċߑؐ MXJ ԤLbL*ppgeMN!!0|-C#(lC ً ?#q| Hѩnr}= oG,3רv/&{Ld*pO*OIKX.eKrKɂL6(7 Fű 8hT^Gs;8D8 N >Gâ9({\7 +mb(~݈e{!rG+}W7t<'߫g:p,Ʊ P $=οqIx>0g|EM&L9?x^G=X,4h,g)6nA+~: ; b@zO +L0ri{)@RE )GL6,1[F)Txc}>1J, %[.[xYFSX| " 3d"{*j8JRQ1h XX +6I"J r-^dm!k!HOCl_>A[ΊߎcQ_JQ +"IQqME < YV.]w;"H.%)f"_L`OqNZLjܸ9 3a҉#~[T?7!ݒ^KAH&r;{@$J?%e%BO.H?uM}\{wOŶ{[r`q{n_>rAJÅNQٗG$,ܔ.Luo?>[Xg~ݧQ>8M/9EN`O90K LJ%H(R;DrhdizBV4""9bJ$zhr<1-b(5F%i@50:VCG9f3$e! R~!{\o_aIȿ3OQah>ixo7p z,~wm7t+o7v_}Ǖy[6ed&aͲol)1`/f,6Xo枟+:ܨ Jm%^7[.֏Q+|ޮyķHSsݭ>ljO"+%%җ6H65~4?gLkv>{>=7ݳU  $_F-K*5ux~̱nc:ӴFmg!<33Æcj-6V"0Flv8 Xؗg<sNí[-W/V6Yܕ&oÁ׍ڇXfsX;0tC+rܕɻ/GOm1yڽ%r徑g$fDXzPʼnC?]P˅i4-ʩh1 ,]XF"D 7sF:E^ԎVa +uN'Z9uɈgj"$̓مхw.ZIZzw[9E;Ǿf +XwwN hAj7AR37l OjD_)* q8V\7ڍFnl\C#Ys X5s,,NK*|JӕG"]|ȋbP6Q.v#GC#ȡ/ KGXwI(w:gr3OE|oƆE#S3`X_<43st\QPNnGKh Gv|dG VHNxhF 83h9G}<G}*}G}\d,ǀ<{(ǀE5\ÓA)=Ml}ݡ6ޝ^h$mp|g,Xg+xJ{lvC ޶XB\5^z#^@MH_uU8U[O ~<-ipH+/Wћ73z3H3 9nLU̷HgV +"AF}RcR4U潢@8@ۖY&z~l#GFFcTT 0ש- 1Tн*PG{*@vTAQw +lַ{{MVЦa +DzKԋf 4!ҚC,0@"Ų#)(!Q4ڎ0`D7r?ȦH ̻CTZ.nix>Be7D}{ ~ `ReRhо0}Śk#sX|p-Rxh* u`.n`T%M'2REzMb$Yrh˒lw aEr?ܘ7 %~2* Ь~d4 @-4xp^7FZX$(sK[fɧ a KLpub.@~Fxș&G6hӮWOO~}.6C^f2WKA҂.Pq< +M01+4c8NYa%eja3k4r/ڋiT[Tw0H+8lO #Iqaǽq3S-`p=xc=(zÂcMWؓ*j;V3Ȣ? t"Z>TzI}^O\hHrט0)8A+GY_^#p~J;yO,Z`Ih3UXzQvuk. +m4S\Բ0EJb)>_`2ԋū_bbD=a[jw@ 'D""W*NDBF"r1j݄#0 nYfJ||n˦e6C\fjrnJ;8۳r[C8V-N<(ڀ[vL#[c#]=[ +ωZy/ď ^'ñF%&CG;˲ +s?ÙsqT-3x,p\9z%LN*MjIaG-.'T`hZy`6R)(3LqQa.Mfؾș4y=}"롯Ē +6Q1iZ2 L @9`CxB%6 $B~iy ;=%FtlŹ9 + =p϶xWӃ]- WbI节drK_%RB\U8gi#Ƅ]aXKIخ\JxȨkԳ]Guy+=G:?||dO[cA +˨ 8t2)v-Çr-#L5v0TyW^:qgK +R&(ASФ 8~-"R \,ȦnW`{c m°HH}`i"lB^Yu>RH\'^τU~O vMt E( ?}A05uU$"!dmTpͿU h-ki0,* 9h^҄ɇ0 ]D}E>Qb.-l |i‰cHSJ)S€Q J|Oe +cSIb4*ǚaw4!M^. ,H`=)V8 +6B0gZ!U>_`r}]^ +=,wv5eaێ,ai'޻X :΂"019S∨aTs){d#>[Tc:uB h1s=Pn/({q; ԡk*STל%.ˤP6 S#PO݀1mύ1 >$ 4:Zg<{ĿDğ` @T כK >q] ⠖H, pFF +~˜eAh" yEܪ R +ΝwefYf"Ki?Wn;^IfsMzU_WƩ(ABڅlw-Xc%Sp66pl"p7jާoc^m3Ͷ=BNH08uWp1O +`O6r#-}J2gxb^ŜHSc1讟VHiF-6-y՚ѕo=䙄ubg+\ՓhHG84!-*^ȭc}x'6 ^YMKXjg5p@܈uFr*ݻeql*W[ Ufmͩā&+EӖ'EՅGڊ&%)^jEѤ՝R;0@EM+(a P_+' +ƲW!6mN6w5[Lku~'z9Mf+"593@#*hp$ *"/AGnzhXmqbhӅtR|n呬udX׫cbm0h +'ʁnе03Bn.讖fK)˺<[bBqDfyNS3&ZW$WltmC7$I:? [g@sX Ⱦ0>)ЄF)+CUVŐ/l<__eAEJ֫LBdih^@޵@&n( V OFi܅Nǵl\ı#,963hNc//4$HZb4Dڴf­oX7dnpD e7dnyuCG7|Dfv4 c{l7leL=vrnvCfᆗov't<ГP+H>c>uDW79#8 ox^~ä^&(󛜟oK燁0is~ô>rHn$#3plorpTHFf7:M7<ݔ SuSͫrft(;!G74s&@b !p#  6E 7.Ta)f[Clܸ,&}fZȍ#7@r# %7%`rф6w'?0Aa +/ڹm%Z` )g 玍)oϯ6bO7bјGD +F 3o#--9xgwvh +8X^ŵֺ]2}]`nb_kѸ'D3r@XدHͯb9(l߃ݛqJ{O|+ L[ fIetL؜̉`Qa#ʀjfz$FHcFdl/K0cxԚSxjK:O/)4ΟP(%D"Dσ OPv U~kkJV kLs4Or?wO;mvxչnF2778VoCc vڙ-ˇ +>|MDe(L.rXD==<(Vӝ\^[ڂ0;j m ).M=WLԺƐu_ѫgZloxNau/ɝbc>YES,a0)>F`CsSbn!>;[ML˹޾~,0qɡPw]q ~<ேB+Db[b\˦8?RcP,ݮ<9Y&g1fƽ_0K +yj=3id5f(LCwH6V6nDX?c^$zZ.e @jϤ0<t"U8Gy+,L/C,n$mUޯ<0(n @ I " +FE-T.;?# +15`8hlIXjקBPv H ]_l5Ö^ ›#v >BB|]lrmq18 $>0CH5Q0eS3nkҚjEP8hD?sө9a}L4';UpkQ  zJ}~Ui;hiZj8&9YpH6f:2\nR|Р͔$x@3ps<M +tg88Cr_aF۽>LɎ@,rz쐤bKT5%[PB~گ$,aIɥGD5S{DI*q#2qdFĤ$3E6>+KB*:q L)vAԣ>2(gM}sȘ"#NSЪO-R[hҍMQ5e{G{5!@F%@ULX{DHZ[OYoy94ͯqWր.uAނqTr (w;$28):EV1hN58\(A١zIXuBI;<@ s<n|*RU?A, PѮA, @2.gRZSpߜbۮ hJFջg4h9 j q`Plmc#9XSJcX7> G(o DTz^œ:RQx|'H H+2m*g&,C>U43ч贉EPi;!իyqcpA"f-xIb|}B)1\&VqCu;|,1U,JS=`nl>Cp{@؄ ̖Y[LELaLW5X!{WRkϟe<; G)P/q( ဓȦ}4VLT5Ihq&(ce+^$SA2v@dYG9b|A΍ABo*ɛ +[4Ajz O m[Tm=b+Ma}Ml2 ! #i\n| r8sZ ?߮c$ZP=* +2,`D>NG #˜7P ːY`IvL`tn9ç +([kp'%z^_ekh*D`Ň SShTW*(*ŞnQgpܚjC?ϧ9A0)2)OPpz1x~CfMnk"($('t3 }0<l͔জ-L +0ķ~d ߺ@hleaŎ^nkPor%$ALңC, 9'8Y6 xjpm:gcfNXr1[i^QҍTX|+Jb&,hjDeC"/6X,Gb@O"T/ɝW:n!"-8ʼ;N2}iye`}p^#՝cn+ + k9\+ep^&SOiΫjDcIyJ`#:+IGڐ +DnWvq^ J畡;m8AVլ ^ Q+ |=ƕ%\YV+ݕ%)\YhPc5Nd:'%1uy5ȦKg5/yg /j=<5cҺԌDz8V{)kFCYlq1hyPT_a4g=hh^Dq-VYFGjkxg^֨Rک"j]@:-4ї!xD>@7v %F}\W"$^3Uh:\I50>7-Xj SJtKu]ףxjXR Ɛ*+M uLaoFf7lRK x"`TV(qdũeruլAjgia>IaZMW.M\-b !Χ"ҔK[:K={o_?zR6)7[S~V:d#tIU6Kwqu+` S8/(% | gړr*_n8klyWB@ӂF;v + ӘAP**vԓpֻwgq}s,%hŽ2b'w*r]*BSH)#a0}3]CLױXE./ryˋ\5aU CےbZqs4f, Q_֮L!!.f/lz]M`ekpbg`mGnRkr?`՝BY86{s#ƥɹvOfW(Ғx[䒮wv$[]8J@Px +F +Mr%/x%i Xj-=e۞ du-c^7YycX!X(>p/3OԶ/'/A\mGn_j%ۆ863'8)8-~ya2KS E'˄"}78"mp҉9l'hkxM O;gÓyE'XZޒ%Z[jHC% _5,QWusfKQ8%*G젏r:+k{\`? fqC#G4/(`+S%L|V(h BKDs[WA>rI,8Y$|d`l1 " OXa%t,+6#Jg7g@d@A݃z '0P +IQ0\ u!*} +#lֶDY@h톘 +c~8d}- @L暻BLimE:9& #TIbX4%/\U/]iMfcyjp=HzFS$XYGj`s]PiCM@_tXJYŋ$^$"IBA҂voѴ lC2"sZr7V][3<3eѱ[cD}P[jn.TIM-b H2+"ȷ.բR5X=4(r i3og$VJ]Ã-PSj7a%5ĂYP, RHom`F3'Xlﳕ"EcPegF ((^9xErL<11y+^̅3WmV3؆3iFBHzڥ7lF"QF[mbVcAB0dgc&0ᅉ>%5043fjpj=1K%MMc60ȝ.4#8pJ7 +he-/xIB%c@.'D^PDEx1 ^L">ZG\Zf_z٥$ 7t$Ah,͹˃7LUycq'4;|S^΅J;i8)&Dɴfuj.qNd6‡} 74VM73|3F/~xË?v}T0 Ơ瑩d0v(Me ~N v0,Cl#WY(J:9ӚUnu &C:86:~/xB4Y&D͖&KTX'ƌ~紙PqPb#1j5d +%K'udAA@/^B9I7%DYXT ZIorY4b n5d_è*̠Džu'nŻʎ7cmxY2*ˤQKx'iFxA~vF룡CVƎRl3xXcjmGx 2cA ^pCFYw)fj jC05i&Lw#6I-?T!`ռ-ϛ{AR3_D1H/뙀Xn2¼BX9]He}_hQ`+!ټ6L-/(n_j$0BhtHڡkT9Fq^ dlX#,Wr0[՗.Q #k4YԮ5(sE,bM΂i7IZW:c;L^eU-a +k5*6̙AK(N ψqW(~vO3sGu%J&kς[C5<:ׯ:DFtrmH6ïk\PQGh eTȬgÇRX2NwϛjCs`ߎpf: \.00Z\HF781|$#}cҥlJ̖.hZ=GdmӃClqK8@: T5hBhu1)x k?}q+3>T!0Oz73` ^?nb?F7(e=ϜKihlAA=AlW7ZLy`Y&oFjC6_϶̀3݈tqt-χlݕe#~y\30$Q[ǏKa$ g{w0>i"(3әJd,na<=n@*X3䂫l&p^пf6kپT K} +F NcthLHnh&"K50v`pi\z{53 Z>.]9K8DR>]Z<^͹_8KfnahC(Szt,A($ַ3'R *Gf>䂽 )IR8SkHq{$WO.jSe7 +%Ѓg$'-+pOhT0k\=e6ǭ$)$J#!^ei[Mk&&5,A{pTmĦˉ' X$A2"^4W)vD H%%]@S!fa6R̄mgI%ev +<#Cg0'+e4b :ԡ X#sRʙZtGD4'@]o$H#;m&w{.ʤ8J:0ůoךguSxA %MZiq*7 Nϸ`H- w@ő g<@-2_ @8p :&^͢ΰtS1Q=蚕>ؿ84zxVq2K~f50 jXOca0 ی9r.۔1F,тŋnMbH8-蕯2R8  ;vc\BR.WHTRd߂`c/N;o™VI,. <]diP. +N0M|1 7mC ߛj٥󲋗].WvIc60X(Ц<5,E-@ꝠaXxO}Hۧûe`3A;,:u!ou{o}}5)8AiD8-g"qЭ7ZBnex)tAHxBivRH 5+h13)ir}.<WdVK"nF{VwJFuٞ0u'&SiB\ѥt ^n+zRcVD3< +i,bAP=f^~H.-{cc<4:Aށv +fߘawǞ#mxQLx5fZIfVB.6 aQXl ,zhPcbYD6\uq5Gh %ur5'%b_%:/ l!&:1њ +E͡!41Ұ#/o2{'VT($ []KC86ڒ2IjDhj+[m9|!ֺi5PQ0(aS3`>dR៮ֈNX~+5&az S>mXJ.zGmI]j={1)Ipa=ot{c ҫ +Z0JͭiygYMjhPIGd^.}}mYT@fjN$TC, cȋ XbldƖOivYZ73 +9ً ^d0*U=A6U$X|*y,ۀE:+n_bi +׈uWĮc'30=:w0aLK,|,Q{Y&K*#:a +^WГXz 6Hc!N ''CjƏu<OPbjwSa1cfb{&2E]mZ N LRX5IgՈ k'0ۨv=\>$̇iZNH߰R0e{ 8Ve]W:(XP=Gψ/=7z˛Y!3kPӸpOXXS`uDXPTͤ͘Uq",7i2gj)KRW'\ mZֱ-+={ O+h@~Uz4nVTKce>|,,k5@ Nn}l(Xze{e +/x2D4L6}AV2RgorF_K5I !30cI2vMsG[g]0' [uԒK `|GШ$FxFH4o1{j Os:χ.Yũ0JXD^{uGw +rt/X nq8>M!vA_plT8tV`[8LA9t˄ +”B_$&UGd*cnB_99 opw[ew'6|)7p6r6pfHސp@5ʫ`2Ž1V +؀7c(9:ʱf.8<[.$qg?c=:#ifGv4Uxng +gs,Oxi;@>`^6Ûc<.пcvY&U*3' fEu \=<b8C}DG*s, iEjj3DmyaM9)H=^% +q1e`o3GQoz&|N"avz6g$wMt(|Di']n(r{sbJ-yK)qTb:)c +gm&9=TÇkPT&ty|8D`bAC\B6Kwj/*;j70mjH " L(|u`}~5+}eW bTc9'"0 ub t] uT6Ы2E.;+ɴ*@'ٷ<%0M_@Ȇd=E䟶f{(4+IHtJ>c |l͌Tb-.63CLjOފ^iT{ϥg3=\etz,ΠqʫT'$ OgSeoezDm@t# ztvU\ͩ⢊mNVȾiN|m}$Ar|ar$u=kd֑q/(m X,opNGr`ϞL%Iǐ#2-ql0ÏY +$w+s* +}tҋNJfy J=\ QCC/Ԥ*GHMH>tIB)|Bh e)H1RA23ϓPRCd_̓ y]2풬3 y)" L&ve^ioROS@FY)X0eMrb r!lO{{s7]9I Q騇/M[S\Eggb2aa}(B!HRPQ_:P/8:0V5~̉(/j ؘTq%1"Qs|6 +4a&Z59P>uxhAs0bLbB>S$#G l::`G:`UGqA X doNm>fU]tvC&c_bo7 +-Ox;0:4Хrf$.VM!/,0E[[ *REWkltA/Z +y9!D6sj&7øf`ҷ?MO4< d1Pxl"Ɔ? v%/D~45Ј/l @{e` ;!c] |TV|ٌ^\Hl#FV Yti\&脗@<ΪjS @IH7QIjK77p Bxu9a xp/ ZN .ƐL*>1pfo[` /$3`:m J +XIf̓4Yn}}oȝ%_jnz9Ol;N/N}3iT}8Y,W +"NDZ_}: )m9xI-@SIma$/xl )B<*_JXʮOvQ2T5Y,$E9OQ9^H#U* 1:ZLi1cͦ4#1|r>$0V_S*kMq\VDv_ %(佧rUhcT 5DVSm0L(X + M-yG4/@EѡˬenbǏ6#2N#VL@Xz:Xv>!!0|f<SCb= ./uY&i9V_R].wi/w)* ^ <]7U:uzK-+ة/|vz6Q/"nkTNFɕv񮖽qpHB`2_pVPm5S7b^'\"qB9ar.v/OB+4Y#VS'h1OpXk4HT0# +!>crڳ1X`Fxui-l0bFIo Eٖ*kEƱotI LՇKcD$~Vh.LKQ y҂ʥ&ә9Z%ƪRԓ02Ciiqe$NqOee}"Cڻ5R$ܹyZҍQHTqbk%"=\@q$kXI-4TTђZr%vjκE-=nXkR/ʨ0Nmr6 ^磑`"A|C?]h@Yuz +8 +fcs՘,0l8XZ--p:~Dt\Ìv{}dOj_fa`U&zc@}z%Ev0E b >9:S ܗ4&/y@@fH@A^VAOVfv[3֑MJ_YFŒ>L[3aC_2A8SrLpvK+L`hFqq؈k?%ewlx6;BX%"1N¶ϼ$+Bᰒ^ E$t&(xv  +> "]=w?wzz]Ib /˅2 \ ,%"洗S¯VNE:äPQWIx\lRu\LE`'), +MiC572_&G*el 4Q i, 9neDnb[=ĭGdQ5iS*6nW"ꌟkVA@t֞l oI )r"Ux.R:llD"=>7?9~DŽDkk5%녽L޴}_H2w}T" NbN$ Et qD+5R炚Nhpa1ZѭވzpֺO]HA%{4TpV,;dրWE$q3jem5l1VR#[ J76 a78}zeZ/\2ñǡ ]H%Yچ$RDd2j/v}OK/7㇙by@8H9NР`)H S"dѽ]\N\x$c -ˉ}Q!Sz;)kYچ$Q6$Z*Qd6$ W-ې8n!ykA!QqY}C͆7\- > j 2$*3}Lj נ'Fy"iM.k [Py\i Z% p쀍r*=MXژ_ryKTZijP6'Xp%$6ĥ0[@b% {7,M{,9o /hxA˧ CI "H0%x{JU"OoM,|W-2ZtzAXswl9 QAm9VU݋,^d<%5( ,G1UQ]jzh$þΠa ͘SR`F!CI]F &ޛ6j+DralH@~Vv @m]DjĠ87`C|w.$x)K)'w r̊`$M!`dd$b+{c(k61TƪU HɮN{j"о!cZqeNCFar +ްQ[G Xk|nC9'&xO3Uff25J.B[^~/>V 6BHi߃0|p06ѻ.\x M]X-c]. cJ1| 17e/\ ƍc4f֜ [&J/s$ӆ RoXbmk5UF2H4 +/TxkH:h*n@牻M&UMO}NcvʂBll"Ӆd ztb9WUl &,CHm`H^jRI-l4CKHR5ḛ.Q=H=t(-ӼAAΦQ ڑi5#sc#daüv6Ҋ;RDm7CTEnԒ7zk{pEޞBP|݂ CU|b6]"/^+؋lK(^B< % ޛ*߷ 1!1"Vd]Anzh-a6Y@aVj*rmO ^ZΑݬ&(xEUDmƋEb9cFEk s#_qQ ƺtƊ4hp_c}eZBS'8Վ:A|lZ]y'@\X%]Z]hLBrr^ %vS& +EPeq \632ٸ){ RV@$0oTT(4Q5A:| Zc6BgE^oEk9T]9 FB*(Y<WՊ@M#:\eU'G6<} !kA+4h>q'ѪrFxLB1Á15,8p̺G:0}zN9#~v=PCp# (@K8z5f%ddv2W0DO< A;F5ENxTT24'e0VSK'nɖxGԀf(,GZ&7^Qb5vø",o9ΊI5Q+-'^)S$}z6ޥ5uv/$hMmԎ6a~Z-<{пPЏ'ɤMrpvn2nޥ %Ptu-sA_<59nLJp'ohk-AF]͠6J *Lon ܪ3_.6]n)En*7o[2tNI07yIK^l?$#v' vzL/Ikv_/י_\\@v9+Z;orj۾N] +9N43aVϱ,u&,[1*40*I f$r"Ilr/Crk!6;ހ7 Nkc[!GoL[.-y_IT9)qz^YVeJT]øvu8@{Y vچ^ .,:3dz zhHK*.QA&i1,xIKQ<}[Zqq++95KTNݴtJ*d3Q|7ՐZڼr/YR݁4O wur^5E=fpĮuMBB1 (=|AJ+$Skg@XRý3ab#S @Qc b#i5CVu>Ë?|YR?Ӥ*|)8O[))5kdһOmqxs:n>R'jq6|B+B0tKYKLT9/۝,}nEyOT*?*qN-*UufY<8^Cllw$2mvgaT^é^h?40 +qʋc.y$H6,A z'sLW:pZxcZMn_ +5Ng&鼕|3-5)/@/7 :'DyNy Fq_h6Z.X(sx[ S㄃FYbonějGZ[F gZiQg + 8.5={.Ջ$R,Q\6[| dP5.tNqw u:#h ^wL[n o-y֒8*z$51 M G!s7 #6cVr0.7tc%PU!Ec<3-+'ŋA&7f4JA3lt4㞷QFjogL8cScp,_B%&Ӿ +28؞fE~G/nPH`B z!-^n$ş Lv 10*Fk~kq/D@>{y^g2d]Sb1#P ӈ7$A +Kv7m.`V /Mon=xLJw"D"0EIJD(>dO9JnGѵbwЪ!\DXyt9 +Ok08Ǽ34GZ?9:0yG7Rtuc;:.>G?qQT~\>oOw1s>hBiBtgÃ#@yUqYN9L0xS^` obBWML 0 \ڭP#W %1O,e–F/ԝҙv=UNQVې4P; nff-Jpf䔖7VYݧIG݇H^7Mދ~㓊~rAjFH Wyn􂜍}CdlTǡi! FV _-Y\mHd1@p~ + :̻F/5: /ibú muե` xr2c3b^VZ)=mkĶvP͕jAXb^Ӥ}!9E*z ٘OkS&KmԎ'2$D3O z ?+ݠrCTb O[7xWg\ 1fK`?XXIP  ;wA+Of>~6}=ڇw SV\,$ "mYqG&U )iyOa{VO>ѯX\y ѱzlO!M༅VJy/R+?/`0nT-;a4Km6 ﳧݾ d`AZذ*I7kMڦty Li6 +E!*TAδɵ'Zm 5GFfi8^ fMkZF!TZJTXQ;VZ]Ny4ZZݢt<MQMnөӹT s@F2PC5d/{<9T6SC(F3p`9)n|xhQ_&.s5[!;Ն <AR@ͫ?=zթU^S@Q U2v2~zEQ.xwWz2bW.%=\$˴᪌!T&(T>³2<+_ꆨ8.hLDwj]NdzE41(h늡|4'iإLSm«U3ѵ +dZר*rՐI lWLEڨ(a4SjZЁf88cpО&[n;q+nF9pHfa< +X%QP/q?q\lw5vX>fHj>_c@pFR\6t :6nS3^JuYQU١Kߕ&=J ?p8׀4)3N0`|A4 /" 6&#c7[RƑ@A\b@"=B# 5UfV9P?7R9eZݔyiVN5mh<~5]uTݶ z[ Q΁x{e}૵G +tZu4zu׫u=zu׫@s*3X_F7*L%f"Oppm/6O[uG4b=5f9:^D>tTM]0zS^UUhWL8o 0,@Me +%#,NoAT&9K%BG0A9la)@_3 $7(Tӕ-pV%'0S_% ExR~L+5< ~մG^snOE Ok#ӛy^ec49=)/Hcm aXA?J;>T< T/(%3rvq1w 'V\[W"WQ!UGT|AxbqY{eV.Epmv9+p c5zBK;B숻pP^I}ޮh,/i#Cl6h<92RΎ(VB5Sc_m˜x3[D9q@A9xNAEJ`19x̎[ifņl肒FT=TFUT>J4B{'VKM_zUkBO /H#qK2V7_h{ڦ ?hYًEusދ#'O7*fk-`}U&uwC}ݭC@DjgL +e +/Z?\Coed+JqkN]Dƌ/@_^k0.M^2`,Zm H $ V]hpd;#Oܫ lQ4qca$p&ȗrJa&S,T %S+k謬./T^zKl,40`4*0 ./H>ZǵmSj^k 9+\vm?X';t`!:`@f + Sw4{0N'scjnh)6F[YrFX^Gs`UnM\R6󲽤Ͱ +B)BIY4ů(?a, be>O\9vLl(@ hh@9Ǩ{n:D5Dr浞y +r2i% +?zP|SJM }9ܻ ; fH҄%.}JGB3؇ +?g +G7ɂS=(_ +PP?KZe.3>+gLk3%PFkާC/ +B U(eq PW^I["FM52?#֚#a.x-oR&}=-WANxF]m+{&{۟B/4:K^m/|3- "o+#/1vz>1 k;%4eZ9!4E톲ᗻ8xRQ_8qpRqF]|&'1c+&[YF}gGmxψ^#6[,5K!io6ע:s&r` |uYsR &\%N˧~_7;LP,pR0嚕gS3 ++]U 2qkѲ +IC`6,G?u9Vlf\rqgKq 6ATln߆B `"ʊ|r&*9S ə +dA&*ϟiH;$qGwfUWJIU ^Jn73\ yP"`a9 +[$z+-뭲ެBSFݳ}@Vc9 +߂=43m\{, g<`p:O)ad_&[k.d:g`:Ye08hSE{?,jCl9eH7 5o1o&d3 !O$͓X>AT#=_ GgBv$o]d4e8)Y?2 ?QW8VyJ*KVSv8_^ HJ ࣬޲g|jO FM~Z4 28$ހ=8K!8+/NmzIEvai"Va)TqaW-(aȾa5~f\7YovtϿQP4bAEyo OAH ƉSx> +~}fG`)W.ZD5*k2 t`>ll`QUAE;B$MӨMzh(HR@zĨy +q܊ VHI L*YE,٪"W3[ʩA; ֋ + &)X oCR#Jn  QGg™iB4Ǫ48"vLW+Шjs 2+ViG +x UeĂg5\*Jd% &!)Ƶ,hx+QFEKϽ +F݄k9Ǟv`u1v9& + jipd2Amc+.)ڐIbh}z' LӪ^Ov$mŚد/th8v!'w^Bh<)u!OtZh΋`4r=/R6`>5RCa/ xY]Y DHgm1 Ȑ=k=Ib.6vZTSq=cΑqNjfǃ C} }>_Q>~Rudz(S@>;dmפfBWE+&`֊&džۢjď>!ؙ#4 LCP!xDovq' +qf+ʒ6?eMvvaubf\+7`f.j V!IW1U0k#;j|sPqWTOeMbf{"f.N̬^GYϗx9o`?~gb/ ]mG_H>{fT˂k8ܸW3hR3-A; S~PM7]ӊbE0-AX7t5,*ke͊GSr!W-A´sJMYW(/dbP܏-7;{l LJ{srQ 3q,Vq bˀ[ʸʔ•-{Oe20]%f~ĥ2[5ԯHu +L2 f,yC&m|Ѕ6f+r,e0N\T wCv\0Ũݦ@ZNV SUl|9LZ&hB3@ C(-i屑P"Fkw R<$HiIG[AIQg4QW笊w7 zP$|fSיI4X xdO:UL*ebҫUG#ZF\Yx:`Ts4QQ+t@qE ҂t0??y6(ɔBae(Ň]=yp*YRX'O\JhXMt6̷Tr:h5S\"OY/ μ +Ў Wv0PsxS.:[_ X[h/?>Y%hS$T5qkR<@b&: ܠ6q|Bmg~N"{"NCgT*۸e$o#):mv6lBӦq|Rw]n|䪡[r6,{" 8 `U.fo޷buj6niDDZԡg:K9, biFS*)KG˪A!<Ɖf[l3滖S5tQD/1Wt]% LW'I?I"ʽ;v{8;Y- 2J/SL4y/N g* +뎩# +{d5St0~kTuTMݝBd *T:w:U;FxѕCIcufU5T^5qi8{Ԟs;Ĥ0Lh{ +;=l8dQeK^D BOymTL*3Pi'j,cD|Y=p=XƂ`$"*,j+J;UPC,"8ubn|LYNvMOD'ؕT̀;1n{vlgҭ+ +H iJ;M#N$jg b_;f2PN\yREh٢"M=GČ(P.ccj0 ѥqPЈ$*D\ Iq\|mwX5rqY?}޽1 񥦾Ԛj4L 2e-V)2J͸~sI a)1%eB&!|C]H +K3 +;maa+\ aZ8v- mDeV +qa +H#_xF_]?eMvN éZ>_=S f\G6wBx +ilTPŇ& +P(+/pCqJ +pі'5Nj8j#Z=Z +O׿>}6A2HԿ| k11($Z!A\+ +M_'}v w_Nn+2w5˝{q+: ȴz]߭78)"UD0[zV ;d\8̹pm}[fU;FeVyu,99q7fWT_ś`-JoaRm\X0z0m#9OoѺFj-`b{Sz~JRV;`*KDH-߶9F&3qڄmUe۸U;yU *33*0+Vq^5l9L1ɛZb9whąʨ"#yb^6M+Anrq=v;b$t\?U`U6X{8O BĂU+~CyP BҹC6:ZE\C +='0``SpmTD_"Wk@c +ŝ D冪bc3) VP*lWJnpxNdĊ%.ک 0_l#k4 ,S!/"L +Ox6p-"G9U- E"@4Pxv5 1ȘC mWʯZ`A׈ǜcWsCy^XoNZ7B͝h}&99ɇϚKKuO_F; q11,Naaԝ{~_^ݛӊw(/Z 3"ҳS%T"l4<.'?D"{!m#9?hاS=Ķ>. mS08bH|fiCx@dQ"-G 7 \3&m6qBpnEI^ 3Yx&h@`KjI+J;}o"OEm$n^Us9gm&]\6Dj6kJ%<MWN |29&d3]d,X~2\cdbOЬYO6B8 I /J 중eUĮ\~ϓoJmnRUA䞖N%q$CjʇtNUv|p٩ȏ g}B70hxA~ɨy63 EdԎ i=iXO;rZ*H!Eۖ:Ds2Yq+"7I3IA]<4Fm^%0O +aV-l`d*Q&P}Զת=W?g / ͦӌT :'UAFMfVh~=-wģ! Vsv2͔hSJJZ4wcxī&&ɬI0;Mr' jDg^ܗC__F36W,ZMek5WZ 6 +\mЮ[ƥ}%\=޿eSgGVf^WeLT0*]@镩xs;s$hthr!}ShbdPUwH#l&*E\a)3ipt5NR2ժZ e& c{Lbn-(:?f D$ާ*I$Ъ T^pz/ȿo[{7S^%&ʇs}I)7F4xݬu\l}&CycuN['qvI Ơόq\0ANit<מe,7v%5߿ oAr'G &q!.>|"It~ +-H Pc/%m6rp ta??~B]G#v8 4q3H/5ȧ`: 5+gaBP6rU ټN{K\ղz1#?,Yߗls)|Uh.E+M&T.EKH!ȑ?9(H)vEy4|Tp6w䛧2ln%Q&UK87tl R]qzbfyU䞺@nrap$@8wbt6ks^a`Tq_2ɗ}oygI +NHhl +0PfϑE })ݤLwnU/L~ hFNI.!\U֔ӌm2 mozfG кņ@gZ}u\dU=7+\I~zAxʆ3]dK`nNSt'J'~-)yߤ̎V|h3pE7PGB~gf2E2Ď@[d!\W/i0c8Y Σ @@Ajqz5y:@&:3Y$ [ hM _mzm m_C&At.}X/úި _Cn"&Q1aWu*ɗģgZZRw/˻?4"0"j<{ZQʝX~e(v*"*]Ǯ-ٕ6bɮ|%7:mɮT:̮1v +Əǝc=W'XO`k% l!!f +w.vIK\|ȢyD [2/NH;x$zgM"Miuf=lR C@v Z?V +ڥU^@+hZA9RQB0S) +e A0OT J+8kDZޜTcN ^|-[$N!O# :tCA&>oזŷŹdqkMZnMAqzun*0m"Юfn[%t."ŜEY'[ '%} Gr[??RD_-L 5N6[1Wub a_ɇ9B4=~{vgÑdߘgr}ZEyF]ޘc:s*FZ0[XsX.9.x| {4 +gF&1no9U'V%JBRnz{h1zv"ƺPl[T݃7*PBwɺ0=4/{S~_/|LQ`oe:= Uxu"M+7(g}Că5bb\<=z'*Aeɇ^CK Xi6Kך_4WLcMoxiwo0%q LXeE_.,3!M|%>ZrIkI82$#@))~'xku-\J?Ӆś:TK]&p۟9IyCZ*0<1K1rʣT- V +ld:]I~?G'0d-jiʧѣlF(U(8 -å6߿THUlTŦVm"V+T.,ɣGm;mhj y!7!Kޑڐ0 Gr΋KX14ԏ܌g=>_=-UP2MN8M?hYrIN&r8Sq jÝ^z] ;K`N)iW™G A0ҽ*@ +21oE2U QKt¨]lc2qHf\XEpق#ͽ!#"@S# +\YLߒ_}-7kGV+s'=!CИu3b~@A7{Do{V\sn]||YS&3܅Ŗ<Z L|%緒qB:2pSʨ=e>#S O!weQy(C(㐮Vlf*; dّ'pb8dg%~GG6myP3Y`۱e45-VMx=5HA 7=t Pos0;rIy8 +Haq{_C<2rj#a*ܑB/xVUOH vsqqᎮMMxL7;'Q_)zi s um{!U !9>E"Zx-WvLinHZ B;.iF+L" {WWumw^pPLfy95r”.R7Z@Ђ 7ZX}yGFc'' YIղe5F14IBRS>oE7[2@VFThB-;WzcYc ~ 뢪 Mty(ݬ`,@r񫼹gpvzfΨ2d 0AMBykE?=` m~8.mw}۪7{k+7q๤1΍hU`*1Z1}FAݛ< gJ)< u꼔OO䭢;ѧOEte=m?4- ' +DWGp oK}-1um)lvxyǖkp, @p\@U Q *+r~]/_L+^:8V#rΤ#yP؃BgN)lW>\5SwF DIi"~_SВJݟѿ+|*@<ژ ^ Df 6NB:gh|<l0vo8q1,ʅ$¬B \Vq:Xnjku`vgL +k\=m #cĖ$#n~:js Zˣn΂j]YK,Cm + j7n;L!:+Gzsxk!.Ȋ9 CS"٨ѸVJb:H9OXƻ_dlI{iMI>ի imhl%9Ki,rh5Prt?S3'#5Z*9z#uC2XE.1Z1:gBh&at +&8.﹧Hgz;(!w4̔鼆Se[%joha+ܷɑ~ 6&fSc>n|}\sL>m26LL4=Ys[I?qψߐNNnj,{,&8JlZ.vD; J] Dz;[~l!y Rcr:Giuq YIn">@a@=z2M2HF=_%9ܟ_2ߜkacIRY!MΕLIOmiϬH/{ +sH/[BɹH+-tn/? E*$!:b.BC8J%R#Nt8}2'=mߕ"+oC)0 +!GHL.4s;qTz5d}|Z^O1tկT[h4h- +Emdϥ[)byAd쳟 K 1ƶ %K6)m +`lbH+ s8A!Uuoq<s &9.fs,ߓ&͚B궤0g nn/}[ܮ^cꊇ"E+PψVCor7%],Ii.iRe.iK/rIoU.ivg 9M<@m#E}̩OJ@c0_T + + yD̃;됶xVcTiʿM{0~bZ4Q3h˲ODliMp{P$P +=^t=,g.5%YpWD'-B@gz77Lr[:E=!+"UQx+fJMj)frE]tMsKT^Rqr9J+z^;ZY tv#Q4%r(>."tqBvXe ί^^?qXiW~Fy-uJut{)p,QW ^̕Vf/qil5Mjи9FQTe@aXCAf rxzi5YR J^x}> PJP9ЪZaݧ_ϙ'[Թ¾3쟻N֋җBhjdIh(B;7ZpgbXNaMƹBpb ]џç^i.:;)u#8+,b=_l}JSn% ||~- ?5ix5×tlBS4 }OD~Y z+8c.+RJ' [6 c={h+ I囼v7o:]fw!ޯfǐ4d5^Z ރwՇ!ޏ@ C=j='fϮ[1aAQ +;$?X6 8@P|!\|]- YqV'v3#$_3{cד+ScZ2@0|v&IlVDS ]`T\&h$L?!\XRzaD7Hn̥$ :w?S~BC٠LP7Zע;r2M[VMۢۋ#1<_zAv1+:Eբ:Dj9񙰙f(СwyȁOsp6pO8Mد^=iWTWkWi`:3j؞~Br$4+~3pBs.ӌ& s};eK TAi`8?^il%)vlvP_d좐rZ~Tqq$+[mWUsAGU+{ A<,2E$ 4e6Rìkm 9nU@k8nKN=pcs;XpL]ZhM+J'r(;A?8I雲]ioqܸR4І̅ ،hg\YA?"uhxʆ|Aе:N%nRĚ':F^4 ՗ wal^JnC\C0,YŜe>[)]4MUGͳC#֙b$yG7NkhƵ]rSj5l+,ToSbuNZfq &=\ab+g95[뫓2l̝:),D~#$ NtK ~hFdq˼aoѨI2 ЁT0"?\)MW6$,aNQIXF>oNQMp[SV!5 +uyX,`xhP\DP+n|kXӍ?Р#&C#ֺH8/0(bJ~[jLu0C%4W Qו9]2ӻtOUڟn7Me}7`NыDe?Au۷o~. ۉ,΅K 8Wr p-8=JΨ]w+OQ+Օ@>T~<"3Մ\Ps=%ͣLکXnWĖBJ<3weq#OhBzeH +Ө7W-d7o4z]=__ ׾jN` +{ KP[e6Mol?~/ee(IZN!YFvzbK++WQ@YA"I3f 3\3^_\9<5<ʺ,|@̠ΘlQlD!{@2Gv(h(`<P=|I*ˌ~kdu5c٩9X%*Zh2*7h\zdܫ_zcU"j[ JPG ?8#8MGH-$UL:"EVw"w2ߐdP4ܤ#@7Xhk\?8{6xN:v|~G-kZQ:ak.Ĺg\M( +QnsLRy< q&t{h Hq +,nf:9NXʷdN_Ky"ԙ2'!&{)C+G49nI2RP܅9-Sܗى~͏M]vc\"fGe +jj5c|۰Fg'FK$Fߋ^I%L-S6La-~M~X/Zޗx?(V$0 rB687)FK_oW'uf*q"d_ЯXy]e^c)~No7{LVaߟKų䄰rj8{[VH\[;$ʻA}BtD20b +ZQ=WE^4k) dpv [F m.x ^mx{䲊tlUwr/lqdN,.ɍks0Շyr];=tmRzt|%|%Jod`o{%Yټ +=$~GZ0a66(3NdSF }B{Ia':(xLk+<'mN0\8z:6Bd ]O/*΀}A)8Zs4mQlnJϻk!]} Ͽ͌ p@ +`ʵZ@`>6y _ pZIFoNʚGp]Mܗ{oaU;وQoS-qxN۟ȘbFƐn#cSN7.~=KN[i8%s0s_=o +Kv2 F.nO]\,=u+ (A.0gB]nłE&LFڕބKt2@I+F|fIƔ-6`r^nm6@߶c=h Głp4 aߥbhdlXıG3u䋙ha/_I_Đ[~/F鼤G'(͏EW(t7DGçzӦˣ_T;l~vP\JvG*o ba(*~+gԕ$>Rx~699P%E\w`k]D5n-@ܭϠ G.._D V YK#;c-қ|d +0[+X8ݧ_r&?l>xSWh!$Ѓ*ܵ`0v녜Lq2? rĬ +ww.I]bq4Eclkc 1CdSޭ <ܛ͏.p3oaHroZmoj%*T%x6h.%ӰJ% (g{zUS#\*t.[Фv9H}<Íny\0b0=pO{bVDaq;N6 Tz^W΂h<?l't6 U/i-tVw - U r.?1-N`노. {diy<!DUSAu2Wy F$39M7+ ;{2"xwlY#bʷWj7#+Al m䅲w ~'{vKST֬DwA{ K_.EoNx=g`RY{!Mul:) NfzFh1ѭ/t3ֿ"K  Sc_ ֿhA2^5mƳ +5"Oc ]_Nj3ߔ$NE)g$74` v^'詮 Ս<='k%XP`~x ¹ !$4̈1)D^GS}B'(| "πMyw` +ȉH¾"jjwo1{ ESzmtT)+4)CeO]#rp0]ߥ +CӃ2cPA1|w2#i(r'pJssQtiK~ +HX{vE2"$B-vyBWSyqx*!1AnwTs~8+F^_ +k|WY"$o[Ҽm@˼ms1^#/4ąrVQXW> R!o?Ë!.3߽x[tVTEQMCΥݹXIԆ\l旧߳ϘdXh5DV! ^4ξe[B@n+Cq?%@FӖ^=܅Ycώ>v&۝IAqa8 +ͧ_ .݅tJqWN%F%ADL|:{b}r" ё3ŽOl|ZUmj~_@"i"P>FV +E1с*_opf@g0vY7]zBm4^M5[_J{_i^:ar#>dn`T)`+6 cAA$ +jEZ`iw>5-CjQS"M`bV- ߵ1Wji"u c:F0L51/) Ah0W`L[&8WI +[L+ +(,J]I`|2q%NևK +1\[j +@͕ +Լ@4PH)A* T*nXM"s{,׮v嵫B?/j جѮny%(Y$Ŗ@#^HR5{&إE;pqp`eFW@XNӚ|H{-ka^ :0 +N^ {Zz^ &g).**9v.uTaZ2yvK޿7iye+Sc5'j;ZE@9>k*{W 'WU E k4W+?Z}#Жd(޶ݡ]`@Woz\Kԛ \#^nr|q|ZD38`.V*NCݪýU/J zUT:vzDnV2BTګJ V⺐020+hm3SQ~(n[m]UD>]"x؁$f7ya7LJ.HvwGJSe T+}YmT[:5ߔ׀Ty`&ML # a9:&LgM ~g -xz%M8iCЮ8 chUA74Qk-(JD^p@a= \or Uv/'M + +Uy jm`;Q/ՓvwSAI^ZrDsjph )G/&D0fdZ޾j~?66Vz`1fSDۿIMx\̺\^+>a,MwUrLP/cPRzU %e=)q=m6ѯ}$[Uآl: cQi7^nrJ}oI\MܤP"KQN`ew)kܮ+v~ߗ*CrxO [#*[yxg&YE+r:n"[Xt Aؤ.٤˺g:["Vw%j"S&f䏅d(wh#/=ktQ; Hdه!ȑ~گ[zA6jw7c:Ɖ9hcLI\Ú&NG*G`E Ԕz4Ԑ9uhRU ޴ve:}jfa}:Snv j G7дBHDGhMft"xNV8~8^gK{aڟkDkȿCBF'p~Kw$ٰX=K z*H4_@ ׫ P:h<ȡlnUt$j«(YMfjūmHt%eDV.!QU܍f`2 +f SڗW;ރ< +GʗUz&W-j{VO?/LڠG-l611GB Dc/Z4Ew>4YDl=n9K灟,2Sܾjk8$յ鼫#SжcfÚ ogcI{[e&3P[M׊aofc#;JƤc+kR~ ;şߨzY)j¿ gq9! 4kzN{AZ 7Ujz̭q.}[fY$ubd6_~אNeNJ@uvT0%%~96d@K6gYoٜUn8Sʲ6 Fp86f>'|9ƚYJ7sK%F~XcK=DQuMZR&;l9g+H;]9?j 7H so+lekX9C.B' ~U`+?4~r/aBe~Hˆ't$ֳg)Rǂ_RduVMښ9يőQ.P+ڑ'R#З@IηpٯV.RՒℶ,QH"r`U%?g2MU4IlujF`ŜͣN, 'uhWwjr`(UwD#ΌO[M{B?8v+OM#U-Ik/!)=7Գt i&F^t}\/ӘC2;o蝦ǻ ]U#>ڜ`x<$MG]U7_8_m7T+_=|sh"3fwVpFF}ͱq`KݾmAIƭʷZ)OeCC_$o߸J]c:&j}"T~5yZLtq.yy CW!)lY_[UMTf!5T jgrXinsert('entity_test_revlog') + ->fields([ + 'id', + 'revision_id', + 'type', + 'uuid', + 'langcode', + 'revision_created', + 'revision_user', + 'revision_log_message', + 'name', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '2', + 'type' => 'entity_test_revlog', + 'uuid' => 'f0b962b1-391b-441b-a664-2468ad520d96', + 'langcode' => 'en', + 'revision_created' => '1476268518', + 'revision_user' => '1', + 'revision_log_message' => 'second revision', + 'name' => 'entity 1', + ]) + ->execute(); + +$connection->insert('entity_test_revlog_revision') + ->fields([ + 'id', + 'revision_id', + 'langcode', + 'revision_created', + 'revision_user', + 'revision_log_message', + 'name', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '1', + 'langcode' => 'en', + 'revision_created' => '1476268517', + 'revision_user' => '1', + 'revision_log_message' => 'first revision', + 'name' => 'entity 1', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '2', + 'langcode' => 'en', + 'revision_created' => '1476268518', + 'revision_user' => '1', + 'revision_log_message' => 'second revision', + 'name' => 'entity 1', + ]) + ->execute(); + +// Data for entity type "entity_test_mul_revlog" +$connection->insert('entity_test_mul_revlog') + ->fields([ + 'id', + 'revision_id', + 'type', + 'uuid', + 'langcode', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '2', + 'type' => 'entity_test_mul_revlog', + 'uuid' => '6f04027a-1cbd-46e3-a67e-72636b493d4f', + 'langcode' => 'en', + ]) + ->execute(); + +$connection->insert('entity_test_mul_revlog_property_data') + ->fields([ + 'id', + 'revision_id', + 'type', + 'langcode', + 'revision_created', + 'revision_user', + 'revision_log_message', + 'name', + 'default_langcode', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '2', + 'type' => 'entity_test_mul_revlog', + 'langcode' => 'en', + 'revision_created' => '1476268518', + 'revision_user' => '1', + 'revision_log_message' => 'second revision', + 'name' => 'entity 1', + 'default_langcode' => '1', + ]) + ->execute(); + +$connection->insert('entity_test_mul_revlog_property_revision') + ->fields([ + 'id', + 'revision_id', + 'langcode', + 'revision_created', + 'revision_user', + 'revision_log_message', + 'name', + 'default_langcode', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '1', + 'langcode' => 'en', + 'revision_created' => '1476268517', + 'revision_user' => '1', + 'revision_log_message' => 'first revision', + 'name' => 'entity 1', + 'default_langcode' => '1', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '2', + 'langcode' => 'en', + 'revision_created' => '1476268518', + 'revision_user' => '1', + 'revision_log_message' => 'second revision', + 'name' => 'entity 1', + 'default_langcode' => '1', + ]) + ->execute(); + +$connection->insert('entity_test_mul_revlog_revision') + ->fields([ + 'id', + 'revision_id', + 'langcode', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '1', + 'langcode' => 'en', + ]) + ->values([ + 'id' => '1', + 'revision_id' => '2', + 'langcode' => 'en', + ]) + ->execute(); diff --git a/core/modules/system/tests/fixtures/update/drupal-8.views-revision-metadata-fields-2248983.php b/core/modules/system/tests/fixtures/update/drupal-8.views-revision-metadata-fields-2248983.php new file mode 100644 index 0000000..59b05a5 --- /dev/null +++ b/core/modules/system/tests/fixtures/update/drupal-8.views-revision-metadata-fields-2248983.php @@ -0,0 +1,35 @@ +insert('config') + ->fields(array( + 'collection', + 'name', + 'data', + )) + ->values(array( + 'collection' => '', + 'name' => 'views.view.' . $views_config['id'], + 'data' => serialize($views_config), + )) + ->execute(); +} diff --git a/core/modules/system/tests/fixtures/update/views.view.entity_test_mul_revlog_for_2248983.yml b/core/modules/system/tests/fixtures/update/views.view.entity_test_mul_revlog_for_2248983.yml new file mode 100644 index 0000000..75ae527 --- /dev/null +++ b/core/modules/system/tests/fixtures/update/views.view.entity_test_mul_revlog_for_2248983.yml @@ -0,0 +1,435 @@ +uuid: 25b89168-a8e5-4ae1-8fb5-c8efb91f0938 +langcode: en +status: true +dependencies: + module: + - entity_test +id: entity_test_mul_revlog +label: entity_test_mul_revlog +module: views +description: '' +tag: '' +base_table: entity_test_mul_revlog_property_data +base_field: id +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: none + options: { } + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: mini + options: + items_per_page: 10 + offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: ‹‹ + next: ›› + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + override: true + sticky: false + caption: '' + summary: '' + description: '' + columns: + name: name + info: + name: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + default: '-1' + empty_table: false + row: + type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true + fields: + name: + table: entity_test_mul_revlog_property_data + field: name + id: name + entity_type: entity_test_mul_revlog + entity_field: name + plugin_id: field + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: { } + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + revision_created: + id: revision_created + table: entity_test_mul_revlog_property_data + field: revision_created + relationship: none + group_type: group + admin_label: '' + label: 'Revision create time' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: timestamp + settings: + date_format: medium + custom_date_format: '' + timezone: '' + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_mul_revlog + entity_field: revision_created + plugin_id: field + revision_id: + id: revision_id + table: entity_test_mul_revlog_property_data + field: revision_id + relationship: none + group_type: group + admin_label: '' + label: 'Revision ID' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: number_integer + settings: + thousand_separator: '' + prefix_suffix: true + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_mul_revlog + entity_field: revision_id + plugin_id: field + revision_log_message: + id: revision_log_message + table: entity_test_mul_revlog_property_data + field: revision_log_message + relationship: none + group_type: group + admin_label: '' + label: 'Revision log message' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: basic_string + settings: { } + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_mul_revlog + entity_field: revision_log_message + plugin_id: field + revision_user: + id: revision_user + table: entity_test_mul_revlog_property_data + field: revision_user + relationship: none + group_type: group + admin_label: '' + label: 'Revision user' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: target_id + type: entity_reference_label + settings: + link: true + group_column: target_id + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_mul_revlog + entity_field: revision_user + plugin_id: field + filters: { } + sorts: { } + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + tags: { } diff --git a/core/modules/system/tests/fixtures/update/views.view.entity_test_revlog_for_2248983.yml b/core/modules/system/tests/fixtures/update/views.view.entity_test_revlog_for_2248983.yml new file mode 100644 index 0000000..ce0c743 --- /dev/null +++ b/core/modules/system/tests/fixtures/update/views.view.entity_test_revlog_for_2248983.yml @@ -0,0 +1,436 @@ +uuid: 5a8b00d2-67ce-415b-9e7d-6c013bf7f6b8 +langcode: en +status: true +dependencies: + module: + - entity_test +id: entity_test_revlog +label: entity_test_revlog +module: views +description: '' +tag: '' +base_table: entity_test_revlog +base_field: id +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: none + options: { } + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: mini + options: + items_per_page: 10 + offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: ‹‹ + next: ›› + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + override: true + sticky: false + caption: '' + summary: '' + description: '' + columns: + name: name + info: + name: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + default: '-1' + empty_table: false + row: + type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true + fields: + name: + id: name + table: entity_test_revlog + field: name + relationship: none + group_type: group + admin_label: '' + label: Name + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_revlog + entity_field: name + plugin_id: field + revision_created: + id: revision_created + table: entity_test_revlog + field: revision_created + relationship: none + group_type: group + admin_label: '' + label: 'Revision create time' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: timestamp + settings: + date_format: medium + custom_date_format: '' + timezone: '' + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_revlog + entity_field: revision_created + plugin_id: field + revision_id: + id: revision_id + table: entity_test_revlog + field: revision_id + relationship: none + group_type: group + admin_label: '' + label: 'Revision ID' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: number_integer + settings: + thousand_separator: '' + prefix_suffix: true + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_revlog + entity_field: revision_id + plugin_id: field + revision_log_message: + id: revision_log_message + table: entity_test_revlog + field: revision_log_message + relationship: none + group_type: group + admin_label: '' + label: 'Revision log message' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: basic_string + settings: { } + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_revlog + entity_field: revision_log_message + plugin_id: field + revision_user: + id: revision_user + table: entity_test_revlog + field: revision_user + relationship: none + group_type: group + admin_label: '' + label: 'Revision user' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: target_id + type: entity_reference_label + settings: + link: true + group_column: target_id + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: entity_test_revlog + entity_field: revision_user + plugin_id: field + filters: { } + sorts: { } + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + tags: { } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulWithRevisionLog.php similarity index 65% copy from core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php copy to core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulWithRevisionLog.php index 4f4f4f1..e396d64 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulWithRevisionLog.php @@ -2,31 +2,32 @@ namespace Drupal\entity_test\Entity; -use Drupal\Core\Entity\RevisionableContentEntityBase; - /** * Defines the test entity class. * * @ContentEntityType( - * id = "entity_test_revlog", - * label = @Translation("Test entity - revisions log"), + * id = "entity_test_mul_revlog", + * label = @Translation("Test entity - data table, revisions log"), * handlers = { - * "access" = "Drupal\entity_test\EntityTestAccessControlHandler", + * "list_builder" = "Drupal\entity_test\EntityTestListBuilder", * "view_builder" = "Drupal\entity_test\EntityTestViewBuilder", + * "access" = "Drupal\entity_test\EntityTestAccessControlHandler", * "form" = { * "default" = "Drupal\entity_test\EntityTestForm", * "delete" = "Drupal\entity_test\EntityTestDeleteForm" * }, - * "view_builder" = "Drupal\entity_test\EntityTestViewBuilder", * "translation" = "Drupal\content_translation\ContentTranslationHandler", * "views_data" = "Drupal\views\EntityViewsData", * "route_provider" = { * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider", * }, * }, - * base_table = "entity_test_revlog", - * revision_table = "entity_test_revlog_revision", + * base_table = "entity_test_mul_revlog", + * data_table = "entity_test_mul_revlog_property_data", + * revision_table = "entity_test_mul_revlog_revision", + * revision_data_table = "entity_test_mul_revlog_property_revision", * admin_permission = "administer entity_test content", + * translatable = TRUE, * entity_keys = { * "id" = "id", * "uuid" = "uuid", @@ -35,6 +36,11 @@ * "label" = "name", * "langcode" = "langcode", * }, + * revision_metadata_keys = { + * "revision_user" = "revision_user", + * "revision_created" = "revision_created", + * "revision_log_message" = "revision_log_message" + * }, * links = { * "canonical" = "/entity_test_revlog/manage/{entity_test_revlog}", * "delete-form" = "/entity_test/delete/entity_test_revlog/{entity_test_revlog}", @@ -43,6 +49,6 @@ * } * ) */ -class EntityTestWithRevisionLog extends RevisionableContentEntityBase { +class EntityTestMulWithRevisionLog extends EntityTestWithRevisionLog { } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php index 4f4f4f1..d9b8274 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php @@ -2,7 +2,9 @@ namespace Drupal\entity_test\Entity; +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\RevisionableContentEntityBase; +use Drupal\Core\Field\BaseFieldDefinition; /** * Defines the test entity class. @@ -11,13 +13,13 @@ * id = "entity_test_revlog", * label = @Translation("Test entity - revisions log"), * handlers = { - * "access" = "Drupal\entity_test\EntityTestAccessControlHandler", + * "list_builder" = "Drupal\entity_test\EntityTestListBuilder", * "view_builder" = "Drupal\entity_test\EntityTestViewBuilder", + * "access" = "Drupal\entity_test\EntityTestAccessControlHandler", * "form" = { * "default" = "Drupal\entity_test\EntityTestForm", * "delete" = "Drupal\entity_test\EntityTestDeleteForm" * }, - * "view_builder" = "Drupal\entity_test\EntityTestViewBuilder", * "translation" = "Drupal\content_translation\ContentTranslationHandler", * "views_data" = "Drupal\views\EntityViewsData", * "route_provider" = { @@ -35,6 +37,11 @@ * "label" = "name", * "langcode" = "langcode", * }, + * revision_metadata_keys = { + * "revision_user" = "revision_user", + * "revision_created" = "revision_created", + * "revision_log_message" = "revision_log_message" + * }, * links = { * "canonical" = "/entity_test_revlog/manage/{entity_test_revlog}", * "delete-form" = "/entity_test/delete/entity_test_revlog/{entity_test_revlog}", @@ -45,4 +52,29 @@ */ class EntityTestWithRevisionLog extends RevisionableContentEntityBase { + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + $fields['name'] = BaseFieldDefinition::create('string') + ->setLabel(t('Name')) + ->setDescription(t('The name of the test entity.')) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE) + ->setSetting('max_length', 32) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'string', + 'weight' => -5, + )) + ->setDisplayOptions('form', array( + 'type' => 'string_textfield', + 'weight' => -5, + )); + + return $fields; + } + } diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index 56b4b00..153691f 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -533,6 +533,9 @@ public function testGetTableMappingRevisionable(array $entity_keys) { array('bundle', $entity_keys['bundle']), array('revision', $entity_keys['revision']), ))); + $this->entityType->expects($this->any()) + ->method('getRevisionMetadataKeys') + ->will($this->returnValue([])); $this->setUpEntityStorage(); @@ -573,13 +576,13 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) { // PHPUnit does not allow for multiple data providers. $test_cases = array( array(), - array('revision_timestamp'), - array('revision_uid'), - array('revision_log'), - array('revision_timestamp', 'revision_uid'), - array('revision_timestamp', 'revision_log'), - array('revision_uid', 'revision_log'), - array('revision_timestamp', 'revision_uid', 'revision_log'), + array('revision_created' => 'revision_timestamp'), + array('revision_user' => 'revision_uid'), + array('revision_log_message' => 'revision_log'), + array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid'), + array('revision_created' => 'revision_timestamp', 'revision_log_message' => 'revision_log'), + array('revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'), + array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'), ); foreach ($test_cases as $revision_metadata_field_names) { $this->setUp(); @@ -590,7 +593,7 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) { $revisionable_field_names = array('description', 'owner'); $field_names = array_merge($field_names, $revisionable_field_names); - $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, $revision_metadata_field_names), array('isRevisionable' => TRUE)); + $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), array('isRevisionable' => TRUE)); $this->entityType->expects($this->exactly(2)) ->method('isRevisionable') @@ -604,6 +607,10 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) { array('revision', $entity_keys['revision']), ))); + $this->entityType->expects($this->any()) + ->method('getRevisionMetadataKeys') + ->will($this->returnValue($revision_metadata_field_names)); + $this->setUpEntityStorage(); $mapping = $this->entityStorage->getTableMapping(); @@ -615,7 +622,7 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) { $expected = array_merge( array($entity_keys['id'], $entity_keys['revision']), $revisionable_field_names, - $revision_metadata_field_names + array_values($revision_metadata_field_names) ); $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision')); @@ -760,6 +767,11 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) 'uuid' => $entity_keys['uuid'], 'langcode' => 'langcode', ); + $revision_metadata_keys = array( + 'revision_created' => 'revision_timestamp', + 'revision_user' => 'revision_uid', + 'revision_log_message' => 'revision_log' + ); $this->entityType->expects($this->atLeastOnce()) ->method('isRevisionable') @@ -779,6 +791,9 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) array('revision', $entity_keys['revision']), array('langcode', $entity_keys['langcode']), ))); + $this->entityType->expects($this->any()) + ->method('getRevisionMetadataKeys') + ->will($this->returnValue($revision_metadata_keys)); $this->setUpEntityStorage(); @@ -809,6 +824,7 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) $entity_keys['revision'], $entity_keys['langcode'], ))); + $expected = array_merge($expected, array_values($revision_metadata_keys)); $actual = $mapping->getFieldNames('entity_test_revision'); $this->assertEquals($expected, $actual); // The UUID is not stored on the data table. @@ -864,13 +880,13 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent // PHPUnit does not allow for multiple data providers. $test_cases = array( array(), - array('revision_timestamp'), - array('revision_uid'), - array('revision_log'), - array('revision_timestamp', 'revision_uid'), - array('revision_timestamp', 'revision_log'), - array('revision_uid', 'revision_log'), - array('revision_timestamp', 'revision_uid', 'revision_log'), + array('revision_created' => 'revision_timestamp'), + array('revision_user' => 'revision_uid'), + array('revision_log_message' => 'revision_log'), + array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid'), + array('revision_created' => 'revision_timestamp', 'revision_log_message' => 'revision_log'), + array('revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'), + array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'), ); foreach ($test_cases as $revision_metadata_field_names) { $this->setUp(); @@ -880,7 +896,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent $this->fieldDefinitions = $this->mockFieldDefinitions($field_names); $revisionable_field_names = array('description', 'owner'); - $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, $revision_metadata_field_names), array('isRevisionable' => TRUE)); + $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), array('isRevisionable' => TRUE)); $this->entityType->expects($this->atLeastOnce()) ->method('isRevisionable') @@ -900,6 +916,9 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent array('revision', $entity_keys['revision']), array('langcode', $entity_keys['langcode']), ))); + $this->entityType->expects($this->any()) + ->method('getRevisionMetadataKeys') + ->will($this->returnValue($revision_metadata_field_names)); $this->setUpEntityStorage(); @@ -937,7 +956,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent $entity_keys['id'], $entity_keys['revision'], $entity_keys['langcode'], - )), $revision_metadata_field_names); + )), array_values($revision_metadata_field_names)); $actual = $mapping->getFieldNames('entity_test_revision'); $this->assertEquals($expected, $actual); // The UUID is not stored on the data table.