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