/[drupal]/contributions/modules/paging/paging.module
ViewVC logotype

Contents of /contributions/modules/paging/paging.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.45 - (show annotations) (download) (as text)
Sun Apr 19 11:29:35 2009 UTC (7 months ago) by Gurpartap
Branch: MAIN
CVS Tags: HEAD
Changes since 1.44: +2 -2 lines
File MIME type: text/x-php
#438002 by Gurpartap Singh & RTH: Missing argument error on filter tips page.
1 <?php
2 // $Id: paging.module,v 1.44 2009/04/18 21:50:06 Gurpartap Exp $
3 // Original module written by Marco Scutari.
4 // Rewritten and considerably shortened and made more Drupal-friendly by Earl Miles.
5 // Yet again rewritten, extended and currently maintained by Gurpartap Singh.
6
7 /**
8 * Implementation of hook_help().
9 */
10 function paging_help($path, $arg) {
11 switch ($path) {
12 case 'admin/help#paging':
13 return '<p>' . t('Break long pages into smaller ones by means of a page break tag (e.g. %separator):</p>
14 <pre>First page here.
15 %separator
16 Second page here.
17 %separator
18 More pages here.</pre>', array('%separator' => '<!--pagebreak-->')) . '<p>' . t('Automatic page breaking based on character or word count is also supported.') . '</p>';
19 break;
20 }
21 }
22
23 /**
24 * Implementation of hook_theme().
25 */
26 function paging_theme() {
27 return array(
28 // Secondary Pager Navigation with page names in drop down list.
29 'paging_drop_down' => array('tags' => NULL, 'limit' => NULL, 'element' => NULL, 'parameters' => NULL, 'quantity' => NULL),
30 // Helper theme function to generates the select list for drop down pager.
31 'paging_drop_down_option' => array('url_chunk' => NULL, 'page_name' => NULL, 'page_no' => NULL, 'selected' => NULL),
32 );
33 }
34
35 /**
36 * Implementation of hook_menu().
37 */
38 function paging_menu() {
39 $items = array();
40
41 $items['admin/settings/paging'] = array(
42 'title' => t('Paging'),
43 'description' => t('Enable or disable paging, configure separator string, toggle automatic paging and more for each content types.'),
44 'page callback' => 'drupal_get_form',
45 'page arguments' => array('paging_settings'),
46 'access arguments' => array('administer site configuration'),
47 );
48
49 return $items;
50 }
51
52 /**
53 * Menu callback; display module settings form.
54 */
55 function paging_settings() {
56 $form = array();
57
58 // Set the id of the top-level form tag
59 $form['#id'] = 'paging';
60
61 $paging_filter = FALSE;
62 // Retrieve all input filters.
63 foreach (filter_formats() as $format) {
64 // Further retrieve all input formats.
65 foreach (filter_list_format($format->format) as $filter) {
66 // Check if any of the input formats have paging filter enabled.
67 if ($filter->module == 'paging') {
68 $paging_filter = TRUE;
69 break;
70 }
71 }
72 }
73 if (!$paging_filter) {
74 // Warn if paging filter is not yet enabled for any input format.
75 drupal_set_message(t('Paging filter has not yet been enabled for any input formats. !link!', array('!link' => l(t('Take action'), 'admin/settings/filters'))), 'warning paging-warning');
76 }
77
78 // Traverse available node types.
79 foreach (node_get_types('names') as $type => $name) {
80 // Container fieldset.
81 $form[$type]['paging_config'] = array(
82 '#type' => 'fieldset',
83 '#title' => $name,
84 '#collapsible' => TRUE,
85 '#collapsed' => FALSE,
86 '#attributes' => array('class' => 'paging-fieldset', 'id' => 'paging-type-' . $type),
87 );
88
89 // Left column fieldset.
90 $form[$type]['paging_config']['paging_left'] = array(
91 '#type' => 'fieldset',
92 '#collapsible' => FALSE,
93 '#collapsed' => FALSE,
94 '#attributes' => array('class' => 'paging-left'),
95 );
96 // Paging toggle checkbox.
97 $form[$type]['paging_config']['paging_left']['paging_enabled_' . $type] = array(
98 '#type' => 'checkbox',
99 '#title' => '',
100 '#default_value' => variable_get('paging_enabled_' . $type, 0),
101 '#attributes' => array('class' => 'paging-enabled'),
102 );
103 // Paging separator string.
104 $form[$type]['paging_config']['paging_left']['paging_separator_' . $type] = array(
105 '#type' => 'textfield',
106 '#title' => t('Page separator string'),
107 '#size' => 20,
108 '#maxlength' => 255,
109 '#required' => TRUE,
110 '#default_value' => variable_get('paging_separator_' . $type, '<!--pagebreak-->'),
111 '#description' => t('Use an HTML tag that will render reasonably when paging is not enabled, such as %pagebreak or %hr.', array('%pagebreak' => '<!--pagebreak-->', '%hr' => '<hr />')),
112 );
113 // "Paging separator" insertion widget. Can be a normal or image form button.
114 // Accompanied by paging.js.
115 $form[$type]['paging_config']['paging_left']['paging_separator_widget_' . $type] = array(
116 '#type' => 'radios',
117 '#title' => t('Paging separator widget'),
118 '#options' => array(t('Disabled'), t('Image'), t('Button'),
119 ),
120 '#required' => TRUE,
121 '#description' => t('Choose the style of separator string widget. This widget attaches itself to the body textarea and when clicked, inserts separator at cursor position.'),
122 '#default_value' => variable_get('paging_separator_widget_' . $type, 0),
123 '#attributes' => array('class' => 'paging-method'),
124 );
125 // Change "Read more" path when first page is greater than or equal to the teaser.
126 $form[$type]['paging_config']['paging_left']['paging_read_more_enabled_' . $type] = array(
127 '#type' => 'checkbox',
128 '#title' => t('Link "Read more" to second page'),
129 '#description' => t('When enabled, the "Read more" link under teasers will link to the second page of the content, iff the teaser is larger than the first page or if they are the same.'),
130 '#default_value' => variable_get('paging_read_more_enabled_' . $type, 0),
131 );
132 // Style/theme of page navigation links. "Custom" option enables a textfield to enter function name.
133 // Accompanied by paging.js.
134 $form[$type]['paging_config']['paging_left']['paging_pager_widget_' . $type] = array(
135 '#type' => 'radios',
136 '#title' => t('Pager style'),
137 '#options' => array(
138 'pager' => t('Default <small>- <code>!pager</code></small>', array(
139 '!pager' => l('theme_pager()', 'http://api.drupal.org/api/function/theme_pager/6', array('attributes' => array('target' => '_blank'))),
140 )),
141 'paging_drop_down' => t('Drop down list <small>- <code>theme_paging_drop_down()</code></small>'),
142 'custom' => t('Custom'),
143 ),
144 '#required' => TRUE,
145 '#description' => t('Choose style of page navigation. See also: !link.', array('!link' => l('Overriding theme output', 'http://drupal.org/node/173880#function-override'))),
146 '#default_value' => variable_get('paging_pager_widget_' . $type, 'pager'),
147 '#attributes' => array('class' => 'paging-pager'),
148 );
149 // Textfield to accept custom pager theme function name.
150 $form[$type]['paging_config']['paging_left']['paging_pager_widget_custom_' . $type] = array(
151 '#type' => 'textfield',
152 '#title' => t('Custom pager theme function'),
153 '#size' => 20,
154 '#maxlength' => 255,
155 '#required' => TRUE,
156 '#default_value' => variable_get('paging_pager_widget_custom_' . $type, 'pager'),
157 '#description' => t('Enter the part after the prefix <em>theme_</em> of your custom theme function.'),
158 '#attributes' => array('class' => 'paging-pager-custom-' . $type),
159 '#field_prefix' => 'theme_',
160 );
161
162 // Right column fieldset.
163 $form[$type]['paging_config']['paging_right'] = array(
164 '#type' => 'fieldset',
165 '#collapsible' => FALSE,
166 '#collapsed' => FALSE,
167 '#attributes' => array('class' => 'paging-right'),
168 );
169 // Positions to place the page navigation links at.
170 $form[$type]['paging_config']['paging_right']['paging_pager_widget_position_' . $type] = array(
171 '#type' => 'radios',
172 '#title' => t('Pager position'),
173 '#options' => array(
174 'below' => t('Below content'),
175 'above' => t('Above content'),
176 'both' => t('Below and above content'),
177 'manual' => t('None (No output)'),
178 ),
179 '#required' => TRUE,
180 '#description' => t('Choose the position of page navigation. If set to %none, <code>@paging</code> can be used to place it at a customizable location.', array('%none' => t('None'), '@paging' => '$node->paging'))
181 . (module_exists('content') ? ' ' . t('Position of enabled pager(s) can further be customized for <a href="@url">content types</a> under <em>Manage fields</em> tab.', array('@url' => url('admin/content/types'))) : ''),
182 '#default_value' => variable_get('paging_pager_widget_position_' . $type, 'below'),
183 '#attributes' => array('class' => 'paging-pager'),
184 );
185 // Toggle dynamic loading of pages using AJAX.
186 $form[$type]['paging_config']['paging_right']['paging_ajax_enabled_' . $type] = array(
187 '#type' => 'checkbox',
188 '#title' => t('Dynamically load pages using AJAX'),
189 '#description' => t('Clicking on a pager links will load the page content dynamically. Supports both <em>Default</em> and <em>Drop down list</em> pager styles.'),
190 '#default_value' => variable_get('paging_ajax_enabled_' . $type, 0),
191 '#attributes' => array('class' => 'paging-ajax'),
192 );
193 // Toggle UI that helps with assigning names to pages. Disabled with automatic paging.
194 $form[$type]['paging_config']['paging_right']['paging_names_enabled_' . $type] = array(
195 '#type' => 'checkbox',
196 '#title' => t('Display Page names interface'),
197 '#description' => t('Add an interface to manage page names, which otherwise is a text format within content body, like <em>&lt;!--pagenames:First page||Page no. 2||Page 3--&gt;</em>. Disabled when an automatic paging method is selected.'),
198 '#default_value' => variable_get('paging_names_enabled_' . $type, 0),
199 '#attributes' => array('class' => 'paging-names'),
200 );
201 // Set the browser's title to current page's name.
202 $form[$type]['paging_config']['paging_right']['paging_name_title_' . $type] = array(
203 '#type' => 'checkbox',
204 '#title' => t('Change page title to name of current page'),
205 '#description' => t("Change the node's and browser window's title into name of the current page."),
206 '#default_value' => variable_get('paging_name_title_' . $type, 0),
207 );
208 // Optional automatic paging method. Each option opens the corresponding character/word length select list.
209 // Accompanied by paging.admin.js.
210 $form[$type]['paging_config']['paging_right']['paging_automatic_method_' . $type] = array(
211 '#type' => 'radios',
212 '#title' => t('Automatic paging method'),
213 '#options' => array(t('Disabled'), t('Limit by characters <small>(recommended)</small>'), t('Limit by words'),
214 ),
215 '#required' => TRUE,
216 '#description' => t('Choose the method for automatic paging. Automatic paging is ignored where paging separator string is used.'),
217 '#default_value' => variable_get('paging_automatic_method_' . $type, 0),
218 '#attributes' => array('class' => 'paging-method'),
219 );
220 // Automatic paging method. Select list to choose the number of characters per page.
221 $form[$type]['paging_config']['paging_right']['paging_automatic_chars_' . $type] = array(
222 '#type' => 'select',
223 '#title' => t('Length of each page'),
224 '#options' => array(
225 500 => t('500 characters'), 750 => t('750 characters'),
226 1000 => t('1000 characters'), 1500 => t('1500 characters'),
227 2000 => t('2000 characters'), 2500 => t('2500 characters'),
228 3000 => t('3000 characters'), 3500 => t('3500 characters'),
229 4000 => t('4000 characters'), 4500 => t('4500 characters'),
230 5000 => t('5000 characters'), 5500 => t('5500 characters'),
231 6000 => t('6000 characters'), 6500 => t('6500 characters'),
232 7000 => t('7000 characters'), 7500 => t('7500 characters'),
233 ),
234 '#required' => TRUE,
235 '#description' => '<br />' . t('Select the number of characters to display per page.'),
236 '#default_value' => variable_get('paging_automatic_chars_' . $type, 4000),
237 '#prefix' => '<div class="container-inline paging-chars paging-chars-' . $type . '">',
238 '#suffix' => '</div>',
239 );
240 // Automatic paging method. Select list to choose the number of words per page.
241 $form[$type]['paging_config']['paging_right']['paging_automatic_words_' . $type] = array(
242 '#type' => 'select',
243 '#title' => t('Length of each page'),
244 '#options' => array(
245 50 => t('100 words'), 150 => t('150 words'),
246 200 => t('200 words'), 250 => t('250 words'),
247 300 => t('300 words'), 350 => t('350 words'),
248 400 => t('400 words'), 450 => t('450 words'),
249 500 => t('500 words'), 550 => t('550 words'),
250 600 => t('600 words'), 650 => t('650 words'),
251 700 => t('700 words'), 750 => t('750 words'),
252 ),
253 '#required' => TRUE,
254 '#description' => '<br />' . t('Select the number of words to display per page.'),
255 '#default_value' => variable_get('paging_automatic_words_' . $type, 400),
256 '#prefix' => '<div class="container-inline paging-words paging-words-' . $type . '">',
257 '#suffix' => '</div>',
258 );
259 }
260
261 // Add HTML to setup tabbed interface.
262 $form['paging_footer'] = array(
263 '#value' => '<div id="paging-vertical-tabs"><ul class="ui-tabs-nav"></ul></div>',
264 );
265
266 // Vertical tabs for node types. Degrades in absence of JavaScript.
267 $module_path = drupal_get_path('module', 'paging');
268 drupal_add_js($module_path . '/admin/jquery.cookie.min.js', 'module');
269 drupal_add_js($module_path . '/admin/ui.tabs.min.js', 'module');
270 drupal_add_js($module_path . '/admin/paging.admin.js', 'module');
271 drupal_add_css($module_path . '/admin/paging.admin.css', 'module');
272
273 return system_settings_form($form);
274 }
275
276 /**
277 * Implementation of hook_form_alter().
278 */
279 function paging_form_alter(&$form, $form_state, $form_id) {
280 // Check if paging is enabled for the node type.
281 if (isset($form['type']) && isset($form['#node']) && variable_get('paging_enabled_' . $form['#node']->type, 0)) {
282 // Load the required variables.
283 $separator = variable_get('paging_separator_' . $form['#node']->type, '<!--pagesbreak-->');
284 $widget = variable_get('paging_separator_widget_' . $form['#node']->type, 0);
285 $page_names = variable_get('paging_names_enabled_' . $form['#node']->type, 0);
286 $automatic_paging = variable_get('paging_automatic_method_' . $form['#node']->type, 0);
287 $module_path = drupal_get_path('module', 'paging');
288
289 // Expose configuration variables.
290 drupal_add_js(array(
291 'paging' => array(
292 'separator' => $separator,
293 'widget' => $widget,
294 'page_names' => $page_names && !$automatic_paging, // Disable page names UI when automatic paging is enabled.
295 'module_path' => $module_path),
296 ), 'setting');
297
298 // Add JS for button and names handling.
299 drupal_add_js($module_path . '/paging.js', 'module');
300
301 // Add CSS to adjust button location.
302 if ($widget) {
303 drupal_add_css($module_path . '/paging.css', 'module');
304 }
305 }
306 }
307
308 /**
309 * Implementation of hook_block().
310 */
311 function paging_block($op = 'list', $delta = 0, $edit = array()) {
312 // List the block in administration.
313 if ($op == 'list') {
314 $blocks[0] = array(
315 'info' => t('Page Navigation (Paging)'),
316 'weight' => 0,
317 'status' => 0, // Disabled by default.
318 );
319 return $blocks;
320 }
321 // View block on node view pages.
322 else if ($op == 'view' && $delta == 0 && $GLOBALS['_paging_display_block']) {
323 $block = array(
324 'subject' => t('Page navigation'),
325 'content' => paging_render_names(), // Load a rendered list of page links.
326 );
327 return $block;
328 }
329 }
330
331 /**
332 * Returns a rendered list of page links.
333 *
334 * @param $nid
335 * Node ID to render page links for.
336 */
337 function paging_render_names($nid = NULL) {
338 global $pager_page_array;
339 // Load node ID form URL, if none was supplied.
340 $nid = $nid ? $nid : arg(1);
341 // Fetch a structured array containing page names.
342 $names = paging_fetch_names($nid);
343 // Load the node object to counting total number of expected pages.
344 $node = node_load($nid);
345 // Invoke 'load' operation in hook_nodeapi() implementation to calculate the actual number of pages in the node body.
346 paging_nodeapi($node, 'load');
347 // Comparing and mapping the number of pages in $names and $node->page_count.
348 $fake = array_fill(0, (($node->page_count - 1) < 1 ? 1 : ($node->page_count - 1)) + 1, '');
349 $length = count($fake) > count($names) ? count($fake) : count($names);
350 for ($i=0; $i<$length; ++$i) {
351 $merged[$i] = $names[$i];
352 }
353 // Fill the empty names with node title and page number.
354 $names = _paging_populate_empty_names($merged, $node->title);
355 $rendered_links = array();
356 // Element value to distinguish between multiple pagers on one page.
357 $element = 1;
358 // Convert the names into links.
359 foreach ($names as $key => $name) {
360 $page_new = pager_load_array($key, $element, $pager_page_array);
361 $rendered_links[] = theme('pager_link', $name, $page_new, $element);
362 }
363 return theme('item_list', $rendered_links);
364 }
365
366 /**
367 * Helper function to populate empty page names.
368 */
369 function _paging_populate_empty_names($names, $title) {
370 foreach ($names as $key => $name) {
371 trim($names[$key]);
372 if (empty($names[$key])) {
373 $names[$key] = t('!title - Page !number', array('!title' => check_plain($title), '!number' => ($key + 1)));;
374 }
375 }
376 return $names;
377 }
378
379 /**
380 * Return an array of page names for a node.
381 *
382 * @param $node_body
383 * Either the nid of the node or the node object itself.
384 *
385 * @return
386 * An array of page names found in the node body.
387 */
388 function paging_fetch_names($node_body) {
389 if (is_numeric($node_body)) {
390 $node = node_load($node_body);
391 // Support for CCK.
392 if (isset($node->field_body[0]['view'])) {
393 $node_body = $node->field_body[0]['view'];
394 }
395 // Support for CCK.
396 elseif (isset($node->field_body[0]['value'])) {
397 $node_body = $node->field_body[0]['value'];
398 }
399 else {
400 $node_body = $node->body;
401 }
402 }
403 preg_match("/<!--pagenames:(.*?)-->/", $node_body, $matches);
404 return explode('||', $matches[1]);
405 }
406
407 /**
408 * Implementation of hook_nodeapi().
409 */
410 function paging_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
411 // Act only when paging is enabled for node's type and when node is being rendered for normal views.
412 if (variable_get('paging_enabled_' . $node->type, 0) && ($node->build_mode == NODE_BUILD_NORMAL)) {
413 switch ($op) {
414 case 'load':
415 case 'view':
416 case 'alter':
417 // Support for CCK.
418 if (isset($node->field_body[0]['view'])) {
419 _paging_nodeapi($node,
420 $node->field_body[0]['view'],
421 $node->field_teaser[0]['view'],
422 $op, $teaser, $page);
423 }
424 // Support for CCK.
425 elseif (isset($node->field_body[0]['value'])) {
426 _paging_nodeapi($node,
427 $node->field_body[0]['value'],
428 $node->field_teaser[0]['value'],
429 $op, $teaser, $page);
430 }
431 else {
432 _paging_nodeapi($node, $node->body, $node->teaser, $op, $teaser, $page);
433 }
434 break;
435 case 'prepare':
436 // Pass the paging separator string for input format filter tips.
437 $GLOBALS['_paging_sep'] = variable_get('paging_separator_' . $node->type, '<!--pagebreak-->');
438 break;
439 }
440 }
441 }
442
443 /**
444 * Helper function for paging_nodeapi().
445 */
446 function _paging_nodeapi(&$node, &$node_body, &$node_teaser, $op, $teaser, $page) {
447 switch ($op) {
448 case 'load':
449 $paging_separator = variable_get('paging_separator_' . $node->type, '<!--pagebreak-->');
450 // Check if manual page separators were used.
451 if (strpos($teaser ? $node_teaser : $node_body, $paging_separator) !== FALSE) {
452 $node->pages = explode($paging_separator, $node_body);
453 $node->page_count = count($node->pages);
454 }
455 else {
456 $body_parts = $node_body;
457 // Automatic paging based on character count.
458 if (variable_get('paging_automatic_method_' . $node->type, 0) == 1 && ($max_chars = variable_get('paging_automatic_chars_' . $node->type, 4000)) != 0) {
459 $total_chars = strlen($node_body);
460 // Check if pagination is possible.
461 if ($total_chars > $max_chars) {
462 $body = $node_body;
463 $breaks = (int)($total_chars / $max_chars);
464 $bodypart = array();
465 for ($i = 0; $i <= $breaks; $i++) {
466 $bodypart[$i] = _paging_body_shift($body, $max_chars);
467 $bodycount = strlen($bodypart[$i]);
468 $body = substr($body, $bodycount);
469 }
470 $body_parts = implode($paging_separator, $bodypart);
471 }
472 }
473 // Automatic paging based on word count.
474 elseif (variable_get('paging_automatic_method_' . $node->type, 0) == 2 && ($max_words = variable_get('paging_automatic_words_' . $node->type, 400)) != 0) {
475 $words = explode(' ', $node_body);
476 $total_words = count($words);
477 // Check if pagination is possible.
478 if ($total_words > $max_words) {
479 $breaks = (int)($total_words / $max_words);
480 for ($i = 1; $i < $breaks; $i++) {
481 $index = $i * $max_words;
482 $words[$index] .= $paging_separator;
483 }
484 }
485 $body_parts = implode(' ', $words);
486 }
487 $node->pages = explode($paging_separator, $body_parts);
488 $node->page_count = count($node->pages);
489 }
490 break;
491 case 'view':
492 // Fetch a structured array containing page names.
493 $node->page_names = paging_fetch_names($node->body);
494 // Check if node is being viewed as a teaser (and not as a preview).
495 if ($teaser && strpos($node_teaser, '<!--paging_filter-->') !== FALSE) {
496 // Check to see if the teaser is longer than our first page.
497 if ($node->page_count > 1 && strlen($node->teaser) > strlen($node->pages[0])) {
498 $node->pagemore = true;
499 }
500 }
501 if (strpos($teaser ? $node_teaser : $node_body, '<!--paging_filter-->') !== FALSE) {
502 // Element value to distinguish between multiple pagers on one page.
503 $element = 1;
504 $page = isset($_GET['page']) ? $_GET['page'] : '';
505 $page_elements = explode(',', $page);
506 $node->page_current = $page_elements[1];
507
508 // Only do paging
509 // a) if not in teaser mode;
510 // b) if there is more than one page;
511 // c) if a printable version is not being requested; or
512 // d) if a non-paged version is not being explicitly requested
513 // e.g. http://www.example.com/node/1?page=full or node/1/full.
514 if (!$teaser && $node->page_count > 1 && arg(2) != 'print' && arg(2) != 'full' && $page != 'full') {
515 global $pager_page_array, $pager_total;
516 $pager_page_array = explode(',', $page);
517 $pager_total[$element] = $node->page_count;
518 $page = isset($pager_page_array[$element]) ? $pager_page_array[$element] : 0;
519
520 // Put the current page contents into the node body.
521 $node->content['body']['#value'] = check_markup($node->pages[$page], $node->format, FALSE);
522
523 // Mapping the pages in $node->page_names and $node->page_count to set number of pages as the array length.
524 $fake = array_fill(0, ($node->page_count - 1) + 1, '');
525 $length = count($fake) > count($node->page_names) ? count($fake) : count($node->page_names);
526 for ($i=0; $i<$length; ++$i) {
527 $merged[$i] = $node->page_names[$i];
528 }
529 // Fill the empty names with node title and page number.
530 $node->page_names = _paging_populate_empty_names($merged, $node->title);
531
532 // For use in AJAX.
533 $pager_id = 'paging-pager-' . $node->nid;
534
535 $return_json = FALSE;
536 // Capture pager JSON request
537 if (isset($_REQUEST['paging_json_request']) && $_REQUEST['paging_json_request'] == $pager_id) {
538 // Unset before calling a pager theming function to prevent unecessarily cluttered link URLs.
539 unset($_REQUEST['paging_json_request']);
540 $return_json = TRUE;
541 }
542
543 // Load the page navigation links into $node->paging. Also accessible in node theming.
544 $node->paging = paging_pager_style($node, $element);
545
546 // Find the position to display the page navigation links at.
547 $position = variable_get('paging_pager_widget_position_' . $node->type, 'below');
548
549 if ($position == 'above' || $position == 'both') {
550 $node->content['paging_above']['#value'] = $node->paging;
551 // Get possible manual weight for paging field from CCK setting.
552 if (function_exists('content_extra_field_weight')) {
553 $node->content['paging_above']['#weight'] = content_extra_field_weight($node->type, 'paging_above');
554 }
555 else {
556 $node->content['paging_above']['#weight'] = $node->content['body']['#weight'] - 1;
557 }
558 }
559 if ($position == 'below' || $position == 'both') {
560 $node->content['paging']['#value'] = $node->paging;
561 // Get possible manual weight for paging field from CCK setting.
562 if (function_exists('content_extra_field_weight')) {
563 $node->content['paging']['#weight'] = content_extra_field_weight($node->type, 'paging');
564 }
565 else {
566 $node->content['paging']['#weight'] = $node->content['body']['#weight'] + 1;
567 }
568 }
569
570 $module_path = drupal_get_path('module', 'paging');
571 drupal_add_css($module_path . '/paging.css', 'module');
572 if (variable_get('paging_ajax_enabled_' . $node->type, 0)) {
573 _paging_content_wrap($node);
574 if ($return_json) {
575 $content = array(
576 'paging_above' => $node->content['paging_above'],
577 'body' => $node->content['body'],
578 'paging' => $node->content['paging'],
579 );
580 $response = array(
581 'content' => drupal_render($content),
582 );
583 // Exit with replacement data.
584 exit(drupal_json($response));
585 }
586 // Add scripts for AJAX driven page loading.
587 drupal_add_js($module_path . '/paging.js', 'module');
588 }
589
590 // Set a global value for block visibility.
591 $GLOBALS['_paging_display_block'] = TRUE;
592
593 if (variable_get('paging_name_title_' . $node->type, 0) && !empty($page)) {
594 // Set the browser title to page's name.
595 drupal_set_title($node->page_names[$page]);
596 }
597 }
598 // Remove internal <!--paging_filter--> tag from final output.
599 $node->content['body']['#value'] = str_replace('<!--paging_filter-->', '', $node->content['body']['#value']);
600 }
601 break;
602 }
603 }
604
605 /**
606 * Wrap the pager(s) and node body in a div for compatibility with AJAX replacement method.
607 */
608 function _paging_content_wrap(&$node) {
609 foreach (array('paging_above', 'body', 'paging') as $value) {
610 if (array_key_exists($value, $node->content)) {
611 $x[$value] = $node->content[$value];
612 }
613 }
614 // Sort by #weight.
615 uasort($x, 'element_sort');
616
617 // Opening tag in first item.
618 $start = reset(array_keys($x));
619 $node->content[$start]['#value'] = '<div id="paging-pager-' . $node->nid . '" class="paging-pager-contents">' . $node->content[$start]['#value'];
620
621 // Closing tag in last item.
622 $end = end(array_keys($x));
623 $node->content[$end]['#value'] .= '</div>';
624 }
625
626 /**
627 * Check and return pager navigation in specified format theme.
628 */
629 function paging_pager_style($node = NULL, $element = NULL) {
630 $theme_widget = variable_get('paging_pager_widget_' . $node->type, 'pager');
631
632 if ($theme_widget == 'custom') {
633 // Load the custom user function.
634 $theme_widget = variable_get('paging_pager_widget_custom_' . $node->type, 'pager');
635 }
636
637 if ($theme_widget == 'paging_drop_down') {
638 // Include CSS for 'Drop down pager' styling.
639 drupal_add_css(drupal_get_path('module', 'paging') . '/paging.css', 'module');
640 }
641
642 // Decode the comma entity.
643 $output = str_replace('%2C', ',', theme($theme_widget, NULL, 1, $element, array(), 9, $node->page_names));
644
645 return '<div class="links">' . $output . '</div>';
646 }
647
648 /**
649 * Helper function for automatic page separation by character limit.
650 */
651 function _paging_body_shift($body, $size) {
652 // If we have a short body, the entire body is the teaser.
653 if (drupal_strlen($body) <= $size) {
654 return $body;
655 }
656
657 // If the delimiter has not been specified, try to split at paragraph or
658 // sentence boundaries.
659
660 // The teaser may not be longer than maximum length specified. Initial slice.
661 $teaser = truncate_utf8($body, $size);
662
663 // Store the actual length of the UTF8 string -- which might not be the same
664 // as $size.
665 $max_rpos = strlen($teaser);
666
667 // How much to cut off the end of the teaser so that it doesn't end in the
668 // middle of a paragraph, sentence, or word.
669 // Initialize it to maximum in order to find the minimum.
670 $min_rpos = $max_rpos;
671
672 // Store the reverse of the teaser. We use strpos on the reversed needle and
673 // haystack for speed and convenience.
674 $reversed = strrev($teaser);
675
676 // Build an array of arrays of break points grouped by preference.
677 $break_points = array();
678
679 // A paragraph near the end of sliced teaser is most preferable.
680 $break_points[] = array('</p>' => 0);
681
682 // If no complete paragraph then treat line breaks as paragraphs.
683 $line_breaks = array('<br />' => 6, '<br>' => 4);
684 // Newline only indicates a line break if line break converter
685 // filter is present.
686 if (isset($filters['filter/1'])) {
687 $line_breaks["\n"] = 1;
688 }
689 $break_points[] = $line_breaks;
690
691 // If the first paragraph is too long, split at the end of a sentence.
692 $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
693
694 // Iterate over the groups of break points until a break point is found.
695 foreach ($break_points as $points) {
696 // Look for each break point, starting at the end of the teaser.
697 foreach ($points as $point => $offset) {
698 // The teaser is already reversed, but the break point isn't.
699 $rpos = strpos($reversed, strrev($point));
700 if ($rpos !== FALSE) {
701 $min_rpos = min($rpos + $offset, $min_rpos);
702 }
703 }
704
705 // If a break point was found in this group, slice and return the teaser.
706 if ($min_rpos !== $max_rpos) {
707 // Don't slice with length 0. Length must be <0 to slice from RHS.
708 return ($min_rpos === 0) ? $teaser : substr($teaser, 0, 0 - $min_rpos);
709 }
710 }
711
712 // If a break point was not found, still return a teaser.
713 return $teaser;
714 }
715
716 /**
717 * Implementation of hook_content_extra_fields().
718 *
719 * CCK hook to allow sorting of the pager field.
720 */
721 function paging_content_extra_fields($type_name = NULL) {
722 $position = variable_get('paging_pager_widget_position_' . $type_name, 'below');
723 if (!empty($type_name) && variable_get('paging_enabled_' . $type_name, 0) && $position != 'manual') {
724 if ($position == 'above' || $position == 'both') {
725 $fields['paging_above'] = array(
726 'label' => t('Paging (Above)'),
727 'description' => t('Pager navigation display.'),
728 'weight' => -1,
729 );
730 }
731 if ($position == 'below' || $position == 'both') {
732 $fields['paging'] = array(
733 'label' => t('Paging (Below)'),
734 'description' => t('Pager navigation display.'),
735 'weight' => 1,
736 );
737 }
738 return $fields;
739 }
740 }
741
742 /**
743 * Implementation of hook_link().
744 */
745 function paging_link($type, $node = NULL, $teaser = FALSE) {
746 $links = array();
747 // Specify a new "Read more" which links to second page of the node if teaser is less than or
748 // equal to the first page contents and if the setting is enabled.
749 if ($type == 'node' && $teaser && $node->teaser && !empty($node->pagemore) && variable_get('paging_read_more_enabled_' . $node->type, 0)) {
750 $links['paging_read_more'] = array(
751 'title' => t('Read more'),
752 'href' => drupal_get_path_alias("node/$node->nid"),
753 'attributes' => array('title' => t('Read the rest of this posting.'), 'class' => 'read-more-paging'),
754 'query' => 'page=0,1'
755 );
756 }
757 return $links;
758 }
759
760 /**
761 * Implementation of hook_link_alter().
762 */
763 function paging_link_alter(&$links, $node) {
764 // Remove the "Read more" link set by node.module.
765 if (!empty($node->teaser) && !empty($node->pagemore) && variable_get('paging_read_more_enabled_' . $node->type, 0)) {
766 unset($links['node_read_more']);
767 }
768 }
769
770 /**
771 * Implementation of hook_filter().
772 */
773 function paging_filter($in_op, $in_delta = 0, $in_format = -1, $in_text = '') {
774 switch ($in_op) {
775 case 'list':
776 return array(t('Paging'));
777 break;
778 case 'description':
779 return t('Allows content to be broken up into pages, using a separator tag (e.g. %separator), configurable on <a href="!url">paging settings page</a>.', array('%separator' => '<!--pagebreak-->', '!url' => url('admin/settings/paging')));
780 break;
781 case 'process':
782 // The filter gets called before the nodeapi 'view' so,
783 // add a comment to the body to inform the nodeapi hook to apply the filter.
784 return '<!--paging_filter-->' . $in_text;
785 break;
786 default:
787 return $in_text;
788 break;
789 }
790 }
791
792 /**
793 * Implementation of hook_filter_tips().
794 */
795 function paging_filter_tips($delta, $format, $long = FALSE) {
796 if ($long) {
797 // Display paging help text in filter tips.
798 return '<h1>' . t('Paging Help') . '</h1>' . paging_help('admin/help#paging', array());
799 }
800 else {
801 return t('Use %separator to create page breaks.', array('%separator' => $GLOBALS['_paging_sep']));
802 }
803 }
804
805 /**
806 * Format a query pager.
807 *
808 * Menu callbacks that display paged query results should call theme('pager') to
809 * retrieve a pager control so that users can view other results.
810 * Format a list of nearby pages with additional query results.
811 *
812 * @param $tags
813 * An array of labels for the controls in the pager.
814 * @param $limit
815 * The number of query results to display per page.
816 * @param $element
817 * An optional integer to distinguish between multiple pagers on one page.
818 * @param $parameters
819 * An associative array of query string parameters to append to the pager links.
820 * @param $quantity
821 * The number of pages in the list.
822 * @return
823 * An HTML string that generates the query pager.
824 *
825 * @ingroup themeable
826 */
827 function theme_paging_drop_down($tags = array(), $limit = 10, $element = 0, $parameters = array(), $quantity = 9, $page_names = array()) {
828 global $pager_page_array, $pager_total;
829
830 $output = '';
831
832 // Calculate various markers within this pager piece:
833 // Middle is used to "center" pages around the current page.
834 $pager_middle = ceil($quantity / 2);
835 // current is the page we are currently paged to
836 $pager_current = $pager_page_array[$element] + 1;
837 // max is the maximum page number
838 $pager_max = $pager_total[$element];
839 // End of marker calculations.
840
841 $prev_name = truncate_utf8(t($page_names[$pager_current - 2]), 50, TRUE, TRUE);
842 $li_previous = theme('pager_previous', (isset($tags[1]) ? $tags[1] : t('Previous page: !prev', array('!prev' => $prev_name))), $limit, $element, 1);
843
844 $next_name = truncate_utf8(t($page_names[$pager_current]), 50, TRUE, TRUE);
845 $li_next = theme('pager_next', (isset($tags[3]) ? $tags[3] : t('Next page: !next', array('!next' => $next_name))), $limit, $element, 1);
846
847 if ($pager_total[$element] > 1) {
848
849 $output .= '<table id="paging-title-navigation" class="paging-drop-down">
850 <tbody>
851 <tr>
852 <td class="previous-page" style="width: 50%; text-align: left;">';
853
854 $output .= '<span>' . $li_previous . '</span>';
855 $options = '';
856
857 // Now generate the actual pager piece.
858 for ($i = 1; $i <= $pager_max; $i++) {
859 $page = $_GET['page'] ? $_GET['page'] : '0,' . ($i-1);
860 if (strpos($page, ',') == 1) {
861 $page = $page[0] . ',' . ($i-1);
862 }
863 else if (!empty($_GET['page']) && strpos($page, ',') === FALSE) {
864 $page = $_GET['page'] . ',' . ($i-1);
865 }
866 $url = $i == 1 ? url($_GET['q']) : url($_GET['q'], array('query' => array('page' => $page)));
867 // Decode the comma entity.
868 $url = str_replace('%2C', ',', $url);
869 $options .= theme('paging_drop_down_option', $url, $page_names[$i-1], $i, ($i == ($pager_current)));
870 }
871
872 $output .= '</td><td class="paging-drop-down" style="text-align: center;">';
873 $output .= '<select name="paging_drop_down_page" onload="Drupal.theme(\'paging_drop_down\');">' . $options . '</select>';
874 $output .= '</td>
875 <td class="next-page" style="width: 50%; text-align: right;">';
876 $output .= '<span>' . $li_next . '</span>';
877 $output .= '</td>
878 </tr>
879 </tbody>
880 </table>';
881
882 drupal_add_js("Drupal.theme.prototype.paging_drop_down = function() {
883 $('.paging-drop-down select').bind('change', function() {
884 location.href = $(this).val();
885 });
886 };
887
888 $(document).ready(function() {
889 Drupal.theme('paging_drop_down');
890 });", 'inline');
891
892 return $output;
893 }
894 }
895
896 /**
897 * Format an "option" HTML element.
898 *
899 * @param $url_chunk
900 * Part of the URL with page query.
901 * @param $page_name
902 * Name of the page.
903 * @param $page_no
904 * Page number.
905 * @param $selected
906 * An optional integer to tell when the option is also the current page.
907 *
908 * @return
909 * An HTML string that generates the select list for drop down pager.
910 *
911 * @ingroup themeable
912 */
913 function theme_paging_drop_down_option($url_chunk = NULL, $page_name = NULL, $page_no = NULL, $selected = NULL) {
914 return '<option' . ($selected ? ' selected=""' : '') . ' value="' . $url_chunk . '">' . $page_name . '</option>';
915 }

  ViewVC Help
Powered by ViewVC 1.1.2