webmaster@1
|
1 <?php |
webmaster@5
|
2 // $Id: book.module,v 1.454.2.3 2008/03/25 14:03:02 goba Exp $ |
webmaster@1
|
3 |
webmaster@1
|
4 /** |
webmaster@1
|
5 * @file |
webmaster@1
|
6 * Allows users to structure the pages of a site in a hierarchy or outline. |
webmaster@1
|
7 */ |
webmaster@1
|
8 |
webmaster@1
|
9 /** |
webmaster@1
|
10 * Implementation of hook_theme() |
webmaster@1
|
11 */ |
webmaster@1
|
12 function book_theme() { |
webmaster@1
|
13 return array( |
webmaster@1
|
14 'book_navigation' => array( |
webmaster@1
|
15 'arguments' => array('book_link' => NULL), |
webmaster@1
|
16 'template' => 'book-navigation', |
webmaster@1
|
17 ), |
webmaster@1
|
18 'book_export_html' => array( |
webmaster@1
|
19 'arguments' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL), |
webmaster@1
|
20 'template' => 'book-export-html', |
webmaster@1
|
21 ), |
webmaster@1
|
22 'book_admin_table' => array( |
webmaster@1
|
23 'arguments' => array('form' => NULL), |
webmaster@1
|
24 ), |
webmaster@1
|
25 'book_title_link' => array( |
webmaster@1
|
26 'arguments' => array('link' => NULL), |
webmaster@1
|
27 ), |
webmaster@1
|
28 'book_all_books_block' => array( |
webmaster@1
|
29 'arguments' => array('book_menus' => array()), |
webmaster@1
|
30 'template' => 'book-all-books-block', |
webmaster@1
|
31 ), |
webmaster@1
|
32 'book_node_export_html' => array( |
webmaster@1
|
33 'arguments' => array('node' => NULL, 'children' => NULL), |
webmaster@1
|
34 'template' => 'book-node-export-html', |
webmaster@1
|
35 ), |
webmaster@1
|
36 ); |
webmaster@1
|
37 } |
webmaster@1
|
38 |
webmaster@1
|
39 /** |
webmaster@1
|
40 * Implementation of hook_perm(). |
webmaster@1
|
41 */ |
webmaster@1
|
42 function book_perm() { |
webmaster@1
|
43 return array('add content to books', 'administer book outlines', 'create new books', 'access printer-friendly version'); |
webmaster@1
|
44 } |
webmaster@1
|
45 |
webmaster@1
|
46 /** |
webmaster@1
|
47 * Implementation of hook_link(). |
webmaster@1
|
48 */ |
webmaster@1
|
49 function book_link($type, $node = NULL, $teaser = FALSE) { |
webmaster@1
|
50 $links = array(); |
webmaster@1
|
51 |
webmaster@1
|
52 if ($type == 'node' && isset($node->book)) { |
webmaster@1
|
53 if (!$teaser) { |
webmaster@1
|
54 $child_type = variable_get('book_child_type', 'book'); |
webmaster@1
|
55 if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH) { |
webmaster@1
|
56 $links['book_add_child'] = array( |
webmaster@1
|
57 'title' => t('Add child page'), |
webmaster@1
|
58 'href' => "node/add/". str_replace('_', '-', $child_type), |
webmaster@1
|
59 'query' => "parent=". $node->book['mlid'], |
webmaster@1
|
60 ); |
webmaster@1
|
61 } |
webmaster@1
|
62 if (user_access('access printer-friendly version')) { |
webmaster@1
|
63 $links['book_printer'] = array( |
webmaster@1
|
64 'title' => t('Printer-friendly version'), |
webmaster@1
|
65 'href' => 'book/export/html/'. $node->nid, |
webmaster@1
|
66 'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.')) |
webmaster@1
|
67 ); |
webmaster@1
|
68 } |
webmaster@1
|
69 } |
webmaster@1
|
70 } |
webmaster@1
|
71 return $links; |
webmaster@1
|
72 } |
webmaster@1
|
73 |
webmaster@1
|
74 /** |
webmaster@1
|
75 * Implementation of hook_menu(). |
webmaster@1
|
76 */ |
webmaster@1
|
77 function book_menu() { |
webmaster@1
|
78 $items['admin/content/book'] = array( |
webmaster@1
|
79 'title' => 'Books', |
webmaster@1
|
80 'description' => "Manage your site's book outlines.", |
webmaster@1
|
81 'page callback' => 'book_admin_overview', |
webmaster@1
|
82 'access arguments' => array('administer book outlines'), |
webmaster@1
|
83 'file' => 'book.admin.inc', |
webmaster@1
|
84 ); |
webmaster@1
|
85 $items['admin/content/book/list'] = array( |
webmaster@1
|
86 'title' => 'List', |
webmaster@1
|
87 'type' => MENU_DEFAULT_LOCAL_TASK, |
webmaster@1
|
88 ); |
webmaster@1
|
89 $items['admin/content/book/settings'] = array( |
webmaster@1
|
90 'title' => 'Settings', |
webmaster@1
|
91 'page callback' => 'drupal_get_form', |
webmaster@1
|
92 'page arguments' => array('book_admin_settings'), |
webmaster@1
|
93 'access arguments' => array('administer site configuration'), |
webmaster@1
|
94 'type' => MENU_LOCAL_TASK, |
webmaster@1
|
95 'weight' => 8, |
webmaster@1
|
96 'file' => 'book.admin.inc', |
webmaster@1
|
97 ); |
webmaster@1
|
98 $items['admin/content/book/%node'] = array( |
webmaster@1
|
99 'title' => 'Re-order book pages and change titles', |
webmaster@1
|
100 'page callback' => 'drupal_get_form', |
webmaster@1
|
101 'page arguments' => array('book_admin_edit', 3), |
webmaster@1
|
102 'access callback' => '_book_outline_access', |
webmaster@1
|
103 'access arguments' => array(3), |
webmaster@1
|
104 'type' => MENU_CALLBACK, |
webmaster@1
|
105 'file' => 'book.admin.inc', |
webmaster@1
|
106 ); |
webmaster@1
|
107 $items['book'] = array( |
webmaster@1
|
108 'title' => 'Books', |
webmaster@1
|
109 'page callback' => 'book_render', |
webmaster@1
|
110 'access arguments' => array('access content'), |
webmaster@1
|
111 'type' => MENU_SUGGESTED_ITEM, |
webmaster@1
|
112 'file' => 'book.pages.inc', |
webmaster@1
|
113 ); |
webmaster@1
|
114 $items['book/export/%/%'] = array( |
webmaster@1
|
115 'page callback' => 'book_export', |
webmaster@1
|
116 'page arguments' => array(2, 3), |
webmaster@1
|
117 'access arguments' => array('access printer-friendly version'), |
webmaster@1
|
118 'type' => MENU_CALLBACK, |
webmaster@1
|
119 'file' => 'book.pages.inc', |
webmaster@1
|
120 ); |
webmaster@1
|
121 $items['node/%node/outline'] = array( |
webmaster@1
|
122 'title' => 'Outline', |
webmaster@1
|
123 'page callback' => 'book_outline', |
webmaster@1
|
124 'page arguments' => array(1), |
webmaster@1
|
125 'access callback' => '_book_outline_access', |
webmaster@1
|
126 'access arguments' => array(1), |
webmaster@1
|
127 'type' => MENU_LOCAL_TASK, |
webmaster@1
|
128 'weight' => 2, |
webmaster@1
|
129 'file' => 'book.pages.inc', |
webmaster@1
|
130 ); |
webmaster@1
|
131 $items['node/%node/outline/remove'] = array( |
webmaster@1
|
132 'title' => 'Remove from outline', |
webmaster@1
|
133 'page callback' => 'drupal_get_form', |
webmaster@1
|
134 'page arguments' => array('book_remove_form', 1), |
webmaster@1
|
135 'access callback' => '_book_outline_remove_access', |
webmaster@1
|
136 'access arguments' => array(1), |
webmaster@1
|
137 'type' => MENU_CALLBACK, |
webmaster@1
|
138 'file' => 'book.pages.inc', |
webmaster@1
|
139 ); |
webmaster@1
|
140 $items['book/js/form'] = array( |
webmaster@1
|
141 'page callback' => 'book_form_update', |
webmaster@1
|
142 'access arguments' => array('access content'), |
webmaster@1
|
143 'type' => MENU_CALLBACK, |
webmaster@1
|
144 'file' => 'book.pages.inc', |
webmaster@1
|
145 ); |
webmaster@1
|
146 return $items; |
webmaster@1
|
147 } |
webmaster@1
|
148 |
webmaster@1
|
149 /** |
webmaster@1
|
150 * Menu item access callback - determine if the outline tab is accessible. |
webmaster@1
|
151 */ |
webmaster@1
|
152 function _book_outline_access($node) { |
webmaster@1
|
153 return user_access('administer book outlines') && node_access('view', $node); |
webmaster@1
|
154 } |
webmaster@1
|
155 |
webmaster@1
|
156 /** |
webmaster@1
|
157 * Menu item access callback - determine if the user can remove nodes from the outline. |
webmaster@1
|
158 */ |
webmaster@1
|
159 function _book_outline_remove_access($node) { |
webmaster@1
|
160 return isset($node->book) && ($node->book['bid'] != $node->nid) && _book_outline_access($node); |
webmaster@1
|
161 } |
webmaster@1
|
162 |
webmaster@1
|
163 /** |
webmaster@1
|
164 * Implementation of hook_init(). Add's the book module's CSS. |
webmaster@1
|
165 */ |
webmaster@1
|
166 function book_init() { |
webmaster@1
|
167 drupal_add_css(drupal_get_path('module', 'book') .'/book.css'); |
webmaster@1
|
168 } |
webmaster@1
|
169 |
webmaster@1
|
170 /** |
webmaster@1
|
171 * Implementation of hook_block(). |
webmaster@1
|
172 * |
webmaster@1
|
173 * Displays the book table of contents in a block when the current page is a |
webmaster@1
|
174 * single-node view of a book node. |
webmaster@1
|
175 */ |
webmaster@1
|
176 function book_block($op = 'list', $delta = 0, $edit = array()) { |
webmaster@1
|
177 $block = array(); |
webmaster@1
|
178 switch ($op) { |
webmaster@1
|
179 case 'list': |
webmaster@1
|
180 $block[0]['info'] = t('Book navigation'); |
webmaster@1
|
181 $block[0]['cache'] = BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_ROLE; |
webmaster@1
|
182 return $block; |
webmaster@1
|
183 case 'view': |
webmaster@1
|
184 $current_bid = 0; |
webmaster@1
|
185 if ($node = menu_get_object()) { |
webmaster@1
|
186 $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid']; |
webmaster@1
|
187 } |
webmaster@1
|
188 if (variable_get('book_block_mode', 'all pages') == 'all pages') { |
webmaster@1
|
189 $block['subject'] = t('Book navigation'); |
webmaster@1
|
190 $book_menus = array(); |
webmaster@1
|
191 $pseudo_tree = array(0 => array('below' => FALSE)); |
webmaster@1
|
192 foreach (book_get_books() as $book_id => $book) { |
webmaster@1
|
193 if ($book['bid'] == $current_bid) { |
webmaster@1
|
194 // If the current page is a node associated with a book, the menu |
webmaster@1
|
195 // needs to be retrieved. |
webmaster@1
|
196 $book_menus[$book_id] = menu_tree_output(menu_tree_all_data($node->book['menu_name'], $node->book)); |
webmaster@1
|
197 } |
webmaster@1
|
198 else { |
webmaster@1
|
199 // Since we know we will only display a link to the top node, there |
webmaster@1
|
200 // is no reason to run an additional menu tree query for each book. |
webmaster@1
|
201 $book['in_active_trail'] = FALSE; |
webmaster@1
|
202 $pseudo_tree[0]['link'] = $book; |
webmaster@1
|
203 $book_menus[$book_id] = menu_tree_output($pseudo_tree); |
webmaster@1
|
204 } |
webmaster@1
|
205 } |
webmaster@1
|
206 $block['content'] = theme('book_all_books_block', $book_menus); |
webmaster@1
|
207 } |
webmaster@1
|
208 elseif ($current_bid) { |
webmaster@1
|
209 // Only display this block when the user is browsing a book. |
webmaster@1
|
210 $title = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $node->book['bid'])); |
webmaster@1
|
211 // Only show the block if the user has view access for the top-level node. |
webmaster@1
|
212 if ($title) { |
webmaster@1
|
213 $tree = menu_tree_all_data($node->book['menu_name'], $node->book); |
webmaster@1
|
214 // There should only be one element at the top level. |
webmaster@1
|
215 $data = array_shift($tree); |
webmaster@1
|
216 $block['subject'] = theme('book_title_link', $data['link']); |
webmaster@1
|
217 $block['content'] = ($data['below']) ? menu_tree_output($data['below']) : ''; |
webmaster@1
|
218 } |
webmaster@1
|
219 } |
webmaster@1
|
220 return $block; |
webmaster@1
|
221 case 'configure': |
webmaster@1
|
222 $options = array( |
webmaster@1
|
223 'all pages' => t('Show block on all pages'), |
webmaster@1
|
224 'book pages' => t('Show block only on book pages'), |
webmaster@1
|
225 ); |
webmaster@1
|
226 $form['book_block_mode'] = array( |
webmaster@1
|
227 '#type' => 'radios', |
webmaster@1
|
228 '#title' => t('Book navigation block display'), |
webmaster@1
|
229 '#options' => $options, |
webmaster@1
|
230 '#default_value' => variable_get('book_block_mode', 'all pages'), |
webmaster@1
|
231 '#description' => t("If <em>Show block on all pages</em> is selected, the block will contain the automatically generated menus for all of the site's books. If <em>Show block only on book pages</em> is selected, the block will contain only the one menu corresponding to the current page's book. In this case, if the current page is not in a book, no block will be displayed. The <em>Page specific visibility settings</em> or other visibility settings can be used in addition to selectively display this block."), |
webmaster@1
|
232 ); |
webmaster@1
|
233 return $form; |
webmaster@1
|
234 case 'save': |
webmaster@1
|
235 variable_set('book_block_mode', $edit['book_block_mode']); |
webmaster@1
|
236 break; |
webmaster@1
|
237 } |
webmaster@1
|
238 } |
webmaster@1
|
239 |
webmaster@1
|
240 /** |
webmaster@1
|
241 * Generate the HTML output for a link to a book title when used as a block title. |
webmaster@1
|
242 * |
webmaster@1
|
243 * @ingroup themeable |
webmaster@1
|
244 */ |
webmaster@1
|
245 function theme_book_title_link($link) { |
webmaster@1
|
246 $link['options']['attributes']['class'] = 'book-title'; |
webmaster@1
|
247 return l($link['title'], $link['href'], $link['options']); |
webmaster@1
|
248 } |
webmaster@1
|
249 |
webmaster@1
|
250 /** |
webmaster@1
|
251 * Returns an array of all books. |
webmaster@1
|
252 * |
webmaster@1
|
253 * This list may be used for generating a list of all the books, or for building |
webmaster@1
|
254 * the options for a form select. |
webmaster@1
|
255 */ |
webmaster@1
|
256 function book_get_books() { |
webmaster@1
|
257 static $all_books; |
webmaster@1
|
258 |
webmaster@1
|
259 if (!isset($all_books)) { |
webmaster@1
|
260 $all_books = array(); |
webmaster@1
|
261 $result = db_query("SELECT DISTINCT(bid) FROM {book}"); |
webmaster@1
|
262 $nids = array(); |
webmaster@1
|
263 while ($book = db_fetch_array($result)) { |
webmaster@1
|
264 $nids[] = $book['bid']; |
webmaster@1
|
265 } |
webmaster@1
|
266 if ($nids) { |
webmaster@1
|
267 $result2 = db_query(db_rewrite_sql("SELECT n.type, n.title, b.*, ml.* FROM {book} b INNER JOIN {node} n on b.nid = n.nid INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE n.nid IN (". implode(',', $nids) .") AND n.status = 1 ORDER BY ml.weight, ml.link_title")); |
webmaster@1
|
268 while ($link = db_fetch_array($result2)) { |
webmaster@1
|
269 $link['href'] = $link['link_path']; |
webmaster@1
|
270 $link['options'] = unserialize($link['options']); |
webmaster@1
|
271 $all_books[$link['bid']] = $link; |
webmaster@1
|
272 } |
webmaster@1
|
273 } |
webmaster@1
|
274 } |
webmaster@1
|
275 return $all_books; |
webmaster@1
|
276 } |
webmaster@1
|
277 |
webmaster@1
|
278 /** |
webmaster@1
|
279 * Implementation of hook_form_alter(). Adds the book fieldset to the node form. |
webmaster@1
|
280 * |
webmaster@1
|
281 * @see book_pick_book_submit() |
webmaster@1
|
282 * @see book_submit() |
webmaster@1
|
283 */ |
webmaster@1
|
284 function book_form_alter(&$form, $form_state, $form_id) { |
webmaster@1
|
285 |
webmaster@1
|
286 if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) { |
webmaster@1
|
287 // Add elements to the node form |
webmaster@1
|
288 $node = $form['#node']; |
webmaster@1
|
289 |
webmaster@1
|
290 $access = user_access('administer book outlines'); |
webmaster@1
|
291 if (!$access) { |
webmaster@1
|
292 if (user_access('add content to books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) { |
webmaster@1
|
293 // Already in the book hierarchy or this node type is allowed |
webmaster@1
|
294 $access = TRUE; |
webmaster@1
|
295 } |
webmaster@1
|
296 } |
webmaster@1
|
297 |
webmaster@1
|
298 if ($access) { |
webmaster@1
|
299 _book_add_form_elements($form, $node); |
webmaster@1
|
300 $form['book']['pick-book'] = array( |
webmaster@1
|
301 '#type' => 'submit', |
webmaster@1
|
302 '#value' => t('Change book (update list of parents)'), |
webmaster@1
|
303 // Submit the node form so the parent select options get updated. |
webmaster@1
|
304 // This is typically only used when JS is disabled. Since the parent options |
webmaster@1
|
305 // won't be changed via AJAX, a button is provided in the node form to submit |
webmaster@1
|
306 // the form and generate options in the parent select corresponding to the |
webmaster@1
|
307 // selected book. This is similar to what happens during a node preview. |
webmaster@1
|
308 '#submit' => array('node_form_submit_build_node'), |
webmaster@1
|
309 '#weight' => 20, |
webmaster@1
|
310 ); |
webmaster@1
|
311 } |
webmaster@1
|
312 } |
webmaster@1
|
313 } |
webmaster@1
|
314 |
webmaster@1
|
315 /** |
webmaster@1
|
316 * Build the parent selection form element for the node form or outline tab |
webmaster@1
|
317 * |
webmaster@1
|
318 * This function is also called when generating a new set of options during the |
webmaster@1
|
319 * AJAX callback, so an array is returned that can be used to replace an existing |
webmaster@1
|
320 * form element. |
webmaster@1
|
321 */ |
webmaster@1
|
322 function _book_parent_select($book_link) { |
webmaster@1
|
323 if (variable_get('menu_override_parent_selector', FALSE)) { |
webmaster@1
|
324 return array(); |
webmaster@1
|
325 } |
webmaster@1
|
326 // Offer a message or a drop-down to choose a different parent page. |
webmaster@1
|
327 $form = array( |
webmaster@1
|
328 '#type' => 'hidden', |
webmaster@1
|
329 '#value' => -1, |
webmaster@1
|
330 '#prefix' => '<div id="edit-book-plid-wrapper">', |
webmaster@1
|
331 '#suffix' => '</div>', |
webmaster@1
|
332 ); |
webmaster@1
|
333 |
webmaster@1
|
334 if ($book_link['nid'] === $book_link['bid']) { |
webmaster@1
|
335 // This is a book - at the top level. |
webmaster@1
|
336 if ($book_link['original_bid'] === $book_link['bid']) { |
webmaster@1
|
337 $form['#prefix'] .= '<em>'. t('This is the top-level page in this book.') .'</em>'; |
webmaster@1
|
338 } |
webmaster@1
|
339 else { |
webmaster@1
|
340 $form['#prefix'] .= '<em>'. t('This will be the top-level page in this book.') .'</em>'; |
webmaster@1
|
341 } |
webmaster@1
|
342 } |
webmaster@1
|
343 elseif (!$book_link['bid']) { |
webmaster@1
|
344 $form['#prefix'] .= '<em>'. t('No book selected.') .'</em>'; |
webmaster@1
|
345 } |
webmaster@1
|
346 else { |
webmaster@1
|
347 $form = array( |
webmaster@1
|
348 '#type' => 'select', |
webmaster@1
|
349 '#title' => t('Parent item'), |
webmaster@1
|
350 '#default_value' => $book_link['plid'], |
webmaster@1
|
351 '#description' => t('The parent page in the book. The maximum depth for a book and all child pages is !maxdepth. Some pages in the selected book may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)), |
webmaster@1
|
352 '#options' => book_toc($book_link['bid'], array($book_link['mlid']), $book_link['parent_depth_limit']), |
webmaster@1
|
353 '#attributes' => array('class' => 'book-title-select'), |
webmaster@1
|
354 ); |
webmaster@1
|
355 } |
webmaster@1
|
356 return $form; |
webmaster@1
|
357 } |
webmaster@1
|
358 |
webmaster@1
|
359 /** |
webmaster@1
|
360 * Build the common elements of the book form for the node and outline forms. |
webmaster@1
|
361 */ |
webmaster@1
|
362 function _book_add_form_elements(&$form, $node) { |
webmaster@1
|
363 // Need this for AJAX. |
webmaster@1
|
364 $form['#cache'] = TRUE; |
webmaster@1
|
365 drupal_add_js("if (Drupal.jsEnabled) { $(document).ready(function() { $('#edit-book-pick-book').css('display', 'none'); }); }", 'inline'); |
webmaster@1
|
366 |
webmaster@1
|
367 $form['book'] = array( |
webmaster@1
|
368 '#type' => 'fieldset', |
webmaster@1
|
369 '#title' => t('Book outline'), |
webmaster@1
|
370 '#weight' => 10, |
webmaster@1
|
371 '#collapsible' => TRUE, |
webmaster@1
|
372 '#collapsed' => TRUE, |
webmaster@1
|
373 '#tree' => TRUE, |
webmaster@1
|
374 '#attributes' => array('class' => 'book-outline-form'), |
webmaster@1
|
375 ); |
webmaster@1
|
376 foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) { |
webmaster@1
|
377 $form['book'][$key] = array( |
webmaster@1
|
378 '#type' => 'value', |
webmaster@1
|
379 '#value' => $node->book[$key], |
webmaster@1
|
380 ); |
webmaster@1
|
381 } |
webmaster@1
|
382 |
webmaster@1
|
383 $form['book']['plid'] = _book_parent_select($node->book); |
webmaster@1
|
384 |
webmaster@1
|
385 $form['book']['weight'] = array( |
webmaster@1
|
386 '#type' => 'weight', |
webmaster@1
|
387 '#title' => t('Weight'), |
webmaster@1
|
388 '#default_value' => $node->book['weight'], |
webmaster@1
|
389 '#delta' => 15, |
webmaster@1
|
390 '#weight' => 5, |
webmaster@1
|
391 '#description' => t('Pages at a given level are ordered first by weight and then by title.'), |
webmaster@1
|
392 ); |
webmaster@1
|
393 $options = array(); |
webmaster@1
|
394 $nid = isset($node->nid) ? $node->nid : 'new'; |
webmaster@1
|
395 |
webmaster@1
|
396 if (isset($node->nid) && ($nid == $node->book['original_bid']) && ($node->book['parent_depth_limit'] == 0)) { |
webmaster@1
|
397 // This is the top level node in a maximum depth book and thus cannot be moved. |
webmaster@1
|
398 $options[$node->nid] = $node->title; |
webmaster@1
|
399 } |
webmaster@1
|
400 else { |
webmaster@1
|
401 foreach (book_get_books() as $book) { |
webmaster@1
|
402 $options[$book['nid']] = $book['title']; |
webmaster@1
|
403 } |
webmaster@1
|
404 } |
webmaster@1
|
405 |
webmaster@1
|
406 if (user_access('create new books') && ($nid == 'new' || ($nid != $node->book['original_bid']))) { |
webmaster@1
|
407 // The node can become a new book, if it is not one already. |
webmaster@1
|
408 $options = array($nid => '<'. t('create a new book') .'>') + $options; |
webmaster@1
|
409 } |
webmaster@1
|
410 if (!$node->book['mlid']) { |
webmaster@1
|
411 // The node is not currently in a the hierarchy. |
webmaster@1
|
412 $options = array(0 => '<'. t('none') .'>') + $options; |
webmaster@1
|
413 } |
webmaster@1
|
414 |
webmaster@1
|
415 // Add a drop-down to select the destination book. |
webmaster@1
|
416 $form['book']['bid'] = array( |
webmaster@1
|
417 '#type' => 'select', |
webmaster@1
|
418 '#title' => t('Book'), |
webmaster@1
|
419 '#default_value' => $node->book['bid'], |
webmaster@1
|
420 '#options' => $options, |
webmaster@1
|
421 '#access' => (bool)$options, |
webmaster@1
|
422 '#description' => t('Your page will be a part of the selected book.'), |
webmaster@1
|
423 '#weight' => -5, |
webmaster@1
|
424 '#attributes' => array('class' => 'book-title-select'), |
webmaster@1
|
425 '#ahah' => array( |
webmaster@1
|
426 'path' => 'book/js/form', |
webmaster@1
|
427 'wrapper' => 'edit-book-plid-wrapper', |
webmaster@1
|
428 'effect' => 'slide', |
webmaster@1
|
429 ), |
webmaster@1
|
430 ); |
webmaster@1
|
431 } |
webmaster@1
|
432 |
webmaster@1
|
433 /** |
webmaster@1
|
434 * Common helper function to handles additions and updates to the book outline. |
webmaster@1
|
435 * |
webmaster@1
|
436 * Performs all additions and updates to the book outline through node addition, |
webmaster@1
|
437 * node editing, node deletion, or the outline tab. |
webmaster@1
|
438 */ |
webmaster@1
|
439 function _book_update_outline(&$node) { |
webmaster@1
|
440 if (empty($node->book['bid'])) { |
webmaster@1
|
441 return FALSE; |
webmaster@1
|
442 } |
webmaster@1
|
443 $new = empty($node->book['mlid']); |
webmaster@1
|
444 |
webmaster@1
|
445 $node->book['link_path'] = 'node/'. $node->nid; |
webmaster@1
|
446 $node->book['link_title'] = $node->title; |
webmaster@1
|
447 $node->book['parent_mismatch'] = FALSE; // The normal case. |
webmaster@1
|
448 |
webmaster@1
|
449 if ($node->book['bid'] == $node->nid) { |
webmaster@1
|
450 $node->book['plid'] = 0; |
webmaster@1
|
451 $node->book['menu_name'] = book_menu_name($node->nid); |
webmaster@1
|
452 } |
webmaster@1
|
453 else { |
webmaster@1
|
454 // Check in case the parent is not is this book; the book takes precedence. |
webmaster@1
|
455 if (!empty($node->book['plid'])) { |
webmaster@1
|
456 $parent = db_fetch_array(db_query("SELECT * FROM {book} WHERE mlid = %d", $node->book['plid'])); |
webmaster@1
|
457 } |
webmaster@1
|
458 if (empty($node->book['plid']) || !$parent || $parent['bid'] != $node->book['bid']) { |
webmaster@1
|
459 $node->book['plid'] = db_result(db_query("SELECT mlid FROM {book} WHERE nid = %d", $node->book['bid'])); |
webmaster@1
|
460 $node->book['parent_mismatch'] = TRUE; // Likely when JS is disabled. |
webmaster@1
|
461 } |
webmaster@1
|
462 } |
webmaster@1
|
463 if (menu_link_save($node->book)) { |
webmaster@1
|
464 if ($new) { |
webmaster@1
|
465 // Insert new. |
webmaster@1
|
466 db_query("INSERT INTO {book} (nid, mlid, bid) VALUES (%d, %d, %d)", $node->nid, $node->book['mlid'], $node->book['bid']); |
webmaster@1
|
467 } |
webmaster@1
|
468 else { |
webmaster@1
|
469 if ($node->book['bid'] != db_result(db_query("SELECT bid FROM {book} WHERE nid = %d", $node->nid))) { |
webmaster@1
|
470 // Update the bid for this page and all children. |
webmaster@1
|
471 book_update_bid($node->book); |
webmaster@1
|
472 } |
webmaster@1
|
473 } |
webmaster@1
|
474 return TRUE; |
webmaster@1
|
475 } |
webmaster@1
|
476 // Failed to save the menu link |
webmaster@1
|
477 return FALSE; |
webmaster@1
|
478 } |
webmaster@1
|
479 |
webmaster@1
|
480 /** |
webmaster@1
|
481 * Update the bid for a page and its children when it is moved to a new book. |
webmaster@1
|
482 * |
webmaster@1
|
483 * @param $book_link |
webmaster@1
|
484 * A fully loaded menu link that is part of the book hierarchy. |
webmaster@1
|
485 */ |
webmaster@1
|
486 function book_update_bid($book_link) { |
webmaster@1
|
487 |
webmaster@1
|
488 for ($i = 1; $i <= MENU_MAX_DEPTH && $book_link["p$i"]; $i++) { |
webmaster@1
|
489 $match[] = "p$i = %d"; |
webmaster@1
|
490 $args[] = $book_link["p$i"]; |
webmaster@1
|
491 } |
webmaster@1
|
492 $result = db_query("SELECT mlid FROM {menu_links} WHERE ". implode(' AND ', $match), $args); |
webmaster@1
|
493 |
webmaster@1
|
494 $mlids = array(); |
webmaster@1
|
495 while ($a = db_fetch_array($result)) { |
webmaster@1
|
496 $mlids[] = $a['mlid']; |
webmaster@1
|
497 } |
webmaster@1
|
498 if ($mlids) { |
webmaster@1
|
499 db_query("UPDATE {book} SET bid = %d WHERE mlid IN (". implode(',', $mlids) .")", $book_link['bid']); |
webmaster@1
|
500 } |
webmaster@1
|
501 } |
webmaster@1
|
502 |
webmaster@1
|
503 /** |
webmaster@1
|
504 * Get the book menu tree for a page, and return it as a linear array. |
webmaster@1
|
505 * |
webmaster@1
|
506 * @param $book_link |
webmaster@1
|
507 * A fully loaded menu link that is part of the book hierarchy. |
webmaster@1
|
508 * @return |
webmaster@1
|
509 * A linear array of menu links in the order that the links are shown in the |
webmaster@1
|
510 * menu, so the previous and next pages are the elements before and after the |
webmaster@1
|
511 * element corresponding to $node. The children of $node (if any) will come |
webmaster@1
|
512 * immediately after it in the array. |
webmaster@1
|
513 */ |
webmaster@1
|
514 function book_get_flat_menu($book_link) { |
webmaster@1
|
515 static $flat = array(); |
webmaster@1
|
516 |
webmaster@1
|
517 if (!isset($flat[$book_link['mlid']])) { |
webmaster@1
|
518 // Call menu_tree_full_data() to take advantage of the menu system's caching. |
webmaster@1
|
519 $tree = menu_tree_all_data($book_link['menu_name'], $book_link); |
webmaster@1
|
520 $flat[$book_link['mlid']] = array(); |
webmaster@1
|
521 _book_flatten_menu($tree, $flat[$book_link['mlid']]); |
webmaster@1
|
522 } |
webmaster@1
|
523 return $flat[$book_link['mlid']]; |
webmaster@1
|
524 } |
webmaster@1
|
525 |
webmaster@1
|
526 /** |
webmaster@1
|
527 * Recursive helper function for book_get_flat_menu(). |
webmaster@1
|
528 */ |
webmaster@1
|
529 function _book_flatten_menu($tree, &$flat) { |
webmaster@1
|
530 foreach ($tree as $data) { |
webmaster@1
|
531 if (!$data['link']['hidden']) { |
webmaster@1
|
532 $flat[$data['link']['mlid']] = $data['link']; |
webmaster@1
|
533 if ($data['below']) { |
webmaster@1
|
534 _book_flatten_menu($data['below'], $flat); |
webmaster@1
|
535 } |
webmaster@1
|
536 } |
webmaster@1
|
537 } |
webmaster@1
|
538 } |
webmaster@1
|
539 |
webmaster@1
|
540 /** |
webmaster@1
|
541 * Fetches the menu link for the previous page of the book. |
webmaster@1
|
542 */ |
webmaster@1
|
543 function book_prev($book_link) { |
webmaster@1
|
544 // If the parent is zero, we are at the start of a book. |
webmaster@1
|
545 if ($book_link['plid'] == 0) { |
webmaster@1
|
546 return NULL; |
webmaster@1
|
547 } |
webmaster@1
|
548 $flat = book_get_flat_menu($book_link); |
webmaster@1
|
549 // Assigning the array to $flat resets the array pointer for use with each(). |
webmaster@1
|
550 $curr = NULL; |
webmaster@1
|
551 do { |
webmaster@1
|
552 $prev = $curr; |
webmaster@1
|
553 list($key, $curr) = each($flat); |
webmaster@1
|
554 } while ($key && $key != $book_link['mlid']); |
webmaster@1
|
555 |
webmaster@1
|
556 if ($key == $book_link['mlid']) { |
webmaster@1
|
557 // The previous page in the book may be a child of the previous visible link. |
webmaster@1
|
558 if ($prev['depth'] == $book_link['depth'] && $prev['has_children']) { |
webmaster@1
|
559 // The subtree will have only one link at the top level - get its data. |
webmaster@1
|
560 $data = array_shift(book_menu_subtree_data($prev)); |
webmaster@1
|
561 // The link of interest is the last child - iterate to find the deepest one. |
webmaster@1
|
562 while ($data['below']) { |
webmaster@1
|
563 $data = end($data['below']); |
webmaster@1
|
564 } |
webmaster@1
|
565 return $data['link']; |
webmaster@1
|
566 } |
webmaster@1
|
567 else { |
webmaster@1
|
568 return $prev; |
webmaster@1
|
569 } |
webmaster@1
|
570 } |
webmaster@1
|
571 } |
webmaster@1
|
572 |
webmaster@1
|
573 /** |
webmaster@1
|
574 * Fetches the menu link for the next page of the book. |
webmaster@1
|
575 */ |
webmaster@1
|
576 function book_next($book_link) { |
webmaster@1
|
577 $flat = book_get_flat_menu($book_link); |
webmaster@1
|
578 // Assigning the array to $flat resets the array pointer for use with each(). |
webmaster@1
|
579 do { |
webmaster@1
|
580 list($key, $curr) = each($flat); |
webmaster@1
|
581 } while ($key && $key != $book_link['mlid']); |
webmaster@1
|
582 if ($key == $book_link['mlid']) { |
webmaster@1
|
583 return current($flat); |
webmaster@1
|
584 } |
webmaster@1
|
585 } |
webmaster@1
|
586 |
webmaster@1
|
587 /** |
webmaster@1
|
588 * Format the menu links for the child pages of the current page. |
webmaster@1
|
589 */ |
webmaster@1
|
590 function book_children($book_link) { |
webmaster@1
|
591 $flat = book_get_flat_menu($book_link); |
webmaster@1
|
592 |
webmaster@1
|
593 $children = array(); |
webmaster@1
|
594 |
webmaster@1
|
595 if ($book_link['has_children']) { |
webmaster@1
|
596 // Walk through the array until we find the current page. |
webmaster@1
|
597 do { |
webmaster@1
|
598 $link = array_shift($flat); |
webmaster@1
|
599 } while ($link && ($link['mlid'] != $book_link['mlid'])); |
webmaster@1
|
600 // Continue though the array and collect the links whose parent is this page. |
webmaster@1
|
601 while (($link = array_shift($flat)) && $link['plid'] == $book_link['mlid']) { |
webmaster@1
|
602 $data['link'] = $link; |
webmaster@1
|
603 $data['below'] = ''; |
webmaster@1
|
604 $children[] = $data; |
webmaster@1
|
605 } |
webmaster@1
|
606 } |
webmaster@1
|
607 return $children ? menu_tree_output($children) : ''; |
webmaster@1
|
608 } |
webmaster@1
|
609 |
webmaster@1
|
610 /** |
webmaster@1
|
611 * Generate the corresponding menu name from a book ID. |
webmaster@1
|
612 */ |
webmaster@1
|
613 function book_menu_name($bid) { |
webmaster@1
|
614 return 'book-toc-'. $bid; |
webmaster@1
|
615 } |
webmaster@1
|
616 |
webmaster@1
|
617 /** |
webmaster@1
|
618 * Build an active trail to show in the breadcrumb. |
webmaster@1
|
619 */ |
webmaster@1
|
620 function book_build_active_trail($book_link) { |
webmaster@1
|
621 static $trail; |
webmaster@1
|
622 |
webmaster@1
|
623 if (!isset($trail)) { |
webmaster@1
|
624 $trail = array(); |
webmaster@1
|
625 $trail[] = array('title' => t('Home'), 'href' => '<front>', 'localized_options' => array()); |
webmaster@1
|
626 |
webmaster@1
|
627 $tree = menu_tree_all_data($book_link['menu_name'], $book_link); |
webmaster@1
|
628 $curr = array_shift($tree); |
webmaster@1
|
629 |
webmaster@1
|
630 while ($curr) { |
webmaster@1
|
631 if ($curr['link']['href'] == $book_link['href']) { |
webmaster@1
|
632 $trail[] = $curr['link']; |
webmaster@1
|
633 $curr = FALSE; |
webmaster@1
|
634 } |
webmaster@1
|
635 else { |
webmaster@1
|
636 if ($curr['below'] && $curr['link']['in_active_trail']) { |
webmaster@1
|
637 $trail[] = $curr['link']; |
webmaster@1
|
638 $tree = $curr['below']; |
webmaster@1
|
639 } |
webmaster@1
|
640 $curr = array_shift($tree); |
webmaster@1
|
641 } |
webmaster@1
|
642 } |
webmaster@1
|
643 } |
webmaster@1
|
644 return $trail; |
webmaster@1
|
645 } |
webmaster@1
|
646 |
webmaster@1
|
647 /** |
webmaster@1
|
648 * Implementation of hook_nodeapi(). |
webmaster@1
|
649 * |
webmaster@1
|
650 * Appends book navigation to all nodes in the book, and handles book outline |
webmaster@1
|
651 * insertions and updates via the node form. |
webmaster@1
|
652 */ |
webmaster@1
|
653 function book_nodeapi(&$node, $op, $teaser, $page) { |
webmaster@1
|
654 switch ($op) { |
webmaster@1
|
655 case 'load': |
webmaster@1
|
656 // Note - we cannot use book_link_load() because it will call node_load() |
webmaster@1
|
657 $info['book'] = db_fetch_array(db_query('SELECT * FROM {book} b INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE b.nid = %d', $node->nid)); |
webmaster@1
|
658 if ($info['book']) { |
webmaster@1
|
659 $info['book']['href'] = $info['book']['link_path']; |
webmaster@1
|
660 $info['book']['title'] = $info['book']['link_title']; |
webmaster@1
|
661 $info['book']['options'] = unserialize($info['book']['options']); |
webmaster@1
|
662 return $info; |
webmaster@1
|
663 } |
webmaster@1
|
664 break; |
webmaster@1
|
665 case 'view': |
webmaster@1
|
666 if (!$teaser) { |
webmaster@1
|
667 if (!empty($node->book['bid']) && $node->build_mode == NODE_BUILD_NORMAL) { |
webmaster@1
|
668 |
webmaster@1
|
669 $node->content['book_navigation'] = array( |
webmaster@1
|
670 '#value' => theme('book_navigation', $node->book), |
webmaster@1
|
671 '#weight' => 100, |
webmaster@1
|
672 ); |
webmaster@1
|
673 |
webmaster@1
|
674 if ($page) { |
webmaster@1
|
675 menu_set_active_trail(book_build_active_trail($node->book)); |
webmaster@1
|
676 menu_set_active_menu_name($node->book['menu_name']); |
webmaster@1
|
677 } |
webmaster@1
|
678 } |
webmaster@1
|
679 } |
webmaster@1
|
680 break; |
webmaster@1
|
681 case 'presave': |
webmaster@1
|
682 // Always save a revision for non-administrators. |
webmaster@1
|
683 if (!empty($node->book['bid']) && !user_access('administer nodes')) { |
webmaster@1
|
684 $node->revision = 1; |
webmaster@1
|
685 } |
webmaster@1
|
686 // Make sure a new node gets a new menu link. |
webmaster@1
|
687 if (empty($node->nid)) { |
webmaster@1
|
688 $node->book['mlid'] = NULL; |
webmaster@1
|
689 } |
webmaster@1
|
690 break; |
webmaster@1
|
691 case 'insert': |
webmaster@1
|
692 case 'update': |
webmaster@1
|
693 if (!empty($node->book['bid'])) { |
webmaster@1
|
694 if ($node->book['bid'] == 'new') { |
webmaster@1
|
695 // New nodes that are their own book. |
webmaster@1
|
696 $node->book['bid'] = $node->nid; |
webmaster@1
|
697 } |
webmaster@1
|
698 $node->book['nid'] = $node->nid; |
webmaster@1
|
699 $node->book['menu_name'] = book_menu_name($node->book['bid']); |
webmaster@1
|
700 _book_update_outline($node); |
webmaster@1
|
701 } |
webmaster@1
|
702 break; |
webmaster@1
|
703 case 'delete': |
webmaster@1
|
704 if (!empty($node->book['bid'])) { |
webmaster@1
|
705 if ($node->nid == $node->book['bid']) { |
webmaster@1
|
706 // Handle deletion of a top-level post. |
webmaster@1
|
707 $result = db_query("SELECT b.nid FROM {menu_links} ml INNER JOIN {book} b on b.mlid = ml.mlid WHERE ml.plid = %d", $node->book['mlid']); |
webmaster@1
|
708 while ($child = db_fetch_array($result)) { |
webmaster@1
|
709 $child_node = node_load($child['nid']); |
webmaster@1
|
710 $child_node->book['bid'] = $child_node->nid; |
webmaster@1
|
711 _book_update_outline($child_node); |
webmaster@1
|
712 } |
webmaster@1
|
713 } |
webmaster@1
|
714 menu_link_delete($node->book['mlid']); |
webmaster@1
|
715 db_query('DELETE FROM {book} WHERE mlid = %d', $node->book['mlid']); |
webmaster@1
|
716 } |
webmaster@1
|
717 break; |
webmaster@1
|
718 case 'prepare': |
webmaster@1
|
719 // Prepare defaults for the add/edit form. |
webmaster@1
|
720 if (empty($node->book) && (user_access('add content to books') || user_access('administer book outlines'))) { |
webmaster@1
|
721 $node->book = array(); |
webmaster@1
|
722 if (empty($node->nid) && isset($_GET['parent']) && is_numeric($_GET['parent'])) { |
webmaster@1
|
723 // Handle "Add child page" links: |
webmaster@1
|
724 $parent = book_link_load($_GET['parent']); |
webmaster@1
|
725 if ($parent && $parent['access']) { |
webmaster@1
|
726 $node->book['bid'] = $parent['bid']; |
webmaster@1
|
727 $node->book['plid'] = $parent['mlid']; |
webmaster@1
|
728 $node->book['menu_name'] = $parent['menu_name']; |
webmaster@1
|
729 } |
webmaster@1
|
730 } |
webmaster@1
|
731 // Set defaults. |
webmaster@1
|
732 $node->book += _book_link_defaults(!empty($node->nid) ? $node->nid : 'new'); |
webmaster@1
|
733 } |
webmaster@1
|
734 else { |
webmaster@1
|
735 if (isset($node->book['bid']) && !isset($node->book['original_bid'])) { |
webmaster@1
|
736 $node->book['original_bid'] = $node->book['bid']; |
webmaster@1
|
737 } |
webmaster@1
|
738 } |
webmaster@1
|
739 // Find the depth limit for the parent select. |
webmaster@1
|
740 if (isset($node->book['bid']) && !isset($node->book['parent_depth_limit'])) { |
webmaster@1
|
741 $node->book['parent_depth_limit'] = _book_parent_depth_limit($node->book); |
webmaster@1
|
742 } |
webmaster@1
|
743 break; |
webmaster@1
|
744 } |
webmaster@1
|
745 } |
webmaster@1
|
746 |
webmaster@1
|
747 /** |
webmaster@1
|
748 * Find the depth limit for items in the parent select. |
webmaster@1
|
749 */ |
webmaster@1
|
750 function _book_parent_depth_limit($book_link) { |
webmaster@1
|
751 return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? menu_link_children_relative_depth($book_link) : 0); |
webmaster@1
|
752 } |
webmaster@1
|
753 |
webmaster@1
|
754 /** |
webmaster@1
|
755 * Form altering function for the confirm form for a single node deletion. |
webmaster@1
|
756 */ |
webmaster@1
|
757 function book_form_node_delete_confirm_alter(&$form, $form_state) { |
webmaster@1
|
758 |
webmaster@1
|
759 $node = node_load($form['nid']['#value']); |
webmaster@1
|
760 |
webmaster@1
|
761 if (isset($node->book) && $node->book['has_children']) { |
webmaster@1
|
762 $form['book_warning'] = array( |
webmaster@1
|
763 '#value' => '<p>'. t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', array('%title' => $node->title)) .'</p>', |
webmaster@1
|
764 '#weight' => -10, |
webmaster@1
|
765 ); |
webmaster@1
|
766 } |
webmaster@1
|
767 } |
webmaster@1
|
768 |
webmaster@1
|
769 /** |
webmaster@1
|
770 * Return an array with default values for a book link. |
webmaster@1
|
771 */ |
webmaster@1
|
772 function _book_link_defaults($nid) { |
webmaster@1
|
773 return array('original_bid' => 0, 'menu_name' => '', 'nid' => $nid, 'bid' => 0, 'router_path' => 'node/%', 'plid' => 0, 'mlid' => 0, 'has_children' => 0, 'weight' => 0, 'module' => 'book', 'options' => array()); |
webmaster@1
|
774 } |
webmaster@1
|
775 |
webmaster@1
|
776 /** |
webmaster@1
|
777 * Process variables for book-navigation.tpl.php. |
webmaster@1
|
778 * |
webmaster@1
|
779 * The $variables array contains the following arguments: |
webmaster@1
|
780 * - $book_link |
webmaster@1
|
781 * |
webmaster@1
|
782 * @see book-navigation.tpl.php |
webmaster@1
|
783 */ |
webmaster@1
|
784 function template_preprocess_book_navigation(&$variables) { |
webmaster@1
|
785 $book_link = $variables['book_link']; |
webmaster@1
|
786 |
webmaster@1
|
787 // Provide extra variables for themers. Not needed by default. |
webmaster@1
|
788 $variables['book_id'] = $book_link['bid']; |
webmaster@1
|
789 $variables['book_title'] = check_plain($book_link['link_title']); |
webmaster@1
|
790 $variables['book_url'] = 'node/'. $book_link['bid']; |
webmaster@1
|
791 $variables['current_depth'] = $book_link['depth']; |
webmaster@1
|
792 |
webmaster@1
|
793 $variables['tree'] = ''; |
webmaster@1
|
794 if ($book_link['mlid']) { |
webmaster@1
|
795 $variables['tree'] = book_children($book_link); |
webmaster@1
|
796 |
webmaster@1
|
797 if ($prev = book_prev($book_link)) { |
webmaster@1
|
798 $prev_href = url($prev['href']); |
webmaster@1
|
799 drupal_add_link(array('rel' => 'prev', 'href' => $prev_href)); |
webmaster@1
|
800 $variables['prev_url'] = $prev_href; |
webmaster@1
|
801 $variables['prev_title'] = check_plain($prev['title']); |
webmaster@1
|
802 } |
webmaster@1
|
803 if ($book_link['plid'] && $parent = book_link_load($book_link['plid'])) { |
webmaster@1
|
804 $parent_href = url($parent['href']); |
webmaster@1
|
805 drupal_add_link(array('rel' => 'up', 'href' => $parent_href)); |
webmaster@1
|
806 $variables['parent_url'] = $parent_href; |
webmaster@1
|
807 $variables['parent_title'] = check_plain($parent['title']); |
webmaster@1
|
808 } |
webmaster@1
|
809 if ($next = book_next($book_link)) { |
webmaster@1
|
810 $next_href = url($next['href']); |
webmaster@1
|
811 drupal_add_link(array('rel' => 'next', 'href' => $next_href)); |
webmaster@1
|
812 $variables['next_url'] = $next_href; |
webmaster@1
|
813 $variables['next_title'] = check_plain($next['title']); |
webmaster@1
|
814 } |
webmaster@1
|
815 } |
webmaster@1
|
816 |
webmaster@1
|
817 $variables['has_links'] = FALSE; |
webmaster@1
|
818 // Link variables to filter for values and set state of the flag variable. |
webmaster@1
|
819 $links = array('prev_url', 'prev_title', 'parent_url', 'parent_title', 'next_url', 'next_title'); |
webmaster@1
|
820 foreach ($links as $link) { |
webmaster@1
|
821 if (isset($variables[$link])) { |
webmaster@1
|
822 // Flag when there is a value. |
webmaster@1
|
823 $variables['has_links'] = TRUE; |
webmaster@1
|
824 } |
webmaster@1
|
825 else { |
webmaster@1
|
826 // Set empty to prevent notices. |
webmaster@1
|
827 $variables[$link] = ''; |
webmaster@1
|
828 } |
webmaster@1
|
829 } |
webmaster@1
|
830 } |
webmaster@1
|
831 |
webmaster@1
|
832 /** |
webmaster@1
|
833 * A recursive helper function for book_toc(). |
webmaster@1
|
834 */ |
webmaster@1
|
835 function _book_toc_recurse($tree, $indent, &$toc, $exclude, $depth_limit) { |
webmaster@1
|
836 foreach ($tree as $data) { |
webmaster@1
|
837 if ($data['link']['depth'] > $depth_limit) { |
webmaster@1
|
838 // Don't iterate through any links on this level. |
webmaster@1
|
839 break; |
webmaster@1
|
840 } |
webmaster@1
|
841 if (!in_array($data['link']['mlid'], $exclude)) { |
webmaster@1
|
842 $toc[$data['link']['mlid']] = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, TRUE); |
webmaster@1
|
843 if ($data['below']) { |
webmaster@1
|
844 _book_toc_recurse($data['below'], $indent .'--', $toc, $exclude, $depth_limit); |
webmaster@1
|
845 } |
webmaster@1
|
846 } |
webmaster@1
|
847 } |
webmaster@1
|
848 } |
webmaster@1
|
849 |
webmaster@1
|
850 /** |
webmaster@1
|
851 * Returns an array of book pages in table of contents order. |
webmaster@1
|
852 * |
webmaster@1
|
853 * @param $bid |
webmaster@1
|
854 * The ID of the book whose pages are to be listed. |
webmaster@1
|
855 * @param $exclude |
webmaster@1
|
856 * Optional array of mlid values. Any link whose mlid is in this array |
webmaster@1
|
857 * will be excluded (along with its children). |
webmaster@1
|
858 * @param $depth_limit |
webmaster@1
|
859 * Any link deeper than this value will be excluded (along with its children). |
webmaster@1
|
860 * @return |
webmaster@1
|
861 * An array of mlid, title pairs for use as options for selecting a book page. |
webmaster@1
|
862 */ |
webmaster@1
|
863 function book_toc($bid, $exclude = array(), $depth_limit) { |
webmaster@1
|
864 |
webmaster@1
|
865 $tree = menu_tree_all_data(book_menu_name($bid)); |
webmaster@1
|
866 $toc = array(); |
webmaster@1
|
867 _book_toc_recurse($tree, '', $toc, $exclude, $depth_limit); |
webmaster@1
|
868 |
webmaster@1
|
869 return $toc; |
webmaster@1
|
870 } |
webmaster@1
|
871 |
webmaster@1
|
872 /** |
webmaster@1
|
873 * Process variables for book-export-html.tpl.php. |
webmaster@1
|
874 * |
webmaster@1
|
875 * The $variables array contains the following arguments: |
webmaster@1
|
876 * - $title |
webmaster@1
|
877 * - $contents |
webmaster@1
|
878 * - $depth |
webmaster@1
|
879 * |
webmaster@1
|
880 * @see book-export-html.tpl.php |
webmaster@1
|
881 */ |
webmaster@1
|
882 function template_preprocess_book_export_html(&$variables) { |
webmaster@1
|
883 global $base_url, $language; |
webmaster@1
|
884 |
webmaster@1
|
885 $variables['title'] = check_plain($variables['title']); |
webmaster@1
|
886 $variables['base_url'] = $base_url; |
webmaster@1
|
887 $variables['language'] = $language; |
webmaster@1
|
888 $variables['language_rtl'] = (defined('LANGUAGE_RTL') && $language->direction == LANGUAGE_RTL) ? TRUE : FALSE; |
webmaster@1
|
889 $variables['head'] = drupal_get_html_head(); |
webmaster@1
|
890 } |
webmaster@1
|
891 |
webmaster@1
|
892 /** |
webmaster@1
|
893 * Traverse the book tree to build printable or exportable output. |
webmaster@1
|
894 * |
webmaster@1
|
895 * During the traversal, the $visit_func() callback is applied to each |
webmaster@1
|
896 * node, and is called recursively for each child of the node (in weight, |
webmaster@1
|
897 * title order). |
webmaster@1
|
898 * |
webmaster@1
|
899 * @param $tree |
webmaster@1
|
900 * A subtree of the book menu hierarchy, rooted at the current page. |
webmaster@1
|
901 * @param $visit_func |
webmaster@1
|
902 * A function callback to be called upon visiting a node in the tree. |
webmaster@1
|
903 * @return |
webmaster@1
|
904 * The output generated in visiting each node. |
webmaster@1
|
905 */ |
webmaster@1
|
906 function book_export_traverse($tree, $visit_func) { |
webmaster@1
|
907 $output = ''; |
webmaster@1
|
908 |
webmaster@1
|
909 foreach ($tree as $data) { |
webmaster@1
|
910 // Note- access checking is already performed when building the tree. |
webmaster@1
|
911 if ($node = node_load($data['link']['nid'], FALSE)) { |
webmaster@1
|
912 $children = ''; |
webmaster@1
|
913 if ($data['below']) { |
webmaster@1
|
914 $children = book_export_traverse($data['below'], $visit_func); |
webmaster@1
|
915 } |
webmaster@1
|
916 |
webmaster@1
|
917 if (function_exists($visit_func)) { |
webmaster@1
|
918 $output .= call_user_func($visit_func, $node, $children); |
webmaster@1
|
919 } |
webmaster@1
|
920 else { |
webmaster@1
|
921 // Use the default function. |
webmaster@1
|
922 $output .= book_node_export($node, $children); |
webmaster@1
|
923 } |
webmaster@1
|
924 } |
webmaster@1
|
925 } |
webmaster@1
|
926 return $output; |
webmaster@1
|
927 } |
webmaster@1
|
928 |
webmaster@1
|
929 /** |
webmaster@1
|
930 * Generates printer-friendly HTML for a node. |
webmaster@1
|
931 * |
webmaster@1
|
932 * @see book_export_traverse() |
webmaster@1
|
933 * |
webmaster@1
|
934 * @param $node |
webmaster@1
|
935 * The node to generate output for. |
webmaster@1
|
936 * @param $children |
webmaster@1
|
937 * All the rendered child nodes within the current node. |
webmaster@1
|
938 * @return |
webmaster@1
|
939 * The HTML generated for the given node. |
webmaster@1
|
940 */ |
webmaster@1
|
941 function book_node_export($node, $children = '') { |
webmaster@1
|
942 |
webmaster@1
|
943 $node->build_mode = NODE_BUILD_PRINT; |
webmaster@1
|
944 $node = node_build_content($node, FALSE, FALSE); |
webmaster@1
|
945 $node->body = drupal_render($node->content); |
webmaster@1
|
946 |
webmaster@1
|
947 return theme('book_node_export_html', $node, $children); |
webmaster@1
|
948 } |
webmaster@1
|
949 |
webmaster@1
|
950 /** |
webmaster@1
|
951 * Process variables for book-node-export-html.tpl.php. |
webmaster@1
|
952 * |
webmaster@1
|
953 * The $variables array contains the following arguments: |
webmaster@1
|
954 * - $node |
webmaster@1
|
955 * - $children |
webmaster@1
|
956 * |
webmaster@1
|
957 * @see book-node-export-html.tpl.php |
webmaster@1
|
958 */ |
webmaster@1
|
959 function template_preprocess_book_node_export_html(&$variables) { |
webmaster@1
|
960 $variables['depth'] = $variables['node']->book['depth']; |
webmaster@1
|
961 $variables['title'] = check_plain($variables['node']->title); |
webmaster@1
|
962 $variables['content'] = $variables['node']->body; |
webmaster@1
|
963 } |
webmaster@1
|
964 |
webmaster@1
|
965 /** |
webmaster@1
|
966 * Determine if a given node type is in the list of types allowed for books. |
webmaster@1
|
967 */ |
webmaster@1
|
968 function book_type_is_allowed($type) { |
webmaster@1
|
969 return in_array($type, variable_get('book_allowed_types', array('book'))); |
webmaster@1
|
970 } |
webmaster@1
|
971 |
webmaster@1
|
972 /** |
webmaster@1
|
973 * Implementation of hook_node_type(). |
webmaster@1
|
974 * |
webmaster@1
|
975 * Update book module's persistent variables if the machine-readable name of a |
webmaster@1
|
976 * node type is changed. |
webmaster@1
|
977 */ |
webmaster@1
|
978 function book_node_type($op, $type) { |
webmaster@1
|
979 |
webmaster@1
|
980 switch ($op) { |
webmaster@1
|
981 case 'update': |
webmaster@1
|
982 if (!empty($type->old_type) && $type->old_type != $type->type) { |
webmaster@1
|
983 // Update the list of node types that are allowed to be added to books. |
webmaster@1
|
984 $allowed_types = variable_get('book_allowed_types', array('book')); |
webmaster@1
|
985 $key = array_search($type->old_type, $allowed_types); |
webmaster@1
|
986 if ($key !== FALSE) { |
webmaster@1
|
987 $allowed_types[$type->type] = $allowed_types[$key] ? $type->type : 0; |
webmaster@1
|
988 unset($allowed_types[$key]); |
webmaster@1
|
989 variable_set('book_allowed_types', $allowed_types); |
webmaster@1
|
990 } |
webmaster@1
|
991 // Update the setting for the "Add child page" link. |
webmaster@1
|
992 if (variable_get('book_child_type', 'book') == $type->old_type) { |
webmaster@1
|
993 variable_set('book_child_type', $type->type); |
webmaster@1
|
994 } |
webmaster@1
|
995 } |
webmaster@1
|
996 break; |
webmaster@1
|
997 } |
webmaster@1
|
998 } |
webmaster@1
|
999 |
webmaster@1
|
1000 /** |
webmaster@1
|
1001 * Implementation of hook_help(). |
webmaster@1
|
1002 */ |
webmaster@1
|
1003 function book_help($path, $arg) { |
webmaster@1
|
1004 switch ($path) { |
webmaster@1
|
1005 case 'admin/help#book': |
webmaster@1
|
1006 $output = '<p>'. t('The book module is suited for creating structured, multi-page hypertexts such as site resource guides, manuals, and Frequently Asked Questions (FAQs). It permits a document to have chapters, sections, subsections, etc. Authors with suitable permissions can add pages to a collaborative book, placing them into the existing document by adding them to a table of contents menu.') .'</p>'; |
webmaster@1
|
1007 $output .= '<p>'. t('Pages in the book hierarchy have navigation elements at the bottom of the page for moving through the text. These links lead to the previous and next pages in the book, and to the level above the current page in the book\'s structure. More comprehensive navigation may be provided by enabling the <em>book navigation block</em> on the <a href="@admin-block">blocks administration page</a>.', array('@admin-block' => url('admin/build/block'))) .'</p>'; |
webmaster@1
|
1008 $output .= '<p>'. t('Users can select the <em>printer-friendly version</em> link visible at the bottom of a book page to generate a printer-friendly display of the page and all of its subsections. ') .'</p>'; |
webmaster@1
|
1009 $output .= '<p>'. t("Users with the <em>administer book outlines</em> permission can add a post of any content type to a book, by selecting the appropriate book while editing the post or by using the interface available on the post's <em>outline</em> tab.") .'</p>'; |
webmaster@1
|
1010 $output .= '<p>'. t('Administrators can view a list of all books on the <a href="@admin-node-book">book administration page</a>. The <em>Outline</em> page for each book allows section titles to be edited or rearranged.', array('@admin-node-book' => url('admin/content/book'))) .'</p>'; |
webmaster@1
|
1011 $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@book">Book module</a>.', array('@book' => 'http://drupal.org/handbook/modules/book/')) .'</p>'; |
webmaster@1
|
1012 return $output; |
webmaster@1
|
1013 case 'admin/content/book': |
webmaster@1
|
1014 return '<p>'. t('The book module offers a means to organize a collection of related posts, collectively known as a book. When viewed, these posts automatically display links to adjacent book pages, providing a simple navigation system for creating and reviewing structured content.') .'</p>'; |
webmaster@1
|
1015 case 'node/%/outline': |
webmaster@1
|
1016 return '<p>'. t('The outline feature allows you to include posts in the <a href="@book">book hierarchy</a>, as well as move them within the hierarchy or to <a href="@book-admin">reorder an entire book</a>.', array('@book' => url('book'), '@book-admin' => url('admin/content/book'))) .'</p>'; |
webmaster@1
|
1017 } |
webmaster@1
|
1018 } |
webmaster@1
|
1019 |
webmaster@1
|
1020 /** |
webmaster@1
|
1021 * Like menu_link_load(), but adds additional data from the {book} table. |
webmaster@1
|
1022 * |
webmaster@1
|
1023 * Do not call when loading a node, since this function may call node_load(). |
webmaster@1
|
1024 */ |
webmaster@1
|
1025 function book_link_load($mlid) { |
webmaster@1
|
1026 if ($item = db_fetch_array(db_query("SELECT * FROM {menu_links} ml INNER JOIN {book} b ON b.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid))) { |
webmaster@1
|
1027 _menu_link_translate($item); |
webmaster@1
|
1028 return $item; |
webmaster@1
|
1029 } |
webmaster@1
|
1030 return FALSE; |
webmaster@1
|
1031 } |
webmaster@1
|
1032 |
webmaster@1
|
1033 /** |
webmaster@1
|
1034 * Get the data representing a subtree of the book hierarchy. |
webmaster@1
|
1035 * |
webmaster@1
|
1036 * The root of the subtree will be the link passed as a parameter, so the |
webmaster@1
|
1037 * returned tree will contain this item and all its descendents in the menu tree. |
webmaster@1
|
1038 * |
webmaster@1
|
1039 * @param $item |
webmaster@1
|
1040 * A fully loaded menu link. |
webmaster@1
|
1041 * @return |
webmaster@1
|
1042 * An subtree of menu links in an array, in the order they should be rendered. |
webmaster@1
|
1043 */ |
webmaster@1
|
1044 function book_menu_subtree_data($item) { |
webmaster@1
|
1045 static $tree = array(); |
webmaster@1
|
1046 |
webmaster@5
|
1047 // Generate a cache ID (cid) specific for this $menu_name and $item. |
webmaster@5
|
1048 $cid = 'links:'. $item['menu_name'] .':subtree-cid:'. $item['mlid']; |
webmaster@1
|
1049 |
webmaster@1
|
1050 if (!isset($tree[$cid])) { |
webmaster@1
|
1051 $cache = cache_get($cid, 'cache_menu'); |
webmaster@1
|
1052 if ($cache && isset($cache->data)) { |
webmaster@5
|
1053 // If the cache entry exists, it will just be the cid for the actual data. |
webmaster@5
|
1054 // This avoids duplication of large amounts of data. |
webmaster@5
|
1055 $cache = cache_get($cache->data, 'cache_menu'); |
webmaster@5
|
1056 if ($cache && isset($cache->data)) { |
webmaster@5
|
1057 $data = $cache->data; |
webmaster@5
|
1058 } |
webmaster@1
|
1059 } |
webmaster@5
|
1060 // If the subtree data was not in the cache, $data will be NULL. |
webmaster@5
|
1061 if (!isset($data)) { |
webmaster@1
|
1062 $match = array("menu_name = '%s'"); |
webmaster@1
|
1063 $args = array($item['menu_name']); |
webmaster@1
|
1064 $i = 1; |
webmaster@1
|
1065 while ($i <= MENU_MAX_DEPTH && $item["p$i"]) { |
webmaster@1
|
1066 $match[] = "p$i = %d"; |
webmaster@1
|
1067 $args[] = $item["p$i"]; |
webmaster@1
|
1068 $i++; |
webmaster@1
|
1069 } |
webmaster@1
|
1070 $sql = " |
webmaster@1
|
1071 SELECT b.*, m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, ml.* |
webmaster@1
|
1072 FROM {menu_links} ml INNER JOIN {menu_router} m ON m.path = ml.router_path |
webmaster@1
|
1073 INNER JOIN {book} b ON ml.mlid = b.mlid |
webmaster@1
|
1074 WHERE ". implode(' AND ', $match) ." |
webmaster@1
|
1075 ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC"; |
webmaster@1
|
1076 |
webmaster@1
|
1077 $data['tree'] = menu_tree_data(db_query($sql, $args), array(), $item['depth']); |
webmaster@1
|
1078 $data['node_links'] = array(); |
webmaster@1
|
1079 menu_tree_collect_node_links($data['tree'], $data['node_links']); |
webmaster@5
|
1080 // Compute the real cid for book subtree data. |
webmaster@5
|
1081 $tree_cid = 'links:'. $menu_name .':subtree-data:'. md5(serialize($data)); |
webmaster@5
|
1082 // Cache the data, if it is not already in the cache. |
webmaster@5
|
1083 if (!cache_get($tree_cid, 'cache_menu')) { |
webmaster@5
|
1084 cache_set($tree_cid, $data, 'cache_menu'); |
webmaster@5
|
1085 } |
webmaster@5
|
1086 // Cache the cid of the (shared) data using the menu and item-specific cid. |
webmaster@5
|
1087 cache_set($cid, $tree_cid, 'cache_menu'); |
webmaster@1
|
1088 } |
webmaster@1
|
1089 // Check access for the current user to each item in the tree. |
webmaster@1
|
1090 menu_tree_check_access($data['tree'], $data['node_links']); |
webmaster@1
|
1091 $tree[$cid] = $data['tree']; |
webmaster@1
|
1092 } |
webmaster@1
|
1093 |
webmaster@1
|
1094 return $tree[$cid]; |
webmaster@1
|
1095 } |
webmaster@1
|
1096 |