system/classes/listfactory.class.php
author Sami Barakat
Sun, 18 Oct 2009 02:25:07 +0100
branchHEAD
changeset 7389 abd5bf00c26b
parent 7334 0b090631301e
child 7391 0bee991937cc
permissions -rwxr-xr-x
Fixed sorting and limiting the results at the same time (bug #0001007)
     1 <?php
     2 
     3 /* Reminder: always indent with 4 spaces (no tabs). */
     4 // +---------------------------------------------------------------------------+
     5 // | Geeklog 1.6.1                                                             |
     6 // +---------------------------------------------------------------------------+
     7 // | listfactory.class.php                                                     |
     8 // |                                                                           |
     9 // | This class allows personalised lists or tables to be easily generated     |
    10 // | from arrays or SQL statements. It will also supports the sorting and      |
    11 // | paging of results.                                                        |
    12 // +---------------------------------------------------------------------------+
    13 // | Copyright (C) 2000-2009 by the following authors:                         |
    14 // |                                                                           |
    15 // | Authors: Sami Barakat     - s.m.barakat AT gmail DOT com                  |
    16 // +---------------------------------------------------------------------------+
    17 // |                                                                           |
    18 // | This program is free software; you can redistribute it and/or             |
    19 // | modify it under the terms of the GNU General Public License               |
    20 // | as published by the Free Software Foundation; either version 2            |
    21 // | of the License, or (at your option) any later version.                    |
    22 // |                                                                           |
    23 // | This program is distributed in the hope that it will be useful,           |
    24 // | but WITHOUT ANY WARRANTY; without even the implied warranty of            |
    25 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             |
    26 // | GNU General Public License for more details.                              |
    27 // |                                                                           |
    28 // | You should have received a copy of the GNU General Public License         |
    29 // | along with this program; if not, write to the Free Software Foundation,   |
    30 // | Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           |
    31 // |                                                                           |
    32 // +---------------------------------------------------------------------------+
    33 
    34 if (strpos(strtolower($_SERVER['PHP_SELF']), 'listfactory.class.php') !== false) {
    35     die('This file can not be used on its own.');
    36 }
    37 
    38 /* Example Use
    39 
    40     // Initiate an instance of the class with the URL of the current page
    41     $url = $_SERVER['PHP_SELF'];
    42     $obj = new ListFactory($url);
    43 
    44     // Set up some hidden fields that will be used to help format the data later on
    45     $obj->setField('ID', 'id', false);
    46 
    47     // Set up the fields that will be seen by the user
    48     $obj->setField(
    49         '#',            // Title of the field
    50         ROW_NUMBER,     // The field identifier can be either:
    51                         //   ROW_NUMBER - The number of each row will be displayed
    52                         //   SQL_TITLE  - The title given the the SQL query will be displayed
    53                         //   <string>   - SQL column name
    54         true,           // Enables the field
    55         true,           // The field can be sorted
    56         '<b>%d.</b>'    // Formats the data
    57     );
    58     $obj->setField('Type', SQL_TITLE, true, true, '<b>%s</b>');
    59     $obj->setField('Title', 'title');
    60     $obj->setField('Text', 'text');
    61     $obj->setField('Date', 'date');
    62 
    63     // Set the default field to sort by
    64     $obj->setDefaultSort('date');
    65 
    66     // Set the style of output
    67     $obj->setStyle('table');
    68 
    69     // Sets the call back function to add any extra formatting to the fields
    70     $obj->setRowFunction('test_list_func');
    71 
    72     // Set up some queries to execute
    73     $sql = 'SELECT sid AS id, title, introtext AS text, date FROM stories';
    74     $obj->setQuery(
    75         'Story', // The name given to the query which will be displayed in the SQL_TITLE field (optional)
    76         'story',
    77         $sql,    // The SQL string without the LIMIT or ORDER BY clauses. Notice the column names match the field identifiers
    78         5        // The rank of the query, 5 highest = more results, 1 lowest = least results
    79     );
    80     $sql = 'SELECT cid AS id, title, comment AS text, date FROM comments';
    81     $obj->setQuery('Comment', 'comment', $sql, 2);
    82 
    83     // Append some extra rows to the output
    84     // Note: the array must match the field identifier names stated previously
    85     $extra_row = array(
    86         'id' => -1,
    87         SQL_TITLE => 'Extra Row',
    88         'title' => 'An extra row example',
    89         'text' => 'With some really really really long text.....<b>and HTML</b>',
    90         'date' => '2008-07-08 03:00:00'
    91     );
    92     // Add the extra row, notice it is not automatically passed to the row function
    93     $obj->addResult($extra_row);
    94 
    95     // Prints out the list
    96     $results = $obj->ExecuteQueries();
    97     $title = 'Test ListFactory';
    98     $text = 'Showing %d - %d of %d results.';
    99     $retval = $obj->getFormattedOutput($results, $title, $text);
   100     echo $retval;
   101 
   102     // This function is called by the ListFactory to provide furthur formatting of the results.
   103     function test_list_func($preSort, $row)
   104     {
   105         if ($preSort)
   106         {
   107             // extract any further information from the results.
   108             // such as converting user ID's to user names
   109         }
   110         else
   111         {
   112             // Create a link from the title and id
   113             $row['title'] = '<a href="http://www.geeklog.net/list_test.php?id='.$row['id'].'">'.$row['title'].'</a>';
   114 
   115             // Shorten the text and strip any HTML tags
   116             $row['text'] = substr(strip_tags($row['text']), 0, 20);
   117         }
   118 
   119         // Return the reformatted row
   120         return $row;
   121     }
   122 
   123 */
   124 
   125 /**
   126 * Geeklog List Factory Class
   127 *
   128 * @author Sami Barakat, s.m.barakat AT gmail DOT com
   129 *
   130 */
   131 class ListFactory {
   132 
   133     // PRIVATE VARIABLES
   134     var $_fields = array();
   135     var $_query_arr = array();
   136     var $_total_rank = 0;
   137     var $_sort_arr = array();
   138     var $_def_sort_arr = array();
   139     var $_page = 1;
   140     var $_per_page = 0;
   141     var $_page_limits = array();
   142     var $_function = '';
   143     var $_preset_rows = array();
   144     var $_page_url = '';
   145     var $_style = 'table';
   146 
   147     /**
   148     * Constructor
   149     *
   150     * Sets up private url variable and defines the
   151     * SQL_TITLE, SQL_NAME and ROW_NUMBER constants.
   152     *
   153     * @access public
   154     * @param string $url The URL of the page the table appears on
   155     * @param array $limits The avaliable page limits
   156     * @param int $per_page The default number or rows per page
   157     *
   158     */
   159     function ListFactory( $url, $limits = '10,15,20,25,30,35', $per_page = 20 )
   160     {
   161         $url .= (strpos($url,'?') === false ? '?' : '&amp;');
   162         $this->_page_url = $url;
   163         $this->_style = 'table';
   164         $this->_per_page = $per_page;
   165 
   166         if (is_string($limits)) {
   167             $this->_page_limits = explode(',', $limits);
   168         } else if (is_array($limits)) {
   169             $this->_page_limits = $limits;
   170         } else {
   171             $this->_page_limits = array(10, 15, 20, 25, 30, 35);
   172         }
   173 
   174         define('SQL_TITLE', 0);
   175         define('SQL_NAME', 1);
   176         define('ROW_NUMBER', 2);
   177     }
   178 
   179     /**
   180     * Determins which set of templates to load when formatting the output
   181     *
   182     * @access public
   183     * @param string $style Either 'table' or 'inline'
   184     *
   185     */
   186     function setStyle( $style )
   187     {
   188         $this->_style = $style;
   189     }
   190 
   191     /**
   192     * Sets a field in the list.
   193     *
   194     * Note: ROW_NUMBER cannot be sorted
   195     *
   196     * @access public
   197     * @param string $title The title of the field which is displayed to the user
   198     * @param string $name The local name given to the field
   199     * @param boolean $display True if the field is to be displayed to the user otherwise false
   200     * @param boolean $sort True if the field can be sorted otherwise false
   201     * @param string $format The format string with one type specifier
   202     *
   203     */
   204     function setField( $title, $name, $display = true, $sort = true, $format = '%s' )
   205     {
   206         if ($name === ROW_NUMBER) {
   207             $sort = false;
   208         }
   209         $this->_fields[] = array('title' => $title, 'name' => $name, 'display' => $display, 'sort' => $sort, 'format' => $format);
   210     }
   211 
   212     /**
   213     * Sets the SQL query that will generate rows
   214     *
   215     * @access public
   216     * @param string $title The text that's displayed to the user
   217     * @param string $name The local name given to the query
   218     * @param string $sql The SQL string without the ORDER BY or LIMIT clauses
   219     * @param int $rank The rating that determins how many results will be returned
   220     *
   221     */
   222     function setQuery( $title, $name, $sql, $rank )
   223     {
   224         $this->_query_arr[] = array('title' => $title, 'name' => $name, 'sql' => $sql, 'rank' => $rank);
   225         $this->_total_rank += $rank;
   226     }
   227 
   228     /**
   229     * Sets the callback function that gets called when formatting a row
   230     *
   231     * @access public
   232     * @param callback $function Any callable function, method or lambda
   233     *
   234     */
   235     function setRowFunction( $callback )
   236     {
   237         $this->_function = $callback;
   238     }
   239 
   240     /**
   241     * Sets the default sort field
   242     *
   243     * @access public
   244     * @param string $field The field name to sort
   245     * @param string $direction 'asc' for ascending order and 'desc' for descending order
   246     *
   247     */
   248     function setDefaultSort( $field, $direction = 'desc' )
   249     {
   250         $this->_def_sort_arr = array('field' => $field, 'direction' => $direction);
   251     }
   252 
   253     /**
   254     * Appends a single result to the list
   255     *
   256     * @access public
   257     * @param array $result A single result that will be appended to the rest
   258     *
   259     */
   260     function addResult( $result )
   261     {
   262         $this->_preset_rows[] = $result;
   263     }
   264 
   265     /**
   266     * Appends several results to the list
   267     *
   268     * @access public
   269     * @param array $result An array of result that will be appended to the rest
   270     *
   271     */
   272     function addResultArray( $arr )
   273     {
   274         $this->_preset_rows = array_merge($this->_preset_rows, $arr);
   275     }
   276 
   277     /**
   278     * Gets the total number of results from a query
   279     *
   280     * @access private
   281     * @param string $sql The query
   282     * @return int Total number of rows
   283     *
   284     */
   285     function _numRows( $sql )
   286     {
   287         if (is_array($sql))
   288         {
   289             $sql['mysql'] = preg_replace('/SELECT.*FROM/is', 'SELECT COUNT(*) FROM', $sql['mysql']);
   290             $sql['mssql'] = preg_replace('/SELECT.*FROM/is', 'SELECT COUNT(*) FROM', $sql['mssql']);
   291         }
   292         else
   293         {
   294             $sql = preg_replace('/SELECT.*FROM/is', 'SELECT COUNT(*) FROM', $sql);
   295         }
   296         $result = DB_query($sql);
   297         $num_rows = DB_numRows($result);
   298         if ($num_rows <= 1)
   299         {
   300             $B = DB_fetchArray($result, true);
   301             $num_rows = $B[0];
   302         }
   303         return $num_rows ? $num_rows : 0;
   304     }
   305 
   306     /**
   307     * Calculates the offset and limits for each query based on
   308     * the number of rows to be displayed per query per page.
   309     *
   310     * @access private
   311     * @param array $totals The total number of results per query
   312     * @return array The offsets and limits for a given page
   313     *
   314     */
   315     function _getLimits( $totals )
   316     {
   317         $order = range(0, count($totals)-1);
   318         array_multisort($totals, $order);
   319         $fin = array('total' => 0, 'offset' => 0, 'limit' => 0);
   320         $fin = array_fill(0, count($totals), $fin);
   321 
   322         for ($p = 0; $p < $this->_page; $p++)
   323         {
   324             $extra = 0;
   325             for ($q = 0; $q < count($totals); $q++)
   326             {
   327                 $fin[$q]['offset'] = $fin[$q]['offset'] + $fin[$q]['limit'];
   328                 $extra_pp = $extra + $totals[$q]['pp'];
   329                 if ($extra_pp - $totals[$q]['total'] >= 0)
   330                 {
   331                     $fin[$q]['limit'] = $totals[$q]['total'];
   332                     $extra = $extra_pp - $totals[$q]['total'];
   333                     $totals[$q]['total'] = 0;
   334                 }
   335                 else if ($totals[$q]['total'] - $extra_pp >= 0)
   336                 {
   337                     $fin[$q]['limit'] = $extra_pp;
   338                     $totals[$q]['total'] = $totals[$q]['total'] - $extra_pp;
   339                     $extra = 0;
   340                 }
   341                 else
   342                 {
   343                     $fin[$q]['limit'] = $totals[$q]['pp'];
   344                     $totals[$q]['total'] = $totals[$q]['total'] - $totals[$q]['pp'];
   345                 }
   346             }
   347             array_multisort($totals, $order, $fin);
   348         }
   349 
   350         array_multisort($order, $fin);
   351 
   352         return $fin;
   353     }
   354 
   355     /**
   356     * Executes pre set queries
   357     *
   358     * @access public
   359     * @return array The results found
   360     *
   361     */
   362     function ExecuteQueries()
   363     {
   364         // Get the details for sorting the list
   365         $this->_sort_arr['field'] = isset($_GET['order']) ? COM_applyFilter($_GET['order']) : $this->_def_sort_arr['field'];
   366         if (isset($_GET['direction']))
   367             $this->_sort_arr['direction'] = $_GET['direction'] == 'asc' ? 'asc' : 'desc';
   368         else
   369             $this->_sort_arr['direction'] = $this->_def_sort_arr['direction'];
   370 
   371         if (is_numeric($this->_sort_arr['field']))
   372         {
   373             $ord = $this->_def_sort_arr['field'];
   374             $this->_sort_arr['field'] = SQL_TITLE;
   375         }
   376         else
   377         {
   378             $ord = $this->_sort_arr['field'];
   379         }
   380         $order_sql = ' ORDER BY "' . addslashes($ord) . '" ' . strtoupper($this->_sort_arr['direction']);
   381 
   382         $this->_page = isset($_GET['page']) ? COM_applyFilter($_GET['page'], true) : 1;
   383         if (isset($_GET['results'])) {
   384             $this->_per_page = COM_applyFilter($_GET['results'], true);
   385         }
   386 
   387         $rows_arr = $this->_preset_rows;
   388         $this->_total_found = count($this->_preset_rows);
   389 
   390         // When the preset rows exceed per_page bail early
   391         if ($this->_total_found > $this->_per_page)
   392             return array_slice($rows_arr, 0, $this->_per_page);
   393 
   394         // Calculate the limits for each query
   395         $num_query_results = $this->_per_page - $this->_total_found;
   396         $pp_total = $this->_total_found;
   397         $limits = array();
   398         for ($i = 0; $i < count($this->_query_arr); $i++)
   399         {
   400             $limits[$i]['total'] = $this->_numRows($this->_query_arr[$i]['sql']);
   401             $limits[$i]['pp'] = round(($this->_query_arr[$i]['rank'] / $this->_total_rank) * $num_query_results);
   402             $this->_total_found += $limits[$i]['total'];
   403             $pp_total += $limits[$i]['pp'];
   404         }
   405         if ($pp_total < $this->_per_page) {
   406             $limits[0]['pp'] += $this->_per_page - $pp_total;
   407         } else if ($this->_per_page < $pp_total) {
   408             $limits[0]['pp'] -= $pp_total - $this->_per_page;
   409         }
   410         $limits = $this->_getLimits($limits);
   411 
   412         // Execute each query in turn
   413         for ($i = 0; $i < count($this->_query_arr); $i++)
   414         {
   415             if ($limits[$i]['limit'] <= 0) {
   416                 continue;
   417             }
   418             $limit_sql = " LIMIT {$limits[$i]['offset']},{$limits[$i]['limit']}";
   419 
   420             if (is_array($this->_query_arr[$i]['sql']))
   421             {
   422                 $this->_query_arr[$i]['sql']['mysql'] .= $order_sql . $limit_sql;
   423                 $this->_query_arr[$i]['sql']['mssql'] .= $order_sql . $limit_sql;
   424             }
   425             else
   426             {
   427                 $this->_query_arr[$i]['sql'] .= $order_sql . $limit_sql;
   428             }
   429 
   430             $result = DB_query($this->_query_arr[$i]['sql']);
   431 
   432             while ($A = DB_fetchArray($result))
   433             {
   434                 $col = array();
   435                 $col[SQL_TITLE] = $this->_query_arr[$i]['title'];
   436                 $col[SQL_NAME] = $this->_query_arr[$i]['name'];
   437 
   438                 foreach ($this->_fields as $field)
   439                 {
   440                     if (!is_numeric($field['name']) && $field['name'][0] != '_') {
   441                         if (empty($A[ $field['name'] ])) {
   442                             $col[ $field['name'] ] = 'LF_NULL';
   443                         } else {
   444                             $col[ $field['name'] ] = $A[ $field['name'] ];
   445                         }
   446                     }
   447                 }
   448 
   449                 // Need to call the format function before and after
   450                 // sorting the results.
   451                 if (is_callable($this->_function)) {
   452                     $col = call_user_func_array($this->_function, array(true, $col));
   453                 }
   454 
   455                 $rows_arr[] = $col;
   456             }
   457         }
   458 
   459         // Sort the final array
   460         $direction = $this->_sort_arr['direction'] == 'asc' ? SORT_ASC : SORT_DESC;
   461         $column = array();
   462         foreach ($rows_arr as $sortarray) {
   463             $tmp = strip_tags($sortarray[ $this->_sort_arr['field'] ]);
   464             $column[] = ($tmp == 'LF_NULL' ? 0 : $tmp);
   465         }
   466         array_multisort($column, $direction, $rows_arr);
   467 
   468         return $rows_arr;
   469     }
   470 
   471     /**
   472     * Generates the HTML code based on the preset style
   473     *
   474     * @access public
   475     * @param array $rows_arr The rows to display in the list
   476     * @param string $title The title of the list
   477     * @param string $list_top HTML that will appear before the list is printed
   478     * @param string $list_bottom HTML that will appear after the list is printed
   479     * @param boolean $show_sort True to enable column sorting, false to disable
   480     * @param boolean $show_limit True to show page limits, false to hide
   481     * @return string HTML output
   482     *
   483     */
   484     function getFormattedOutput( $rows_arr, $title, $list_top = '', $list_bottom = '', $show_sort = true, $show_limit = true )
   485     {
   486         global $_CONF, $_IMAGE_TYPE, $LANG_ADMIN, $LANG09;
   487 
   488         // get all template fields.
   489         $list_templates = new Template($_CONF['path_layout'] . 'lists/' . $this->_style);
   490         $list_templates->set_file (array (
   491             'list' => 'list.thtml',
   492             'limit' => 'page_limit.thtml',
   493             'sort' => 'page_sort.thtml',
   494             'row' => 'item_row.thtml',
   495             'field' => 'item_field.thtml'
   496         ));
   497 
   498         // insert std. values into the template
   499         $list_templates->set_var('xhtml', XHTML);
   500         $list_templates->set_var('site_url', $_CONF['site_url']);
   501         $list_templates->set_var('layout_url', $_CONF['layout_url']);
   502 
   503         if (count($rows_arr) == 0)
   504         {
   505             $list_templates->set_var('show_sort', 'display:none;');
   506             $list_templates->set_var('show_limit', 'display:none;');
   507             $list_templates->set_var('message', $LANG_ADMIN['no_results']);
   508             $list_templates->set_var('list_top', $list_top);
   509             $list_templates->set_var('list_bottom', $list_bottom);
   510             $list_templates->parse('output', 'list');
   511 
   512             // No results to show so quickly print a message and exit
   513             $retval = '';
   514             if (!empty($title)) {
   515                 $retval .= COM_startBlock($title, '', COM_getBlockTemplate('_admin_block', 'header'));
   516             }
   517             $retval .= $list_templates->finish($list_templates->get_var('output'));
   518             if (!empty($title)) {
   519                 $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer'));
   520             }
   521 
   522             return $retval;
   523         }
   524 
   525         // Draw the page limit select box
   526         if ($show_limit)
   527         {
   528             foreach ($this->_page_limits as $key => $val)
   529             {
   530                 $text = is_numeric($key) ? sprintf($LANG09[67], $val) : $key;
   531                 $href = $this->_page_url . "order={$this->_sort_arr['field']}&amp;" .
   532                             "direction={$this->_sort_arr['direction']}&amp;results=$val";
   533 
   534                 $selected = $this->_per_page == $val ? ' selected="selected"' : '';
   535 
   536                 $list_templates->set_var('limit_text', $text);
   537                 $list_templates->set_var('limit_href', $href);
   538                 $list_templates->set_var('limit_selected', $selected);
   539                 $list_templates->parse('page_limit', 'limit', true);
   540             }
   541         }
   542         else
   543         {
   544             $list_templates->set_var('show_limit', 'display:none;');
   545         }
   546 
   547         // Create how to display the sort field
   548         if ($this->_style == 'table')
   549         {
   550             $arrow = $this->_sort_arr['direction'] == 'asc' ? 'bararrowdown' : 'bararrowup';
   551             $sort_selected = "{$_CONF['layout_url']}/images/$arrow.$_IMAGE_TYPE";
   552             $sort_selected = ' &nbsp;' . COM_createImage($sort_selected, $arrow);
   553             $sort_text = '';
   554         }
   555         else
   556         {
   557             $sort_selected = ' selected="selected"';
   558             $sort_text = $LANG09[68].' ';
   559             if (!$show_sort) {
   560                 $list_templates->set_var('show_sort', 'display:none;');
   561             }
   562 
   563             // Write field
   564             $list_templates->set_var('sort_text', "$sort_text...");
   565             $list_templates->set_var('sort_href', "");
   566             $list_templates->set_var('sort_selected', $sort_selected);
   567             $list_templates->parse('page_sort', 'sort', true);
   568         }
   569 
   570         // Draw the sorting select box/table headings
   571         foreach ($this->_fields as $field)
   572         {
   573             if ($field['display'] == true && $field['title'] != '')
   574             {
   575                 $text = $sort_text . $field['title'];
   576                 $href = '';
   577                 $selected = '';
   578                 if ($show_sort && $field['sort'] != false)
   579                 {
   580                     $direction = $this->_def_sort_arr['direction'];
   581 
   582                     // Show the sort arrow
   583                     if ($this->_sort_arr['field'] === $field['name']) {
   584                         //$selected = $sort_selected;
   585                         $direction = $this->_sort_arr['direction'] == 'asc' ? 'desc' : 'asc';
   586                         $text .= " ($direction)";
   587                     }
   588 
   589                     $href = $this->_page_url . "results={$this->_per_page}&amp;" .
   590                                 "order={$field['name']}&amp;direction=$direction";
   591 
   592                     if ($this->_style == 'table') {
   593                         $text = "<a href=\"$href\">$text</a>";
   594                     }
   595                 }
   596 
   597                 // Write field
   598                 $list_templates->set_var('sort_text', $text);
   599                 $list_templates->set_var('sort_href', $href);
   600                 $list_templates->set_var('sort_selected', $selected);
   601                 $list_templates->parse('page_sort', 'sort', true);
   602             }
   603         }
   604 
   605         $offset = ($this->_page-1) * $this->_per_page;
   606 
   607         $list_templates->set_var('show_message', 'display:none;');
   608 
   609         // Run through all the results
   610         $r = 1;
   611         foreach ($rows_arr as $row)
   612         {
   613             if (is_callable($this->_function)) {
   614                 $row = call_user_func_array($this->_function, array(false, $row));
   615             }
   616 
   617             foreach ($this->_fields as $field)
   618             {
   619                 if ($field['display'] == true)
   620                 {
   621                     $fieldvalue = '';
   622                     if ($field['name'] == ROW_NUMBER) {
   623                         $fieldvalue = $r + $offset;
   624                     } else if (!empty($row[ $field['name'] ])) {
   625                         $fieldvalue = $row[ $field['name'] ];
   626                     }
   627 
   628                     if ($fieldvalue != 'LF_NULL') {
   629                         $fieldvalue = sprintf($field['format'], $fieldvalue, $field['title']);
   630 
   631                         // Write field
   632                         $list_templates->set_var('field_text', $fieldvalue);
   633                         $list_templates->parse('item_field', 'field', true);
   634                     }
   635                 }
   636             }
   637 
   638             // Write row
   639             $r++;
   640             $list_templates->set_var('cssid', ($r % 2) + 1);
   641             $list_templates->parse('item_row', 'row', true);
   642             $list_templates->clear_var('item_field');
   643         }
   644 
   645         // Print page numbers
   646         $page_url = $this->_page_url . 'order=' . $this->_sort_arr['field'] . '&amp;direction=' . $this->_sort_arr['direction'] . '&amp;results=' . $this->_per_page;
   647         $num_pages = ceil($this->_total_found / $this->_per_page);
   648         if ($num_pages > 1) {
   649             $list_templates->set_var('google_paging', COM_printPageNavigation($page_url, $this->_page, $num_pages, 'page=', false, '', ''));
   650         } else {
   651             $list_templates->set_var('google_paging', '');
   652         }
   653 
   654         $list_top = sprintf($list_top, $offset+1, $r+$offset-1, $this->_total_found);
   655         $list_templates->set_var('list_top', $list_top);
   656         $list_templates->set_var('list_bottom', $list_bottom);
   657 
   658         $list_templates->parse('output', 'list');
   659 
   660         // Do the actual output
   661         $retval = '';
   662 
   663         if (!empty($title)) {
   664             $retval .= COM_startBlock($title, '', COM_getBlockTemplate('_admin_block', 'header'));
   665         }
   666 
   667         $retval .= $list_templates->finish($list_templates->get_var('output'));
   668 
   669         if (!empty($title)) {
   670             $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer'));
   671         }
   672 
   673         return $retval;
   674     }
   675 }
   676 
   677 ?>