From a9e1246035d63818adf81f0715985aa5d7637840 Mon Sep 17 00:00:00 2001
From: owenbush <owenbush@2765259.no-reply.drupal.org>
Date: Sat, 26 Dec 2020 10:09:50 -0700
Subject: [PATCH] Issue #3173249 by owenbush, MrPaulDriver: Allow Registrants
 to be Versionable and Add Status

---
 .../recurring_events_registration.install     |  71 ++++++-
 .../recurring_events_registration.module      |  46 +++-
 ...urring_events_registration.post_update.php |  92 ++++++++
 ...recurring_events_registration.services.yml |   4 +
 .../src/Entity/Registrant.php                 |  42 +++-
 .../src/Form/RegistrantDeleteForm.php         |   2 +-
 .../src/Form/RegistrantForm.php               |  42 +++-
 .../src/RegistrantListBuilder.php             |   2 +
 .../src/Routing/RouteSubscriber.php           |  28 +++
 .../optional/views.view.registrations.yml     | 201 ++++++++++++++++++
 10 files changed, 515 insertions(+), 15 deletions(-)
 create mode 100644 modules/recurring_events_registration/recurring_events_registration.post_update.php
 create mode 100644 modules/recurring_events_registration/src/Routing/RouteSubscriber.php

diff --git a/modules/recurring_events_registration/recurring_events_registration.install b/modules/recurring_events_registration/recurring_events_registration.install
index a49ff68f..ed2f1622 100644
--- a/modules/recurring_events_registration/recurring_events_registration.install
+++ b/modules/recurring_events_registration/recurring_events_registration.install
@@ -189,4 +189,73 @@ function recurring_events_registration_update_8004() {
     ->set('already_registered', 'ser already registered for this event.')
     ->set('registration_closed', 'Unfortunately, registration is not available at this time.')
     ->save(TRUE);
-}
\ No newline at end of file
+}
+
+/**
+ * Install the new status field.
+ */
+function recurring_events_registration_update_8005() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+  // Add the status field.
+  $status = BaseFieldDefinition::create('boolean')
+    ->setLabel(t('Status'))
+    ->setDescription(t('Is this registration complete?'))
+    ->setDefaultValue(FALSE)
+    ->setInitialValue(FALSE)
+    ->setSetting('on_label', 'Complete')
+    ->setDisplayOptions('form', [
+      'type' => 'boolean_checkbox',
+      'settings' => [
+        'display_label' => FALSE,
+      ],
+      'weight' => 100,
+    ])
+    ->setDisplayConfigurable('form', TRUE)
+    ->setDisplayOptions('view', [
+      'type' => 'boolean',
+      'label' => 'above',
+      'weight' => 0,
+      'settings' => [
+        'format' => 'enabled-disabled',
+      ],
+    ])
+    ->setDisplayConfigurable('view', FALSE);
+  $definition_update_manager->installFieldStorageDefinition('status', 'registrant', 'registrant', $status);
+}
+
+/**
+ * Update existing registrants to be marked as completed.
+ */
+function recurring_events_registration_update_8006(&$sandbox) {
+  // This is a multipass update.
+  // Grab all the registrants.
+  if (empty($sandbox['registrants'])) {
+    $registrants = \Drupal::entityTypeManager()
+      ->getStorage('registrant')
+      ->getQuery()
+      ->execute();
+
+    $sandbox['registrants'] = $registrants;
+    $sandbox['max'] = count($registrants);
+    $sandbox['limit'] = 20;
+    $sandbox['progress'] = 0;
+  }
+
+  if (count($sandbox['registrants']) > 0) {
+    // Loop through chunks of 20 registrants at a time.
+    $registrant_ids = array_splice($sandbox['registrants'], 0, $sandbox['limit'], []);
+    if (!empty($registrant_ids)) {
+      $registrants = \Drupal::entityTypeManager()->getStorage('registrant')->loadMultiple($registrant_ids);
+      foreach ($registrants as $registrant) {
+        $registrant->status = 1;
+        $registrant->save();
+        $sandbox['progress']++;
+      }
+      $sandbox['#finished'] = ($sandbox['progress'] / $sandbox['max']);
+    }
+  }
+  else {
+    $sandbox['#finished'] = 1;
+  }
+}
diff --git a/modules/recurring_events_registration/recurring_events_registration.module b/modules/recurring_events_registration/recurring_events_registration.module
index 1b034c3a..2d6493f5 100644
--- a/modules/recurring_events_registration/recurring_events_registration.module
+++ b/modules/recurring_events_registration/recurring_events_registration.module
@@ -10,11 +10,13 @@ use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\recurring_events_registration\Entity\Registrant;
 use Drupal\recurring_events\Entity\EventSeries;
 use Drupal\recurring_events\Entity\EventInstance;
 use Drupal\Core\Url;
 use Drupal\Component\Utility\Html;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\recurring_events_registration\Entity\RegistrantInterface;
+use Drupal\recurring_events_registration\Form\RegistrantForm;
 
 /**
  * Implements hook_help().
@@ -254,10 +256,10 @@ function recurring_events_registration_recurring_events_pre_delete_instances(Eve
  *
  * @param string $key
  *   The mail key used to determine the message and subject.
- * @param \Drupal\recurring_events_registration\Entity\Registrant $registrant
+ * @param \Drupal\recurring_events_registration\Entity\RegistrantInterface $registrant
  *   The registrant this email relates to.
  */
-function recurring_events_registration_send_notification($key, Registrant $registrant) {
+function recurring_events_registration_send_notification($key, RegistrantInterface $registrant) {
   $config = \Drupal::config('recurring_events_registration.registrant.config');
   $send_email = $config->get('email_notifications');
   if ($send_email) {
@@ -364,3 +366,41 @@ function recurring_events_registration_registrant_insert(EntityInterface $entity
     $state->set($state_key, $state_values);
   }
 }
+
+/**
+ * Implements hook_form_alter().
+ *
+ * @TODO: Remove when https://www.drupal.org/node/3173241 drops.
+ */
+function recurring_events_registration_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
+  /* @var \Drupal\recurring_events_registration\Form\RegistrantForm $form_object */
+  $form_object = $form_state->getFormObject();
+  if ($form_object instanceof RegistrantForm)
+    /* @var \Drupal\recurring_events_registration\Entity\RegistrantInterface $entity */
+    $entity = $form_object->getEntity();
+    if (!empty($entity) && $entity instanceof RegistrantInterface && $entity->getEntityTypeId() === 'registrant') {
+      foreach ($form['actions']['submit']['#submit'] as $key => $submit) {
+        if (is_array($submit) && $submit[0] === 'Drupal\content_moderation\EntityTypeInfo') {
+          unset($form['actions']['submit']['#submit'][$key]);
+        }
+      }
+    }
+}
+
+/**
+ * Implements hook_module_implements_alter().
+ *
+ * @TODO: Remove when https://www.drupal.org/node/3173241 drops.
+ */
+function recurring_events_registration_module_implements_alter(&$implementations, $hook) {
+  if ($hook == 'form_alter') {
+    // Move recurring_events_registration_form_alter() to the end of the
+    // list because we need to make sure we override the content moderation
+    // latest-version link template to also pass through the eventinstance ID to
+    // the URL.
+    // @see recurring_events_registration_form_alter().
+    $group = $implementations['recurring_events_registration'];
+    unset($implementations['recurring_events_registration']);
+    $implementations['recurring_events_registration'] = $group;
+  }
+}
diff --git a/modules/recurring_events_registration/recurring_events_registration.post_update.php b/modules/recurring_events_registration/recurring_events_registration.post_update.php
new file mode 100644
index 00000000..abd426e6
--- /dev/null
+++ b/modules/recurring_events_registration/recurring_events_registration.post_update.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Post update hooks for the recurring_events_registration module.
+ */
+
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Update registrant to be revisionable.
+ */
+function recurring_events_registration_post_update_make_registrant_revisionable(&$sandbox) {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+  /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */
+  $last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
+
+  $entity_type = $definition_update_manager->getEntityType('registrant');
+  $field_storage_definitions = $last_installed_schema_repository->getLastInstalledFieldStorageDefinitions('registrant');
+
+  // Update the entity type definition.
+  $entity_keys = $entity_type->getKeys();
+  $entity_keys['revision'] = 'revision_id';
+  $entity_keys['published'] = 'status';
+  $entity_type->set('entity_keys', $entity_keys);
+  $entity_type->set('revision_table', 'registrant_revision');
+  $revision_metadata_keys = [
+    'revision_default' => 'revision_default',
+    'revision_user' => 'revision_user',
+    'revision_created' => 'revision_created',
+    'revision_log_message' => 'revision_log_message',
+  ];
+  $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
+
+  // Update the field storage definitions and add the new ones required by a
+  // revisionable entity type.
+  $field_storage_definitions['user_id']->setRevisionable(TRUE);
+  $field_storage_definitions['email']->setRevisionable(TRUE);
+  $field_storage_definitions['waitlist']->setRevisionable(TRUE);
+  $field_storage_definitions['changed']->setRevisionable(TRUE);
+  $field_storage_definitions['status']->setRevisionable(TRUE);
+
+  $field_storage_definitions['revision_id'] = BaseFieldDefinition::create('integer')
+    ->setName('revision_id')
+    ->setTargetEntityTypeId('registrant')
+    ->setTargetBundle(NULL)
+    ->setLabel(new TranslatableMarkup('Revision ID'))
+    ->setReadOnly(TRUE)
+    ->setSetting('unsigned', TRUE);
+
+  $field_storage_definitions['revision_default'] = BaseFieldDefinition::create('boolean')
+    ->setName('revision_default')
+    ->setTargetEntityTypeId('registrant')
+    ->setTargetBundle(NULL)
+    ->setLabel(new TranslatableMarkup('Default revision'))
+    ->setDescription(new TranslatableMarkup('A flag indicating whether this was a default revision when it was saved.'))
+    ->setStorageRequired(TRUE)
+    ->setInternal(TRUE)
+    ->setTranslatable(FALSE)
+    ->setRevisionable(TRUE);
+
+  $field_storage_definitions['revision_created'] = BaseFieldDefinition::create('created')
+    ->setName('revision_created')
+    ->setTargetEntityTypeId('registrant')
+    ->setTargetBundle(NULL)
+    ->setLabel(new TranslatableMarkup('Revision create time'))
+    ->setDescription(new TranslatableMarkup('The time that the current revision was created.'))
+    ->setRevisionable(TRUE);
+
+  $field_storage_definitions['revision_user'] = BaseFieldDefinition::create('entity_reference')
+    ->setName('revision_user')
+    ->setTargetEntityTypeId('registrant')
+    ->setTargetBundle(NULL)
+    ->setLabel(new TranslatableMarkup('Revision user'))
+    ->setDescription(new TranslatableMarkup('The user ID of the author of the current revision.'))
+    ->setSetting('target_type', 'user')
+    ->setRevisionable(TRUE);
+
+  $field_storage_definitions['revision_log_message'] = BaseFieldDefinition::create('string_long')
+    ->setName('revision_log_message')
+    ->setTargetEntityTypeId('registrant')
+    ->setTargetBundle(NULL)
+    ->setLabel(new TranslatableMarkup('Revision log message'))
+    ->setDescription(new TranslatableMarkup('Briefly describe the changes you have made.'))
+    ->setRevisionable(TRUE)
+    ->setDefaultValue('');
+
+  $definition_update_manager->updateFieldableEntityType($entity_type, $field_storage_definitions, $sandbox);
+
+  return t('Registrants have been converted to be revisionable.');
+}
diff --git a/modules/recurring_events_registration/recurring_events_registration.services.yml b/modules/recurring_events_registration/recurring_events_registration.services.yml
index 08712920..8b8f0ba9 100644
--- a/modules/recurring_events_registration/recurring_events_registration.services.yml
+++ b/modules/recurring_events_registration/recurring_events_registration.services.yml
@@ -8,3 +8,7 @@ services:
   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']
+  recurring_events_registration.route_subscriber:
+    class: Drupal\recurring_events_registration\Routing\RouteSubscriber
+    tags:
+      - { name: event_subscriber }
diff --git a/modules/recurring_events_registration/src/Entity/Registrant.php b/modules/recurring_events_registration/src/Entity/Registrant.php
index 37a22933..7b1b51c1 100644
--- a/modules/recurring_events_registration/src/Entity/Registrant.php
+++ b/modules/recurring_events_registration/src/Entity/Registrant.php
@@ -4,7 +4,7 @@ namespace Drupal\recurring_events_registration\Entity;
 
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
-use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EditorialContentEntityBase;
 use Drupal\Core\Entity\EntityChangedTrait;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\user\UserInterface;
@@ -37,19 +37,29 @@ use Drupal\recurring_events_registration\Plugin\Field\ComputedRegistrantTitleFie
  *     "access" = "Drupal\recurring_events_registration\RegistrantAccessControlHandler",
  *   },
  *   base_table = "registrant",
+ *   revision_table = "registrant_revision",
+ *   show_revision_ui = TRUE,
  *   translatable = FALSE,
  *   fieldable = TRUE,
  *   admin_permission = "administer registrant entities",
  *   entity_keys = {
  *     "id" = "id",
+ *     "revision" = "revision_id",
  *     "uuid" = "uuid",
  *     "uid" = "user_id",
  *     "label" = "title",
  *     "bundle" = "bundle",
+ *     "status" = "status",
+ *     "published" = "status",
+ *   },
+ *   revision_metadata_keys = {
+ *     "revision_user" = "revision_user",
+ *     "revision_created" = "revision_created",
+ *     "revision_log_message" = "revision_log_message"
  *   },
  *   links = {
- *     "canonical" = "/events/{eventinstance}/registrations//{registrant}",
- *     "edit-form" = "/events/{eventinstance}/registrations//{registrant}/edit",
+ *     "canonical" = "/events/{eventinstance}/registrations/{registrant}",
+ *     "edit-form" = "/events/{eventinstance}/registrations/{registrant}/edit",
  *     "delete-form" = "/events/{eventinstance}/registrations/{registrant}/delete",
  *     "anon-edit-form" = "/events/{eventinstance}/registrations/{registrant}/{uuid}/edit",
  *     "anon-delete-form" = "/events/{eventinstance}/registrations/{registrant}/{uuid}/delete"
@@ -58,7 +68,7 @@ use Drupal\recurring_events_registration\Plugin\Field\ComputedRegistrantTitleFie
  *   field_ui_base_route = "entity.registrant_type.edit_form"
  * )
  */
-class Registrant extends ContentEntityBase implements RegistrantInterface {
+class Registrant extends EditorialContentEntityBase implements RegistrantInterface {
 
   use EntityChangedTrait;
 
@@ -109,6 +119,14 @@ class Registrant extends ContentEntityBase implements RegistrantInterface {
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function setStatus($status) {
+    $this->set('status', $status);
+    return $this;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -147,6 +165,7 @@ class Registrant extends ContentEntityBase implements RegistrantInterface {
 
     $fields['user_id'] = BaseFieldDefinition::create('entity_reference')
       ->setLabel(t('Authored by'))
+      ->setRevisionable(TRUE)
       ->setDescription(t('The user ID of author of the Registrant entity.'))
       ->setSetting('target_type', 'user')
       ->setSetting('handler', 'default')
@@ -171,6 +190,7 @@ class Registrant extends ContentEntityBase implements RegistrantInterface {
 
     $fields['email'] = BaseFieldDefinition::create('email')
       ->setLabel(t('Email Address'))
+      ->setRevisionable(TRUE)
       ->setDescription(t('The email address of the registrant'))
       ->setDisplayOptions('form', [
         'type' => 'email_default',
@@ -205,6 +225,7 @@ class Registrant extends ContentEntityBase implements RegistrantInterface {
       ->setSetting('target_type', 'eventinstance');
 
     $fields['waitlist'] = BaseFieldDefinition::create('boolean')
+      ->setRevisionable(TRUE)
       ->setLabel(t('Waitlist'))
       ->setDescription(t('Whether this registrant is waitlisted.'));
 
@@ -221,6 +242,7 @@ class Registrant extends ContentEntityBase implements RegistrantInterface {
       ->setDescription(t('The time that the entity was created.'));
 
     $fields['changed'] = BaseFieldDefinition::create('changed')
+      ->setRevisionable(TRUE)
       ->setLabel(t('Changed'))
       ->setDescription(t('The time that the entity was last edited.'));
 
@@ -230,6 +252,18 @@ class Registrant extends ContentEntityBase implements RegistrantInterface {
       ->setComputed(TRUE)
       ->setClass(ComputedRegistrantTitleFieldItemList::class);
 
+      $fields['status']
+      ->setLabel(t('Status'))
+      ->setDescription(t('Is this registration complete?'))
+      ->setDisplayOptions('form', [
+        'type' => 'boolean_checkbox',
+        'settings' => [
+          'display_label' => TRUE,
+        ],
+        'weight' => 120,
+      ])
+      ->setDisplayConfigurable('form', TRUE);
+
     return $fields;
   }
 
diff --git a/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php b/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php
index fca043a1..21a5acbd 100644
--- a/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php
+++ b/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php
@@ -189,7 +189,7 @@ class RegistrantDeleteForm extends ContentEntityDeleteForm {
    * {@inheritdoc}
    */
   protected function getDeletionMessage() {
-    /** @var \Drupal\omega_events\EventInterface $entity */
+    /** @var \Drupal\recurring_events\EventInterface $entity */
     $entity = $this->getEntity();
 
     return $this->t('Your registration for %email for %event has been cancelled.', [
diff --git a/modules/recurring_events_registration/src/Form/RegistrantForm.php b/modules/recurring_events_registration/src/Form/RegistrantForm.php
index 2013a09e..35ad3cf6 100644
--- a/modules/recurring_events_registration/src/Form/RegistrantForm.php
+++ b/modules/recurring_events_registration/src/Form/RegistrantForm.php
@@ -17,6 +17,7 @@ use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Drupal\Component\Datetime\TimeInterface;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\content_moderation\ModerationInformation;
 
 /**
  * Form controller for Registrant edit forms.
@@ -81,6 +82,13 @@ class RegistrantForm extends ContentEntityForm {
    */
   protected $cacheTagsInvalidator;
 
+  /**
+   * The moderation information service.
+   *
+   * @var \Drupal\content_moderation\ModerationInformation
+   */
+  protected $moderationInformation;
+
   /**
    * {@inheritdoc}
    */
@@ -96,7 +104,8 @@ class RegistrantForm extends ContentEntityForm {
       $container->get('entity_field.manager'),
       $container->get('current_route_match'),
       $container->get('entity_type.manager'),
-      $container->get('cache_tags.invalidator')
+      $container->get('cache_tags.invalidator'),
+      $container->has('content_moderation.moderation_information') ? $container->get('content_moderation.moderation_information') : NULL,
     );
   }
 
@@ -125,6 +134,8 @@ class RegistrantForm extends ContentEntityForm {
    *   The entity type manager service.
    * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
    *   The cache tags invalidator.
+   * @param \Drupal\content_moderation\ModerationInformation $moderation_information
+   *   The moderation information service.
    */
   public function __construct(
     EntityRepositoryInterface $entity_repository,
@@ -137,7 +148,8 @@ class RegistrantForm extends ContentEntityForm {
     EntityFieldManager $field_manager,
     RouteMatchInterface $route_match,
     EntityTypeManagerInterface $entity_type_manager,
-    CacheTagsInvalidatorInterface $cache_tags_invalidator) {
+    CacheTagsInvalidatorInterface $cache_tags_invalidator,
+    ModerationInformation $moderation_information = NULL) {
     $this->messenger = $messenger;
     $this->creationService = $creation_service;
     $this->currentUser = $current_user;
@@ -146,6 +158,7 @@ class RegistrantForm extends ContentEntityForm {
     $this->routeMatch = $route_match;
     $this->entityTypeManager = $entity_type_manager;
     $this->cacheTagsInvalidator = $cache_tags_invalidator;
+    $this->moderationInformation = $moderation_information;
     parent::__construct($entity_repository, $entity_type_bundle_info, $time);
   }
 
@@ -323,7 +336,7 @@ class RegistrantForm extends ContentEntityForm {
 
     // Prevent the form being displayed if registration is closed, or there are
     // no spaces left, and no waitlist.
-    if (($availability === 0 && !$waitlist) || !$registration_open) {
+    if ((($availability === 0 && !$waitlist) || !$registration_open) && $new) {
       foreach ($form_fields as $field_name => $field) {
         if (isset($form[$field_name]) && $new) {
           $form[$field_name]['#printed'] = TRUE;
@@ -341,6 +354,11 @@ class RegistrantForm extends ContentEntityForm {
     if (!$this->currentUser->hasPermission('modify registrant author')) {
       $form['user_id']['#access'] = FALSE;
     }
+
+    if (!$this->currentUser->hasPermission('administer registrant entity')) {
+      $form['revision_information']['#access'] = FALSE;
+      $form['status']['#access'] = FALSE;
+    }
   }
 
   /**
@@ -349,7 +367,7 @@ class RegistrantForm extends ContentEntityForm {
   public function validateForm(array &$form, FormStateInterface $form_state) {
     parent::validateForm($form, $form_state);
 
-    /* @var $entity \Drupal\omega_events\Entity\Registrant */
+    /* @var $entity \Drupal\recurring_events\Entity\Registrant */
     $entity = $this->entity;
 
     // Only perform the checks if the entity is new.
@@ -398,6 +416,9 @@ class RegistrantForm extends ContentEntityForm {
     $event_instance = $this->routeMatch->getParameter('eventinstance');
     $event_series = $event_instance->getEventSeries();
 
+    /* @var $entity \Drupal\recurring_events\Entity\RegistrantInterface */
+    $entity = $this->entity;
+
     // Use the registration creation service to grab relevant data.
     $this->creationService->setEventInstance($event_instance);
     // Just to be sure we have a fresh copy of the event series.
@@ -409,8 +430,6 @@ class RegistrantForm extends ContentEntityForm {
     $reg_type = $this->creationService->getRegistrationType();
     $registration = $this->creationService->hasRegistration();
 
-    $form_state->setRedirect('entity.registrant.add_form', ['eventinstance' => $event_instance->id()]);
-
     if ($registration && $registration_open && ($availability > 0 || $availability == -1 || $waitlist)) {
       $add_to_waitlist = (int) $form_state->getValue('add_to_waitlist');
       $this->entity->setEventSeries($event_series);
@@ -455,6 +474,17 @@ class RegistrantForm extends ContentEntityForm {
       $this->messenger->addMessage($this->t($this->config('recurring_events_registration.registrant.config')->get('registration_closed')));
     }
 
+    $form_state->setRedirect('entity.registrant.add_form', ['eventinstance' => $event_instance->id()]);
+
+    // @TODO: Remove when https://www.drupal.org/node/3173241 drops.
+    if ($this->moderationInformation) {
+      if ($this->moderationInformation->hasPendingRevision($entity) && $entity->hasLinkTemplate('latest-version')) {
+        $form_state->setRedirect('entity.registrant.latest_version', [
+          'eventinstance' => $entity->getEventInstance()->id(),
+          'registrant' => $entity->id(),
+        ]);
+      }
+    }
   }
 
 }
diff --git a/modules/recurring_events_registration/src/RegistrantListBuilder.php b/modules/recurring_events_registration/src/RegistrantListBuilder.php
index 7fc6fb04..93707d6e 100644
--- a/modules/recurring_events_registration/src/RegistrantListBuilder.php
+++ b/modules/recurring_events_registration/src/RegistrantListBuilder.php
@@ -100,6 +100,7 @@ class RegistrantListBuilder extends EntityListBuilder {
     }
     $header['email'] = $this->t('Email');
     $header['waitlist'] = $this->t('Waitlist');
+    $header['status'] = $this->t('Status');
     return $header + parent::buildHeader();
   }
 
@@ -123,6 +124,7 @@ class RegistrantListBuilder extends EntityListBuilder {
     }
     $row['email'] = $entity->get('email')->value;
     $row['waitlist'] = $entity->get('waitlist')->value ? $this->t('Yes') : $this->t('No');
+    $row['status'] = $entity->get('status')->value ? $this->t('Complete') : $this->t('Pending');
     return $row + parent::buildRow($entity);
   }
 
diff --git a/modules/recurring_events_registration/src/Routing/RouteSubscriber.php b/modules/recurring_events_registration/src/Routing/RouteSubscriber.php
new file mode 100644
index 00000000..e7271b0f
--- /dev/null
+++ b/modules/recurring_events_registration/src/Routing/RouteSubscriber.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\recurring_events_registration\Routing;
+
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Listens to the dynamic route events.
+ */
+class RouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+    // Change path '/user/login' to '/login'.
+    if ($route = $collection->get('entity.registrant.latest_version')) {
+      $route->setRequirement('eventinstance', '\d+');
+      $option = $route->getOption('parameters');
+      $option['eventinstance'] = [
+        'type' => 'entity:eventinstance',
+        'load_latest_revision' => TRUE,
+      ];
+      $route->setOption('parameters', $option);
+    }
+  }
+}
diff --git a/modules/recurring_events_views/config/optional/views.view.registrations.yml b/modules/recurring_events_views/config/optional/views.view.registrations.yml
index 289190ad..6875d58f 100644
--- a/modules/recurring_events_views/config/optional/views.view.registrations.yml
+++ b/modules/recurring_events_views/config/optional/views.view.registrations.yml
@@ -704,6 +704,73 @@ display:
           entity_type: registrant
           entity_field: user_id
           plugin_id: field
+        status:
+          id: status
+          table: registrant
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Status
+          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: boolean
+          settings:
+            format: custom
+            format_custom_true: Complete
+            format_custom_false: Pending
+          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: registrant
+          entity_field: status
+          plugin_id: field
         operations:
           id: operations
           table: registrant
@@ -1555,6 +1622,73 @@ display:
           entity_type: registrant
           entity_field: user_id
           plugin_id: field
+        status:
+          id: status
+          table: registrant
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Status
+          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: boolean
+          settings:
+            format: custom
+            format_custom_true: Complete
+            format_custom_false: Pending
+          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: registrant
+          entity_field: status
+          plugin_id: field
         operations:
           id: operations
           table: registrant
@@ -2428,6 +2562,73 @@ display:
           entity_type: registrant
           entity_field: type
           plugin_id: field
+        status:
+          id: status
+          table: registrant
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Status
+          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: boolean
+          settings:
+            format: custom
+            format_custom_true: Complete
+            format_custom_false: Pending
+          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: registrant
+          entity_field: status
+          plugin_id: field
         operations:
           id: operations
           table: registrant
-- 
GitLab