web/drupal/includes/pager.inc
branchdrupal
changeset 74 0ff3ba646492
equal deleted inserted replaced
73:fcf75e232c5b 74:0ff3ba646492
       
     1 <?php
       
     2 // $Id: pager.inc,v 1.63.2.1 2009/07/01 20:51:55 goba Exp $
       
     3 
       
     4 /**
       
     5  * @file
       
     6  * Functions to aid in presenting database results as a set of pages.
       
     7  */
       
     8 
       
     9 /**
       
    10  * Perform a paged database query.
       
    11  *
       
    12  * Use this function when doing select queries you wish to be able to page. The
       
    13  * pager uses LIMIT-based queries to fetch only the records required to render a
       
    14  * certain page. However, it has to learn the total number of records returned
       
    15  * by the query to compute the number of pages (the number of records / records
       
    16  * per page). This is done by inserting "COUNT(*)" in the original query. For
       
    17  * example, the query "SELECT nid, type FROM node WHERE status = '1' ORDER BY
       
    18  * sticky DESC, created DESC" would be rewritten to read "SELECT COUNT(*) FROM
       
    19  * node WHERE status = '1' ORDER BY sticky DESC, created DESC". Rewriting the
       
    20  * query is accomplished using a regular expression.
       
    21  *
       
    22  * Unfortunately, the rewrite rule does not always work as intended for queries
       
    23  * that already have a "COUNT(*)" or a "GROUP BY" clause, and possibly for
       
    24  * other complex queries. In those cases, you can optionally pass a query that
       
    25  * will be used to count the records.
       
    26  *
       
    27  * For example, if you want to page the query "SELECT COUNT(*), TYPE FROM node
       
    28  * GROUP BY TYPE", pager_query() would invoke the incorrect query "SELECT
       
    29  * COUNT(*) FROM node GROUP BY TYPE". So instead, you should pass "SELECT
       
    30  * COUNT(DISTINCT(TYPE)) FROM node" as the optional $count_query parameter.
       
    31  *
       
    32  * @param $query
       
    33  *   The SQL query that needs paging.
       
    34  * @param $limit
       
    35  *   The number of query results to display per page.
       
    36  * @param $element
       
    37  *   An optional integer to distinguish between multiple pagers on one page.
       
    38  * @param $count_query
       
    39  *   An SQL query used to count matching records.
       
    40  * @param ...
       
    41  *   A variable number of arguments which are substituted into the query (and
       
    42  *   the count query) using printf() syntax. Instead of a variable number of
       
    43  *   query arguments, you may also pass a single array containing the query
       
    44  *   arguments.
       
    45  * @return
       
    46  *   A database query result resource, or FALSE if the query was not executed
       
    47  *   correctly.
       
    48  *
       
    49  * @ingroup database
       
    50  */
       
    51 function pager_query($query, $limit = 10, $element = 0, $count_query = NULL) {
       
    52   global $pager_page_array, $pager_total, $pager_total_items;
       
    53   $page = isset($_GET['page']) ? $_GET['page'] : '';
       
    54 
       
    55   // Substitute in query arguments.
       
    56   $args = func_get_args();
       
    57   $args = array_slice($args, 4);
       
    58   // Alternative syntax for '...'
       
    59   if (isset($args[0]) && is_array($args[0])) {
       
    60     $args = $args[0];
       
    61   }
       
    62 
       
    63   // Construct a count query if none was given.
       
    64   if (!isset($count_query)) {
       
    65     $count_query = preg_replace(array('/SELECT.*?FROM /As', '/ORDER BY .*/'), array('SELECT COUNT(*) FROM ', ''), $query);
       
    66   }
       
    67 
       
    68   // Convert comma-separated $page to an array, used by other functions.
       
    69   $pager_page_array = explode(',', $page);
       
    70 
       
    71   // We calculate the total of pages as ceil(items / limit).
       
    72   $pager_total_items[$element] = db_result(db_query($count_query, $args));
       
    73   $pager_total[$element] = ceil($pager_total_items[$element] / $limit);
       
    74   $pager_page_array[$element] = max(0, min((int)$pager_page_array[$element], ((int)$pager_total[$element]) - 1));
       
    75   return db_query_range($query, $args, $pager_page_array[$element] * $limit, $limit);
       
    76 }
       
    77 
       
    78 /**
       
    79  * Compose a query string to append to pager requests.
       
    80  *
       
    81  * @return
       
    82  *   A query string that consists of all components of the current page request
       
    83  *   except for those pertaining to paging.
       
    84  */
       
    85 function pager_get_querystring() {
       
    86   static $string = NULL;
       
    87   if (!isset($string)) {
       
    88     $string = drupal_query_string_encode($_REQUEST, array_merge(array('q', 'page', 'pass'), array_keys($_COOKIE)));
       
    89   }
       
    90   return $string;
       
    91 }
       
    92 
       
    93 /**
       
    94  * Format a query pager.
       
    95  *
       
    96  * Menu callbacks that display paged query results should call theme('pager') to
       
    97  * retrieve a pager control so that users can view other results.
       
    98  * Format a list of nearby pages with additional query results.
       
    99  *
       
   100  * @param $tags
       
   101  *   An array of labels for the controls in the pager.
       
   102  * @param $limit
       
   103  *   The number of query results to display per page.
       
   104  * @param $element
       
   105  *   An optional integer to distinguish between multiple pagers on one page.
       
   106  * @param $parameters
       
   107  *   An associative array of query string parameters to append to the pager links.
       
   108  * @param $quantity
       
   109  *   The number of pages in the list.
       
   110  * @return
       
   111  *   An HTML string that generates the query pager.
       
   112  *
       
   113  * @ingroup themeable
       
   114  */
       
   115 function theme_pager($tags = array(), $limit = 10, $element = 0, $parameters = array(), $quantity = 9) {
       
   116   global $pager_page_array, $pager_total;
       
   117 
       
   118   // Calculate various markers within this pager piece:
       
   119   // Middle is used to "center" pages around the current page.
       
   120   $pager_middle = ceil($quantity / 2);
       
   121   // current is the page we are currently paged to
       
   122   $pager_current = $pager_page_array[$element] + 1;
       
   123   // first is the first page listed by this pager piece (re quantity)
       
   124   $pager_first = $pager_current - $pager_middle + 1;
       
   125   // last is the last page listed by this pager piece (re quantity)
       
   126   $pager_last = $pager_current + $quantity - $pager_middle;
       
   127   // max is the maximum page number
       
   128   $pager_max = $pager_total[$element];
       
   129   // End of marker calculations.
       
   130 
       
   131   // Prepare for generation loop.
       
   132   $i = $pager_first;
       
   133   if ($pager_last > $pager_max) {
       
   134     // Adjust "center" if at end of query.
       
   135     $i = $i + ($pager_max - $pager_last);
       
   136     $pager_last = $pager_max;
       
   137   }
       
   138   if ($i <= 0) {
       
   139     // Adjust "center" if at start of query.
       
   140     $pager_last = $pager_last + (1 - $i);
       
   141     $i = 1;
       
   142   }
       
   143   // End of generation loop preparation.
       
   144 
       
   145   $li_first = theme('pager_first', (isset($tags[0]) ? $tags[0] : t('« first')), $limit, $element, $parameters);
       
   146   $li_previous = theme('pager_previous', (isset($tags[1]) ? $tags[1] : t('‹ previous')), $limit, $element, 1, $parameters);
       
   147   $li_next = theme('pager_next', (isset($tags[3]) ? $tags[3] : t('next ›')), $limit, $element, 1, $parameters);
       
   148   $li_last = theme('pager_last', (isset($tags[4]) ? $tags[4] : t('last »')), $limit, $element, $parameters);
       
   149 
       
   150   if ($pager_total[$element] > 1) {
       
   151     if ($li_first) {
       
   152       $items[] = array(
       
   153         'class' => 'pager-first',
       
   154         'data' => $li_first,
       
   155       );
       
   156     }
       
   157     if ($li_previous) {
       
   158       $items[] = array(
       
   159         'class' => 'pager-previous',
       
   160         'data' => $li_previous,
       
   161       );
       
   162     }
       
   163 
       
   164     // When there is more than one page, create the pager list.
       
   165     if ($i != $pager_max) {
       
   166       if ($i > 1) {
       
   167         $items[] = array(
       
   168           'class' => 'pager-ellipsis',
       
   169           'data' => '…',
       
   170         );
       
   171       }
       
   172       // Now generate the actual pager piece.
       
   173       for (; $i <= $pager_last && $i <= $pager_max; $i++) {
       
   174         if ($i < $pager_current) {
       
   175           $items[] = array(
       
   176             'class' => 'pager-item',
       
   177             'data' => theme('pager_previous', $i, $limit, $element, ($pager_current - $i), $parameters),
       
   178           );
       
   179         }
       
   180         if ($i == $pager_current) {
       
   181           $items[] = array(
       
   182             'class' => 'pager-current',
       
   183             'data' => $i,
       
   184           );
       
   185         }
       
   186         if ($i > $pager_current) {
       
   187           $items[] = array(
       
   188             'class' => 'pager-item',
       
   189             'data' => theme('pager_next', $i, $limit, $element, ($i - $pager_current), $parameters),
       
   190           );
       
   191         }
       
   192       }
       
   193       if ($i < $pager_max) {
       
   194         $items[] = array(
       
   195           'class' => 'pager-ellipsis',
       
   196           'data' => '…',
       
   197         );
       
   198       }
       
   199     }
       
   200     // End generation.
       
   201     if ($li_next) {
       
   202       $items[] = array(
       
   203         'class' => 'pager-next',
       
   204         'data' => $li_next,
       
   205       );
       
   206     }
       
   207     if ($li_last) {
       
   208       $items[] = array(
       
   209         'class' => 'pager-last',
       
   210         'data' => $li_last,
       
   211       );
       
   212     }
       
   213     return theme('item_list', $items, NULL, 'ul', array('class' => 'pager'));
       
   214   }
       
   215 }
       
   216 
       
   217 
       
   218 /**
       
   219  * @name Pager pieces
       
   220  * @{
       
   221  * Use these pieces to construct your own custom pagers in your theme. Note that
       
   222  * you should NOT modify this file to customize your pager.
       
   223  */
       
   224 
       
   225 /**
       
   226  * Format a "first page" link.
       
   227  *
       
   228  * @param $text
       
   229  *   The name (or image) of the link.
       
   230  * @param $limit
       
   231  *   The number of query results to display per page.
       
   232  * @param $element
       
   233  *   An optional integer to distinguish between multiple pagers on one page.
       
   234  * @param $parameters
       
   235  *   An associative array of query string parameters to append to the pager links.
       
   236  * @return
       
   237  *   An HTML string that generates this piece of the query pager.
       
   238  *
       
   239  * @ingroup themeable
       
   240  */
       
   241 function theme_pager_first($text, $limit, $element = 0, $parameters = array()) {
       
   242   global $pager_page_array;
       
   243   $output = '';
       
   244 
       
   245   // If we are anywhere but the first page
       
   246   if ($pager_page_array[$element] > 0) {
       
   247     $output = theme('pager_link', $text, pager_load_array(0, $element, $pager_page_array), $element, $parameters);
       
   248   }
       
   249 
       
   250   return $output;
       
   251 }
       
   252 
       
   253 /**
       
   254  * Format a "previous page" link.
       
   255  *
       
   256  * @param $text
       
   257  *   The name (or image) of the link.
       
   258  * @param $limit
       
   259  *   The number of query results to display per page.
       
   260  * @param $element
       
   261  *   An optional integer to distinguish between multiple pagers on one page.
       
   262  * @param $interval
       
   263  *   The number of pages to move backward when the link is clicked.
       
   264  * @param $parameters
       
   265  *   An associative array of query string parameters to append to the pager links.
       
   266  * @return
       
   267  *   An HTML string that generates this piece of the query pager.
       
   268  *
       
   269  * @ingroup themeable
       
   270  */
       
   271 function theme_pager_previous($text, $limit, $element = 0, $interval = 1, $parameters = array()) {
       
   272   global $pager_page_array;
       
   273   $output = '';
       
   274 
       
   275   // If we are anywhere but the first page
       
   276   if ($pager_page_array[$element] > 0) {
       
   277     $page_new = pager_load_array($pager_page_array[$element] - $interval, $element, $pager_page_array);
       
   278 
       
   279     // If the previous page is the first page, mark the link as such.
       
   280     if ($page_new[$element] == 0) {
       
   281       $output = theme('pager_first', $text, $limit, $element, $parameters);
       
   282     }
       
   283     // The previous page is not the first page.
       
   284     else {
       
   285       $output = theme('pager_link', $text, $page_new, $element, $parameters);
       
   286     }
       
   287   }
       
   288 
       
   289   return $output;
       
   290 }
       
   291 
       
   292 /**
       
   293  * Format a "next page" link.
       
   294  *
       
   295  * @param $text
       
   296  *   The name (or image) of the link.
       
   297  * @param $limit
       
   298  *   The number of query results to display per page.
       
   299  * @param $element
       
   300  *   An optional integer to distinguish between multiple pagers on one page.
       
   301  * @param $interval
       
   302  *   The number of pages to move forward when the link is clicked.
       
   303  * @param $parameters
       
   304  *   An associative array of query string parameters to append to the pager links.
       
   305  * @return
       
   306  *   An HTML string that generates this piece of the query pager.
       
   307  *
       
   308  * @ingroup themeable
       
   309  */
       
   310 function theme_pager_next($text, $limit, $element = 0, $interval = 1, $parameters = array()) {
       
   311   global $pager_page_array, $pager_total;
       
   312   $output = '';
       
   313 
       
   314   // If we are anywhere but the last page
       
   315   if ($pager_page_array[$element] < ($pager_total[$element] - 1)) {
       
   316     $page_new = pager_load_array($pager_page_array[$element] + $interval, $element, $pager_page_array);
       
   317     // If the next page is the last page, mark the link as such.
       
   318     if ($page_new[$element] == ($pager_total[$element] - 1)) {
       
   319       $output = theme('pager_last', $text, $limit, $element, $parameters);
       
   320     }
       
   321     // The next page is not the last page.
       
   322     else {
       
   323       $output = theme('pager_link', $text, $page_new, $element, $parameters);
       
   324     }
       
   325   }
       
   326 
       
   327   return $output;
       
   328 }
       
   329 
       
   330 /**
       
   331  * Format a "last page" link.
       
   332  *
       
   333  * @param $text
       
   334  *   The name (or image) of the link.
       
   335  * @param $limit
       
   336  *   The number of query results to display per page.
       
   337  * @param $element
       
   338  *   An optional integer to distinguish between multiple pagers on one page.
       
   339  * @param $parameters
       
   340  *   An associative array of query string parameters to append to the pager links.
       
   341  * @return
       
   342  *   An HTML string that generates this piece of the query pager.
       
   343  *
       
   344  * @ingroup themeable
       
   345  */
       
   346 function theme_pager_last($text, $limit, $element = 0, $parameters = array()) {
       
   347   global $pager_page_array, $pager_total;
       
   348   $output = '';
       
   349 
       
   350   // If we are anywhere but the last page
       
   351   if ($pager_page_array[$element] < ($pager_total[$element] - 1)) {
       
   352     $output = theme('pager_link', $text, pager_load_array($pager_total[$element] - 1, $element, $pager_page_array), $element, $parameters);
       
   353   }
       
   354 
       
   355   return $output;
       
   356 }
       
   357 
       
   358 
       
   359 /**
       
   360  * Format a link to a specific query result page.
       
   361  *
       
   362  * @param $page_new
       
   363  *   The first result to display on the linked page.
       
   364  * @param $element
       
   365  *   An optional integer to distinguish between multiple pagers on one page.
       
   366  * @param $parameters
       
   367  *   An associative array of query string parameters to append to the pager link.
       
   368  * @param $attributes
       
   369  *   An associative array of HTML attributes to apply to a pager anchor tag.
       
   370  * @return
       
   371  *   An HTML string that generates the link.
       
   372  *
       
   373  * @ingroup themeable
       
   374  */
       
   375 function theme_pager_link($text, $page_new, $element, $parameters = array(), $attributes = array()) {
       
   376   $page = isset($_GET['page']) ? $_GET['page'] : '';
       
   377   if ($new_page = implode(',', pager_load_array($page_new[$element], $element, explode(',', $page)))) {
       
   378     $parameters['page'] = $new_page;
       
   379   }
       
   380 
       
   381   $query = array();
       
   382   if (count($parameters)) {
       
   383     $query[] = drupal_query_string_encode($parameters, array());
       
   384   }
       
   385   $querystring = pager_get_querystring();
       
   386   if ($querystring != '') {
       
   387     $query[] = $querystring;
       
   388   }
       
   389 
       
   390   // Set each pager link title
       
   391   if (!isset($attributes['title'])) {
       
   392     static $titles = NULL;
       
   393     if (!isset($titles)) {
       
   394       $titles = array(
       
   395         t('« first') => t('Go to first page'),
       
   396         t('‹ previous') => t('Go to previous page'),
       
   397         t('next ›') => t('Go to next page'),
       
   398         t('last »') => t('Go to last page'),
       
   399       );
       
   400     }
       
   401     if (isset($titles[$text])) {
       
   402       $attributes['title'] = $titles[$text];
       
   403     }
       
   404     else if (is_numeric($text)) {
       
   405       $attributes['title'] = t('Go to page @number', array('@number' => $text));
       
   406     }
       
   407   }
       
   408 
       
   409   return l($text, $_GET['q'], array('attributes' => $attributes, 'query' => count($query) ? implode('&', $query) : NULL));
       
   410 }
       
   411 
       
   412 /**
       
   413  * @} End of "Pager pieces".
       
   414  */
       
   415 
       
   416 /**
       
   417  * Helper function
       
   418  *
       
   419  * Copies $old_array to $new_array and sets $new_array[$element] = $value
       
   420  * Fills in $new_array[0 .. $element - 1] = 0
       
   421  */
       
   422 function pager_load_array($value, $element, $old_array) {
       
   423   $new_array = $old_array;
       
   424   // Look for empty elements.
       
   425   for ($i = 0; $i < $element; $i++) {
       
   426     if (!$new_array[$i]) {
       
   427       // Load found empty element with 0.
       
   428       $new_array[$i] = 0;
       
   429     }
       
   430   }
       
   431   // Update the changed element.
       
   432   $new_array[$element] = (int)$value;
       
   433   return $new_array;
       
   434 }