From 73656e523b66573813805cc0cf504fda4cebc945 Mon Sep 17 00:00:00 2001
From: Gisle Hannemyr <gisle@hannemyr.no>
Date: Sun, 18 May 2014 16:41:53 +0200
Subject: [PATCH] by gisle: Initial D7 port.

---
 README.txt       | 134 +++++++++++++++++++-----
 html2book.info   |   5 +-
 html2book.module | 307 +++++++++++++++++++++++++++++++++----------------------
 3 files changed, 296 insertions(+), 150 deletions(-)

diff --git a/README.txt b/README.txt
index 8e57f35..83ad17f 100644
--- a/README.txt
+++ b/README.txt
@@ -1,35 +1,105 @@
-Html2Book adds an option below book page body fields to automatically create 
-a new book page for each html heading and subheading in the body text. This 
-makes it possible to create a book in a word processing program, then use 
-the resulting html to create a multipage Drupal book in a single step.
+HTML2BOOK README.txt
+====================
 
-Each new book page will have the same author, categories, settings, and other 
-characteristics of the original page. If Organic Groups is used and the original
-page has been assigned to one or more groups, all child book pages will belong to
-the same groups.
 
-All text before the first heading will be retained as the body of the original 
-page. Subsequent pages will be added as children of that page, using the 
-heading as their title and all text from that point to the next heading as 
-their body. Child pages will be nested based on the subheadding number, if 
-the subheadings are logically organized.
+CONTENTS OF THIS FILE
+---------------------
 
-For best results, combine with Html Corrector and HTML Tidy modules. 
+* Introduction
+* Installation
+* Use
+* Example
+* Configuration
+* Maintainers
 
-EXAMPLE:
+
+INTRODUCTION
+------------
+
+The HTML2Book module allows you to create multi-page books by
+splitting the body text of a book node into multiple nodes upon save.
+The split points is based upon HTML heading tags.
+
+This makes it possible to create a book in a word processing program,
+export the result to HTML, and then use this HTML to create a
+multipage Drupal book in a single step.
+
+
+INSTALLATION
+------------
+
+1. Extract the HTML2Body project directory into the directory where
+   you keep contributed modules (e.g. sites/all/modules/).
+
+2. Enable the HTML2Body module on the Modules list page.
+
+3. Navigate to the Admin » People » Permissions page and give the
+   roles you will grant access to create multi-page books by means of
+   this module the permission "Use HTML2Book".
+
+
+USE
+---
+
+The module adds a field group named "HTML2Book Splitter" below book
+page "Body" field.
+
+Expanding this field group allows the author to create a new book page
+for each HTML heading in the body text.
+
+Each new book page will have the same author, categories, settings,
+and other characteristics of the original page.
+
+All text before the first HTML heading will be retained as the body of
+the original (book root) page. Subsequent pages will be added as
+children of that page, using the heading as their title and all text
+from that point to the next heading as their body. Child pages will be
+nested based on the subheading number, provided the subheadings are
+logically organized.
+
+You can choose which heading levels will be used to create new pages.
+You may, for example, create new pages only when a <h1> heading is
+encountered, or make a new page at every header.
+
+If Organic Groups is used and the original page has been assigned to
+one or more groups, all child book pages will belong to the same
+groups.
+
+If the source is a word processing document, be sure it has been saved
+as html rather than as a word processing document so the HTML2Book
+module can locate the headings in the text. Microsoft Word documents
+pasted into the body should come from text first saved as 'Filtered
+HTML' for best results. Badly structured documents will yield
+unpredictable results, clean and simple html will work the best.
+
+For best results, combine with Html Corrector and HTML Tidy modules.
+When setting up HTML Tidy, choose the option to clean up Microsoft
+Word text if the source is Microsoft Word.
+
+
+EXAMPLE
+-------
 
 Node 1 Title: My Book
 
-Node 1 Body:
-<div>Here is my page.</div>
+Here is my page.  This text is the introduction.
+
 <h1>Page 1</h1>
-  <p>Here is my text for page 1.</p>
-  <h2>Page 1a</h2>
-    <p>This is page 1a.</p>
-  <h2>Page 1b</h2>
-    <p>This is page 1b.</p>
+
+<p>Here is the first line of my text for page 1.</p>
+<p>Here is the second line of my text for page 1.</p>
+
+<h2>Page 1a</h2>
+
+<p>This is page 1a.</p>
+
+<h2>Page 1b</h2>
+
+<p>This is page 1b.</p>
+
 <h1>Page 2</h1>
-  <p>This is page 2.</p>
+
+<p>This is page 2 and the last line of text.</p>
 
 Will create the following book pages:
 
@@ -58,6 +128,18 @@ Node 1 Weight: -15
     Node 5 Parent: Node 1
     Node 5 Weight: -14
 
-For best results with html book text pasted from Microsoft Word documents, 
-save the document as 'HTML, filtered' and use the Html Tidy module. When 
-setting up HTML Tidy, choose the option to clean up Microsoft Word text. 
+
+CONFIGURATION
+-------------
+
+The module has no menu or modifiable settings.  There is no
+configuration.  When enabled, the module will add the field group
+named "HTML2Book Splitter" below book page "Body" field.  To removed
+this field group, disable the module and clear caches.
+
+
+CREDITS
+-------
+
+KarenS (https://drupal.org/user/45874) is the original author.
+Gisle Hannemyr <gisle@hannemyr.no> ported it to Drupal 7.
diff --git a/html2book.info b/html2book.info
index df1a849..fca98a5 100644
--- a/html2book.info
+++ b/html2book.info
@@ -1,4 +1,5 @@
 name = HTML2Book
-description = Add option to split book body into new book pages at each HTML heading in the text.
-core = 6.x
+description = "Add option to split book body into new book pages at each HTML heading in the text."
+package = Other
+core = 7.x
 dependencies[] = book
diff --git a/html2book.module b/html2book.module
index f0186ca..62a8d81 100644
--- a/html2book.module
+++ b/html2book.module
@@ -1,5 +1,8 @@
 <?php
 
+// Global array to hold the pages.
+$html2book_pages = array();
+
 /**
  * Implementation of hook_help().
  */
@@ -57,14 +60,19 @@ Node 1 Weight: -15
 }
 
 /**
- * Implementation of hook_perm().
+ * Implements hook_permission().
  */
-function html2book_perm() {
-  return array('use html2book');
+function html2book_permission() {
+  return array(
+    'use html2book' => array(
+      'title' => t('Use HTML2Book'),
+      'description' => t('Create multi-page books based upon HTML markup.'),
+    ),
+  );
 }
 
 /**
- * Implementation of hook_form_alter().
+ * Implements hook_form_alter().
  */
 function html2book_form_alter(&$form, $form_state, $form_id) {
   if ($form_id == 'book_node_form' && user_access('use html2book')) {
@@ -73,14 +81,17 @@ function html2book_form_alter(&$form, $form_state, $form_id) {
       '#title' => t('HTML2Book Splitter'),
       '#collapsible' => TRUE,
       '#collapsed' => TRUE,
-      );
+    );
     $form['html2book_group']['html2book'] = array(
       '#type' => 'radios',
       '#title' => t('Heading treatment'),
       '#description' => t('Should this text be !help  at each HTML heading?', array('!help' => l(t('split into new book pages'), 'admin/help/html2book'))),
-      '#default_value' => $form['html2book'] ? $form['html2book'] : 0,
-      '#options' => array(0 => t('Do not split text into book pages.'), 1 => t('Create a new book page at each HTML heading.')),
-      );
+      '#default_value' => 1,
+      '#options' => array(
+        0 => t('Do not split text into book pages.'),
+        1 => t('Create a new book page at each HTML heading.'),
+      ),
+    );
     $form['html2book_group']['splitters'] = array(
       '#type' => 'checkboxes',
       '#title' => t('Split on'),
@@ -95,142 +106,194 @@ function html2book_form_alter(&$form, $form_state, $form_id) {
         '7' => '&lt;h7&gt;',
       ),
       '#default_value' => array(1, 2, 3),
-    );  
+    );
   }
 }
 
 /**
- * Implementation of hook_nodeapi().
- *
- * @TODO Might be nice to have a confirmation message.
+ * Implements hook_node_presave().
  */
-function html2book_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+function html2book_node_presave($node) {
+   global $html2book_pages;
+
+   if (!empty($html2book_pages)) {
+     return;
+   }
+
   if ($node->type == 'book' && !empty($node->html2book) && user_access('use html2book')) {
-    switch ($op) {
-    case 'update':  
-    case 'insert':
-      html2book_split($node, $op);
-      unset($node->temp);
-      break;
+    _html2book_split($node);
+
+    // Check if root page is already part of a book.
+    // Create a new book if it is not.
+    if ($node->book['bid'] == 0) {
+      //$node->book['bid'] = $node->nid;
+      $node->book = array(
+        'bid' =>'new', // use 'new' to create new top level book
+        'plid' => 0,   // this is the top level
+      );
     }
+
+    // Set a parent nid for this level that can be used by later children.
+    $node->parent = $node->nid;
   }
 }
 
+
 /**
- * html2book operation.
+ * Implements hook_node_insert().
  */
-function html2book_split(&$node, $op) {
-  
-  // This may take some time since new nodes are created.
-  // @TODO It would be better to make this into a batch script,
-  // but the processing is all intertwined, so it's hard to see how to do it.
-  $original_time = ini_get('max_execution_time');
-  ini_set('max_execution_time', 0);
+function html2book_node_insert($node) {
+  if (!empty($node->html2book)) {
+   _html2book_node_expand($node);
+  }
+}
 
-  // Break body into separate pages wherever there is a heading.
-  // Identify the heading numbers we want to use to create a new book.
-  $splitters = array_filter(array_values($node->splitters));
-  $items = preg_split('/<h(['. implode('|', $splitters) .'].*)>/U', $node->temp, -1, PREG_SPLIT_DELIM_CAPTURE );
 
-  $node->temp = $node->body;
-  $node->body = $items[0];
-  $node->teaser = node_teaser($node->body);
-  
-  // Track heading levels, weights, and page parents and children.
+/**
+ * Implements hook_node_update().
+ */
+function html2book_node_update($node) {
+  _html2book_node_expand($node);
+}
+
+
+/**
+ * Split node into an page array.
+ */
+function _html2book_split($node) {
+   global $html2book_pages;
+
+  $lang = $node->language;
+  if (!isset($node->body[$lang])) {
+    $lang =  LANGUAGE_NONE;
+  }
+  $body = $node->body[$lang][0]['value'];
+
+  // Break up the body into separate pages wherever there is a heading
+  // flagged as a splitter.
+  $splitters = array_filter(array_values($node->splitters));
+  $items = preg_split('/<h(['. implode('|', $splitters) .'].*)>/U', $body, -1, PREG_SPLIT_DELIM_CAPTURE);
+  $ii = $level = 0;
+  $label = '';
+  //$html2book_pages = array();
   $istag = FALSE;
-  $weight[0] = -15;
-  $prev_level = 0;
+
   foreach ($items as $item) {
     if ($istag) {
-      $parts = explode(' ', $item);
-      $level = $parts[0];
-    }
-    else {
-      if (isset($level)) {
-        $string = '<h'. $level . '>'. $item;
-        $split = explode('</h'. $level .'>', $string);
-        $title = trim(str_replace('&nbsp;', ' ', strip_tags($split[0])));
-        $body = trim($split[1]);
-      }
-      else {
-        // If $level isn't set, this is the body text that preceeds the first heading.
-        // This will become the parent node's body text.
-        $title = '';
-        $body = $item;
-      }
-      if (!empty($title) || !empty($body) || !isset($parent)) {
-        if (!isset($parent)) {
-          // The first time through, make sure we have a nid for the parent
-          // and adjust the parent body and teaser.
-          $parent = array(0 => $node->nid);
-          $book = array(0 => $node);
-          //Update hack. save_node spirals into a infinite loop, so 
-          //check if the initial page is part of a book. If not -
-          //create a new book (:-)
-          if($op == 'update'){
-			      if ($node->book['bid'] == 0){
-					    module_load_include('inc', 'book', 'book.pages');
-					    $formvals = array();
-					    $formvals['values']['book']['bid'] = $node->nid;
-					    $formvals['values']['book']['weight'] = 0;
-					    drupal_execute('book_outline_form', $formvals, (object)$node);
-				    }
-			    }
-          if ($op == 'insert'){
-			      if($node->book['bid'] == 0 || !isset($node->book['bid'])){
-						  $node->book['bid'] = $node->nid;
-						  node_save($node);
-				    }
-			
-			    }
-			    $page = 1;
-        }
-        else {
-          // After the first time through, clone the parent as a starting point for child nodes.
-          $child_node = drupal_clone($node);
-          // Set the child nid and vid to zero so new nodes are created.
-          $child_node->nid = 0;
-          $child_node->vid = 0;
-          // Make sure the title is not empty and create the child's body, teaser, and weight.
-          $child_node->title = !empty($title) ? $title : t('Page !$page', array('!page' => $page));
-          $child_node->body = $body;
-          $child_node->teaser = node_teaser($body);
-
-          if ($level > $prev_level || !isset($weight[$level])) {
-            $weight[$level] = -15;
-          }
-          else {
-            // There might be too many pages to increase this forever.
-            // Don't overrun the book module max weight.
-            if ($weight[$level] <= 15) $weight[$level]++;
-          }
-          $child_node->book['weight'] = $weight[$level];
-
-          // Find the parent id for this node using the value stored for the
-          // next higher level.
-          $parent_level = intval($level - 1);
-          $child_node->parent = !empty($parent[$parent_level]) ? $parent[$parent_level] : $node->nid;
-
-          $child_node->book['bid'] = $parent[0];
-          $child_node->book['menu_name'] = $book[0]->book['menu_name'];
-          $child_node->book['plid'] = $book[$parent_level]->book['mlid'];
-
-          // Make sure html2book is not run again on the child nodes that are created.
-          $child_node->html2book = 0;
-          // Save the child node.
-          node_save($child_node);
-          // Set a parent nid for this level that can be used by later children.
-          $parent[$level] = $child_node->nid;
-          $book[$level] = $child_node;
-          $page++;
+      $label = '';
+      $headele = preg_split('/ /', $item);
+      if (isset($headele[1])) {
+        if (preg_match('/id=\"(.*)\"/', $headele[1], $matches)) {
+          $label = $matches[1];
         }
       }
+      $level = $headele[0];
+    }
+    else {
+      //preg_match('/<\/h(['. implode('|', $splitters) .'].*)>/', $item, $matches);
+      //$level = (isset($matches[1])) ? $matches[1] : 0;
+      $html2book_pages[$ii]['level'] = $level;
+      $html2book_pages[$ii]['label'] = $label;
+      $html2book_pages[$ii]['cntnt'] = $item;
+      $ii++;
     }
     $istag = !$istag;
-    $prev_level = $level;
   }
-  // Make sure html2book isn't run more than once on the same text.
-  $node->html2book = 0;
+
+  // Array $html2book_pages holds all our pages.
+  $node->body[$lang][0]['value'] = $html2book_pages[0]['cntnt'];
+  array_shift($html2book_pages);
+}
+
+/**
+ * Expand array $html2book_pages into nodes
+ */
+function _html2book_node_expand($node) {
+  global $html2book_pages;
+
+  $lang = $node->language;
+  if (!isset($node->body[$lang])) {
+    $lang =  LANGUAGE_NONE;
+  }
+
+  // Set the three arrays that keeps track at each level.
+  $parent = array(
+    0 => $node->nid
+  );
+  $bookml = array(
+    0 => $node->book['mlid'],
+  );
+  $weight = array(
+    0 => -15,
+  );
+
+  $prev_level = 0;
+  $pageno = 1;
+
+  // This loop may take some time since new nodes are created.
+  // @TODO It would be better to make this into a batch script,
+  // but the processing is all intertwined, so it's hard to see how to do it.
+  $original_time = ini_get('max_execution_time');
+  //ini_set('max_execution_time', 0);
+
+
+  foreach ($html2book_pages as $page) {
+    $level = $page['level'];
+    $label = $page['label'];
+
+    $nodecntnt = '<h'. $level . '>' . $page['cntnt'];
+    $split = explode('</h'. $level .'>', $nodecntnt);
+    $title = trim(str_replace('&nbsp;', ' ', strip_tags($split[0])));
+    $body = trim($split[1]);
+
+    // After the first time through, clone the parent as a starting point for child nodes.
+    $child_node = clone($node);
+    // Set the child nid and vid to zero so new nodes are created.
+    $child_node->nid = 0;
+    $child_node->vid = 0;
+    // Make sure the title is never an empty string.
+    $child_node->title = $title ? $title : t('Page !pageno', array('!pageno' => $pageno));
+
+    // Create the child's body.
+    $child_node->body[$lang][0]['value'] = $body;
+
+    // Create the child's weight.
+    if ($level > $prev_level || !isset($weight[$level])) {
+      $weight[$level] = -15;
+    }
+    else {
+      // There might be too many pages to increase this forever.
+      // Don't overrun the book module max weight.
+      if ($weight[$level] <= 15) $weight[$level]++;
+    }
+    $child_node->book['weight'] = $weight[$level];
+
+    // Find the parent id for this node using the value stored for the
+    // next higher level.
+    $parent_level = intval($level - 1);
+    $child_node->parent = $parent[$parent_level] ? $parent[$parent_level] : $node->nid;
+
+    $child_node->book['bid']  = $parent[0];
+    $child_node->book['plid'] = $bookml[$parent_level];
+
+    // Make sure html2book is not run again on the child nodes.
+    $child_node->html2book = 0;
+    $child_node->is_new = TRUE;
+    node_save($child_node);
+
+    // Set a parent nid and bookml mlid for this level that can be
+    // used by later children.
+    $parent[$level] = $child_node->nid;
+    $bookml[$level] = $child_node->book['mlid'];
+
+    $prev_level = $level;
+    $pageno++;
+  } // foreach page
+
   // Reset max_execution_time
   ini_set('max_execution_time', $original_time);
+
+  if ($pageno > 1) {
+    drupal_set_message(t('Body field was expanded into !pageno pages by HTML2Book module.', array('!pageno' => $pageno)));
+  }
 }
-- 
1.8.2.1

