Score:1

Attempting to return region in theme suggestions for the book module book tree block

cn flag

Editing question based on information from @NoSssweat

The book module uses the same hook for the book tree block and the rendered index view in the book page.

I have used the below code to return file name suggestions, so that I can override the html output for the book tree block in a specific region - "sidebar-first". I expected the below code would spit out something like book-tree--book-toc-180--sidebar-first.html.twig. Instead, it just spits out a file name suggestion that is already being suggested:

<!-- FILE NAME SUGGESTIONS:
   * book-tree--book-toc-180.html.twig
   * book-tree--book-toc-180.html.twig
   x book-tree.html.twig
-->

This file name suggestion is the same for the main content on the book page. How can I target any book tree blocks within the sidebar-first region?

// Add a region variable to a block.
// http://kristiankaa.dk/article/drupal8-region-specific-menu-theme-hook-suggestion
function uswds_subtheme_preprocess_book_tree__book_toc_180(&$variables) {
    if (isset($variables["elements"]["#id"])) {
        $block_id = $variables["elements"]["#id"];
        $block = \Drupal\block\Entity\Block::load($block_id);

        if ($block) {
            $variables["content"]["#attributes"]["region"] = $block->getRegion();
        }
    }
}

// add a template suggestion based on region name
// http://kristiankaa.dk/article/drupal8-region-specific-menu-theme-hook-suggestion
function uswds_subtheme_theme_suggestions_book_tree__book_toc_180_alter(array &$suggestions, array $variables) {
    if (isset($variables["attributes"]["region"])) {
        $suggestions[] = $variables["theme_hook_original"] . "__" . $variables["attributes"]["region"];
    }
}

2nd attempt I did the below, and it works but it only works for the book module default block, although any other book module block plugin all mostly use book-tree.html.twig

// Add a region variable to a block.
function uswds_subtheme_preprocess_block(&$variables) {
  if (isset($variables["elements"]["#id"])) {
    $block_id = $variables["elements"]["#id"];
    $block = \Drupal\block\Entity\Block::load($block_id);

    if ($block) {
      $variables["content"]["#attributes"]["region"] = $block->getRegion();
    }
  }
}

// add a template suggestion based on region name
function uswds_subtheme_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if (isset($variables["attributes"]["region"])) {
    $suggestions[] = $variables["theme_hook_original"] . "__" . $variables["attributes"]["region"];
  }
}
No Sssweat avatar
ua flag
Does this answer your question? [Determine which region a menu block is rendered in](https://drupal.stackexchange.com/questions/218907/determine-which-region-a-menu-block-is-rendered-in)
cn flag
@NoSssweat - not quite, as I'm looking for if it's possible within twig
cn flag
@NoSssweat - further question: I attempted to add this via my subtheme by creating my_subtheme.theme file, but this didn't appear to work (my guess is that inheritance doesn't work that way?).
No Sssweat avatar
ua flag
Assuming your sub theme is set as the current theme, it should've worked, perhaps you didn't flush the cache so the hooks get pickedup?
cn flag
@NoSssweat - see updated. Cache cleared.
Score:2
ua flag

or how can I do something in twig, like {% if region == 'sidebar-first' %} and just override book-tree.html.twig

// Adds a region attribute to a block.
function uswds_subtheme_preprocess_block(&$variables) {
  if (isset($variables["elements"]["#id"])) {
    $block_id = $variables["elements"]["#id"];
    $block = \Drupal\block\Entity\Block::load($block_id);
    if ($block) {
      $variables["content"]["#attributes"]["region"] = $block->getRegion();
    }
  }
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Creates region variable
 *
 * Gets rid of the region attribute added in uswds_subtheme_preprocess_block() as it's
 * not a valid HTML attribute.
 */
function uswds_subtheme_preprocess_book_tree(&$variables) {
  if (isset($variables['attributes']['region'])) {
    // creates region variable for book-tree.html.twig
    $variables['region'] = $variables['attributes']['region'];
    // unset invalid html attribute.
    unset($variables['attributes']['region']);
  }
}
cn flag
Unfortunately, this never gets called - `uswds_subtheme_preprocess_book_tree`.
No Sssweat avatar
ua flag
It should be, did you flush the cache? If yes, check to see if it's failing the isset conditional.
Score:1
ua flag

Nice try, since this is a book and not menu, the process is similar, but not exactly the same. I was hoping you would figure it out.

But anyways, here you go:

/**
 * Implements hook_preprocess_HOOK().
 *
 * Pass block region value to content so this can be used in
 * uswds_subtheme_theme_suggestions_menu_alter() since $variables['elements']
 * is not available there.
 */
function uswds_subtheme_preprocess_block(&$variables) {
  if (isset($variables['elements']['#id']) && $variables['base_plugin_id'] === 'book_navigation') {
    $region = \Drupal\block\Entity\Block::load($variables['elements']['#id'])->getRegion();
    $content = $variables['content'];
    foreach ($content as $content_key => $content_info) {
      if (is_numeric($content_key)) {
        $variables['content'][$content_key]['#attributes']['region'] = $region;
      }
    }
  }
}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 *
 * Provide region based book suggestions.
 */
function uswds_subtheme_theme_suggestions_book_tree_alter(&$suggestions, array $variables) {
  if (isset($variables['attributes']['region'])) {
    $suggestion = 'book__' . $variables['theme_hook_original'] . '__' . $variables['attributes']['region'];
    $suggestion = str_replace('-', '_', $suggestion);
    $suggestions[] = $suggestion;
  }
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Get rid of the region attribute added in uswds_subtheme_preprocess_block() as it's
 * not a valid HTML attribute.
 */
function uswds_subtheme_preprocess_book_tree(&$variables) {
  if (isset($variables['attributes']['region'])) {
    unset($variables['attributes']['region']);
  }
}
cn flag
Unfortunately, I'm not seeing any change in suggestions for the book-tree.html.twig
cn flag
Looking further, I don' believe the second function is executing, as I can change some of the values - such as 'book__' to 'books__' and that change never occurs.
Score:1
cn flag

Using all of the help from @NoSssweat and I was able get this working:

// Add a region variable to a block.
function uswds_subtheme_preprocess_block(&$variables) {
  if (isset($variables["elements"]["#id"])) {
    $block_id = $variables["elements"]["#id"];
    $block = \Drupal\block\Entity\Block::load($block_id);

    if ($block) {
      $variables["content"]["#attributes"]["region"] = $block->getRegion();
    }
  }
}

// add a template suggestion based on region name
function uswds_subtheme_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if (isset($variables["attributes"]["region"])) {
    $suggestions[] = $variables["theme_hook_original"] . "__" . $variables["attributes"]["region"];
  }
}

Which outputs (the second suggestion including the region):

<!-- FILE NAME SUGGESTIONS:
   * book-tree--book-toc-180.html.twig
   * book-tree--book-toc-180--sidebar-first.html.twig
   x book-tree.html.twig
-->

Update to working version:

<?php
// Add a region variable to a block. Only applies to some versions of plugins that use book-tree.html.twig 
function uswds_subtheme_preprocess_block(&$variables) {
    if (isset($variables["elements"]["#id"])) {
      $block_id = $variables["elements"]["#id"];
      $block = \Drupal\block\Entity\Block::load($block_id);
  
      if ($block) {
        $variables["content"]["#attributes"]["region"] = $block->getRegion();
      }
    }
  }

// Adds template suggestion to blocks. Seems to work everywhere, but doesn't target the book tree
function uswds_subtheme_theme_suggestions_block_alter(array &$suggestions, array $variables){
    if (!empty($variables['elements']['#id'])) {
        $block = \Drupal\block\Entity\Block::load($variables['elements']['#id']);
        $region = $block->getRegion();
        // adds suggestion with region and block id
        $suggestions[] = 'block__' . $region . '__' . $variables['elements']['#id'];
        // adds suggestion with region id
        $suggestions[] = 'block__' . $region;
    }
}


// add a template suggestion based on region name
function uswds_subtheme_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if (isset($variables["attributes"]["region"])) {
    $suggestions[] = $variables["theme_hook_original"] . "__" . $variables["attributes"]["region"];
  }
}
No Sssweat avatar
ua flag
I used Bartik when I wrote my code, you should add my last hook function; otherwise, you end up with an illegal HTML attribute.
cn flag
@NoSssweat - one more question (and thanks for all of the help) - my above code only seems to work specifically with the block provided by the book module. It does not provide suggestions for any other plugin, although the other plugins are also dependent on `book-tree.html.twig`. How do I get additional suggestions for ANY block that is using `book-tree.html.twig` and is already outputting `book-tree--book-toc-180.html.twig`?
cn flag
@NoSssweat or how can I do something in twig, like `{% if region == 'sidebar-first' %}` and just override `book-tree.html.twig`?
No Sssweat avatar
ua flag
see [my newest answer](https://drupal.stackexchange.com/a/308235/27710)
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.