0byt3m1n1
Path:
/
data
/
applications
/
aps
/
geeklog
/
1.8.0-0
/
standard
/
htdocs
/
system
/
classes
/
[
Home
]
File: listfactory.class.php
<?php /* Reminder: always indent with 4 spaces (no tabs). */ // +---------------------------------------------------------------------------+ // | Geeklog 1.8 | // +---------------------------------------------------------------------------+ // | listfactory.class.php | // | | // | This class allows personalised lists or tables to be easily generated | // | from arrays or SQL statements. It will also supports the sorting and | // | paging of results. | // +---------------------------------------------------------------------------+ // | Copyright (C) 2008-2011 by the following authors: | // | | // | Authors: Sami Barakat - sami AT sbarakat DOT co DOT uk | // +---------------------------------------------------------------------------+ // | | // | This program is free software; you can redistribute it and/or | // | modify it under the terms of the GNU General Public License | // | as published by the Free Software Foundation; either version 2 | // | of the License, or (at your option) any later version. | // | | // | This program is distributed in the hope that it will be useful, | // | but WITHOUT ANY WARRANTY; without even the implied warranty of | // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | // | GNU General Public License for more details. | // | | // | You should have received a copy of the GNU General Public License | // | along with this program; if not, write to the Free Software Foundation, | // | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | // | | // +---------------------------------------------------------------------------+ if (strpos(strtolower($_SERVER['PHP_SELF']), 'listfactory.class.php') !== false) { die('This file can not be used on its own.'); } /* Example Use // Initiate an instance of the class with the URL of the current page $url = $_SERVER['PHP_SELF']; $obj = new ListFactory($url); // Set up some hidden fields that will be used to help format the data later on $obj->setField('ID', 'id', false); // Set up the fields that will be seen by the user $obj->setField( '#', // Title of the field LF_ROW_NUMBER, // The field identifier can be either: // LF_ROW_NUMBER - The number of each row will be displayed // LF_SOURCE_TITLE - The title given the the SQL query will be displayed // <string> - SQL column name true, // Enables the field true, // The field can be sorted '<b>%d.</b>' // Formats the data ); $obj->setField('Type', LF_SOURCE_TITLE, true, true, '<b>%s</b>'); $obj->setField('Title', 'title'); $obj->setField('Text', 'text'); $obj->setField('Date', 'date'); // Set the default field to sort by $obj->setDefaultSort('date'); // Set the style of output $obj->setStyle('table'); // Sets the call back function to add any extra formatting to the fields $obj->setRowFunction('test_list_func'); // Set up some queries to execute $sql = 'SELECT sid AS id, title, introtext AS text, date FROM stories'; $obj->setQuery( 'Story', // The name given to the query which will be displayed in the LF_SOURCE_TITLE field (optional) 'story', $sql, // The SQL string without the LIMIT or ORDER BY clauses. Notice the column names match the field identifiers 5 // The rank of the query, 5 highest = more results, 1 lowest = least results ); $sql = 'SELECT cid AS id, title, comment AS text, date FROM comments'; $obj->setQuery('Comment', 'comment', $sql, 2); // Append some extra rows to the output // Note: the array must match the field identifier names stated previously $extra_row = array( 'id' => -1, LF_SOURCE_TITLE => 'Extra Row', 'title' => 'An extra row example', 'text' => 'With some really really really long text.....<b>and HTML</b>', 'date' => '2008-07-08 03:00:00' ); // Add the extra row, notice it is not automatically passed to the row function $obj->addResult($extra_row); // Prints out the list $results = $obj->ExecuteQueries(); $title = 'Test ListFactory'; $text = 'Showing %d - %d of %d results.'; $retval = $obj->getFormattedOutput($results, $title, $text); echo $retval; // This function is called by the ListFactory to provide furthur formatting of the results. function test_list_func($preSort, $row) { if ($preSort) { // extract any further information from the results. // such as converting user ID's to usernames } else { // Create a link from the title and id $row['title'] = '<a href="http://www.geeklog.net/list_test.php?id='.$row['id'].'">'.$row['title'].'</a>'; // Shorten the text and strip any HTML tags $row['text'] = substr(strip_tags($row['text']), 0, 20); } // Return the reformatted row return $row; } */ /** * Geeklog List Factory Class * * @author Sami Barakat, s.m.barakat AT gmail DOT com * */ class ListFactory { // PRIVATE VARIABLES private $_fields = array(); private $_sources_arr = array(); private $_total_rank = 0; private $_sort_arr = array(); private $_def_sort_arr = array(); private $_page = 1; private $_per_page = 0; private $_page_limits = array(); private $_function = ''; private $_preset_rows = array(); private $_page_url = ''; private $_style = 'table'; /** * Constructor * * Sets up private url variable and defines the * LF_SOURCE_TITLE, LF_SOURCE_NAME and LF_ROW_NUMBER constants. * * @access public * @param string $url The URL of the page the table appears on * @param array $limits The avaliable page limits * @param int $per_page The default number or rows per page * */ function ListFactory( $url, $limits = '10,15,20,25,30,35', $per_page = 20 ) { $url .= (strpos($url,'?') === false ? '?' : '&'); $this->_page_url = $url; $this->_style = 'table'; $this->_per_page = $per_page; if (is_string($limits)) { $this->_page_limits = explode(',', $limits); } else if (is_array($limits)) { $this->_page_limits = $limits; } else { $this->_page_limits = array(10, 15, 20, 25, 30, 35); } define('LF_SOURCE_TITLE', 0); define('LF_SOURCE_NAME', 1); define('LF_ROW_NUMBER', 2); } /** * Determins which set of templates to load when formatting the output * * @access public * @param string $style Either 'table' or 'inline' * */ function setStyle( $style ) { $this->_style = $style; } /** * Sets a field in the list. * * Note: LF_ROW_NUMBER cannot be sorted * * @access public * @param string $title The title of the field which is displayed to the user * @param string $name The local name given to the field * @param boolean $display True if the field is to be displayed to the user otherwise false * @param boolean $sort True if the field can be sorted otherwise false * @param string $format The format string with one type specifier * */ function setField( $title, $name, $display = true, $sort = true, $format = '%s' ) { if ($name === LF_ROW_NUMBER) { $sort = false; } $this->_fields[] = array( 'title' => $title, 'name' => $name, 'display' => $display, 'sort' => $sort, 'format' => $format ); } /** * Sets the SQL query that will generate rows * * @access public * @param string $title The text that's displayed to the user * @param string $name The local name given to the query * @param string $sql The SQL string without the ORDER BY or LIMIT clauses * @param int $rank The rating that determins how many results will be returned * */ function setQuery( $title, $name, $sql, $rank ) { $this->_sources_arr[] = array( 'type' => 'sql', 'title' => $title, 'name' => $name, 'sql' => $sql, 'rank' => $rank ); $this->_total_rank += $rank; } /** * Sets a callback function that provides another source for results. * * The function will be passed two parameters, $offset and $limit, * which will determine how many results are requested. The callback * function should then return a multidimensional array containing * the results. This provides an alternative to the setQuery() * function as results can be sourced from anywhere. * * @access public * @param string $title The text that's displayed to the user * @param string $name The local name given to the query * @param string $function Any callable function, method or lambda * @param int $rank The rating that determins how many results will be returned * @param int $total The total number of results that are avaliable * */ function setCallback( $title, $name, $callback, $rank, $total ) { $this->_sources_arr[] = array( 'type' => 'callback', 'title' => $title, 'name' => $name, 'func' => $callback, 'rank' => $rank, 'total' => $total ); $this->_total_rank += $rank; } /** * Sets the callback function thats called on every row for styling * or formatting. * * @access public * @param callback $function Any callable function, method or lambda * */ function setRowFunction( $callback ) { $this->_function = $callback; } /** * Sets the default sort field * * @access public * @param string $field The field name to sort * @param string $direction 'asc' for ascending order and 'desc' for descending order * */ function setDefaultSort( $field, $direction = 'desc' ) { $this->_def_sort_arr = array('field' => $field, 'direction' => $direction); } /** * Appends a single result to the list * * @access public * @param array $result A single result that will be appended to the rest * */ function addResult( $result ) { $this->_preset_rows[] = $result; } /** * Appends several results to the list * * @access public * @param array $result An array of result that will be appended to the rest * */ function addResultArray( $arr ) { $this->_preset_rows = array_merge($this->_preset_rows, $arr); } /** * Gets the total number of results from source item, either an sql * query or a callback function. * * @access private * @param array $source The source we are currently working with * @return int Total number of rows * */ function _getTotal( $source ) { if ($source['type'] == 'callback') { return $source['total']; } else { $sql = $source['sql']; } if (is_array($sql)) { $sql['mysql'] = preg_replace('/SELECT.*?FROM/is', 'SELECT COUNT(*) FROM', $sql['mysql']); $sql['mssql'] = preg_replace('/SELECT.*?FROM/is', 'SELECT COUNT(*) FROM', $sql['mssql']); $sql['pgsql'] = preg_replace('/SELECT.*?FROM/is', 'SELECT COUNT(*) FROM', $sql['pgsql']); } else { $sql = preg_replace('/SELECT.*?FROM/is', 'SELECT COUNT(*) FROM', $sql); } $result = DB_query($sql); $num_rows = DB_numRows($result); if ($num_rows <= 1) { $B = DB_fetchArray($result, true); $num_rows = $B[0]; } return $num_rows ? $num_rows : 0; } /** * Calculates the offset and limits for each query based on * the number of rows to be displayed per query per page. * * @access private * @param array $totals The total number of results per query * @return array The offsets and limits for a given page * */ function _getLimits( $totals ) { $order = range(0, count($totals)-1); array_multisort($totals, $order); $fin = array('total' => 0, 'offset' => 0, 'limit' => 0); $fin = array_fill(0, count($totals), $fin); for ($p = 0; $p < $this->_page; $p++) { $extra = 0; for ($q = 0; $q < count($totals); $q++) { $fin[$q]['offset'] = $fin[$q]['offset'] + $fin[$q]['limit']; $extra_pp = $extra + $totals[$q]['pp']; if ($extra_pp - $totals[$q]['total'] >= 0) { $fin[$q]['limit'] = $totals[$q]['total']; $extra = $extra_pp - $totals[$q]['total']; $totals[$q]['total'] = 0; } else if ($totals[$q]['total'] - $extra_pp >= 0) { $fin[$q]['limit'] = $extra_pp; $totals[$q]['total'] = $totals[$q]['total'] - $extra_pp; $extra = 0; } else { $fin[$q]['limit'] = $totals[$q]['pp']; $totals[$q]['total'] = $totals[$q]['total'] - $totals[$q]['pp']; } } array_multisort($totals, $order, $fin); } array_multisort($order, $fin); return $fin; } /** * Applies styling to each row and adds extra meta details that are * used else where in the ListFactory. * * @access private * @param array $row_arr A single results row * @param array $source The source we are currently working with * @return array The row with styling applied and extra meta details * */ function _fillrow( $row_arr, $source ) { $col = array(); $col[LF_SOURCE_TITLE] = $source['title']; $col[LF_SOURCE_NAME] = $source['name']; foreach ($this->_fields as $field) { if (!is_numeric($field['name']) && $field['name'][0] != '_') { if (empty($row_arr[ $field['name'] ])) { $col[ $field['name'] ] = 'LF_NULL'; } else { $col[ $field['name'] ] = $row_arr[ $field['name'] ]; } } } // Need to call the format function before and after // sorting the results. if (is_callable($this->_function)) { $col = call_user_func_array($this->_function, array(true, $col)); } return $col; } /** * Executes pre set queries * * @access public * @return array The results found * */ function ExecuteQueries() { // Set to default sort, we will check the passed param in the next bit $this->_sort_arr['field'] = $this->_def_sort_arr['field']; if (isset($_GET['order'])) { // Loop though the order fields and find a match against $_GET param foreach ($this->_fields as $field) { if ($field['sort'] == true && strcmp($field['name'], $_GET['order']) == 0) { $this->_sort_arr['field'] = $field['name']; // Use a trusted value break; } } } if (isset($_GET['direction'])) { $this->_sort_arr['direction'] = $_GET['direction'] == 'asc' ? 'asc' : 'desc'; } else { $this->_sort_arr['direction'] = $this->_def_sort_arr['direction']; } if (is_numeric($this->_sort_arr['field'])) { $ord = $this->_def_sort_arr['field']; $this->_sort_arr['field'] = LF_SOURCE_TITLE; } else { $ord = $this->_sort_arr['field']; } $order_sql = ' ORDER BY ' . $ord . ' ' . strtoupper($this->_sort_arr['direction']); $this->_page = isset($_GET['page']) ? COM_applyFilter($_GET['page'], true) : 1; if (isset($_GET['results'])) { $this->_per_page = COM_applyFilter($_GET['results'], true); } $rows_arr = $this->_preset_rows; $this->_total_found = count($this->_preset_rows); // When the preset rows exceed per_page bail early if ($this->_total_found > $this->_per_page) { return array_slice($rows_arr, 0, $this->_per_page); } // Calculate the limits for each query $num_query_results = $this->_per_page - $this->_total_found; $pp_total = $this->_total_found; $limits = array(); $num = count($this->_sources_arr); for ($i = 0; $i < $num; $i++) { $limits[$i]['total'] = $this->_getTotal($this->_sources_arr[$i]); $limits[$i]['pp'] = round(($this->_sources_arr[$i]['rank'] / $this->_total_rank) * $num_query_results); $this->_total_found += $limits[$i]['total']; $pp_total += $limits[$i]['pp']; } if ($num == 0) { $limits[0]['total'] = 0; $limits[0]['pp'] = 0; } if ($pp_total < $this->_per_page) { $limits[0]['pp'] += $this->_per_page - $pp_total; } else if ($this->_per_page < $pp_total) { $limits[0]['pp'] -= $pp_total - $this->_per_page; } $limits = $this->_getLimits($limits); // Retrieve the results from each source in turn for ($i = 0; $i < count($this->_sources_arr); $i++) { if ($limits[$i]['limit'] <= 0) { continue; } // This is a callback function if ($this->_sources_arr[$i]['type'] == 'callback') { if (is_callable($this->_sources_arr[$i]['func'])) { $callback_rows = call_user_func_array( $this->_sources_arr[$i]['func'], array($limits[$i]['offset'], $limits[$i]['limit']) ); foreach ($callback_rows as $row) { $rows_arr[] = $this->_fillrow($row, $this->_sources_arr[$i]); } } else { COM_errorLog('ListFactory: A callback function was set for "'. $this->_sources_arr[$i]['name'].'", but it could not be found.'); } continue; } // This is an SQL query, so execute it and format the results $limit_sql = " LIMIT {$limits[$i]['offset']},{$limits[$i]['limit']}"; if (is_array($this->_sources_arr[$i]['sql'])) { $this->_sources_arr[$i]['sql']['mysql'] .= $order_sql . $limit_sql; $this->_sources_arr[$i]['sql']['mssql'] .= $order_sql . $limit_sql; } else { $this->_sources_arr[$i]['sql'] .= $order_sql . $limit_sql; } $result = DB_query($this->_sources_arr[$i]['sql']); while ($A = DB_fetchArray($result)) { $rows_arr[] = $this->_fillrow($A, $this->_sources_arr[$i]); } } // Sort the final array $direction = $this->_sort_arr['direction'] == 'asc' ? SORT_ASC : SORT_DESC; $column = array(); foreach ($rows_arr as $sortarray) { $c = strip_tags($sortarray[ $this->_sort_arr['field'] ]); $column[] = $c == 'LF_NULL' ? '0' : $c; } array_multisort($column, $direction, $rows_arr); return $rows_arr; } /** * Generates the HTML code based on the preset style * * @access public * @param array $rows_arr The rows to display in the list * @param string $title The title of the list * @param string $list_top HTML that will appear before the list is printed * @param string $list_bottom HTML that will appear after the list is printed * @param boolean $show_sort True to enable column sorting, false to disable * @param boolean $show_limit True to show page limits, false to hide * @return string HTML output * */ function getFormattedOutput( $rows_arr, $title, $list_top = '', $list_bottom = '', $show_sort = true, $show_limit = true ) { global $_CONF, $_IMAGE_TYPE, $LANG_ADMIN, $LANG09; // get all template fields. $list_templates = COM_newTemplate($_CONF['path_layout'] . 'lists/' . $this->_style); $list_templates->set_file (array ( 'list' => 'list.thtml', 'limit' => 'page_limit.thtml', 'sort' => 'page_sort.thtml', 'row' => 'item_row.thtml', 'field' => 'item_field.thtml' )); if (count($rows_arr) == 0) { $list_templates->set_var('show_sort', 'display:none;'); $list_templates->set_var('show_limit', 'display:none;'); $list_templates->set_var('message', $LANG_ADMIN['no_results']); $list_templates->set_var('list_top', $list_top); $list_templates->set_var('list_bottom', $list_bottom); $list_templates->parse('output', 'list'); // No results to show so quickly print a message and exit $retval = ''; if (!empty($title)) { $retval .= COM_startBlock($title, '', COM_getBlockTemplate('_admin_block', 'header')); } $retval .= $list_templates->finish($list_templates->get_var('output')); if (!empty($title)) { $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); } return $retval; } // Draw the page limit select box if ($show_limit) { foreach ($this->_page_limits as $key => $val) { $text = is_numeric($key) ? sprintf($LANG09[67], $val) : $key; $href = $this->_page_url . "order={$this->_sort_arr['field']}&" . "direction={$this->_sort_arr['direction']}&results=$val"; // Prevent displaying too many limit items if ($this->_total_found <= $val) { // If this is the last item, chances are its going to be selected $selected = $this->_per_page >= $val ? ' selected="selected"' : ''; $list_templates->set_var('limit_href', $href); $list_templates->set_var('limit_text', $text); $list_templates->set_var('limit_selected', $selected); $list_templates->parse('page_limit', 'limit', true); break; } $selected = $this->_per_page == $val ? ' selected="selected"' : ''; $list_templates->set_var('limit_text', $text); $list_templates->set_var('limit_href', $href); $list_templates->set_var('limit_selected', $selected); $list_templates->parse('page_limit', 'limit', true); } if (empty($text)) { $list_templates->set_var('show_limit', 'display:none;'); } } else { $list_templates->set_var('show_limit', 'display:none;'); } // Create how to display the sort field if ($this->_style == 'table') { $arrow = $this->_sort_arr['direction'] == 'asc' ? 'bararrowdown' : 'bararrowup'; $sort_selected = "{$_CONF['layout_url']}/images/$arrow.$_IMAGE_TYPE"; $sort_selected = ' ' . COM_createImage($sort_selected, $arrow); $sort_text = ''; } else { $sort_selected = ''; $sort_text = $LANG09[68].' '; if (!$show_sort) { $list_templates->set_var('show_sort', 'display:none;'); } } // Draw the sorting select box/table headings foreach ($this->_fields as $field) { if ($field['display'] == true && $field['title'] != '') { $text = $sort_text . $field['title']; $href = ''; $selected = ''; if ($this->_style == 'inline' && $show_sort && $field['sort'] != false) { $direction = $this->_def_sort_arr['direction']; // Show the sort arrow if ($this->_sort_arr['field'] === $field['name']) { // Add drop down item for current sort order if ($this->_sort_arr['direction'] == 'asc') { $list_templates->set_var('sort_text', $text . ' (' . $LANG09[71] . ')'); } else { $list_templates->set_var('sort_text', $text . ' (' . $LANG09[72] . ')'); } $list_templates->set_var('sort_href', ''); $list_templates->set_var('sort_selected', ' selected="selected"'); $list_templates->parse('page_sort', 'sort', true); // Set up the sort order for the opposite direction $direction = $this->_sort_arr['direction'] == 'asc' ? 'desc' : 'asc'; if ($direction == 'asc') { $text .= ' (' . $LANG09[71] . ')'; } else { $text .= ' (' . $LANG09[72] . ')'; } } $href = $this->_page_url . "results={$this->_per_page}&" . "order={$field['name']}&direction=$direction"; // Write field $list_templates->set_var('sort_text', $text); $list_templates->set_var('sort_href', $href); $list_templates->set_var('sort_selected', ''); $list_templates->parse('page_sort', 'sort', true); } else if ($this->_style == 'table') { $direction = $this->_sort_arr['direction'] == 'asc' ? 'desc' : 'asc'; $href = $this->_page_url . "results={$this->_per_page}&" . "order={$field['name']}&direction=$direction"; if ($show_sort && $field['sort'] != false) { $text = "<a href=\"$href\">$text</a>"; if ($this->_sort_arr['field'] === $field['name']) { $selected = $sort_selected; } } // Write field $list_templates->set_var('sort_text', $text); $list_templates->set_var('sort_href', $href); $list_templates->set_var('sort_selected', $selected); $list_templates->parse('page_sort', 'sort', true); } } } $offset = ($this->_page-1) * $this->_per_page; $list_templates->set_var('show_message', 'display:none;'); // Run through all the results $r = 1; foreach ($rows_arr as $row) { if (is_callable($this->_function)) { $row = call_user_func_array($this->_function, array(false, $row)); } foreach ($this->_fields as $field) { if ($field['display'] == true) { $fieldvalue = ''; if ($field['name'] == LF_ROW_NUMBER) { $fieldvalue = $r + $offset; } else if (!empty($row[ $field['name'] ])) { $fieldvalue = $row[ $field['name'] ]; } if ($fieldvalue != 'LF_NULL') { $fieldvalue = sprintf($field['format'], $fieldvalue, $field['title']); // Write field $list_templates->set_var('field_text', $fieldvalue); $list_templates->parse('item_field', 'field', true); } else { // Write an empty field $list_templates->set_var('field_text', ' '); $list_templates->parse('item_field', 'field', true); } } } // Write row $r++; $list_templates->set_var('cssid', ($r % 2) + 1); $list_templates->parse('item_row', 'row', true); $list_templates->clear_var('item_field'); } // Print page numbers $page_url = $this->_page_url.'order='.$this->_sort_arr['field'] . '&direction='.$this->_sort_arr['direction'].'&results='.$this->_per_page; $num_pages = ceil($this->_total_found / $this->_per_page); if ($num_pages > 1) { $list_templates->set_var('google_paging', COM_printPageNavigation($page_url, $this->_page, $num_pages, 'page=', false, '', '')); } else { $list_templates->set_var('google_paging', ''); } $list_top = sprintf($list_top, $offset+1, $r+$offset-1, $this->_total_found); $list_templates->set_var('list_top', $list_top); $list_templates->set_var('list_bottom', $list_bottom); $list_templates->parse('output', 'list'); // Do the actual output $retval = ''; if (!empty($title)) { $retval .= COM_startBlock($title, '', COM_getBlockTemplate('_admin_block', 'header')); } $retval .= $list_templates->finish($list_templates->get_var('output')); if (!empty($title)) { $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); } return $retval; } } ?>