From 99e09111ebd2e778f8170ff3e9310d425f466dd1 Mon Sep 17 00:00:00 2001
From: Rick Jones <rick@activeservice.co.uk>
Date: Fri, 3 Nov 2017 14:37:34 +0000
Subject: [PATCH] by Rick J: Change to avoid marking nodes changed, including
 enhanced VBO action

---
 book_helper.admin.inc |  83 ++++++++++-------------
 book_helper.js        |  10 +--
 book_helper.module    | 183 +++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 211 insertions(+), 65 deletions(-)

diff --git a/book_helper.admin.inc b/book_helper.admin.inc
index 9cd059f..f260a4b 100644
--- a/book_helper.admin.inc
+++ b/book_helper.admin.inc
@@ -173,9 +173,9 @@ function book_helper_admin_edit_validate($form, &$form_state) {
  * @see menu_overview_form_submit()
  */
 function book_helper_admin_edit_submit($form, &$form_state) {
+  $node = $form['#node'];
   // Custom code: Allow top-level pages to be removed from books. http://drupal.org/node/283045
   if ($form_state['values']['op'] == t('Revert')) {
-    $node = $form['#node'];
     menu_link_delete($node->book['mlid']);
     db_delete('book')
       ->condition('bid', $node->nid)
@@ -197,56 +197,41 @@ function book_helper_admin_edit_submit($form, &$form_state) {
   foreach (element_children($form['table']) as $key) {
     if ($form['table'][$key]['#item']) {
       $row = $form['table'][$key];
-      $values = $form_state['values']['table'][$key];
-
-      /* Over-ridden code: Do not delete.
-      // Update menu item if moved.
-      if ($row['plid']['#default_value'] != $values['plid'] || $row['weight']['#default_value'] != $values['weight']) {
-        $row['#item']['plid'] = $values['plid'];
-        $row['#item']['weight'] = $values['weight'];
-        menu_link_save($row['#item']);
-      }
-      */
-
-      // Updated code: Add hidden and collapsed to update menu item if moved.
-      if ( $row['plid']['#default_value'] != $values['plid'] ||
-          $row['weight']['#default_value'] != $values['weight'] ||
-          $row['hidden']['#default_value'] != $values['hidden']
-        ) {
-        $row['#item']['plid'] = $values['plid'];
-        $row['#item']['weight'] = $values['weight'];
-        // Hidden is a special case, the value needs to be reversed.
-        $row['#item']['hidden'] = ($values['hidden']) ? 0 : 1;
-        menu_link_save($row['#item']);
-      }
-
-      // Update the title if changed.
-      if ($row['title']['#default_value'] != $values['title'] || $row['node_title']['#default_value'] != $values['node_title']) {
-        $node = node_load($values['nid']);
-        /* Over-ridden code: Do not delete.
-        $node->title = $values['title'];
-        $node->book['link_title'] = $values['title'];
+      $values['book'] = $form_state['values']['table'][$key];
+      $node = node_load($values['book']['nid']);
+
+      // Fill the menu link values into the node.
+      $link_changed = _book_helper_fill_link($node, $values);
+
+      // Check if node title has changed.
+      $old_title = $row['title']['#default_value'];
+      $new_title = $values['book']['title'];
+      // Do a full node save only if a change of title and revisions are enabled.
+      if ($new_title != $old_title && !variable_get('book_helper_order_disable_revisions', 1)) {
+        $node->title = $new_title;
         $node->revision = 1;
-        */
-
-        // New code: This is where the custom book link title is set.
-        $node->title = $values['node_title'];
-        $node->book['link_title'] = $values['node_title'];
-        $node->book['book_helper_link_title_custom'] = $values['title'];
-        $node->book['book_helper_link_title_sync'] = $values['sync'];
 
-        // Modified code: Allow admin to decide if new revisions should be disabled.
-        $node->revision = (variable_get('book_helper_order_disable_revisions', 1) == 1) ? 0 : 1;
-        menu_cache_clear($node->book['menu_name']);
-
-        $node->log = t('Title changed from %original to %current.', array('%original' => $node->title, '%current' => $values['title']));
+        $node->log = t('Title changed from %original to %current.', array('%original' => $old_title, '%current' => $new_title));
 
+        // This updates everything via hooks
         node_save($node);
-        watchdog('content', 'book: updated %title.', array('%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), 'node/' . $node->nid));
+        watchdog('content', 'book: updated %title.', array('%title' => $new_title), WATCHDOG_NOTICE, l(t('view'), 'node/' . $node->nid));
+      }
+      else {
+        global $user;
+        // Just save the elements according to what's changed.
+        if ($new_title != $old_title) {
+          $node->title = $new_title;
+          // Save the title field directly.
+          drupal_write_record('node', $node, 'nid');
+          _node_save_revision($node, $user->uid, 'vid');
+        }
+        if ($link_changed) {
+          _book_helper_outline_submit($node);
+        }
       }
     }
   }
-
   drupal_set_message(t('Updated book %title.', array('%title' => $form['#node']->title)));
 }
 
@@ -315,7 +300,7 @@ function _book_helper_admin_table_tree($tree, &$form, $bid) {
       'nid' => array('#type' => 'value', '#value' => $data['link']['nid']),
       'depth' => array('#type' => 'value', '#value' => $data['link']['depth']),
       'href' => array('#type' => 'value', '#value' => $data['link']['href']),
-      'title' => array(
+      'link_title' => array(
         '#type' => 'textfield',
         '#default_value' => $data['link']['link_title'],
         '#maxlength' => 255,
@@ -323,13 +308,13 @@ function _book_helper_admin_table_tree($tree, &$form, $bid) {
       ),
 
       // New code: Allows node title to be customized.
-      'node_title' => array(
+      'title' => array(
         '#type' => 'textfield',
         '#default_value' => $node_title,
         '#maxlength' => 255,
         '#size' => 15,
       ),
-      'sync' => array(
+      'link_title_sync' => array(
         '#type' => 'checkbox',
         '#default_value' => ($node_title == $data['link']['link_title']) ? TRUE : FALSE,
       ),
@@ -417,9 +402,9 @@ function theme_book_helper_admin_table($variables) {
       theme('indentation', array('size' => $form[$key]['depth']['#value'] - 2))/* . drupal_render($form[$key]['node_title'])*/,
 
       // New code: Add menu title and sync checkbox.
-      drupal_render($form[$key]['node_title']),
       drupal_render($form[$key]['title']),
-      array('data' => drupal_render($form[$key]['sync']), 'class' => 'checkbox'),
+      drupal_render($form[$key]['link_title']),
+      array('data' => drupal_render($form[$key]['link_title_sync']), 'class' => 'checkbox'),
 
       drupal_render($form[$key]['weight']),
       drupal_render($form[$key]['plid']) . drupal_render($form[$key]['mlid']),
diff --git a/book_helper.js b/book_helper.js
index 3cb1b1a..ee2a4d6 100644
--- a/book_helper.js
+++ b/book_helper.js
@@ -35,7 +35,7 @@ Drupal.bookHelperSyncTitles  = function (syncSelector, mainTitleSelector, second
 // Hide/show book outline fields.
 Drupal.bookHelperToggleSync = function() {
   var effect = ($('#edit-book-bid').val() == '0') ? 'hide' : 'show';
-  $('.form-item-book-book-helper-link-title-custom, .form-item-book-book-helper-link-title-sync, .form-item-book-weight')[effect]();
+  $('.form-item-book-link-title, .form-item-book-link-title-sync, .form-item-book-weight')[effect]();
 }
 
 // Book helper behaviors
@@ -49,9 +49,9 @@ Drupal.behaviors.bookHelper = {
 
       // Sync book titles on node edit form.
       Drupal.bookHelperSyncTitles(
-        '#edit-book-book-helper-link-title-sync',
+        '#edit-book-link-title-sync',
         '#edit-title',
-        '#edit-book-book-helper-link-title-custom'
+        '#edit-book-link-title'
       );
     }
 
@@ -61,9 +61,9 @@ Drupal.behaviors.bookHelper = {
         // edit-table-book-admin-{nid}-sync
         var syncSelector = '#' + this.id;
         // edit-table-book-admin-{nid}-node-title
-        var mainTitleSelector = syncSelector.replace('-sync', '-node-title');
+        var mainTitleSelector = syncSelector.replace('-link-title-sync', '-title');
         // edit-table-book-admin-{nid}-title
-        var secondaryTitleSelector = syncSelector.replace('-sync', '-title');
+        var secondaryTitleSelector = syncSelector.replace('-sync', '');
 
         Drupal.bookHelperSyncTitles(syncSelector, mainTitleSelector, secondaryTitleSelector);
       });
diff --git a/book_helper.module b/book_helper.module
index 0903b84..fb3421e 100644
--- a/book_helper.module
+++ b/book_helper.module
@@ -149,14 +149,6 @@ function book_helper_node_insert($node) {
  * Implements hook_node_update().
  */
 function book_helper_node_update($node) {
-  // Get link title sync and custom from the book.module's array().
-  if (isset($node->book['book_helper_link_title_sync'])) {
-    $node->book_helper['link_title_sync'] = $node->book['book_helper_link_title_sync'];
-  }
-  if (isset($node->book['book_helper_link_title_custom'])) {
-    $node->book_helper['link_title_custom'] = $node->book['book_helper_link_title_custom'];
-  }
-
   // Update the book page's menu link title if it has been customized.
   if (!empty($node->book['bid']) && isset($node->book_helper) && !empty($node->book_helper['link_title_custom']) && isset($node->book_helper['link_title_sync']) && !$node->book_helper['link_title_sync'] && $node->book_helper['link_title_custom'] != $node->book['link_title']) {
     db_update('menu_links')
@@ -179,7 +171,7 @@ function book_helper_node_load($nodes, $types) {
       $node_title = (function_exists('node_parent_title_remove')) ? node_parent_title_remove($nodes[$record->nid]->title) : $nodes[$record->nid]->title;
       $nodes[$record->nid]->book_helper = array(
         'link_title_custom' => $record->link_title,
-        'link_title_sync' => ($record->link_title == $node_title) ? TRUE : FALSE,
+        'link_title_sync' => $record->link_title == $node_title,
       );
     }
   }
@@ -275,21 +267,96 @@ function book_helper_form_node_form_alter(&$form, &$form_state) {
 
     // Add inputs to allow user to customize the book link title separately from the node's title.
     drupal_add_js(drupal_get_path('module', 'book_helper') . '/book_helper.js');
-    $form['book']['book_helper_link_title_custom'] = array(
+    $form['book']['link_title'] = array(
       '#type' => 'textfield',
       '#title' => t('Book link title'),
       '#default_value' => $node->book_helper['link_title_custom'],
       '#maxlength' => 255,
     );
 
-    $form['book']['book_helper_link_title_sync'] = array(
+    $form['book']['link_title_sync'] = array(
       '#type' => 'checkbox',
       '#title' => t("Synchronize this node's title with its book link title."),
       '#default_value' => $node->book_helper['link_title_sync'],
     );
+
+    // If not a new node, add button to update book outline only.
+    if (isset($node->nid)) {
+      $form['book']['book_helper_submit'] = array(
+        '#type' => 'submit',
+        '#value' => t("Update book outline"),
+        '#weight' => 30,
+        '#submit' => array('book_helper_outline_submit'),
+      );
+    }
+    // Add button to remove from book.
+    // The #access function ensures the button is only displayed where relevant.
+    $form['book']['book_helper_remove'] = array(
+      '#type' => 'submit',
+      '#value' => t("Remove from book"),
+      '#access' => _book_node_is_removable($node),
+      '#weight' => 35,
+      '#submit' => array('book_helper_remove_submit'),
+    );
   }
 }
 
+function book_helper_outline_submit($form, &$form_state) {
+  $node = $form['#node'];
+  if (_book_helper_fill_link($node, $form_state['values'])) {
+    _book_helper_outline_submit($node);
+  }
+}
+
+function book_helper_remove_submit($form, &$form_state) {
+  $form_state['redirect'] = 'node/' . $form['#node']->nid . '/outline/remove';
+}
+
+/**
+ * Fill the menu link and custom title values into the node.
+ *
+ * @return
+ *   TRUE if anything was changed, else FALSE
+ */
+function _book_helper_fill_link($node, &$values) {
+  // Populate node from form values.
+  if (isset($values['book']['hidden'])) {
+    // Value of 'hidden' on the form means "enabled", so is inverse of hidden flag in menu.
+    $hidden = $values['book']['hidden']? 0 : 1;
+  }
+  // Set the $changed flag if any value has changed.
+  $changed = isset($hidden) && $node->book['hidden'] != $hidden ||
+    isset($values['book']['bid']) && $node->book['bid'] != $values['book']['bid'] ||
+    $node->book['plid'] != $values['book']['plid'] ||
+    $node->book['weight'] != $values['book']['weight'] ||
+    $node->book_helper['link_title_sync'] != $values['book']['link_title_sync'] ||
+    $node->book_helper['link_title_custom'] != $values['book']['link_title']
+  ;
+  // If anything changed, set all values.
+  if ($changed) {
+    if (isset($hidden)) {
+      $node->book['hidden'] = $hidden;
+    }
+    if (isset($values['book']['bid'])) {
+      $node->book['bid'] = $values['book']['bid'];
+    }
+    $node->book['plid'] = $values['book']['plid'];
+    $node->book['weight'] = $values['book']['weight'];
+    $node->book_helper['link_title_sync'] = $values['book']['link_title_sync'];
+    $node->book_helper['link_title_custom'] = $values['book']['link_title'];
+  }
+  return $changed;
+}
+
+function _book_helper_outline_submit($node) {
+  // Update the standard book outline.
+  _book_update_outline($node);
+  // Update the menu link title override.
+  book_helper_node_update($node);
+  // Clear the menu cache (as advised after invoking menu_link_save(), which is done indirectly above).
+  menu_cache_clear_all();
+}
+
 /**
  * Display a warning warning when deleting a book page that has child pages.
  */
@@ -421,3 +488,97 @@ function book_helper_admin_paths() {
     return $paths;
   }
 }
+
+/**
+ * Action to provide an "Add to book" function for VBO.
+ *
+ * This is similar to VBO's built-in "Move to book", but avoids
+ * marking the node as updated.
+ *
+ * Implements hook_action_info().
+ *
+ */
+function book_helper_action_info() {
+  $actions = array();
+  $actions['book_helper_action'] = array(
+    'type' => 'node',
+    'label' => t('Add to book'),
+    'vbo_configurable' => TRUE,
+    'configurable' => TRUE,
+    'behavior' => array('views_property'),
+    'triggers' => array('any'),
+    );
+  return $actions;
+}
+
+function book_helper_action_views_bulk_operations_form($options) {
+//   $books = array_column(book_get_books(), 'title');
+  $all_books = book_get_books();
+  foreach ($all_books as $value) {
+    $books[$value['nid']] = $value['title'];
+  }
+  $form['books'] = array(
+    '#type' => 'select',
+    '#multiple' => TRUE,
+    '#title' => t('Select books to offer'),
+    '#options' => $books,
+    '#default_value' => !empty($options['books']) ? $options['books'] : array(),
+  );
+  $form['depth'] = array(
+    '#type' => 'select',
+    '#title' => t('Page depth'),
+    '#options' => array(1 => 1, 2, 3, 4),
+    '#default_value' => !empty($options['depth']) ? $options['depth'] : 1,
+  );
+  return $form;
+}
+
+function book_helper_action_form($context) {
+  $settings = &$context['settings'];
+  $books = $settings['books'];
+  $depth = $settings['depth'];
+  $all_books = book_get_books();
+  $options = array();
+  foreach ($all_books as $book) {
+    if (empty($books) || in_array($book['nid'], $books)) {
+      $options += book_toc($book['nid'], $depth);
+    }
+  }
+
+  if (empty($options)) {
+    drupal_set_message(t('You have no books.'), 'error');
+    return array();
+  }
+
+  $form['book'] = array(
+    '#type' => 'select',
+    '#title' => t('Choose a parent book page'),
+    '#options' => $options,
+    '#description' => t('Select the parent book page you wish to move the book page into'),
+    );
+    return $form;
+}
+
+function book_helper_action_submit($form, $form_state) {
+  return array('book' => $form_state['values']['book']);
+}
+
+function book_helper_action($node, $context = array()) {
+  if (isset($context['book'])) {
+    // The selected value from the form is the mlid of the page we want
+    // as the parent of this page - i.e. its plid.
+    $plid = $context['book'];
+    // Use this to lookup the bid in the book table.
+    $bid = db_select('book' , 'bk')
+      ->condition('bk.mlid' , $plid)
+      ->fields('bk' , array('bid'))
+      ->execute()
+      ->fetchField();
+    $node->book['bid'] = $bid;
+    $node->book['plid'] = $plid;
+    $node->book['module'] = 'book';
+
+    _book_update_outline($node);
+    menu_cache_clear_all();
+  }
+}
-- 
2.11.0

