0

When a child page is added to a book via "Add child page" (node/add/{content-type}), the Weight drop-down field in the Book Outline tab has a range of -/+ 15. This is due to line 191 in web/core/modules/book/src/BookManager.php:

$form['book']['weight'] = [
  '#type' => 'weight',
  '#title' => $this->t('Weight'),
  '#default_value' => $node->book['weight'],
  '#delta' => max(15, abs($node->book['weight'])), // line 191
  '#weight' => 5,
  '#description' => $this->t('Pages at a given level are ordered first by weight and then by title.'),
 ];

When a child node is initially added, $node->book['weight'] is always 0, resulting in a #delta value of 15. #delta controls the min and max values in the Weight drop-down field mentioned above. There is no way to assign a weight higher than 15.

To simplify the work of non-technical editors repetitively adding chapters to books, we tell them to set the Weight value to the chapter number. This doesn't work for books with more than 15 chapters (which happens).

QUESTION: What is the correct, non-core-hacking way to set the limit to say, 25 in Drupal 8? (see my answer below)

AMENDMENT ADDED 2018-11-24

In addition to the issue expressed above, a related issue is that on the book outline reorder page (admin/structure/book/{book-nid}) there is a drop-down field for the weight of each child page. Child pages with a current weight within the range -/+ 15 get a range of -/+ 15 in their drop-down fields. Child pages with weights outside that range get a drop-down with the range increased to include their current weight.

This is due to lines 202, 203, and 235 in web/core/modules/book/src/Form/BookAdminEditForm.php:

$count = count($tree); // line 202 - count of all child pages for the book
$delta = ($count < 30) ? 15 : intval($count / 2) + 1; // line 203

$form[$id]['weight'] = [
  '#type' => 'weight',
  '#default_value' => $data['link']['weight'],
  '#delta' => max($delta, abs($data['link']['weight'])), // line 235
  '#title' => $this->t('Weight for @title', ['@title' => $data['link']['title']]),
  '#title_display' => 'invisible',
  '#attributes' => [
    'class' => ['book-weight'],
  ],
];

The challenge with varying the weight range based on current weight is that it's not possible to assign a weight greater than 15 to a child page whose current weight is 15 or less because its drop-down allows a maximum of 15. I can add several child pages with weight 0, but I can't change any of them to 16. Maybe I'm missing something in the current user interface?

All of this said, there seems to be a fairly straightforward solution to both of these problems, explained in my answer below. Not knowing every detail of how the Book module works, there may be flaws in what I suggest. I'm using it right now and it seems to work well, but I welcome corrections or suggestions for improvement.

0

Many people agree it's a big accessibility problem, but it isn't easy to solve and there always seem to be some obstacle.

I just posted about this, possible duplicate, very relevant for this question because the listed issues mention various methods on how to approach solving this and the obstacles you might encounter: How to increase book weight range for a same-level chapters.

The simplest way to solve it might be to rely on an outside sorting method, like Weight, and not on overriding the Book module. I don't have enough expertise to evaluate the difficulty level to modify the Book code, it just seems that way reading the lines and between them on those threads.

  • Thanks, @prkos. If you have time, please look at my answer to see if you have suggestions or cautions. A related-but-different problem occurs in the book_admin_edit form (admin/structure/book/book-id) where editors can reorder the book outline. You can't move a child page with a weight <= 15 to a weight > 15 even if other child pages have a weight > 15. I'm working on that now. – Marshall Morrise Nov 24 '18 at 0:18
  • Yes, it seems to be a more complex issue that it seems at first. I'm not an expert so I can't comment on your answer. I can only say it seems too easy and that it would have already been solved if there are no consequences to take into consideration. Maybe you should post your solution to one of the Issues I linked in that other answer. Since people there already know the problem inside-out and have been testing it they'll be able to give an informed opinion. – prkos Nov 24 '18 at 0:33
  • Again, thank you @prkos. I had already reviewed the posts/pages you mention, but went back again to make sure I'm not missing anything. None of them seem to address exactly what I'm talking about, but I'll go to them and add a comment with a link to my answer here with an invitation to correct or improve it. – Marshall Morrise Nov 25 '18 at 0:46
0

Having spent quite some time reading others' posts and in trial and error, I came to this answer to my own question (as amended above).

Create a custom module (see drupal.org/docs/8/creating-custom-modules). In the MYMODULE.module file, add the following (replace "section" with the content type you're using):

use Drupal\Core\Form\FormStateInterface;

function MYMODULE_form_alter(&$form, FormStateInterface $form_state, $form_id)
{
  switch($form_id) {

    case 'node_section_form':
    case 'node_section_edit_form':
    case 'node_section_book_outline_form:

      $form['book']['weight']['#delta'] = 25;
      break;

    case 'book_admin_edit':

      foreach ($form['table'] as $key => &$arr) {

        if (strpos($key, 'book-admin-') === 0) {
          $arr['weight']['#delta'] = 25;
        }
      }

      break;
  }
}

The first case increases the weight range on the add content page, on the edit content page, and on the book outline page for the content type. The second case increases the weight range for each child on the reorder book outline page.

This is working well for me, but I welcome corrections and suggestions for improvement, especially suggestions for making the solution less "brittle".

NOTE THAT even when you've done this, the editor will still have to clear the cache and refresh the book outline to get added child pages or reordering to show up in the redisplayed book outline.

FIRST UPDATE TO ANSWER:

Based on more reading and more trial and error, it appears that adding the following to a custom .theme file (see drupal.org/docs/8/theming) will disable caching of the book navigation block for all but anonymous users. Thus, editors who add or reorder child pages will see their changes immediately upon returning to the book outline without clearing the cache.

function MYTHEME_preprocess_block(&$vars)
{
  if ($vars['plugin_id'] == 'book_navigation') {
    $vars['#cache']['max-age'] = 0;
  }
}

SECOND UPDATE TO ANSWER:

The answer given above for overcoming the clear cache problem works, but it requires a cache clear before changes made by an editor are seen by anonymous users.

A better solution is, instead of adding the code above to the .theme file, to add this (replace "section" with the content type you're using):

function MYTHEME_preprocess_block(&$vars)
{
  if ($vars['plugin_id'] == 'book_navigation') {
    $vars['#cache']['tags'] = ['node_type:section'];
  }
}

And then add the following to the .module file (again, replace "section" with the content type you're using):

use Drupal\node\NodeInterface;
use Drupal\Core\Cache\Cache;

function MYMODLE_node_presave(NodeInterface $node)
{
  if ($node->getType() == 'section') {
    Cache::invalidateTags(['node_type:section']);
  }
}

function rsc_module_node_delete(NodeInterface $node)
{
  if ($node->getType() == 'section') {
    Cache::invalidateTags(['node_type:section']);
  }
}

This solution retains the benefit of caching the book navigation block and invalidates it only when a section in it is changed or deleted. Thus, both editors and anonymous users see the editor's changes immediately when a change is made to a section.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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