pager.inc 10.9 KB
Newer Older
Dries's avatar
 
Dries committed
1
<?php
Dries's avatar
 
Dries committed
2
// $Id$
Dries's avatar
 
Dries committed
3

4
/**
Dries's avatar
 
Dries committed
5 6 7 8 9
 * @defgroup pager_api Pager API
 * @{
 *
 * Pager external functions (API).
 */
10 11

/**
Dries's avatar
 
Dries committed
12 13 14 15 16 17 18 19 20
 * Use this function when doing select queries you wish to be able to page. The
 * pager uses LIMIT-based queries to fetch only the records required to render a
 * certain page. However, it has to learn the total number of records returned
 * by the query to (among others) compute the number of pages (= number of all
 * records / number of records per page). This is done by inserting "COUNT(*)"
 * in the original query, ie. by rewriting the original query, say "SELECT nid,
 * type FROM node WHERE status = '1' ORDER BY static DESC, created DESC" to read
 * "SELECT COUNT(*) FROM node WHERE status = '1' ORDER BY static DESC, created
 * DESC". Rewriting the query is accomplished using a regular expression.
21
 *
Dries's avatar
 
Dries committed
22 23 24 25
 * Unfortunately, the rewrite rule does not always work as intended for queries
 * that (already) have a "COUNT(*)" or a "GROUP BY" clause, and possibly for
 * other complex queries. In those cases, you can optionally pass a query that
 * will be used to count the records.
26
 *
Dries's avatar
 
Dries committed
27 28 29 30
 * For example, if you want to page this query: "SELECT COUNT(*), TYPE FROM node
 * GROUP BY TYPE", pager_query() would invoke the wrong query, being: "SELECT
 * COUNT(*) FROM node GROUP BY TYPE". So instead, you should pass "SELECT
 * COUNT(DISTINCT(TYPE)) FROM node" as the optional $count_query parameter.
31
 *
Dries's avatar
 
Dries committed
32 33 34 35 36 37
 * @param $query the SQL query that needs paging
 * @param $limit the number of rows per page
 * @param $element optional attribute to distringuish between multiple pagers
 *   on one page
 * @param $count_query an optional SQL query used to count records when
 *   rewriting the query would fail
38
 *
Dries's avatar
 
Dries committed
39
 * @return SQL query result
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
 */
function pager_query($query, $limit = 10, $element = 0, $count_query = "") {
  global $pager_from_array, $pager_total;
  $from = $_GET["from"];

  // count the total number of records in this query:
  if ($count_query == "") {
    $pager_total[$element] = db_result(db_query(preg_replace(array("/SELECT.*FROM/i", "/ORDER BY .*/"), array("SELECT COUNT(*) FROM", ""), $query)));

  }
  else {
    $pager_total[$element] = db_result(db_query($count_query));
  }

  // convert comma separated $from to an array, used by other functions:
  $pager_from_array = explode(",", $from);

  return db_query_range($query, (int)$pager_from_array[$element], (int)$limit);

}
Dries's avatar
 
Dries committed
60
/** @} End of defgroup pager_api */
61 62 63 64 65

/**
 * @addtogroup themeable
 * @{
 */
Dries's avatar
 
Dries committed
66

Dries's avatar
 
Dries committed
67
/**
Dries's avatar
Dries committed
68 69
 * When writing themes, you can rewrite this pager function in your theme.
 * You need to call theme("pager", ...) to get a pager.
Dries's avatar
 
Dries committed
70
 */
Dries's avatar
Dries committed
71
function theme_pager($tags = "", $limit = 10, $element = 0, $attributes = array()) {
Dries's avatar
Dries committed
72 73
  global $pager_total;
  if ($pager_total[$element] > $limit) {
Dries's avatar
 
Dries committed
74 75 76 77 78 79 80
    $output .= "<div id=\"pager\" class=\"container-inline\">";
    $output .= "<div>". pager_first(($tags[0] ? $tags[0] : t("first page")), $limit, $element, $attributes) ."</div>";
    $output .= "<div>". pager_previous(($tags[1] ? $tags[1] : t("previous page")), $limit, $element, 1, $attributes) ."</div>";
    $output .= "<div>". pager_list($limit, $element, ($tags[2] ? $tags[2] : 9 ), "", $attributes) ."</div>";
    $output .= "<div>". pager_next(($tags[3] ? $tags[3] : t("next page")), $limit, $element, 1, $attributes) ."</div>";
    $output .= "<div>". pager_last(($tags[4] ? $tags[4] : t("last page")), $limit, $element, $attributes) ."</div>";
    $output .= "</div>";
Dries's avatar
 
Dries committed
81

Dries's avatar
Dries committed
82 83
    return $output;
  }
Dries's avatar
 
Dries committed
84
}
Dries's avatar
 
Dries committed
85
/** @} End of addtogroup themeable */
Dries's avatar
 
Dries committed
86

87
/**
Dries's avatar
 
Dries committed
88 89 90
 * @name pager pieces Use these pieces to construct your own custom pagers in
 *   your theme. Note that you should NOT modify this file to customize your
 *   pager.
91 92
 * @{
 */
Dries's avatar
 
Dries committed
93

Dries's avatar
 
Dries committed
94 95 96
/**
 * displays a "first-page" link
 *
Dries's avatar
 
Dries committed
97 98 99 100 101
 * @param $text defines the name (or image) of the link
 * @param $limit how many nodes are displayed per page
 * @param $element distinguish between multiple pagers on one page
 * @param $attributes extra html attributes for \<a href> (eg. title,
 *   onMouseOver, etc.)
102
 *
Dries's avatar
 
Dries committed
103
 * @return string html of this pager piece
Dries's avatar
 
Dries committed
104
 */
Dries's avatar
 
Dries committed
105 106
function pager_first($text, $limit, $element = 0, $attributes = array()) {
  global $pager_from_array;
Dries's avatar
 
Dries committed
107

Dries's avatar
 
Dries committed
108 109
  if ($pager_from_array[$element]) {
    return "<a href=\"". pager_link(pager_load_array(0, $element, $pager_from_array), $attributes) ."\">$text</a>";
Dries's avatar
 
Dries committed
110 111 112 113 114 115 116
  }
  else {
    // we are already at the first page, return nothing
    return " ";
  }
}

Dries's avatar
 
Dries committed
117 118 119
/**
 * displays a "previous-page" link
 *
Dries's avatar
 
Dries committed
120 121 122 123 124 125
 * @param $text defines the name (or image) of the link
 * @param $limit how many nodes are displayed per page
 * @param $element distinguish between multiple pagers on one page
 * @param $n how many pages we move back (defaults to 1)
 * @param $attributes extra html attributes for \<a href> (eg. title,
 *   onMouseOver, etc.)
Dries's avatar
 
Dries committed
126
 *
Dries's avatar
 
Dries committed
127
 * @return string html of this pager piece
Dries's avatar
 
Dries committed
128
 */
Dries's avatar
 
Dries committed
129 130 131
function pager_previous($text, $limit, $element = 0, $n = 1, $attributes = array()) {
  global $pager_from_array;
  $from_new = pager_load_array(((int)$pager_from_array[$element] - ((int)$limit * (int)$n)), $element, $pager_from_array);
Dries's avatar
 
Dries committed
132
  if ($from_new[$element] < 1) {
Dries's avatar
 
Dries committed
133
    return pager_first($text, $limit, $element, $attributes);
Dries's avatar
 
Dries committed
134
  }
Dries's avatar
 
Dries committed
135
  return "<a href=\"". pager_link($from_new, $attributes) ."\">$text</a>";
Dries's avatar
 
Dries committed
136 137
}

Dries's avatar
 
Dries committed
138 139 140
/**
 * displays a "next-page" link
 *
Dries's avatar
 
Dries committed
141 142 143 144 145 146
 * @param $text defines the name (or image) of the link
 * @param $limit how many nodes are displayed per page
 * @param $element distinguish between multiple pagers on one page
 * @param $n how many pages we move back (defaults to 1)
 * @param $attributes extra html attributes for \<a href> (eg. title,
 *   onMouseOver, etc.)
147
 *
Dries's avatar
 
Dries committed
148
 * @return string html of this pager piece
Dries's avatar
 
Dries committed
149
 */
Dries's avatar
 
Dries committed
150 151 152
function pager_next($text, $limit, $element = 0, $n = 1, $attributes = array()) {
  global $pager_from_array, $pager_total;
  $from_new = pager_load_array(((int)$pager_from_array[$element] + ((int)$limit * (int)$n)), $element, $pager_from_array);
Dries's avatar
 
Dries committed
153
  if ($from_new[$element] < $pager_total[$element]) {
Dries's avatar
 
Dries committed
154
    return "<a href=\"". pager_link($from_new, $attributes) ."\">$text</a>";
Dries's avatar
 
Dries committed
155 156 157 158
  }
  return " ";
}

Dries's avatar
 
Dries committed
159 160 161
/**
 * displays a "last-page" link
 *
Dries's avatar
 
Dries committed
162 163 164 165 166
 * @param $text defines the name (or image) of the link
 * @param $limit how many nodes are displayed per page
 * @param $element distinguish between multiple pagers on one page
 * @param $attributes extra html attributes for \<a href> (eg. title,
 *   onMouseOver, etc.)
167
 *
Dries's avatar
 
Dries committed
168
 * @return string html of this pager piece
Dries's avatar
 
Dries committed
169
 */
Dries's avatar
 
Dries committed
170 171
function pager_last($text, $limit, $element = 0, $attributes = array()) {
  global $pager_from_array, $pager_total;
Dries's avatar
 
Dries committed
172

Dries's avatar
 
Dries committed
173 174 175
  $from_new = pager_load_array(($pager_total[$element] - $limit), $element, $pager_from_array);
  if ($from_new[$element] < ($pager_from_array[$element] + $limit)) {
    return pager_next($text, $limit, $element, 1, $attributes);
Dries's avatar
 
Dries committed
176
  }
Dries's avatar
 
Dries committed
177 178
  if (($from_new[$element] > $pager_from_array[$element]) && ($from_new[$element] > 0) && $from_new[$element] < $pager_total[$element]) {
    return "<a href=\"". pager_link($from_new, $attributes) ."\">$text</a>";
Dries's avatar
 
Dries committed
179 180 181 182
  }
  return " ";
}

Dries's avatar
 
Dries committed
183 184 185
/**
 * displays "%d through %d of $d" type detail about the cur page
 *
Dries's avatar
 
Dries committed
186 187 188
 * @param $limit how many nodes are displayed per page
 * @param $element distinguish between multiple pagers on one page
 * @param $format allows you to reword the format string
189
 *
Dries's avatar
 
Dries committed
190
 * @return string html of this pager piece
Dries's avatar
 
Dries committed
191
 */
Dries's avatar
 
Dries committed
192
function pager_detail($limit, $element = 0, $format = "%d through %d of %d.") {
Dries's avatar
 
Dries committed
193
  global $pager_from_array, $pager_total;
Dries's avatar
 
Dries committed
194

Dries's avatar
 
Dries committed
195 196
  if ($pager_total[$element] > (int)$pager_from_array[$element] + 1) {
    $output = sprintf($format, (int)$pager_from_array[$element] + 1, ((int)$pager_from_array[$element] + $limit <= $pager_total[$element] ? (int)$pager_from_array[$element] + $limit : $pager_total[$element]), $pager_total[$element]);
Dries's avatar
 
Dries committed
197 198 199 200 201
  }

  return $output;
}

Dries's avatar
 
Dries committed
202 203 204
/**
 * displays a list of nearby pages with additional nodes
 *
Dries's avatar
 
Dries committed
205 206 207 208 209 210
 * @param $limit how many nodes are displayed per page
 * @param $element distinguish between multiple pagers on one page
 * @param $quantity defines the length of the page list
 * @param $text optional text to display before the page list
 * @param $attributes extra html attributes for \<a href> (eg. title,
 *   onMouseOver, etc.)
211
 *
Dries's avatar
 
Dries committed
212
 * @return string html of this pager piece
Dries's avatar
 
Dries committed
213
 */
Dries's avatar
 
Dries committed
214 215
function pager_list($limit, $element = 0, $quantity = 5, $text = "", $attributes = array()) {
  global $pager_from_array, $pager_total;
Dries's avatar
 
Dries committed
216

Dries's avatar
 
Dries committed
217
  // calculate various markers within this pager piece:
Dries's avatar
 
Dries committed
218 219 220
  // middle used to "center" pages around current page
  $pager_middle = ceil((int)$quantity / 2);
  // offset adds "offset" second page
Dries's avatar
 
Dries committed
221
  $pager_offset = (int)$pager_from_array[$element] % (int)$limit;
Dries's avatar
 
Dries committed
222
  // current is the page we are currently paged to
Dries's avatar
 
Dries committed
223
  if (($pager_current = (ceil(($pager_from_array[$element] + 1) / $limit))) < 1) {
Dries's avatar
 
Dries committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    $pager_current = 1;
  }
  // first is the first page listed by this pager piece (re quantity)
  $pager_first = (int)$pager_current - (int)$pager_middle + 1;
  // last is the last page listed by this pager piece (re quantity)
  $pager_last = (int)$pager_current + (int)$quantity - (int)$pager_middle;
  // max is the maximum number of pages content can is devided into
  if (!$pager_max = (ceil($pager_total[$element] / $limit))) {
    $pager_max = 1;
  }
  if ((int)$pager_offset) {
    // adjust for offset second page
    $pager_max++;
    $pager_current++;
  }
// end of various marker calculations

// prepare for generation loop
  $i = (int)$pager_first;
  if ($pager_last > $pager_max) {
    // adjust "center" if at end of query
    $i = $i + (int)($pager_max - $pager_last);
    $pager_last = $pager_max;
  }
  if ($i <= 0) {
    // adjust "center" if at start of query
    $pager_last = $pager_last + (1 - $i);
    $i = 1;
  }
// end of generation loop preparation

Dries's avatar
 
Dries committed
255 256 257 258 259
  // when there is more than one page, create the pager list
  if ($i != $pager_max) {
    $output = "$text";
    if ($i > 1) {
      $output .= "... ";
Dries's avatar
 
Dries committed
260
    }
Dries's avatar
 
Dries committed
261 262 263 264

    // finally we're ready to generate the actual pager piece
    for (; $i <= $pager_last && $i <= $pager_max; $i++) {
      if ($i < $pager_current) {
Dries's avatar
 
Dries committed
265
        $output .= pager_previous($i, $limit, $element, ($pager_current - $i), $attributes) ." ";
Dries's avatar
 
Dries committed
266 267
      }
      if ($i == $pager_current) {
Dries's avatar
 
Dries committed
268
        $output .= "<strong>$i</strong> ";
Dries's avatar
 
Dries committed
269 270
      }
      if ($i > $pager_current) {
Dries's avatar
 
Dries committed
271
        $output .= pager_next($i, $limit, $element, ($i - $pager_current), $attributes) ." ";
Dries's avatar
 
Dries committed
272
      }
Dries's avatar
 
Dries committed
273 274
    }

Dries's avatar
 
Dries committed
275 276 277
    if ($i < $pager_max) {
      $output .= "...";
    }
Dries's avatar
 
Dries committed
278 279 280 281
  }

  return $output;
}
282
/* @} End of member group pager pieces */
Dries's avatar
 
Dries committed
283

Dries's avatar
 
Dries committed
284
function pager_link($from_new, $attributes = array()) {
Dries's avatar
 
Dries committed
285
  $q = $_GET["q"];
Dries's avatar
 
Dries committed
286

Dries's avatar
 
Dries committed
287 288
  foreach($attributes as $key => $value) {
    $query[] = "$key=$value";
Dries's avatar
 
Dries committed
289
  }
Dries's avatar
 
Dries committed
290 291

  if (count($attributes)) {
Dries's avatar
 
Dries committed
292
    $url = url($q, "from=". $from_new[0] ."&amp;". implode("&amp;", $query));
Dries's avatar
 
Dries committed
293 294
  }
  else {
Dries's avatar
Dries committed
295
    $url = url($q, "from=". $from_new[0]);
Dries's avatar
 
Dries committed
296 297
  }

Dries's avatar
 
Dries committed
298
  return $url;
Dries's avatar
 
Dries committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
}

function pager_load_array($value, $element, $old_array) {
  $new_array = $old_array;
  // look for empty elements
  for ($i = 0; $i < $element; $i++) {
    if (!$new_array[$i]) {
      // load found empty element with 0
      $new_array[$i] = 0;
    }
  }
  // update the changed element
  $new_array[$element] = (int)$value;
  return $new_array;
}

315
?>