'book', 'name' => t('Book page'), 'module' => 'node', 'description' => t('A book page is a page of content, organized into a collection of related entries collectively known as a book. A book page automatically displays links to adjacent pages, providing a simple navigation system for organizing and reviewing structured content.'), 'custom' => TRUE, 'modified' => TRUE, 'locked' => FALSE, ); $book_node_type = (object)_node_type_set_defaults($book_node_type); node_type_save($book_node_type); // Default to not promoted. variable_set('node_options_book', array('status')); // Use this default type for adding content to books. variable_set('book_allowed_types', array('book')); variable_set('book_child_type', 'book'); } /** * Drupal 5.x to 6.x update. * * This function moves any existing book hierarchy into the new structure used * in the 6.x module. Rather than storing the hierarchy in the {book} table, * the menu API is used to store the hierarchy in the {menu_links} table and the * {book} table serves to uniquely connect a node to a menu link. * * In order to accomplish this, the current hierarchy is processed using a stack. * The stack insures that each parent is processed before any of its children * in the book hierarchy, and is compatible with batched update processing. * */ function book_update_6000() { $ret = array(); // Set up for a multi-part update. if (!isset($_SESSION['book_update_6000'])) { $schema['book'] = array( 'fields' => array( 'mlid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'bid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), ), 'primary key' => array('mlid'), 'unique keys' => array( 'nid' => array('nid'), ), 'indexes' => array( 'bid' => array('bid'), ), ); // Add the node type. _book_install_type_create(); // Fix role permissions to account for the changed names // Setup the array holding strings to match and the corresponding // strings to replace them with. $replace = array( 'outline posts in books' => 'administer book outlines', 'create book pages' => 'create book content', 'edit book pages' => 'edit any book content', 'edit own book pages' => 'edit own book content', 'see printer-friendly version' => 'access printer-friendly version', ); // Loop over all the roles, and do the necessary transformations. $query = db_query("SELECT rid, perm FROM {permission} ORDER BY rid"); while ($role = db_fetch_object($query)) { // Replace all the old permissions with the corresponding new permissions. $fixed_perm = strtr($role->perm, $replace); // If the user could previously create book pages, they should get the new // 'add content to books' permission. if (strpos($role->perm, 'create book pages') !== FALSE) { $fixed_perm .= ', add content to books'; } // Only save if the permissions have changed. if ($fixed_perm != $role->perm) { $ret[] = update_sql("UPDATE {permission} SET perm = '$fixed_perm' WHERE rid = $role->rid"); } } // Determine whether there are any existing nodes in the book hierarchy. if (db_result(db_query("SELECT COUNT(*) FROM {book}"))) { // Temporary table for the old book hierarchy; we'll discard revision info. $schema['book_temp'] = array( 'fields' => array( 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'parent' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), 'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'tiny') ), 'indexes' => array( 'parent' => array('parent') ), 'primary key' => array('nid'), ); db_create_table($ret, 'book_temp', $schema['book_temp']); // Insert each node in the old table into the temporary table. $ret[] = update_sql("INSERT INTO {book_temp} (nid, parent, weight) SELECT b.nid, b.parent, b.weight FROM {book} b INNER JOIN {node} n on b.vid = n.vid"); $ret[] = update_sql("DROP TABLE {book}"); db_create_table($ret, 'book', $schema['book']); $_SESSION['book_update_6000_orphans']['from'] = 0; $_SESSION['book_update_6000'] = array(); $result = db_query("SELECT * from {book_temp} WHERE parent = 0"); // Collect all books - top-level nodes. while ($a = db_fetch_array($result)) { $_SESSION['book_update_6000'][] = $a; } $ret['#finished'] = FALSE; return $ret; } else { // No exising nodes in the hierarchy, so drop the table and re-create it. $ret[] = update_sql("DROP TABLE {book}"); db_create_table($ret, 'book', $schema['book']); return $ret; } } elseif ($_SESSION['book_update_6000_orphans']) { // Do the first batched part of the update - collect orphans. $update_count = 400; // Update this many at a time $result = db_query_range("SELECT * FROM {book_temp}", $_SESSION['book_update_6000_orphans']['from'], $update_count); $has_rows = FALSE; // Go through the next $update_count book pages and locate the orphans. while ($book = db_fetch_array($result)) { $has_rows = TRUE; // Orphans are defined as nodes whose parent does not exist in the table. if ($book['parent'] && !db_result(db_query("SELECT COUNT(*) FROM {book_temp} WHERE nid = %d", $book['parent']))) { if (empty($_SESSION['book_update_6000_orphans']['book'])) { // The first orphan becomes the parent for all other orphans. $book['parent'] = 0; $_SESSION['book_update_6000_orphans']['book'] = $book; $ret[] = array('success' => TRUE, 'query' => 'Relocated orphan book pages.'); } else { // Re-assign the parent value of the book, and add it to the stack. $book['parent'] = $_SESSION['book_update_6000_orphans']['book']['nid']; $_SESSION['book_update_6000'][] = $book; } } } if ($has_rows) { $_SESSION['book_update_6000_orphans']['from'] += $update_count; } else { // Done with this part if (!empty($_SESSION['book_update_6000_orphans']['book'])) { // The orphans' parent is added last, so it will be processed first. $_SESSION['book_update_6000'][] = $_SESSION['book_update_6000_orphans']['book']; } $_SESSION['book_update_6000_orphans'] = FALSE; } $ret['#finished'] = FALSE; return $ret; } else { // Do the next batched part of the update $update_count = 100; // Update this many at a time while ($update_count && $_SESSION['book_update_6000']) { // Get the last node off the stack. $book = array_pop($_SESSION['book_update_6000']); // Add all of this node's children to the stack $result = db_query("SELECT * FROM {book_temp} WHERE parent = %d", $book['nid']); while ($a = db_fetch_array($result)) { $_SESSION['book_update_6000'][] = $a; } if ($book['parent']) { // If its not a top level page, get its parent's mlid. $parent = db_fetch_array(db_query("SELECT b.mlid AS plid, b.bid FROM {book} b WHERE b.nid = %d", $book['parent'])); $book = array_merge($book, $parent); } else { // There is not a parent - this is a new book. $book['plid'] = 0; $book['bid'] = $book['nid']; } $book += array( 'module' => 'book', 'link_path' => 'node/'. $book['nid'], 'router_path' => 'node/%', 'menu_name' => 'book-toc-'. $book['bid'], ); $book = array_merge($book, db_fetch_array(db_query("SELECT title AS link_title FROM {node} WHERE nid = %d", $book['nid']))); // Items with depth > MENU_MAX_DEPTH cannot be saved. if (menu_link_save($book)) { db_query("INSERT INTO {book} (mlid, nid, bid) VALUES (%d, %d, %d)", $book['mlid'], $book['nid'], $book['bid']); } else { // The depth was greater then MENU_MAX_DEPTH, so attach it to the // closest valid parent. $book['plid'] = db_result(db_query("SELECT plid FROM {menu_links} WHERE mlid = %d", $book['plid'])); if (menu_link_save($book)) { db_query("INSERT INTO {book} (mlid, nid, bid) VALUES (%d, %d, %d)", $book['mlid'], $book['nid'], $book['bid']); } } $update_count--; } $ret['#finished'] = FALSE; } if (empty($_SESSION['book_update_6000'])) { $ret['#finished'] = TRUE; $ret[] = array('success' => TRUE, 'query' => 'Relocated existing book pages.'); $ret[] = update_sql("DROP TABLE {book_temp}"); unset($_SESSION['book_update_6000']); unset($_SESSION['book_update_6000_orphans']); } return $ret; } /** * Implementation of hook_schema(). */ function book_schema() { $schema['book'] = array( 'description' => 'Stores book outline information. Uniquely connects each node in the outline to a link in {menu_links}', 'fields' => array( 'mlid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => "The book page's {menu_links}.mlid.", ), 'nid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => "The book page's {node}.nid.", ), 'bid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => "The book ID is the {book}.nid of the top-level page.", ), ), 'primary key' => array('mlid'), 'unique keys' => array( 'nid' => array('nid'), ), 'indexes' => array( 'bid' => array('bid'), ), ); return $schema; }