diff --git a/modules/group_recurring_events_series/group_recurring_events_series.module b/modules/group_recurring_events_series/group_recurring_events_series.module
index e50e979542f2eb0e7f407193e85854a2c5708123..d98db0fc4bfd4ff77f4b964a7f05738129cb8cae 100644
--- a/modules/group_recurring_events_series/group_recurring_events_series.module
+++ b/modules/group_recurring_events_series/group_recurring_events_series.module
@@ -42,3 +42,12 @@ function group_recurring_events_series_entity_operation(EntityInterface $entity)
 
   return $operations;
 }
+
+/*
+ * hook_entity_type_build
+ */
+function group_recurring_events_series_entity_type_build(array &$entity_types) {
+    if(isset($entity_types['eventinstance'])) {
+        $entity_types['eventinstance']->setHandlerClass('access', 'Drupal\group_recurring_events_series\Access\GroupEventInstanceHandler');
+    }
+}
diff --git a/modules/group_recurring_events_series/src/Access/.gitkeep b/modules/group_recurring_events_series/src/Access/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/modules/group_recurring_events_series/src/Access/GroupEventInstanceHandler.php b/modules/group_recurring_events_series/src/Access/GroupEventInstanceHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..2922bd15870ccd343d8c21350fc5174d31272fd7
--- /dev/null
+++ b/modules/group_recurring_events_series/src/Access/GroupEventInstanceHandler.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\group_recurring_events_series\Access;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Session\AccountInterface;
+
+use Drupal\recurring_events\Entity\EventSeries;
+use Drupal\recurring_events\EventInstanceAccessControlHandler;
+
+class GroupEventInstanceHandler extends EventInstanceAccessControlHandler {
+    protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+        $result = parent::checkAccess($entity, $operation, $account);
+        if(!$result->isAllowed()) {
+            // Need GroupContentAccessControlHandler not EventSeriesAccessControlHandler
+            $manager = \Drupal::service('plugin.manager.group_content_enabler');
+            $type = 'group_recurring_events_series:' . $entity->getType();
+            if($manager->hasHandler($type, 'access')) {
+              $handler = $manager->getAccessControlHandler($type);
+              $result = $handler->entityAccess($entity->getEventSeries(), $operation, $account, TRUE);
+            }
+        }
+        return $result;
+    }
+}
diff --git a/modules/recurring_events_registration/modules/recurring_events_reminders/recurring_events_reminders.module b/modules/recurring_events_registration/modules/recurring_events_reminders/recurring_events_reminders.module
index 7982d2530be71ef4b4d691a7e9e2fb50440511fa..3e5b246f7d0b7b0a4df8ae5123af74b247c54c9d 100644
--- a/modules/recurring_events_registration/modules/recurring_events_reminders/recurring_events_reminders.module
+++ b/modules/recurring_events_registration/modules/recurring_events_reminders/recurring_events_reminders.module
@@ -137,6 +137,7 @@ function recurring_events_reminders_cron() {
     ->condition('reminder_date', NULL, 'IS NOT NULL')
     ->condition('reminder_sent', NULL, 'IS NULL')
     ->condition('reminder_date', time(), '<=')
+    ->accessCheck(FALSE)
     ->execute();
 
   if (!empty($event_instances)) {
@@ -153,6 +154,7 @@ function recurring_events_reminders_cron() {
       $registration_creation_service->setEventInstance($instance);
 
       $registrants = $registration_creation_service->retrieveRegisteredParties();
+
       if (empty($registrants)) {
         return;
       }
@@ -161,7 +163,8 @@ function recurring_events_reminders_cron() {
 
       // Send an email to all registrants.
       foreach ($registrants as $registrant) {
-        recurring_events_registration_send_notification($key, $registrant);
+        // Add each notification to be sent to the queue.
+        \Drupal::service('recurring_events_registration.notification_service')->addEmailNotificationToQueue($key, $registrant);
       }
     }
   }
diff --git a/modules/recurring_events_registration/recurring_events_registration.install b/modules/recurring_events_registration/recurring_events_registration.install
index e950bff77b0fe3dd7fff605c83f16cc4888300bf..da89866f9738eb5040d2c96e2f7216968be313e5 100644
--- a/modules/recurring_events_registration/recurring_events_registration.install
+++ b/modules/recurring_events_registration/recurring_events_registration.install
@@ -116,6 +116,7 @@ function recurring_events_registration_update_8003() {
 
   $registration_values = $database->select($table_name)
     ->fields($table_name, [$id_key, $revision_key] + $reg_fields)
+    ->accessCheck(FALSE)
     ->execute()
     ->fetchAll();
 
@@ -123,6 +124,7 @@ function recurring_events_registration_update_8003() {
 
   $database->update($table_name)
     ->fields($update_fields)
+    ->accessCheck(FALSE)
     ->execute();
 
   $field_storage_definition = $definition_manager->getFieldStorageDefinition('event_registration', 'eventseries');
@@ -166,11 +168,13 @@ function recurring_events_registration_update_8003() {
       $database->update($table_name)
         ->fields($values_to_restore)
         ->condition($id_key, $value->{$id_key})
+        ->accessCheck(FALSE)
         ->execute();
 
       $database->update($revision_table_name)
         ->fields($values_to_restore)
         ->condition($id_key, $value->{$id_key})
+        ->accessCheck(FALSE)
         ->execute();
     }
   }
@@ -235,6 +239,7 @@ function recurring_events_registration_update_8006(&$sandbox) {
     $registrants = \Drupal::entityTypeManager()
       ->getStorage('registrant')
       ->getQuery()
+      ->accessCheck(FALSE)
       ->execute();
 
     $sandbox['registrants'] = $registrants;
@@ -302,6 +307,7 @@ function recurring_events_registration_update_8007() {
   $registration_values = $database->select($table_name)
     ->fields($table_name, [$id_key, $revision_key] + $reg_fields)
     ->condition('event_registration__registration', 1)
+    ->accessCheck(FALSE)
     ->execute()
     ->fetchAll();
 
@@ -310,6 +316,7 @@ function recurring_events_registration_update_8007() {
   $update_fields = array_fill_keys($reg_fields, NULL);
   $database->update($table_name)
     ->fields($update_fields)
+    ->accessCheck(FALSE)
     ->execute();
 
   // Remove the existing registration field.
@@ -349,12 +356,14 @@ function recurring_events_registration_update_8007() {
       $database->update($table_name)
         ->fields($values_to_restore)
         ->condition($id_key, $value->{$id_key})
+        ->accessCheck(FALSE)
         ->execute();
 
       $database->update($revision_table_name)
         ->fields($values_to_restore)
         ->condition($id_key, $value->{$id_key})
         ->condition($revision_key, $value->{$revision_key})
+        ->accessCheck(FALSE)
         ->execute();
     }
   }
@@ -443,6 +452,7 @@ function recurring_events_registration_update_8009() {
   $registration_values = $database->select($table_name)
     ->fields($table_name, [$id_key, $revision_key] + $reg_fields)
     ->condition('event_registration__registration', 1)
+    ->accessCheck(FALSE)
     ->execute()
     ->fetchAll();
 
@@ -451,6 +461,7 @@ function recurring_events_registration_update_8009() {
   $update_fields = array_fill_keys($reg_fields, NULL);
   $database->update($table_name)
     ->fields($update_fields)
+    ->accessCheck(FALSE)
     ->execute();
 
   // Remove the existing registration field.
@@ -490,12 +501,14 @@ function recurring_events_registration_update_8009() {
       $database->update($table_name)
         ->fields($values_to_restore)
         ->condition($id_key, $value->{$id_key})
+        ->accessCheck(FALSE)
         ->execute();
 
       $database->update($revision_table_name)
         ->fields($values_to_restore)
         ->condition($id_key, $value->{$id_key})
         ->condition($revision_key, $value->{$revision_key})
+        ->accessCheck(FALSE)
         ->execute();
     }
   }
diff --git a/modules/recurring_events_registration/recurring_events_registration.module b/modules/recurring_events_registration/recurring_events_registration.module
index e5959ad625bafb27185560c9b9df72294de82dfc..3ba086a6d662b62cc8eab22fa41025657a8bcc35 100644
--- a/modules/recurring_events_registration/recurring_events_registration.module
+++ b/modules/recurring_events_registration/recurring_events_registration.module
@@ -143,26 +143,47 @@ function template_preprocess_registrant(array &$variables) {
  * Implements hook_mail().
  */
 function recurring_events_registration_mail($key, &$message, $params) {
-  /** @var \Drupal\recurring_events_registration\NotificationService */
-  $service = \Drupal::service('recurring_events_registration.notification_service');
-  $service->setKey($key)->setEntity($params['registrant']);
-
-  if ($service->isEnabled()) {
-    if (!empty($params['subject'])) {
-      $service->setSubject($params['subject']);
-    }
-    if (!empty($params['body'])) {
-      $service->setMessage($params['body']);
-    }
-    if (!empty($params['from'])) {
-      $service->setFrom($params['from']);
-    }
+  // Only if a value for 'subject', 'body' or 'from' has not been received in
+  // the `$params`, then use the notification service which is able to get
+  // those values from the `$key` and the `$registrant`.
+  // Some pieces of code can call the `mail()` function with already proccessed
+  // values for 'subject', 'body' and 'from', meaning that it is not necessary
+  // that those values are calculated here by the notification service.
+  // @see \Drupal\recurring_events_registration\Plugin\QueueWorker\EmailNotificationsQueueWorker
+  if ((empty($params['subject']) || empty($params['body']) || empty($params['from']))
+    && !empty($params['registrant'])
+    && $params['registrant'] instanceof Registrant) {
+    /** @var \Drupal\recurring_events_registration\NotificationService $service */
+    $service = \Drupal::service('recurring_events_registration.notification_service');
+    $service->setKey($key)->setEntity($params['registrant']);
+  }
 
+  // If other pieces of code that have called the `mail()` function have
+  // already defined those params, give them precedence. Else, let the
+  // notification service to calculate those values.
+  // Set the `$message['from']`.
+  if (!empty($params['from'])) {
+    $message['from'] = $params['from'];
+  }
+  elseif (isset($service)) {
     $message['from'] = $service->getFrom();
+  }
+
+  // Set the `$message['subject']`.
+  if (!empty($params['subject'])) {
+    $message['subject'] = $params['subject'];
+  }
+  elseif (isset($service)) {
     $message['subject'] = $service->getSubject();
+  }
+
+  // Set the `$message['body']`.
+  if (!empty($params['body'])) {
+    $message['body'][] = $params['body'];
+  }
+  elseif (isset($service)) {
     $message['body'][] = $service->getMessage();
   }
-  return FALSE;
 }
 
 /**
@@ -218,7 +239,8 @@ function recurring_events_registration_recurring_events_save_pre_instances_delet
 
   // Send an email to all registrants.
   foreach ($registrants as $registrant) {
-    recurring_events_registration_send_notification($key, $registrant);
+    // Add each notification to be sent to the queue.
+    \Drupal::service('recurring_events_registration.notification_service')->addEmailNotificationToQueue($key, $registrant);
     $registrant->delete();
   }
 }
@@ -250,7 +272,8 @@ function recurring_events_registration_entity_update(EntityInterface $entity) {
 
       // Send an email to all registrants.
       foreach ($registrants as $registrant) {
-        recurring_events_registration_send_notification($key, $registrant);
+        // Add each notification to be sent to the queue.
+        \Drupal::service('recurring_events_registration.notification_service')->addEmailNotificationToQueue($key, $registrant);
       }
     }
   }
@@ -274,7 +297,8 @@ function recurring_events_registration_recurring_events_pre_delete_instance(Even
   foreach ($registrants as $registrant) {
     // Only send email notifications if this event instance is in the future.
     if ($registration_creation_service->eventInstanceIsInFuture()) {
-      recurring_events_registration_send_notification($key, $registrant);
+      // Add each notification to be sent to the queue.
+      \Drupal::service('recurring_events_registration.notification_service')->addEmailNotificationToQueue($key, $registrant);
     }
     $registrant->delete();
   }
@@ -296,7 +320,8 @@ function recurring_events_registration_recurring_events_pre_delete_instances(Eve
 
     // Send an email to the future registrants.
     foreach ($future_registrants as $registrant) {
-      recurring_events_registration_send_notification($key, $registrant);
+      // Add each notification to be sent to the queue.
+      \Drupal::service('recurring_events_registration.notification_service')->addEmailNotificationToQueue($key, $registrant);
     }
   }
 
@@ -428,6 +453,7 @@ function recurring_events_registration_registrant_insert(EntityInterface $entity
     ->condition('sourceEntityType', ['eventseries', 'eventinstance'], 'IN')
     ->condition('destinationEntityType', $entity_type)
     ->condition('destinationEntityBundle', $bundle)
+    ->accessCheck(FALSE)
     ->execute();
 
   if (!empty($inherited_field_ids)) {
diff --git a/modules/recurring_events_registration/recurring_events_registration.services.yml b/modules/recurring_events_registration/recurring_events_registration.services.yml
index d5dd368e78f204c3395da7ca221056e55f0b987d..7572820796e9fca02895e7558d909fdb224cf578 100644
--- a/modules/recurring_events_registration/recurring_events_registration.services.yml
+++ b/modules/recurring_events_registration/recurring_events_registration.services.yml
@@ -4,7 +4,7 @@ services:
     arguments: ['@string_translation', '@database', '@logger.factory', '@messenger', '@entity_type.manager', '@module_handler', '@token']
   recurring_events_registration.notification_service:
     class: Drupal\recurring_events_registration\NotificationService
-    arguments: ['@string_translation', '@config.factory', '@logger.factory', '@messenger', '@token', '@module_handler', '@recurring_events_registration.creation_service']
+    arguments: ['@string_translation', '@config.factory', '@logger.factory', '@messenger', '@token', '@module_handler', '@recurring_events_registration.creation_service', '@queue']
   recurring_events_registration.access_handler:
     class: Drupal\recurring_events_registration\AccessHandler
     arguments: ['@string_translation', '@recurring_events_registration.creation_service', '@current_route_match', '@entity_type.manager']
@@ -12,4 +12,4 @@ services:
     class: Drupal\recurring_events_registration\Routing\RouteSubscriber
     arguments: [ '@config.factory' ]
     tags:
-      - { name: event_subscriber }
+      - { name: event_subscriber }
\ No newline at end of file
diff --git a/modules/recurring_events_registration/recurring_events_registration.tokens.inc b/modules/recurring_events_registration/recurring_events_registration.tokens.inc
index 83a1150bedf734d51fd02f884e1e5769d70ff403..569ead78a1830c89831d570776e9f11186b53b50 100644
--- a/modules/recurring_events_registration/recurring_events_registration.tokens.inc
+++ b/modules/recurring_events_registration/recurring_events_registration.tokens.inc
@@ -50,6 +50,17 @@ function recurring_events_registration_token_info() {
     'description' => t('The URL to delete a registrant.'),
   ];
 
+  $registrant['eventinstance'] = [
+    'name' => t('Registrant event instance'),
+    'description' => t('The eventinstance associated with a registrant'),
+    'type' => 'eventinstance',
+  ];
+  $registrant['eventseries'] = [
+    'name' => t('Registrant event series'),
+    'description' => t('The eventseries associated with a registrant'),
+    'type' => 'eventseries',
+  ];
+
   return [
     'types' => [
       'eventinstance' => $eventinstance_type,
@@ -67,6 +78,7 @@ function recurring_events_registration_token_info() {
  * Implements hook_tokens().
  */
 function recurring_events_registration_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
+  $token_service = \Drupal::token();
   $replacements = [];
   if ($type == 'eventinstance' && !empty($data['eventinstance'])) {
     $event_instance = $data['eventinstance'];
@@ -108,8 +120,30 @@ function recurring_events_registration_tokens($type, $tokens, array $data, array
           }
           $replacements[$original] = $url;
           break;
+
+        case 'eventinstance':
+          $instance = $registrant->getEventInstance();
+          $bubbleable_metadata->addCacheableDependency($instance);
+          $replacements[$original] = $instance->label();
+          break;
+
+        case 'eventseries':
+          $series = $registrant->getEventSeries();
+          $bubbleable_metadata->addCacheableDependency($series);
+          $replacements[$original] = $series->label();
+          break;
       }
     }
+
+    if ($instance_tokens = $token_service->findWithPrefix($tokens, 'eventinstance')) {
+      $instance = $registrant->getEventInstance();
+      $replacements += $token_service->generate('eventinstance', $instance_tokens, ['eventinstance' => $instance], $options, $bubbleable_metadata);
+    }
+
+    if ($series_tokens = $token_service->findWithPrefix($tokens, 'eventseries')) {
+      $series = $registrant->getEventSeries();
+      $replacements += $token_service->generate('eventseries', $series_tokens, ['eventseries' => $series], $options, $bubbleable_metadata);
+    }
   }
   return $replacements;
 }
diff --git a/modules/recurring_events_registration/src/Form/OrphanedEventRegistrantsForm.php b/modules/recurring_events_registration/src/Form/OrphanedEventRegistrantsForm.php
index b89886b5fd764841dd554deaf480ad2340d5dac0..dbeb3fff06d0e3c1511b4966b5afae8d37ee7671 100644
--- a/modules/recurring_events_registration/src/Form/OrphanedEventRegistrantsForm.php
+++ b/modules/recurring_events_registration/src/Form/OrphanedEventRegistrantsForm.php
@@ -160,6 +160,7 @@ class OrphanedEventRegistrantsForm extends FormBase {
       ->condition('es.id', NULL, 'IS NULL')
       ->condition('ei.id', NULL, 'IS NULL');
     $registrants = $query->condition($or_group)
+      ->accessCheck(FALSE)
       ->execute()
       ->fetchCol();
 
diff --git a/modules/recurring_events_registration/src/NotificationService.php b/modules/recurring_events_registration/src/NotificationService.php
index ac303818083aa06c74cddff0583fe862969c8782..24373e643fe88daf0cf525cd88b8283c821bd08f 100644
--- a/modules/recurring_events_registration/src/NotificationService.php
+++ b/modules/recurring_events_registration/src/NotificationService.php
@@ -10,6 +10,7 @@ use Drupal\Core\Utility\Token;
 use Drupal\recurring_events_registration\Entity\RegistrantInterface;
 use Drupal\Core\Extension\ModuleHandler;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Queue\QueueFactory;
 
 /**
  * Provides a service with helper functions to facilitate notifications.
@@ -114,6 +115,13 @@ class NotificationService {
    */
   protected $custom = FALSE;
 
+  /**
+   * The update fetch queue.
+   *
+   * @var \Drupal\Core\Queue\QueueFactory
+   */
+  protected $queueFactory;
+
   /**
    * Class constructor.
    *
@@ -131,8 +139,10 @@ class NotificationService {
    *   The module handler service.
    * @param \Drupal\recurring_events_registration\RegistrationCreationService $creation_service
    *   The registration creation service.
+   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
+   *   The queue factory.
    */
-  public function __construct(TranslationInterface $translation, ConfigFactory $config_factory, LoggerChannelFactoryInterface $logger, Messenger $messenger, Token $token, ModuleHandler $module_handler, RegistrationCreationService $creation_service) {
+  public function __construct(TranslationInterface $translation, ConfigFactory $config_factory, LoggerChannelFactoryInterface $logger, Messenger $messenger, Token $token, ModuleHandler $module_handler, RegistrationCreationService $creation_service, QueueFactory $queue_factory) {
     $this->translation = $translation;
     $this->configFactory = $config_factory;
     $this->loggerFactory = $logger->get('recurring_events_registration');
@@ -141,6 +151,7 @@ class NotificationService {
     $this->moduleHandler = $module_handler;
     $this->creationService = $creation_service;
     $this->configName = 'recurring_events_registration.registrant.config';
+    $this->queueFactory = $queue_factory;
   }
 
   /**
@@ -154,7 +165,8 @@ class NotificationService {
       $container->get('messenger'),
       $container->get('token'),
       $container->get('module_handler'),
-      $container->get('recurring_events_registration.creation_service')
+      $container->get('recurring_events_registration.creation_service'),
+      $container->get('queue')
     );
   }
 
@@ -246,7 +258,7 @@ class NotificationService {
    * @return string|bool
    *   The key, or FALSE if not set.
    */
-  protected function getKey() {
+  public function getKey() {
     if (empty($this->key)) {
       $this->messenger->addError($this->translation->translate('No key defined for @module notifications.', [
         '@module' => 'recurring_events_registration',
@@ -352,11 +364,7 @@ class NotificationService {
   public function getSubject($parse_tokens = TRUE) {
     $key = $this->getKey();
     if ($key) {
-      $subject = $this->subject;
-      if (empty($subject)) {
-        $subject = $this->getConfigValue('subject');
-        $this->setSubject($subject);
-      }
+      $subject = $this->getConfigValue('subject');
 
       if (empty($subject)) {
         $this->messenger->addError($this->translation->translate('No default subject configured for @key emails in @config_name.', [
@@ -386,11 +394,7 @@ class NotificationService {
   public function getMessage($parse_tokens = TRUE) {
     $key = $this->getKey();
     if ($key) {
-      $message = $this->message;
-      if (empty($message)) {
-        $message = $this->getConfigValue('body');
-        $this->setMessage($message);
-      }
+      $message = $this->getConfigValue('body');
 
       if (empty($message)) {
         $this->messenger->addError($this->translation->translate('No default body configured for @key emails in @config_name.', [
@@ -450,4 +454,56 @@ class NotificationService {
     return $this->creationService->getAvailableTokens($relevant_tokens);
   }
 
+  /**
+   * Adds an email notification to be sent later by the Queue Worker.
+   */
+  public function addEmailNotificationToQueue($key, RegistrantInterface $registrant) {
+    $config = $this->configFactory->get('recurring_events_registration.registrant.config');
+    $send_email = $config->get('email_notifications');
+    $send_email_key = $config->get('notifications' . '.' . $key . '.enabled');
+
+    // Modify $send_email if necessary.
+    if ($registrant instanceof RegistrantInterface) {
+      $this->moduleHandler->alter('recurring_events_registration_send_notification', $send_email, $registrant);
+    }
+
+    if ($send_email && $send_email_key) {
+      // We need to get the parsed email subject and message (after token
+      // replacement) to add them to the `$item` that will be queued. We are
+      // not adding the `$registrant` to the `$item`, since in the queue worker
+      // we cannot rely on operations over the `$registrant` or its parent
+      // instance or series, since at that point those entities might have been
+      // deleted. There are some operations and notification types that require
+      // the `$registrant`to be deleted, for example: the notifications
+      // corresponding to the keys 'series_modification_notification' and
+      // 'instance_deletion_notification'.
+      // @see recurring_events_registration_recurring_events_save_pre_instances_deletion()
+      // @see recurring_events_registration_recurring_events_pre_delete_instance()
+      $this->setKey($key)->setEntity($registrant);
+      $subject = $this->getSubject();
+      $message = $this->getMessage();
+      $from = $this->getFrom();
+
+      // Create the item to be added to the queue.
+      $item = new \stdClass();
+      $item->key = $key;
+      $item->to = $registrant->email->value;
+
+      $params = [
+        'subject' => $subject,
+        'body' => $message,
+        'from' => $from,
+      ];
+      // Allow modules to add data to the `$params`. They can get the data from
+      // `$registrant`. Those `$params` are used later as the
+      // `$message['params']` in mail hooks.
+      $this->moduleHandler->alter('recurring_events_registration_message_params', $params, $registrant);
+      $item->params = $params;
+
+      // Add the item to the queue.
+      $queue = $this->queueFactory->get('recurring_events_registration_email_notifications_queue_worker');
+      $queue->createItem($item);
+    }
+  }
+
 }
diff --git a/modules/recurring_events_registration/src/Plugin/QueueWorker/EmailNotificationsQueueWorker.php b/modules/recurring_events_registration/src/Plugin/QueueWorker/EmailNotificationsQueueWorker.php
new file mode 100644
index 0000000000000000000000000000000000000000..e2ab74a6f8868641215c91959cfb67aa5100498e
--- /dev/null
+++ b/modules/recurring_events_registration/src/Plugin/QueueWorker/EmailNotificationsQueueWorker.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\recurring_events_registration\Plugin\QueueWorker;
+
+use Drupal\Core\Queue\QueueWorkerBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Mail\MailManagerInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+
+/**
+ * Defines 'recurring_events_registration_email_notifications_queue_worker' queue worker.
+ *
+ * @QueueWorker(
+ *   id = "recurring_events_registration_email_notifications_queue_worker",
+ *   title = @Translation("Email Notifications Queue Worker"),
+ *   cron = {"time" = 30}
+ * )
+ */
+class EmailNotificationsQueueWorker extends QueueWorkerBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The mail manager.
+   *
+   * @var \Drupal\Core\Mail\MailManagerInterface
+   */
+  protected $mailManager;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * Constructs a new LocaleTranslation object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param array $plugin_definition
+   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
+   *   The mail manager.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->mailManager = $mail_manager;
+    $this->languageManager = $language_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('plugin.manager.mail'),
+      $container->get('language_manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processItem($item) {
+    if (empty($item->key) || empty($item->to) || (empty($item->params['subject']) && empty($item->params['body']))) {
+      return;
+    }
+    // All this worker has to do is to send the email.
+    // The Subject and Body already have to be included in the `$item`.
+    $this->mailManager->mail('recurring_events_registration', $item->key, $item->to, $this->languageManager->getDefaultLanguage()->getId(), $item->params);
+  }
+
+}
diff --git a/modules/recurring_events_registration/src/RegistrantListBuilder.php b/modules/recurring_events_registration/src/RegistrantListBuilder.php
index 77dd3891c5e4f7217b518fd4f722794a67566d20..e4957762279a612a1dd8a7090edd600a9bbe1f93 100644
--- a/modules/recurring_events_registration/src/RegistrantListBuilder.php
+++ b/modules/recurring_events_registration/src/RegistrantListBuilder.php
@@ -177,7 +177,7 @@ class RegistrantListBuilder extends EntityListBuilder {
     if ($this->limit) {
       $query->pager($this->limit);
     }
-    return $query->execute();
+    return $query->accessCheck(FALSE)->execute();
   }
 
 }
diff --git a/recurring_events.install b/recurring_events.install
index 2ce92d1eed4d96ad3209e33c8c515394ab88a278..59d86f06f533392e6c76f16328d9c83056359dc9 100644
--- a/recurring_events.install
+++ b/recurring_events.install
@@ -4,7 +4,7 @@
  * @file
  * Installation and update functionality for the recurring_events module.
  */
-
+use Drupal\views\Views;
 use Drupal\Core\Config\Entity\ConfigEntityType;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Field\BaseFieldDefinition;
@@ -307,6 +307,7 @@ function recurring_events_update_8007() {
   $inherited_fields = $database->select('config', 'c')
     ->fields('c', ['name'])
     ->condition('name', 'recurring_events.field_inheritance.%', 'LIKE')
+    ->accessCheck(FALSE)
     ->execute()
     ->fetchCol();
   $bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo('eventseries');
@@ -339,6 +340,7 @@ function recurring_events_update_8007() {
     }
     $database->delete('config')
       ->condition('name', 'recurring_events.field_inheritance.%', 'LIKE')
+      ->accessCheck(FALSE)
       ->execute();
   }
 
@@ -378,7 +380,7 @@ function recurring_events_update_8008() {
 function recurring_events_update_8009(&$sandbox) {
   // This is a multipass update.
   if (empty($sandbox['events'])) {
-    $events = \Drupal::entityQuery('eventinstance')->execute();
+    $events = \Drupal::entityQuery('eventinstance')->accessCheck(FALSE)->execute();
     $sandbox['events'] = $events;
     $sandbox['max'] = count($events);
     $sandbox['limit'] = 50;
@@ -402,6 +404,7 @@ function recurring_events_update_8009(&$sandbox) {
           ->condition('sourceEntityType', 'eventseries')
           ->condition('destinationEntityType', $entity_type)
           ->condition('destinationEntityBundle', $bundle)
+          ->accessCheck(FALSE)
           ->execute();
 
         if (!empty($inherited_field_ids)) {
@@ -509,6 +512,7 @@ function recurring_events_update_8011() {
       $database->update($table)
         ->fields([$field => ''])
         ->isNull($field)
+        ->accessCheck(FALSE)
         ->execute();
     }
   }
@@ -526,6 +530,7 @@ function recurring_events_update_8011() {
 
     $database_values = $database->select($table_name)
       ->fields($table_name, array_merge([$id_key, $revision_key], $field_fields))
+      ->accessCheck(FALSE)
       ->execute()
       ->fetchAll();
 
@@ -576,12 +581,14 @@ function recurring_events_update_8011() {
         $database->update($table_name)
           ->fields($values_to_restore)
           ->condition($id_key, $value->{$id_key})
+          ->accessCheck(FALSE)
           ->execute();
 
         $database->update($revision_table_name)
           ->fields($values_to_restore)
           ->condition($id_key, $value->{$id_key})
           ->condition($revision_key, $value->{$revision_key})
+          ->accessCheck(FALSE)
           ->execute();
       }
     }
@@ -604,7 +611,7 @@ function recurring_events_update_8013() {
 
   $config_factory = \Drupal::configFactory();
 
-  if (!$views = \Drupal\views\Views::getAllViews()) {
+  if (!$views = Views::getAllViews()) {
     return [];
   }
 
diff --git a/recurring_events.module b/recurring_events.module
index 1324508a3c664745fc694d57a839f200eb743839..056d83bce41645c1a74c206b33f513d7613500e5 100644
--- a/recurring_events.module
+++ b/recurring_events.module
@@ -176,6 +176,33 @@ function recurring_events_eventseries_insert(EntityInterface $entity) {
   }
 }
 
+/**
+ * Implements hook_ENTITY_TYPE_translation_insert().
+ */
+function recurring_events_eventseries_translation_insert(EntityInterface $translation) {
+  if (\Drupal::isConfigSyncing()) {
+    return;
+  }
+  $creation_service = \Drupal::service('recurring_events.event_creation_service');
+  $creation_service->createInstances($translation);
+
+  $instances = $translation->event_instances->referencedEntities();
+  if (!empty($instances)) {
+    foreach ($instances as $instance) {
+      if ($instance->hasTranslation($translation->language()->getId())) {
+        $instance = $instance->getTranslation($translation->language()->getId());
+      }
+      $instance->set('eventseries_id', $translation->id());
+      $instance->setNewRevision(FALSE);
+
+      $creation_service->configureDefaultInheritances($instance, $translation->id());
+      $creation_service->updateInstanceStatus($instance, $translation);
+
+      $instance->save();
+    }
+  }
+}
+
 /**
  * Implements hook_ENTITY_TYPE_insert().
  */
@@ -381,7 +408,7 @@ function recurring_events_eventseries_type_delete(EntityInterface $entity) {
     ->condition($and_destination)
     ->condition($and_source);
   $query->condition($or);
-  $inherited_field_ids = $query->execute();
+  $inherited_field_ids = $query->accessCheck(FALSE)->execute();
 
   if (!empty($inherited_field_ids)) {
     $inherited_fields = \Drupal::entityTypeManager()->getStorage('field_inheritance')->loadMultiple($inherited_field_ids);
@@ -403,7 +430,7 @@ function recurring_events_eventseries_type_delete(EntityInterface $entity) {
       ->condition($and_destination)
       ->condition($and_source);
     $query->condition($or);
-    $inherited_field_ids = $query->execute();
+    $inherited_field_ids = $query->accessCheck(FALSE)->execute();
 
     if (!empty($inherited_field_ids)) {
       $inherited_fields = \Drupal::entityTypeManager()->getStorage('field_inheritance')->loadMultiple($inherited_field_ids);
@@ -428,7 +455,7 @@ function recurring_events_eventseries_type_delete(EntityInterface $entity) {
         ->condition($and_destination)
         ->condition($and_source);
       $query->condition($or);
-      $inherited_field_ids = $query->execute();
+      $inherited_field_ids = $query->accessCheck(FALSE)->execute();
 
       if (!empty($inherited_field_ids)) {
         $inherited_fields = \Drupal::entityTypeManager()->getStorage('field_inheritance')->loadMultiple($inherited_field_ids);
@@ -727,6 +754,7 @@ function recurring_events_entity_base_field_info_alter(&$fields, EntityTypeInter
         'weight' => 0,
       ])
       ->setDisplayConfigurable('form', FALSE)
+      ->setTranslatable(TRUE)
       ->setClass(EventInstances::class);
   }
 }
diff --git a/src/Entity/EventInstance.php b/src/Entity/EventInstance.php
index 095cb3f482f1f7df601903133a93ba739bdae306..d6655cea834f28f6e9f42fb270e2c7d3cb08799a 100644
--- a/src/Entity/EventInstance.php
+++ b/src/Entity/EventInstance.php
@@ -6,8 +6,9 @@ use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Entity\EditorialContentEntityBase;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\recurring_events\EventInterface;
-use Drupal\user\UserInterface;
+use Drupal\recurring_events\EventUserTrait;
 
 /**
  * Defines the Event Instance entity.
@@ -105,6 +106,7 @@ use Drupal\user\UserInterface;
  *     "uuid" = "uuid",
  *     "label" = "title",
  *     "bundle" = "type",
+ *     "uid" = "uid",
  *   },
  *   revision_metadata_keys = {
  *     "revision_user" = "revision_uid",
@@ -154,6 +156,8 @@ use Drupal\user\UserInterface;
  */
 class EventInstance extends EditorialContentEntityBase implements EventInterface {
 
+  use EventUserTrait;
+
   /**
    * {@inheritdoc}
    *
@@ -234,36 +238,6 @@ class EventInstance extends EditorialContentEntityBase implements EventInterface
     return $this;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwner() {
-    return $this->get('uid')->entity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwnerId() {
-    return $this->get('uid')->target_id;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwnerId($uid) {
-    $this->set('uid', $uid);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwner(UserInterface $account) {
-    $this->setOwnerId($account->id());
-    return $this;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -291,6 +265,7 @@ class EventInstance extends EditorialContentEntityBase implements EventInterface
    */
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
 
     // Standard field, used as unique if primary index.
     $fields['id'] = BaseFieldDefinition::create('integer')
@@ -298,25 +273,6 @@ class EventInstance extends EditorialContentEntityBase implements EventInterface
       ->setDescription(t('The ID of the event entity.'))
       ->setReadOnly(TRUE);
 
-    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
-      ->setLabel(t('Authored by'))
-      ->setDescription(t('The username of the content author.'))
-      ->setRevisionable(TRUE)
-      ->setSetting('target_type', 'user')
-      ->setDefaultValueCallback('Drupal\recurring_events\Entity\EventInstance::getCurrentUserId')
-      ->setTranslatable(TRUE)
-      ->setDisplayOptions('form', [
-        'type' => 'entity_reference_autocomplete',
-        'weight' => 5,
-        'settings' => [
-          'match_operator' => 'CONTAINS',
-          'size' => '60',
-          'placeholder' => '',
-          'match_limit' => 10,
-        ],
-      ])
-      ->setDisplayConfigurable('form', TRUE);
-
     // Standard field, unique outside of the scope of the current project.
     $fields['uuid'] = BaseFieldDefinition::create('uuid')
       ->setLabel(t('UUID'))
@@ -368,7 +324,8 @@ class EventInstance extends EditorialContentEntityBase implements EventInterface
     $fields['eventseries_id'] = BaseFieldDefinition::create('entity_reference')
       ->setLabel(t('Event Series ID'))
       ->setDescription(t('The ID of the event series entity.'))
-      ->setSetting('target_type', 'eventseries');
+      ->setSetting('target_type', 'eventseries')
+      ->setTranslatable(TRUE);
 
     $fields['langcode'] = BaseFieldDefinition::create('language')
       ->setLabel(t('Language code'))
@@ -410,7 +367,11 @@ class EventInstance extends EditorialContentEntityBase implements EventInterface
    *   The event series.
    */
   public function getEventSeries() {
-    return $this->get('eventseries_id')->entity;
+    $entity = $this->get('eventseries_id')->entity;
+    if ($entity->hasTranslation($this->language()->getId())) {
+      return $entity->getTranslation($this->language()->getId());
+    }
+    return $entity;
   }
 
 }
diff --git a/src/Entity/EventSeries.php b/src/Entity/EventSeries.php
index cafb5d95cdaf9a13003fabdee6b87ae5e5c81407..54190cef57fd3c399a681e5b03c905c3a850de83 100644
--- a/src/Entity/EventSeries.php
+++ b/src/Entity/EventSeries.php
@@ -6,7 +6,9 @@ use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Entity\EditorialContentEntityBase;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\recurring_events\EventInterface;
+use Drupal\recurring_events\EventUserTrait;
 use Drupal\user\UserInterface;
 
 /**
@@ -104,6 +106,7 @@ use Drupal\user\UserInterface;
  *     "langcode" = "langcode",
  *     "label" = "title",
  *     "uuid" = "uuid",
+ *     "uid" = "uid",
  *     "bundle" = "type",
  *   },
  *   revision_metadata_keys = {
@@ -156,6 +159,8 @@ use Drupal\user\UserInterface;
  */
 class EventSeries extends EditorialContentEntityBase implements EventInterface {
 
+  use EventUserTrait;
+
   /**
    * {@inheritdoc}
    *
@@ -236,36 +241,6 @@ class EventSeries extends EditorialContentEntityBase implements EventInterface {
     return $this;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwner() {
-    return $this->get('uid')->entity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwnerId() {
-    return $this->get('uid')->target_id;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwnerId($uid) {
-    $this->set('uid', $uid);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwner(UserInterface $account) {
-    $this->setOwnerId($account->id());
-    return $this;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -293,31 +268,13 @@ class EventSeries extends EditorialContentEntityBase implements EventInterface {
    */
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
 
     $fields['id'] = BaseFieldDefinition::create('integer')
       ->setLabel(t('ID'))
       ->setDescription(t('The ID of the eventseries entity.'))
       ->setReadOnly(TRUE);
 
-    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
-      ->setLabel(t('Authored by'))
-      ->setDescription(t('The username of the content author.'))
-      ->setRevisionable(TRUE)
-      ->setSetting('target_type', 'user')
-      ->setDefaultValueCallback('Drupal\recurring_events\Entity\Event::getCurrentUserId')
-      ->setTranslatable(TRUE)
-      ->setDisplayOptions('form', [
-        'type' => 'entity_reference_autocomplete',
-        'weight' => 11,
-        'settings' => [
-          'match_operator' => 'CONTAINS',
-          'size' => '60',
-          'placeholder' => '',
-          'match_limit' => 10,
-        ],
-      ])
-      ->setDisplayConfigurable('form', TRUE);
-
     $fields['uuid'] = BaseFieldDefinition::create('uuid')
       ->setLabel(t('UUID'))
       ->setDescription(t('The UUID of the event entity.'))
diff --git a/src/EventCreationService.php b/src/EventCreationService.php
index 1a716057af755b732dfd27a0ccb01fb1cec663cc..edb51d9a085077608aac5fcd0d8f4d63b4ce83dc 100644
--- a/src/EventCreationService.php
+++ b/src/EventCreationService.php
@@ -44,9 +44,9 @@ class EventCreationService {
   /**
    * Logger Factory.
    *
-   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
+   * @var \Drupal\Core\Logger\LoggerChannel
    */
-  protected $loggerFactory;
+  protected $loggerChannel;
 
   /**
    * The messenger service.
@@ -115,7 +115,7 @@ class EventCreationService {
   public function __construct(TranslationInterface $translation, Connection $database, LoggerChannelFactoryInterface $logger, Messenger $messenger, FieldTypePluginManager $field_type_plugin_manager, EntityFieldManager $entity_field_manager, ModuleHandler $module_handler, EntityTypeManagerInterface $entity_type_manager, KeyValueFactoryInterface $key_value) {
     $this->translation = $translation;
     $this->database = $database;
-    $this->loggerFactory = $logger->get('recurring_events');
+    $this->loggerChannel = $logger->get('recurring_events');
     $this->messenger = $messenger;
     $this->fieldTypePluginManager = $field_type_plugin_manager;
     $this->entityFieldManager = $entity_field_manager;
@@ -528,8 +528,42 @@ class EventCreationService {
 
     $this->moduleHandler->alter('recurring_events_event_instance', $data);
 
-    $entity = $this->entityTypeManager->getStorage('eventinstance')->create($data);
-    $entity->save();
+    $storage = $this->entityTypeManager->getStorage('eventinstance');
+    if ($event->isDefaultTranslation()) {
+      $entity = $storage->create($data);
+    }
+    else {
+      // Grab the untranslated event series.
+      $original = $event->getUntranslated();
+      // Find the corresponding default language event instance that matches
+      // the date and time of the version we wish to translate, so that we are
+      // mapping the translations from default language to translated language
+      // appropriately.
+      $entity_ids = $storage->getQuery()
+        ->condition('date__value', $data['date']['value'])
+        ->condition('date__end_value', $data['date']['end_value'])
+        ->condition('eventseries_id', $data['eventseries_id'])
+        ->condition('type', $data['type'])
+        ->condition('langcode', $original->language()->getId())
+        ->accessCheck(FALSE)
+        ->execute();
+
+      if (!empty($entity_ids)) {
+        // Load the default language version of the event instance.
+        $entity = $storage->load(reset($entity_ids));
+        // Only add a translation if we do not already have one.
+        if (!$entity->hasTranslation($event->language()->getId())) {
+          $entity->addTranslation($event->language()->getId(), $data);
+        }
+      }
+    }
+
+    if ($entity) {
+      $entity->save();
+    }
+    else {
+      $this->loggerChannel->warning('Missing event instance in default language. Translation could not be created');
+    }
 
     return $entity;
   }
@@ -707,13 +741,13 @@ class EventCreationService {
       if ($event->hasField('moderation_state') && $instance->hasField('moderation_state')) {
         $series_query = $this->entityTypeManager->getStorage('workflow')->getQuery()->accessCheck(FALSE);
         $series_query->condition('type_settings.entity_types.eventseries.*', $event->bundle());
-        $series_workflows = $series_query->execute();
+        $series_workflows = $series_query->accessCheck(FALSE)->execute();
         $series_workflows = array_keys($series_workflows);
         $series_workflow = reset($series_workflows);
 
         $instance_query = $this->entityTypeManager->getStorage('workflow')->getQuery()->accessCheck(FALSE);
         $instance_query->condition('type_settings.entity_types.eventinstance.*', $instance->bundle());
-        $instance_workflows = $instance_query->execute();
+        $instance_workflows = $instance_query->accessCheck(FALSE)->execute();
         $instance_workflows = array_keys($instance_workflows);
         $instance_workflow = reset($instance_workflows);
 
diff --git a/src/EventInstanceListBuilder.php b/src/EventInstanceListBuilder.php
index 599a2d498c450c0c0c95eb98d06ea18c7b92bfbd..4df170b575d35e167e4fef7e48168e3e92c11c23 100644
--- a/src/EventInstanceListBuilder.php
+++ b/src/EventInstanceListBuilder.php
@@ -142,14 +142,14 @@ class EventInstanceListBuilder extends EntityListBuilder {
    * {@inheritdoc}
    */
   protected function getEntityIds() {
-    $query = $this->getStorage()->getQuery()
+    $query = $this->getStorage()->getQuery()->accessCheck(TRUE)
       ->sort('date__value', 'ASC');
 
     // Only add the pager if a limit is specified.
     if ($this->limit) {
       $query->pager($this->limit);
     }
-    return $query->execute();
+    return $query->accessCheck(FALSE)->execute();
   }
 
 }
diff --git a/src/EventInstanceStorage.php b/src/EventInstanceStorage.php
index ed1ae7c32fa8651ecab95b4b488c434384d506ee..76e3586f8c5ab89d39c7c78f0dd370b6fcbeb5f5 100644
--- a/src/EventInstanceStorage.php
+++ b/src/EventInstanceStorage.php
@@ -51,6 +51,7 @@ class EventInstanceStorage extends SqlContentEntityStorage implements EventInsta
     return $this->database->update('eventinstance_revision')
       ->fields(['langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED])
       ->condition('langcode', $language->getId())
+      ->accessCheck(FALSE)
       ->execute();
   }
 
diff --git a/src/EventSeriesListBuilder.php b/src/EventSeriesListBuilder.php
index fc59888fdf60f7f3c1e8f3b5c176dd506aa80048..0eb011aba6d9782abe99ea837cba0ff58ad24c8c 100644
--- a/src/EventSeriesListBuilder.php
+++ b/src/EventSeriesListBuilder.php
@@ -149,14 +149,14 @@ class EventSeriesListBuilder extends EntityListBuilder {
    * {@inheritdoc}
    */
   protected function getEntityIds() {
-    $query = $this->getStorage()->getQuery()
+    $query = $this->getStorage()->getQuery()->accessCheck(TRUE)
       ->sort('changed', 'DESC');
 
     // Only add the pager if a limit is specified.
     if ($this->limit) {
       $query->pager($this->limit);
     }
-    return $query->execute();
+    return $query->accessCheck(FALSE)->execute();
   }
 
 }
diff --git a/src/EventSeriesStorage.php b/src/EventSeriesStorage.php
index 858a69e308522f2de6374e22ff3a23aa55049661..852c6c49046d4a6b40052f62253a3c6dfc78fb78 100644
--- a/src/EventSeriesStorage.php
+++ b/src/EventSeriesStorage.php
@@ -51,6 +51,7 @@ class EventSeriesStorage extends SqlContentEntityStorage implements EventSeriesS
     return $this->database->update('eventseries_revision')
       ->fields(['langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED])
       ->condition('langcode', $language->getId())
+      ->accessCheck(FALSE)
       ->execute();
   }
 
diff --git a/src/EventSubscriber/RecurringEventsAdminRouteSubscriber.php b/src/EventSubscriber/RecurringEventsAdminRouteSubscriber.php
index 13a38dc617b9715f28ec7e9f2091682488c78cff..b9334fbf57692c059a07b3d102980782adc30ba5 100644
--- a/src/EventSubscriber/RecurringEventsAdminRouteSubscriber.php
+++ b/src/EventSubscriber/RecurringEventsAdminRouteSubscriber.php
@@ -69,7 +69,7 @@ class RecurringEventsAdminRouteSubscriber extends RouteSubscriberBase {
   /**
    * {@inheritdoc}
    */
-  public static function getSubscribedEvents() : array {
+  public static function getSubscribedEvents(): array {
     $events = parent::getSubscribedEvents();
     $events[ConfigEvents::SAVE][] = ['onConfigSave', 0];
     return $events;
diff --git a/src/EventUserTrait.php b/src/EventUserTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..79694531f0347df2656fb347f13d2fb137d735eb
--- /dev/null
+++ b/src/EventUserTrait.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\recurring_events;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\user\UserInterface;
+
+trait EventUserTrait {
+
+  /**
+   * Returns an array of base field definitions for entity owners.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type to add the uid field to.
+   *
+   * @return \Drupal\Core\Field\BaseFieldDefinition[]
+   *   An array of base field definitions.
+   *
+   * @throws \Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException
+   *   Thrown when the entity type does not have an "uid" entity key.
+   */
+  public static function ownerBaseFieldDefinitions(EntityTypeInterface $entity_type) {
+    if (!$entity_type->hasKey('uid')) {
+      throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not have a "uid" entity key.');
+    }
+
+    return [
+      $entity_type->getKey('uid') => BaseFieldDefinition::create('entity_reference')
+        ->setLabel(new TranslatableMarkup('User ID'))
+        ->setSetting('target_type', 'user')
+        ->setTranslatable($entity_type->isTranslatable())
+        ->setDefaultValueCallback(static::class . '::getDefaultEntityOwner'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOwnerId() {
+    return $this->getEntityKey('uid');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOwnerId($uid) {
+    $key = $this->getEntityType()->getKey('uid');
+    $this->set($key, $uid);
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOwner() {
+    $key = $this->getEntityType()->getKey('uid');
+    return $this->get($key)->entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOwner(UserInterface $account) {
+    $key = $this->getEntityType()->getKey('uid');
+    $this->set($key, $account);
+
+    return $this;
+  }
+
+  /**
+   * Default value callback for 'uid' base field.
+   *
+   * @return mixed
+   *   A default value for the uid field.
+   */
+  public static function getDefaultEntityOwner() {
+    return \Drupal::currentUser()->id();
+  }
+
+  /**
+   * Backwards compatibility for getCurrentUserId().
+   *
+   * @return mixed
+   *   A default value for the uid field.
+   */
+  public static function getCurrentUserId() {
+    return static::getDefaultEntityOwner();
+  }
+
+}
diff --git a/src/Form/OrphanedEventInstanceForm.php b/src/Form/OrphanedEventInstanceForm.php
index 70175b457aa1299ac267181340c2f7d10990af22..83508c60b091f4d48eead4c09f8333aeeb5f956b 100644
--- a/src/Form/OrphanedEventInstanceForm.php
+++ b/src/Form/OrphanedEventInstanceForm.php
@@ -156,6 +156,7 @@ class OrphanedEventInstanceForm extends FormBase {
       ->fields('efd', ['id']);
     $query->leftJoin('eventseries', 'es', 'efd.eventseries_id = es.id');
     $instances = $query->condition('es.id', NULL, 'IS NULL')
+      ->accessCheck(FALSE)
       ->execute()
       ->fetchCol();
 
diff --git a/src/Plugin/ComputedField/EventInstances.php b/src/Plugin/ComputedField/EventInstances.php
index e5efd463c44ad2e4134839680cf86e09fe524a90..769412bc3770c45848d654f2c88bb680c4a026eb 100644
--- a/src/Plugin/ComputedField/EventInstances.php
+++ b/src/Plugin/ComputedField/EventInstances.php
@@ -29,7 +29,7 @@ class EventInstances extends EntityReferenceFieldItemList {
       });
 
       foreach ($instances as $key => $instance) {
-        $this->list[$key] = $this->createItem($key, $instance);
+        $this->list[$key] = $this->createItem($key, $instance->getTranslation($this->getLangcode()));
       }
     }
   }