Problem/Motivation

Room reservation validation fatally errors when the reservation author references a user account that no longer exists (or is anonymous, uid 0). ReservationBase::getOwner() returns NULL in those cases, but two constraint validators call $owner->hasPermission() without a null check.

This crashes any code path that runs full entity validation on existing reservations, including:

  • The intercept_room_reservation_validation Views field on intercept_room_reservations (AJAX render of the management list)
  • RoomReservation::validationWarnings() used by the approve confirm form

Observed error:

Error: Call to a member function hasPermission() on null in
Drupal\intercept_room_reservation\Plugin\Validation\Constraint\LocationOpenHoursConstraintValidator->validate() (line 55)

The same pattern exists in ReservationMaxDurationConstraintValidator (line 68). A related issue in ReservationLimitConstraintValidator passes getReservor() (which can be FALSE or NULL) to currentUserReservations(AccountInterface $user) without guarding, which can cause a TypeError on the same validation path.

Steps to reproduce

  1. Create a room reservation as a staff user (or any user who will later be deleted).
  2. Delete that user account, leaving the reservation's author field pointing at a non-existent uid.

    Alternatively: create a reservation as anonymous (uid 0) so getOwner() is NULL.
  3. As a user with permission to manage room reservations, open the room reservations management view (intercept_room_reservations) that includes the Reservation conflicts field (intercept_room_reservation_validation).
  4. Observe a fatal error when the view tries to render the row for the affected reservation.

SQL to find affected records:

SELECT r.id, r.author
FROM room_reservation_field_data r
LEFT JOIN users_field_data u ON u.uid = r.author
WHERE r.author > 0 AND u.uid IS NULL;

Proposed resolution

Null-check the reservation owner before calling hasPermission(). When the owner cannot be loaded, treat them as unable to bypass constraints (run validation normally).

LocationOpenHoursConstraintValidator.php

$owner = $entity->getOwner();
$owner_can_bypass = $owner
  && $owner->hasPermission('bypass room reservation open hours constraints');

if (!$owner_can_bypass || $entity->__get('warning')) {
  // existing open-hours validation logic
}

ReservationMaxDurationConstraintValidator.php — same pattern for bypass room reservation maximum duration constraints.

ReservationLimitConstraintValidator.php — guard getReservor() before passing to currentUserReservations():

$registrant = $entity->getReservor();
if (!$registrant) {
  return;
}

$reservations = array_filter(
  $this->reservationManager->currentUserReservations($registrant),
  function ($reservation) use ($entity) {
    return $reservation->id() != $entity->id();
  }
);

Optional hardening in ReservationManager::notifyStatusChange(): null-check $reservation_author before $reservation_author->id() when deduplicating cancel emails.

Remaining tasks

  • Add null-safe owner bypass checks to LocationOpenHoursConstraintValidator and ReservationMaxDurationConstraintValidator.
  • Guard getReservor() in ReservationLimitConstraintValidator.
  • Add automated test coverage: reservation with deleted author should validate (and return warnings via validationWarnings()) without fatal error.
  • Confirm bypass behavior is unchanged when owner exists and has bypass permission.
  • (Optional) Harden ReservationManager::notifyStatusChange() for orphaned authors on status-change email paths.

User interface changes

None intended. The room reservations management view and approve form should render validation warnings for affected reservations instead of returning a 500 error.

API changes

None. Internal validator behavior only; no public API, hook, or service contract changes.

Data model changes

None. No schema, field, or entity definition changes. Sites with orphaned author references may still want to reassign author to a valid user for data hygiene, but that is operational cleanup, not required for the code fix.

Comments

robbt created an issue.