| 1 | | <?php |
| 2 | | // $Id: book.install,v 1.23 2008/10/08 03:27:55 webchick Exp $ |
| 3 | | |
| 4 | | /** |
| 5 | | * Implementation of hook_install(). |
| 6 | | */ |
| 7 | 1 | function book_install() { |
| 8 | | // Create tables. |
| 9 | 1 | drupal_install_schema('book'); |
| 10 | | // Add the node type. |
| 11 | 1 | _book_install_type_create(); |
| 12 | 1 | } |
| 13 | | |
| 14 | | /** |
| 15 | | * Implementation of hook_uninstall(). |
| 16 | | */ |
| 17 | 1 | function book_uninstall() { |
| 18 | | // Delete menu links. |
| 19 | 0 | db_query("DELETE FROM {menu_links} WHERE module = 'book'"); |
| 20 | 0 | menu_cache_clear_all(); |
| 21 | | // Remove tables. |
| 22 | 0 | drupal_uninstall_schema('book'); |
| 23 | 0 | } |
| 24 | | |
| 25 | 1 | function _book_install_type_create() { |
| 26 | | // Create an additional node type. |
| 27 | | $book_node_type = array( |
| 28 | 1 | 'type' => 'book', |
| 29 | 1 | 'name' => t('Book page'), |
| 30 | 1 | 'base' => 'node_content', |
| 31 | 1 | 'description' => t('A <em>book page</em> is a page of content,
organized into a collection of related entries collectively known as a
<em>book</em>. A <em>book page</em> automatically displays links to
adjacent pages, providing a simple navigation system for organizing and
reviewing structured content.'), |
| 32 | 1 | 'custom' => 1, |
| 33 | 1 | 'modified' => 1, |
| 34 | 1 | 'locked' => 0, |
| 35 | 1 | ); |
| 36 | | |
| 37 | 1 | $book_node_type = node_type_set_defaults($book_node_type); |
| 38 | 1 | node_type_save($book_node_type); |
| 39 | | // Default to not promoted. |
| 40 | 1 | variable_set('node_options_book', array('status')); |
| 41 | | // Use this default type for adding content to books. |
| 42 | 1 | variable_set('book_allowed_types', array('book')); |
| 43 | 1 | variable_set('book_child_type', 'book'); |
| 44 | 1 | } |
| 45 | | |
| 46 | | /** |
| 47 | | * Drupal 5.x to 6.x update. |
| 48 | | * |
| 49 | | * This function moves any existing book hierarchy into the new structure
used |
| 50 | | * in the 6.x module. Rather than storing the hierarchy in the {book}
table, |
| 51 | | * the menu API is used to store the hierarchy in the {menu_links} table
and the |
| 52 | | * {book} table serves to uniquely connect a node to a menu link. |
| 53 | | * |
| 54 | | * In order to accomplish this, the current hierarchy is processed using a
stack. |
| 55 | | * The stack insures that each parent is processed before any of its
children |
| 56 | | * in the book hierarchy, and is compatible with batched update processing. |
| 57 | | * |
| 58 | | */ |
| 59 | 1 | function book_update_6000() { |
| 60 | 0 | $ret = array(); |
| 61 | | |
| 62 | | // Set up for a multi-part update. |
| 63 | 0 | if (!isset($_SESSION['book_update_6000'])) { |
| 64 | | |
| 65 | 0 | $schema['book'] = array( |
| 66 | | 'fields' => array( |
| 67 | 0 | 'mlid' => array('type' => 'int', 'unsigned' => TRUE, 'not null'
=> TRUE, 'default' => 0), |
| 68 | 0 | 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null'
=> TRUE, 'default' => 0), |
| 69 | 0 | 'bid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' =>
TRUE, 'default' => 0), |
| 70 | 0 | ), |
| 71 | 0 | 'primary key' => array('mlid'), |
| 72 | | 'unique keys' => array( |
| 73 | 0 | 'nid' => array('nid'), |
| 74 | 0 | ), |
| 75 | | 'indexes' => array( |
| 76 | 0 | 'bid' => array('bid'), |
| 77 | 0 | ), |
| 78 | | ); |
| 79 | | // Add the node type. |
| 80 | 0 | _book_install_type_create(); |
| 81 | | |
| 82 | | // Fix role permissions to account for the changed names |
| 83 | | // Setup the array holding strings to match and the corresponding |
| 84 | | // strings to replace them with. |
| 85 | | $replace = array( |
| 86 | 0 | 'outline posts in books' => 'administer book outlines', |
| 87 | 0 | 'create book pages' => 'create book content', |
| 88 | 0 | 'edit book pages' => 'edit any book content', |
| 89 | 0 | 'edit own book pages' => 'edit own book content', |
| 90 | 0 | 'see printer-friendly version' => 'access printer-friendly version', |
| 91 | 0 | ); |
| 92 | | |
| 93 | | // Loop over all the roles, and do the necessary transformations. |
| 94 | 0 | $query = db_query("SELECT rid, perm FROM {permission} ORDER BY rid"); |
| 95 | 0 | while ($role = db_fetch_object($query)) { |
| 96 | | // Replace all the old permissions with the corresponding new
permissions. |
| 97 | 0 | $fixed_perm = strtr($role->perm, $replace); |
| 98 | | // If the user could previously create book pages, they should get
the new |
| 99 | | // 'add content to books' permission. |
| 100 | 0 | if (strpos($role->perm, 'create book pages') !== FALSE) { |
| 101 | 0 | $fixed_perm .= ', add content to books'; |
| 102 | 0 | } |
| 103 | | // Only save if the permissions have changed. |
| 104 | 0 | if ($fixed_perm != $role->perm) { |
| 105 | 0 | $ret[] = update_sql("UPDATE {permission} SET perm = '$fixed_perm'
WHERE rid = $role->rid"); |
| 106 | 0 | } |
| 107 | 0 | } |
| 108 | | |
| 109 | | // Determine whether there are any existing nodes in the book
hierarchy. |
| 110 | 0 | if (db_result(db_query("SELECT COUNT(*) FROM {book}"))) { |
| 111 | | // Temporary table for the old book hierarchy; we'll discard revision
info. |
| 112 | 0 | $schema['book_temp'] = array( |
| 113 | | 'fields' => array( |
| 114 | 0 | 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null'
=> TRUE, 'default' => 0), |
| 115 | 0 | 'parent' => array('type' => 'int', 'not null' => TRUE, 'default'
=> 0), |
| 116 | 0 | 'weight' => array('type' => 'int', 'not null' => TRUE, 'default'
=> 0, 'size' => 'tiny') |
| 117 | 0 | ), |
| 118 | | 'indexes' => array( |
| 119 | 0 | 'parent' => array('parent') |
| 120 | 0 | ), |
| 121 | 0 | 'primary key' => array('nid'), |
| 122 | | ); |
| 123 | | |
| 124 | 0 | db_create_table($ret, 'book_temp', $schema['book_temp']); |
| 125 | | |
| 126 | | // Insert each node in the old table into the temporary table. |
| 127 | 0 | $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"); |
| 128 | 0 | $ret[] = update_sql("DROP TABLE {book}"); |
| 129 | | |
| 130 | 0 | db_create_table($ret, 'book', $schema['book']); |
| 131 | | |
| 132 | 0 | $_SESSION['book_update_6000_orphans']['from'] = 0; |
| 133 | 0 | $_SESSION['book_update_6000'] = array(); |
| 134 | 0 | $result = db_query("SELECT * from {book_temp} WHERE parent = 0"); |
| 135 | | |
| 136 | | // Collect all books - top-level nodes. |
| 137 | 0 | while ($a = db_fetch_array($result)) { |
| 138 | 0 | $_SESSION['book_update_6000'][] = $a; |
| 139 | 0 | } |
| 140 | 0 | $ret['#finished'] = FALSE; |
| 141 | 0 | return $ret; |
| 142 | 0 | } |
| 143 | | else { |
| 144 | | // No exising nodes in the hierarchy, so drop the table and re-create
it. |
| 145 | 0 | $ret[] = update_sql("DROP TABLE {book}"); |
| 146 | 0 | db_create_table($ret, 'book', $schema['book']); |
| 147 | 0 | return $ret; |
| 148 | | } |
| 149 | 0 | } |
| 150 | 0 | elseif ($_SESSION['book_update_6000_orphans']) { |
| 151 | | // Do the first batched part of the update - collect orphans. |
| 152 | 0 | $update_count = 400; // Update this many at a time. |
| 153 | | |
| 154 | 0 | $result = db_query_range("SELECT * FROM {book_temp}",
$_SESSION['book_update_6000_orphans']['from'], $update_count); |
| 155 | 0 | $has_rows = FALSE; |
| 156 | | // Go through the next $update_count book pages and locate the orphans. |
| 157 | 0 | while ($book = db_fetch_array($result)) { |
| 158 | 0 | $has_rows = TRUE; |
| 159 | | // Orphans are defined as nodes whose parent does not exist in the
table. |
| 160 | 0 | if ($book['parent'] && !db_result(db_query("SELECT COUNT(*) FROM
{book_temp} WHERE nid = %d", $book['parent']))) { |
| 161 | 0 | if (empty($_SESSION['book_update_6000_orphans']['book'])) { |
| 162 | | // The first orphan becomes the parent for all other orphans. |
| 163 | 0 | $book['parent'] = 0; |
| 164 | 0 | $_SESSION['book_update_6000_orphans']['book'] = $book; |
| 165 | 0 | $ret[] = array('success' => TRUE, 'query' => 'Relocated orphan
book pages.'); |
| 166 | 0 | } |
| 167 | | else { |
| 168 | | // Re-assign the parent value of the book, and add it to the
stack. |
| 169 | 0 | $book['parent'] =
$_SESSION['book_update_6000_orphans']['book']['nid']; |
| 170 | 0 | $_SESSION['book_update_6000'][] = $book; |
| 171 | | } |
| 172 | 0 | } |
| 173 | 0 | } |
| 174 | 0 | if ($has_rows) { |
| 175 | 0 | $_SESSION['book_update_6000_orphans']['from'] += $update_count; |
| 176 | 0 | } |
| 177 | | else { |
| 178 | 0 | if (!empty($_SESSION['book_update_6000_orphans']['book'])) { |
| 179 | | // The orphans' parent is added last, so it will be processed
first. |
| 180 | 0 | $_SESSION['book_update_6000'][] =
$_SESSION['book_update_6000_orphans']['book']; |
| 181 | 0 | } |
| 182 | 0 | $_SESSION['book_update_6000_orphans'] = FALSE; |
| 183 | | } |
| 184 | 0 | $ret['#finished'] = FALSE; |
| 185 | | |
| 186 | 0 | return $ret; |
| 187 | 0 | } |
| 188 | | else { |
| 189 | | // Run the next batched part of the update. |
| 190 | 0 | $update_count = 100; // Update this many at a time |
| 191 | | |
| 192 | 0 | while ($update_count && $_SESSION['book_update_6000']) { |
| 193 | | // Get the last node off the stack. |
| 194 | 0 | $book = array_pop($_SESSION['book_update_6000']); |
| 195 | | |
| 196 | | // Add all of this node's children to the stack. |
| 197 | 0 | $result = db_query("SELECT * FROM {book_temp} WHERE parent = %d",
$book['nid']); |
| 198 | 0 | while ($a = db_fetch_array($result)) { |
| 199 | 0 | $_SESSION['book_update_6000'][] = $a; |
| 200 | 0 | } |
| 201 | | |
| 202 | 0 | if ($book['parent']) { |
| 203 | | // If its not a top level page, get its parent's mlid. |
| 204 | 0 | $parent = db_fetch_array(db_query("SELECT b.mlid AS plid, b.bid
FROM {book} b WHERE b.nid = %d", $book['parent'])); |
| 205 | 0 | $book = array_merge($book, $parent); |
| 206 | 0 | } |
| 207 | | else { |
| 208 | | // There is no parent - this is a new book. |
| 209 | 0 | $book['plid'] = 0; |
| 210 | 0 | $book['bid'] = $book['nid']; |
| 211 | | } |
| 212 | | |
| 213 | | $book += array( |
| 214 | 0 | 'module' => 'book', |
| 215 | 0 | 'link_path' => 'node/' . $book['nid'], |
| 216 | 0 | 'router_path' => 'node/%', |
| 217 | 0 | 'menu_name' => 'book-toc-' . $book['bid'], |
| 218 | 0 | ); |
| 219 | 0 | $book = array_merge($book, db_fetch_array(db_query("SELECT title AS
link_title FROM {node} WHERE nid = %d", $book['nid']))); |
| 220 | | |
| 221 | | // Items with depth > MENU_MAX_DEPTH cannot be saved. |
| 222 | 0 | if (menu_link_save($book)) { |
| 223 | 0 | db_query("INSERT INTO {book} (mlid, nid, bid) VALUES (%d, %d, %d)",
$book['mlid'], $book['nid'], $book['bid']); |
| 224 | 0 | } |
| 225 | | else { |
| 226 | | // The depth was greater then MENU_MAX_DEPTH, so attach it to the |
| 227 | | // closest valid parent. |
| 228 | 0 | $book['plid'] = db_result(db_query("SELECT plid FROM {menu_links}
WHERE mlid = %d", $book['plid'])); |
| 229 | 0 | if (menu_link_save($book)) { |
| 230 | 0 | db_query("INSERT INTO {book} (mlid, nid, bid) VALUES (%d, %d,
%d)", $book['mlid'], $book['nid'], $book['bid']); |
| 231 | 0 | } |
| 232 | | } |
| 233 | 0 | $update_count--; |
| 234 | 0 | } |
| 235 | 0 | $ret['#finished'] = FALSE; |
| 236 | | } |
| 237 | | |
| 238 | 0 | if (empty($_SESSION['book_update_6000'])) { |
| 239 | 0 | $ret['#finished'] = TRUE; |
| 240 | 0 | $ret[] = array('success' => TRUE, 'query' => 'Relocated existing book
pages.'); |
| 241 | 0 | $ret[] = update_sql("DROP TABLE {book_temp}"); |
| 242 | 0 | unset($_SESSION['book_update_6000']); |
| 243 | 0 | unset($_SESSION['book_update_6000_orphans']); |
| 244 | 0 | } |
| 245 | | |
| 246 | 0 | return $ret; |
| 247 | 0 | } |
| 248 | | |
| 249 | | /** |
| 250 | | * Implementation of hook_schema(). |
| 251 | | */ |
| 252 | 1 | function book_schema() { |
| 253 | 1 | $schema['book'] = array( |
| 254 | 1 | 'description' => t('Stores book outline information. Uniquely connects
each node in the outline to a link in {menu_links}'), |
| 255 | | 'fields' => array( |
| 256 | | 'mlid' => array( |
| 257 | 1 | 'type' => 'int', |
| 258 | 1 | 'unsigned' => TRUE, |
| 259 | 1 | 'not null' => TRUE, |
| 260 | 1 | 'default' => 0, |
| 261 | 1 | 'description' => t("The book page's {menu_links}.mlid."), |
| 262 | 1 | ), |
| 263 | | 'nid' => array( |
| 264 | 1 | 'type' => 'int', |
| 265 | 1 | 'unsigned' => TRUE, |
| 266 | 1 | 'not null' => TRUE, |
| 267 | 1 | 'default' => 0, |
| 268 | 1 | 'description' => t("The book page's {node}.nid."), |
| 269 | 1 | ), |
| 270 | | 'bid' => array( |
| 271 | 1 | 'type' => 'int', |
| 272 | 1 | 'unsigned' => TRUE, |
| 273 | 1 | 'not null' => TRUE, |
| 274 | 1 | 'default' => 0, |
| 275 | 1 | 'description' => t("The book ID is the {book}.nid of the top-level
page."), |
| 276 | 1 | ), |
| 277 | 1 | ), |
| 278 | 1 | 'primary key' => array('mlid'), |
| 279 | | 'unique keys' => array( |
| 280 | 1 | 'nid' => array('nid'), |
| 281 | 1 | ), |
| 282 | | 'indexes' => array( |
| 283 | 1 | 'bid' => array('bid'), |
| 284 | 1 | ), |
| 285 | | ); |
| 286 | | |
| 287 | 1 | return $schema; |
| 288 | 0 | } |
| 289 | 1 | |