You are here

function book_update_6000 in Drupal 6

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.

File

modules/book/book.install, line 58

Code

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;
}