'Custom pagers', 'description' => 'Add custom pagers for content types.', 'page callback' => 'custom_pagers_page', 'access arguments' => array('administer custom pagers'), 'file' => 'custom_pagers.admin.inc', ); $items['admin/build/custom_pagers/add'] = array( 'path' => 'admin/build/custom_pagers/add', 'title' => 'Add custom pager', 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('custom_pagers_form'), 'access arguments' => array('administer custom pagers'), 'file' => 'custom_pagers.admin.inc', ); $items['admin/build/custom_pagers/edit'] = array( 'title' => 'Edit custom pager', 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('custom_pagers_form', 4), 'access arguments' => array('administer custom pagers'), 'file' => 'custom_pagers.admin.inc', ); return $items; } function custom_pagers_perm() { return array('administer custom pagers', 'use php in custom pagers'); } function custom_pagers_theme() { return array( 'custom_pager' => array( 'arguments' => array( 'nav_array' => NULL, 'node' => NULL, 'pager' => NULL, 'position' => 'bottom', ), 'template' => 'custom-pager', ), ); } function custom_pagers_nodeapi(&$node, $op, $teaser, $page) { switch ($op) { case 'view': // We want to make sure we don't try to output when print.module is active. // It's a bit of special casing but it doesn't do much harm. if ($teaser == false && empty($node->printing)) { $pagers = _custom_pagers_load_all_pagers(); foreach ($pagers as $pager) { if ($pager->position != 'block' && _custom_pagers_visibility($pager, $node)) { $nav_array = custom_pager_build_nav($pager, $node); if ($nav_array['current_index'] != -1) { switch ($pager->position) { case 'top': $node->content['custom_pager_top'][$pager->pid] = array('#value' => theme('custom_pager', $nav_array, $node, $pager, 'top')); break; case 'bottom': $node->content['custom_pager_bottom'][$pager->pid] = array('#value' => theme('custom_pager', $nav_array, $node, $pager, 'bottom')); break; case 'both': $node->content['custom_pager_top'][$pager->pid] = array('#value' => theme('custom_pager', $nav_array, $node, $pager, 'top')); $node->content['custom_pager_bottom'][$pager->pid] = array('#value' => theme('custom_pager', $nav_array, $node, $pager, 'bottom')); break; } } } } if (isset($node->content['custom_pager_top'])) { $node->content['custom_pager_top']['#weight'] = -100; } if (isset($node->content['custom_pager_bottom'])) { $node->content['custom_pager_bottom']['#weight'] = 100; } } break; case 'update': case 'insert': case 'delete': // If a user makes any changes to a node, we want to make sure that // their pager cache is cleared. It's ugly, but it should prevent some // of the nastier cache-went-stale issues. unset($_SESSION['custom_pagers']); break; } } /** * Implementation of hook_block(). * * Generates a block with a pager for the current node. */ function custom_pagers_block($op = 'list', $delta = 0) { if ($op == 'list') { $pagers = _custom_pagers_load_all_pagers(); foreach ($pagers as $pager) { if ($pager->position == 'block') { $blocks[$pager->pid]['info'] = $pager->title; } } return $blocks; } else if ($op == 'view' && arg(0) == 'node' && $node = menu_get_object()) { $pagers = _custom_pagers_load_all_pagers(); if ($pager = $pagers[$delta]) { if ($pager->position == 'block' && _custom_pagers_visibility($pager, $node)) { $nav_array = custom_pager_build_nav($pager, $node); if ($nav_array['current_index'] != -1) { if (module_exists('token')) { $block['subject'] = token_replace($pager->title, 'node', $node); } else { $block['subject'] = $pager->title; } $block['content'] = theme('custom_pager', $nav_array, $node, $pager, 'block'); return $block; } } } } } function _custom_pagers_load_pager($pid) { $sql = 'SELECT * FROM {custom_pager} WHERE pid = %d'; $result = db_query($sql, $pid); $pager = db_fetch_object($result); return $pager; } function _custom_pagers_load_all_pagers($refresh = FALSE) { static $pagers; if ($refresh || !isset($pagers)) { $sql = 'SELECT * FROM {custom_pager}'; $result = db_query($sql); $pagers = array(); while($pager = db_fetch_object($result)) { $pagers[$pager->pid] = $pager; } } return $pagers; } function _custom_pagers_save_pager($pager = NULL) { if (isset($pager->pid)) { drupal_write_record('custom_pager', $pager, array('pid')); } else { drupal_write_record('custom_pager', $pager); } cache_clear_all('custom_pagers_', 'cache', TRUE); } function _custom_pagers_delete_pager($pid) { $sql = 'DELETE FROM {custom_pager} WHERE pid = %d'; db_query($sql, $pid); } function _custom_pagers_visibility($pager, $node) { $visible = FALSE; if (!empty($pager->visibility_php)) { // Use PHP code to generate the list. ob_start(); $result = eval(trim($pager->visibility_php)); $visible = ($result == TRUE); ob_end_clean(); } elseif (!empty($pager->node_type)) { $visible = (strpos($pager->node_type, $node->type) !== FALSE); } return $visible; } function custom_pager_build_nav($pager, $node) { static $pager_cache; $list = array(); // First we check the static function cache for this pager. // If it's already been built for this page-load, we'll use it. if (isset($pager_cache[$pager->pid])) { $list = explode(',', $pager_cache[$pager->pid]); } // If it doesn't give us a list, and the pager is set to cache its // data, we'll try to load from the current user's session. We do it // that way rather than via cache_set() because of the potential for // node_access violations. Each user's node list *could* be different. if (empty($list) && $pager->cache_list) { if ($cache = $_SESSION['custom_pagers'][$pager->pid]) { // We should probably set the pager cache lifetime to a configurable // value. If any nodes drop through the cracks, users won't see the // pager when they visit them. Five minutes should keep the pager from // thrashing. In the future, we'll want to develop a better strategy // for this. if ($cache['timestamp'] < (time() - 300)) { unset($_SESSION['custom_pagers'][$pager->pid]); } else { $list = explode(',', $_SESSION['custom_pagers'][$pager->pid]['data']); } } } // If $list is still empty, neither caching strategy produced a list. // Let's build it from scratch! if (empty($list)) { // If the pager uses PHP, execute the PHP and run with the list. // Otherwise, use a view to get a list of node ids. if (!empty($pager->list_php)) { // Use PHP code to generate the list. ob_start(); $result = eval(trim($pager->list_php)); if (is_array($result)) { $list = $result; } ob_end_clean(); } elseif (module_exists('views') && $view = views_get_view($pager->view)) { // Get arguments for the view. if (isset($pager->args)) { $args = explode("\n", $pager->args); if (module_exists('token')) { $args = token_replace($args, 'node', $node); } $view->set_arguments($args); } // Make sure the query is not cached $view->is_cacheable = FALSE; $view->execute(); if (isset($view->result)) { foreach($view->result as $result) { $list[] = $result->nid; } } } if ($pager->reverse_list) { $list = array_reverse($list); } } // If we get to this point, we want to cache what we've made. if ($pager->cache_list) { $_SESSION['custom_pagers'][$pager->pid]['data'] = implode(',', $list); $_SESSION['custom_pagers'][$pager->pid]['timestamp'] = time(); } $pager_cache[$pager->pid] = $list; return pager_entries_by_val($node->nid, $list); } // Helper functions to pull proper entries from a list of nids. function pager_entries_by_val($value, $list) { $list = array_values($list); foreach ($list as $k => $v) { if ($v == $value) { $key = $k; } } if (!isset($key)) { $key = -1; } return pager_entries_by_key($key, $list); } function pager_entries_by_key($key, $list, $increment = 1) { $list = array_values($list); $nav = array( 'first' => $list[0], 'prev' => $list[max($key - $increment, 0)], 'next' => $list[min($key + $increment, (count($list) - 1))], 'last' => $list[count($list) - 1], 'full_list' => $list ); foreach($nav as $k => $v) { if ($nav[$k] == $list[$key]) { $nav[$k] = NULL; } } $nav['current_index'] = $key; return $nav; } function custom_pagers_preprocess_custom_pager(&$vars) { drupal_add_css(drupal_get_path('module', 'custom_pagers') .'/custom_pagers.css'); $node = $vars['node']; $pager = $vars['pager']; $nav = $vars['nav_array']; $vars['previous'] = !empty($nav['prev']) ? l(t('‹ previous'), 'node/'. $nav['prev']) : ''; $vars['key'] = t('@count of @count_total', array('@count' => ($nav['current_index'] + 1), '@count_total' => count($nav['full_list']))); $vars['next'] = !empty($nav['next']) ? l(t('next ›'), 'node/'. $nav['next']) : ''; $vars['suggestions'][] = "custom-pager-{$vars['position']}"; $vars['suggestions'][] = "custom-pager-$node->type"; $vars['suggestions'][] = "custom-pager-$node->type-{$vars['position']}"; $vars['suggestions'][] = "custom-pager-$pager->pid"; $vars['suggestions'][] = "custom-pager-$pager->pid-{$vars['position']}"; }