system/classes/listfactory.class.php
author Sami Barakat
Sun, 25 Oct 2009 22:01:38 +0000
branchHEAD
changeset 7391 0bee991937cc
parent 7389 abd5bf00c26b
child 7392 3872f0c040e4
permissions -rwxr-xr-x
Added callback function to search API (feature request #0000985)
     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(
   210             'title' => $title,
   211             'name' => $name,
   212             'display' => $display,
   213             'sort' => $sort,
   214             'format' => $format
   215         );
   216     }
   217 
   218     /**
   219     * Sets the SQL query that will generate rows
   220     *
   221     * @access public
   222     * @param string $title The text that's displayed to the user
   223     * @param string $name The local name given to the query
   224     * @param string $sql The SQL string without the ORDER BY or LIMIT clauses
   225     * @param int $rank The rating that determins how many results will be returned
   226     *
   227     */
   228     function setQuery( $title, $name, $sql, $rank )
   229     {
   230         $this->_query_arr[] = array(
   231             'type' => 'sql',
   232             'title' => $title,
   233             'name' => $name,
   234             'sql' => $sql,
   235             'rank' => $rank
   236         );
   237         $this->_total_rank += $rank;
   238     }
   239 
   240     function setCallback( $title, $name, $function, $rank, $total )
   241     {
   242         $this->_query_arr[] = array(
   243             'type' => 'callback',
   244             'title' => $title,
   245             'name' => $name,
   246             'func' => $function,
   247             'rank' => $rank,
   248             'total' => $total
   249         );
   250         $this->_total_rank += $rank;
   251     }
   252 
   253     /**
   254     * Sets the callback function that gets called when formatting a row
   255     *
   256     * @access public
   257     * @param callback $function Any callable function, method or lambda
   258     *
   259     */
   260     function setRowFunction( $callback )
   261     {
   262         $this->_function = $callback;
   263     }
   264 
   265     /**
   266     * Sets the default sort field
   267     *
   268     * @access public
   269     * @param string $field The field name to sort
   270     * @param string $direction 'asc' for ascending order and 'desc' for descending order
   271     *
   272     */
   273     function setDefaultSort( $field, $direction = 'desc' )
   274     {
   275         $this->_def_sort_arr = array('field' => $field, 'direction' => $direction);
   276     }
   277 
   278     /**
   279     * Appends a single result to the list
   280     *
   281     * @access public
   282     * @param array $result A single result that will be appended to the rest
   283     *
   284     */
   285     function addResult( $result )
   286     {
   287         $this->_preset_rows[] = $result;
   288     }
   289 
   290     /**
   291     * Appends several results to the list
   292     *
   293     * @access public
   294     * @param array $result An array of result that will be appended to the rest
   295     *
   296     */
   297     function addResultArray( $arr )
   298     {
   299         $this->_preset_rows = array_merge($this->_preset_rows, $arr);
   300     }
   301 
   302     /**
   303     * Gets the total number of results from a query
   304     *
   305     * @access private
   306     * @param string $sql The query
   307     * @return int Total number of rows
   308     *
   309     */
   310     function _getTotal( $param )
   311     {
   312         if ($param['type'] == 'callback') {
   313             return $param['total'];
   314         }
   315         else {
   316             $sql = $param['sql'];
   317         }
   318 
   319         if (is_array($sql)) {
   320             $sql['mysql'] = preg_replace('/SELECT.*FROM/is', 'SELECT COUNT(*) FROM', $sql['mysql']);
   321             $sql['mssql'] = preg_replace('/SELECT.*FROM/is', 'SELECT COUNT(*) FROM', $sql['mssql']);
   322         }
   323         else {
   324             $sql = preg_replace('/SELECT.*FROM/is', 'SELECT COUNT(*) FROM', $sql);
   325         }
   326         $result = DB_query($sql);
   327         $num_rows = DB_numRows($result);
   328         if ($num_rows <= 1) {
   329             $B = DB_fetchArray($result, true);
   330             $num_rows = $B[0];
   331         }
   332         return $num_rows ? $num_rows : 0;
   333     }
   334 
   335     /**
   336     * Calculates the offset and limits for each query based on
   337     * the number of rows to be displayed per query per page.
   338     *
   339     * @access private
   340     * @param array $totals The total number of results per query
   341     * @return array The offsets and limits for a given page
   342     *
   343     */
   344     function _getLimits( $totals )
   345     {
   346         $order = range(0, count($totals)-1);
   347         array_multisort($totals, $order);
   348         $fin = array('total' => 0, 'offset' => 0, 'limit' => 0);
   349         $fin = array_fill(0, count($totals), $fin);
   350 
   351         for ($p = 0; $p < $this->_page; $p++)
   352         {
   353             $extra = 0;
   354             for ($q = 0; $q < count($totals); $q++)
   355             {
   356                 $fin[$q]['offset'] = $fin[$q]['offset'] + $fin[$q]['limit'];
   357                 $extra_pp = $extra + $totals[$q]['pp'];
   358                 if ($extra_pp - $totals[$q]['total'] >= 0)
   359                 {
   360                     $fin[$q]['limit'] = $totals[$q]['total'];
   361                     $extra = $extra_pp - $totals[$q]['total'];
   362                     $totals[$q]['total'] = 0;
   363                 }
   364                 else if ($totals[$q]['total'] - $extra_pp >= 0)
   365                 {
   366                     $fin[$q]['limit'] = $extra_pp;
   367                     $totals[$q]['total'] = $totals[$q]['total'] - $extra_pp;
   368                     $extra = 0;
   369                 }
   370                 else
   371                 {
   372                     $fin[$q]['limit'] = $totals[$q]['pp'];
   373                     $totals[$q]['total'] = $totals[$q]['total'] - $totals[$q]['pp'];
   374                 }
   375             }
   376             array_multisort($totals, $order, $fin);
   377         }
   378 
   379         array_multisort($order, $fin);
   380 
   381         return $fin;
   382     }
   383 
   384     /**
   385     * Executes pre set queries
   386     *
   387     * @access public
   388     * @return array The results found
   389     *
   390     */
   391     function ExecuteQueries()
   392     {
   393         // Get the details for sorting the list
   394         $this->_sort_arr['field'] = isset($_GET['order']) ? COM_applyFilter($_GET['order']) : $this->_def_sort_arr['field'];
   395         if (isset($_GET['direction']))
   396             $this->_sort_arr['direction'] = $_GET['direction'] == 'asc' ? 'asc' : 'desc';
   397         else
   398             $this->_sort_arr['direction'] = $this->_def_sort_arr['direction'];
   399 
   400         if (is_numeric($this->_sort_arr['field']))
   401         {
   402             $ord = $this->_def_sort_arr['field'];
   403             $this->_sort_arr['field'] = SQL_TITLE;
   404         }
   405         else
   406         {
   407             $ord = $this->_sort_arr['field'];
   408         }
   409         $order_sql = ' ORDER BY "' . addslashes($ord) . '" ' . strtoupper($this->_sort_arr['direction']);
   410 
   411         $this->_page = isset($_GET['page']) ? COM_applyFilter($_GET['page'], true) : 1;
   412         if (isset($_GET['results'])) {
   413             $this->_per_page = COM_applyFilter($_GET['results'], true);
   414         }
   415 
   416         $rows_arr = $this->_preset_rows;
   417         $this->_total_found = count($this->_preset_rows);
   418 
   419         // When the preset rows exceed per_page bail early
   420         if ($this->_total_found > $this->_per_page)
   421             return array_slice($rows_arr, 0, $this->_per_page);
   422 
   423         // Calculate the limits for each query
   424         $num_query_results = $this->_per_page - $this->_total_found;
   425         $pp_total = $this->_total_found;
   426         $limits = array();
   427         for ($i = 0; $i < count($this->_query_arr); $i++)
   428         {
   429             $limits[$i]['total'] = $this->_getTotal($this->_query_arr[$i]);
   430             $limits[$i]['pp'] = round(($this->_query_arr[$i]['rank'] / $this->_total_rank) * $num_query_results);
   431             $this->_total_found += $limits[$i]['total'];
   432             $pp_total += $limits[$i]['pp'];
   433         }
   434         if ($pp_total < $this->_per_page) {
   435             $limits[0]['pp'] += $this->_per_page - $pp_total;
   436         } else if ($this->_per_page < $pp_total) {
   437             $limits[0]['pp'] -= $pp_total - $this->_per_page;
   438         }
   439         $limits = $this->_getLimits($limits);
   440 
   441         // Execute each query in turn
   442         for ($i = 0; $i < count($this->_query_arr); $i++)
   443         {
   444             if ($limits[$i]['limit'] <= 0) {
   445                 continue;
   446             }
   447 
   448             // This is a callback function
   449             if ($this->_query_arr[$i]['type'] == 'callback')
   450             {
   451                 if (is_callable($this->_query_arr[$i]['func']))
   452                 {
   453                     $callback_rows = call_user_func_array($this->_query_arr[$i]['func'], array($limits[$i]['offset'], $limits[$i]['limit']));
   454 
   455                     foreach ($callback_rows as $row)
   456                     {
   457                         $col = array();
   458                         $col[SQL_TITLE] = $this->_query_arr[$i]['title'];
   459                         $col[SQL_NAME] = $this->_query_arr[$i]['name'];
   460 
   461                         foreach ($this->_fields as $field)
   462                         {
   463                             if (!is_numeric($field['name']) && $field['name'][0] != '_') {
   464                                 if (empty($row[ $field['name'] ])) {
   465                                     $col[ $field['name'] ] = 'LF_NULL';
   466                                 } else {
   467                                     $col[ $field['name'] ] = $row[ $field['name'] ];
   468                                 }
   469                             }
   470                         }
   471 
   472                         // Need to call the format function before and after
   473                         // sorting the results.
   474                         if (is_callable($this->_function)) {
   475                             $col = call_user_func_array($this->_function, array(true, $col));
   476                         }
   477 
   478                         $rows_arr[] = $col;
   479                     }
   480                 }
   481                 continue;
   482             }
   483 
   484             // This is an SQL query, so execute it and format the results
   485             $limit_sql = " LIMIT {$limits[$i]['offset']},{$limits[$i]['limit']}";
   486 
   487             if (is_array($this->_query_arr[$i]['sql']))
   488             {
   489                 $this->_query_arr[$i]['sql']['mysql'] .= $order_sql . $limit_sql;
   490                 $this->_query_arr[$i]['sql']['mssql'] .= $order_sql . $limit_sql;
   491             }
   492             else
   493             {
   494                 $this->_query_arr[$i]['sql'] .= $order_sql . $limit_sql;
   495             }
   496 
   497             $result = DB_query($this->_query_arr[$i]['sql']);
   498 
   499             while ($A = DB_fetchArray($result))
   500             {
   501                 $col = array();
   502                 $col[SQL_TITLE] = $this->_query_arr[$i]['title'];
   503                 $col[SQL_NAME] = $this->_query_arr[$i]['name'];
   504 
   505                 foreach ($this->_fields as $field)
   506                 {
   507                     if (!is_numeric($field['name']) && $field['name'][0] != '_') {
   508                         if (empty($A[ $field['name'] ])) {
   509                             $col[ $field['name'] ] = 'LF_NULL';
   510                         } else {
   511                             $col[ $field['name'] ] = $A[ $field['name'] ];
   512                         }
   513                     }
   514                 }
   515 
   516                 // Need to call the format function before and after
   517                 // sorting the results.
   518                 if (is_callable($this->_function)) {
   519                     $col = call_user_func_array($this->_function, array(true, $col));
   520                 }
   521 
   522                 $rows_arr[] = $col;
   523             }
   524         }
   525 
   526         // Sort the final array
   527         $direction = $this->_sort_arr['direction'] == 'asc' ? SORT_ASC : SORT_DESC;
   528         $column = array();
   529         foreach ($rows_arr as $sortarray) {
   530             $tmp = strip_tags($sortarray[ $this->_sort_arr['field'] ]);
   531             $column[] = ($tmp == 'LF_NULL' ? 0 : $tmp);
   532         }
   533         array_multisort($column, $direction, $rows_arr);
   534 
   535         return $rows_arr;
   536     }
   537 
   538     /**
   539     * Generates the HTML code based on the preset style
   540     *
   541     * @access public
   542     * @param array $rows_arr The rows to display in the list
   543     * @param string $title The title of the list
   544     * @param string $list_top HTML that will appear before the list is printed
   545     * @param string $list_bottom HTML that will appear after the list is printed
   546     * @param boolean $show_sort True to enable column sorting, false to disable
   547     * @param boolean $show_limit True to show page limits, false to hide
   548     * @return string HTML output
   549     *
   550     */
   551     function getFormattedOutput( $rows_arr, $title, $list_top = '', $list_bottom = '', $show_sort = true, $show_limit = true )
   552     {
   553         global $_CONF, $_IMAGE_TYPE, $LANG_ADMIN, $LANG09;
   554 
   555         // get all template fields.
   556         $list_templates = new Template($_CONF['path_layout'] . 'lists/' . $this->_style);
   557         $list_templates->set_file (array (
   558             'list' => 'list.thtml',
   559             'limit' => 'page_limit.thtml',
   560             'sort' => 'page_sort.thtml',
   561             'row' => 'item_row.thtml',
   562             'field' => 'item_field.thtml'
   563         ));
   564 
   565         // insert std. values into the template
   566         $list_templates->set_var('xhtml', XHTML);
   567         $list_templates->set_var('site_url', $_CONF['site_url']);
   568         $list_templates->set_var('layout_url', $_CONF['layout_url']);
   569 
   570         if (count($rows_arr) == 0)
   571         {
   572             $list_templates->set_var('show_sort', 'display:none;');
   573             $list_templates->set_var('show_limit', 'display:none;');
   574             $list_templates->set_var('message', $LANG_ADMIN['no_results']);
   575             $list_templates->set_var('list_top', $list_top);
   576             $list_templates->set_var('list_bottom', $list_bottom);
   577             $list_templates->parse('output', 'list');
   578 
   579             // No results to show so quickly print a message and exit
   580             $retval = '';
   581             if (!empty($title)) {
   582                 $retval .= COM_startBlock($title, '', COM_getBlockTemplate('_admin_block', 'header'));
   583             }
   584             $retval .= $list_templates->finish($list_templates->get_var('output'));
   585             if (!empty($title)) {
   586                 $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer'));
   587             }
   588 
   589             return $retval;
   590         }
   591 
   592         // Draw the page limit select box
   593         if ($show_limit)
   594         {
   595             foreach ($this->_page_limits as $key => $val)
   596             {
   597                 $text = is_numeric($key) ? sprintf($LANG09[67], $val) : $key;
   598                 $href = $this->_page_url . "order={$this->_sort_arr['field']}&amp;" .
   599                             "direction={$this->_sort_arr['direction']}&amp;results=$val";
   600 
   601                 $selected = $this->_per_page == $val ? ' selected="selected"' : '';
   602 
   603                 $list_templates->set_var('limit_text', $text);
   604                 $list_templates->set_var('limit_href', $href);
   605                 $list_templates->set_var('limit_selected', $selected);
   606                 $list_templates->parse('page_limit', 'limit', true);
   607             }
   608         }
   609         else
   610         {
   611             $list_templates->set_var('show_limit', 'display:none;');
   612         }
   613 
   614         // Create how to display the sort field
   615         if ($this->_style == 'table')
   616         {
   617             $arrow = $this->_sort_arr['direction'] == 'asc' ? 'bararrowdown' : 'bararrowup';
   618             $sort_selected = "{$_CONF['layout_url']}/images/$arrow.$_IMAGE_TYPE";
   619             $sort_selected = ' &nbsp;' . COM_createImage($sort_selected, $arrow);
   620             $sort_text = '';
   621         }
   622         else
   623         {
   624             $sort_selected = ' selected="selected"';
   625             $sort_text = $LANG09[68].' ';
   626             if (!$show_sort) {
   627                 $list_templates->set_var('show_sort', 'display:none;');
   628             }
   629 
   630             // Write field
   631             $list_templates->set_var('sort_text', "$sort_text...");
   632             $list_templates->set_var('sort_href', "");
   633             $list_templates->set_var('sort_selected', $sort_selected);
   634             $list_templates->parse('page_sort', 'sort', true);
   635         }
   636 
   637         // Draw the sorting select box/table headings
   638         foreach ($this->_fields as $field)
   639         {
   640             if ($field['display'] == true && $field['title'] != '')
   641             {
   642                 $text = $sort_text . $field['title'];
   643                 $href = '';
   644                 $selected = '';
   645                 if ($show_sort && $field['sort'] != false)
   646                 {
   647                     $direction = $this->_def_sort_arr['direction'];
   648 
   649                     // Show the sort arrow
   650                     if ($this->_sort_arr['field'] === $field['name']) {
   651                         //$selected = $sort_selected;
   652                         $direction = $this->_sort_arr['direction'] == 'asc' ? 'desc' : 'asc';
   653                         $text .= " ($direction)";
   654                     }
   655 
   656                     $href = $this->_page_url . "results={$this->_per_page}&amp;" .
   657                                 "order={$field['name']}&amp;direction=$direction";
   658 
   659                     if ($this->_style == 'table') {
   660                         $text = "<a href=\"$href\">$text</a>";
   661                     }
   662                 }
   663 
   664                 // Write field
   665                 $list_templates->set_var('sort_text', $text);
   666                 $list_templates->set_var('sort_href', $href);
   667                 $list_templates->set_var('sort_selected', $selected);
   668                 $list_templates->parse('page_sort', 'sort', true);
   669             }
   670         }
   671 
   672         $offset = ($this->_page-1) * $this->_per_page;
   673 
   674         $list_templates->set_var('show_message', 'display:none;');
   675 
   676         // Run through all the results
   677         $r = 1;
   678         foreach ($rows_arr as $row)
   679         {
   680             if (is_callable($this->_function)) {
   681                 $row = call_user_func_array($this->_function, array(false, $row));
   682             }
   683 
   684             foreach ($this->_fields as $field)
   685             {
   686                 if ($field['display'] == true)
   687                 {
   688                     $fieldvalue = '';
   689                     if ($field['name'] == ROW_NUMBER) {
   690                         $fieldvalue = $r + $offset;
   691                     } else if (!empty($row[ $field['name'] ])) {
   692                         $fieldvalue = $row[ $field['name'] ];
   693                     }
   694 
   695                     if ($fieldvalue != 'LF_NULL') {
   696                         $fieldvalue = sprintf($field['format'], $fieldvalue, $field['title']);
   697 
   698                         // Write field
   699                         $list_templates->set_var('field_text', $fieldvalue);
   700                         $list_templates->parse('item_field', 'field', true);
   701                     }
   702                 }
   703             }
   704 
   705             // Write row
   706             $r++;
   707             $list_templates->set_var('cssid', ($r % 2) + 1);
   708             $list_templates->parse('item_row', 'row', true);
   709             $list_templates->clear_var('item_field');
   710         }
   711 
   712         // Print page numbers
   713         $page_url = $this->_page_url . 'order=' . $this->_sort_arr['field'] . '&amp;direction=' . $this->_sort_arr['direction'] . '&amp;results=' . $this->_per_page;
   714         $num_pages = ceil($this->_total_found / $this->_per_page);
   715         if ($num_pages > 1) {
   716             $list_templates->set_var('google_paging', COM_printPageNavigation($page_url, $this->_page, $num_pages, 'page=', false, '', ''));
   717         } else {
   718             $list_templates->set_var('google_paging', '');
   719         }
   720 
   721         $list_top = sprintf($list_top, $offset+1, $r+$offset-1, $this->_total_found);
   722         $list_templates->set_var('list_top', $list_top);
   723         $list_templates->set_var('list_bottom', $list_bottom);
   724 
   725         $list_templates->parse('output', 'list');
   726 
   727         // Do the actual output
   728         $retval = '';
   729 
   730         if (!empty($title)) {
   731             $retval .= COM_startBlock($title, '', COM_getBlockTemplate('_admin_block', 'header'));
   732         }
   733 
   734         $retval .= $list_templates->finish($list_templates->get_var('output'));
   735 
   736         if (!empty($title)) {
   737             $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer'));
   738         }
   739 
   740         return $retval;
   741     }
   742 }
   743 
   744 ?>