1

User's have a field called "Location" (uses the Address module).

I would like to show a message (or enable a block) to users that have not filled out this field.

Specifically, this message should be shown when they log in, view their profile, or when they view their dashboard.

I am attempting to do this with Rules, but no luck yet. Any guidance would be appreciated.

  • Want to display where exactly? I wouldn't go for Rules. I'd use a preprocess hook maybe. Get current user, get field, if empty send uncached message to desired template. – leymannx Jul 26 '19 at 14:48
  • Anywhere really. Messages regions works. Just not on the field itself. A message like "Hey, your profile is incomplete. <link to user/edit> to complete your profile." – ymdahi Jul 26 '19 at 14:51
  • 1
    @leymannx. yes, pages for authenticated users are cached, too. Even with uncacheable blocks on the page, which then get placeholdered and the page is cached without them. – 4k4 Jul 26 '19 at 18:16
  • 1
    @leymannx, this works, but is not good for cache performance. Better place a block with this context on the page. Drupal is preconfigured to deliver such blocks through BigPipe. – 4k4 Jul 27 '19 at 12:10
  • 2
    The problem is still that the question is too broad, there are too many possible answers. It's unclear whether this is about messages, which are normally triggered on events or blocks containing the content of the user entity, which also includes the state of a field being empty or not. – 4k4 Jul 27 '19 at 13:15
3

This solution may be less performance happy than preprocessing (or not, it's difficult to say depending on caching etc), but I thought I'd be good to throw it out there since it's a "no-code" solution.

  1. Create a Views with Block display listing users
  2. Filter according to Location field Is empty to show only users who don't have it filled in
  3. Add a Contextual filter for the user ID and set it to the current user, Hide Views if it doesn't validate
  4. Add Custom text field and enter your message to be shown
  5. Add the No results behaviour, this may need some thinking around how to handle the cases when the uses do have the Location field filled in
  6. Place the block in as many regions of your theme as you need, and configure each Block visibility so it makes sense for your cases
  7. Don't forget to also configure the Block contextual filter settings
  • Great no-code solution - works exactly as I'd hoped. Thanks! – ymdahi Jul 26 '19 at 17:44
  • 1
    Yes, a block is the best solution for a message always shown, which doesn't depend on an event. When caching is configured correctly you get a block which can be lazy built and delivered by Big Pipe. – 4k4 Jul 26 '19 at 18:21
2

Two possible ways I can think of.

First, via a preprocess hook. Maybe template_preprocess_page() or *_html as they are being called on every page request. But you need to ensure to add user to the cache contexts to get a user-specific version of the cached page and to have changes to the user profile bubbling up. This works but still might not be good for cache performance, to have every single page cached user-specifically.

/**
 * Implements template_preprocess_page().
 */
function MYTHEME_preprocess_page(&$variables) {

  // Add the current user to the cache contexts.
  $variables['page']['#cache']['contexts'][] = 'user';

  if (\Drupal::currentUser()->isAuthenticated()) {

    // 1. Get the current user.
    /** @var $user \Drupal\user\UserInterface */
    $user = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());

    // 2. Check if field exists.
    if ($user->hasField('field_location')) {

      // 3. Get field.
      $location = $user->get('field_location');

      // 4. Check if field is empty.
      if ($location->isEmpty()) {

        // 5. Trigger message.
        \Drupal::messenger()->addMessage('Hello World!');
      }
    }
  }
}

Second, via a custom block and hook_block_access() implemented from a custom module. Have core's block_content (Custom Block) module enabled, add a custom block type (maybe "User Message"), create a custom block, remind its machine name (it's the block ID) and add it to the region you wanna have the message displayed. Then implement the following.

/**
 * Implements hook_block_access().
 */
function MYMODULE_block_access(\Drupal\block\Entity\Block $block, $operation, \Drupal\Core\Session\AccountInterface $account) {

  if ($operation == 'view' && $block->id() == 'BLOCK_MACHINE_NAME') {

    if ($account->isAuthenticated()) {

      /** @var $user \Drupal\user\UserInterface */
      $user = \Drupal\user\Entity\User::load($account->id());

      if ($user->hasField('field_location')) {

        $location = $user->get('field_location');

        // Hide if location field is not empty anymore.
        return \Drupal\Core\Access\AccessResult::forbiddenIf($location->isEmpty() === FALSE)
          ->addCacheableDependency($block);
      }
    }
  }

  return \Drupal\Core\Access\AccessResult::neutral();
}
  • Many thanks! Very useful snippet. I wonder if I can render a block with \Drupal::entityTypeManager()-> getViewBuilder('block_content')->view($block); instead of showing a message. Or even start a Tour. Thanks again for your help. – ymdahi Jul 26 '19 at 17:02
  • @ymdahi – Uh yes, this is a much better idea! Place a block and then use hook_block_access to hide it if the user's location field got filled. Will update my answer later! – leymannx Jul 27 '19 at 7:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.