Structures_DataGrid
[ class tree: Structures_DataGrid ] [ index: Structures_DataGrid ] [ all elements ]

Source for file Renderer.php

Documentation is available at Renderer.php

  1. <?php
  2. /**
  3.  * Base class of all Renderer drivers
  4.  * 
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE:
  8.  * 
  9.  * Copyright (c) 1997-2007, Andrew Nagy <asnagy@webitecture.org>,
  10.  *                          Olivier Guilyardi <olivier@samalyse.com>,
  11.  *                          Mark Wiesemann <wiesemann@php.net>
  12.  *                          Sascha Grossenbacher <saschagros@bluewin.ch>
  13.  * All rights reserved.
  14.  *
  15.  * Redistribution and use in source and binary forms, with or without
  16.  * modification, are permitted provided that the following conditions
  17.  * are met:
  18.  *
  19.  *    * Redistributions of source code must retain the above copyright
  20.  *      notice, this list of conditions and the following disclaimer.
  21.  *    * Redistributions in binary form must reproduce the above copyright
  22.  *      notice, this list of conditions and the following disclaimer in the
  23.  *      documentation and/or other materials provided with the distribution.
  24.  *    * The names of the authors may not be used to endorse or promote products
  25.  *      derived from this software without specific prior written permission.
  26.  *
  27.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  28.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  29.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  30.  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  31.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  32.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  33.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  34.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  35.  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  36.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  37.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38.  *
  39.  * CSV file id: $Id: Renderer.php,v 1.85 2007/12/05 12:42:32 olivierg Exp $
  40.  * 
  41.  * @version  $Revision: 1.85 $
  42.  * @package  Structures_DataGrid
  43.  * @category Structures
  44.  * @license  http://opensource.org/licenses/bsd-license.php New BSD License
  45.  */
  46.  
  47. /**
  48.  * Base class of all Renderer drivers
  49.  *
  50.  * SUPPORTED OPTIONS:
  51.  *
  52.  * - buildHeader:         (bool)   Whether to build the header.
  53.  * - buildFooter:         (bool)   Whether to build the footer.
  54.  * - fillWithEmptyRows:   (bool)   Ensures that all pages have the same number of
  55.  *                                 rows.
  56.  * - numberAlign:         (bool)   Whether to right-align numeric values.
  57.  * - defaultCellValue:    (string) What value to put by default into empty cells.
  58.  * - defaultColumnValues: (array)  Per-column default cell value. This is an array
  59.  *                                 of the form: array(fieldName => value, ...).
  60.  * - hideColumnLinks:     (array)  By default sorting links are enabled on all
  61.  *                                 columns. With this option it is possible to
  62.  *                                 disable sorting links on specific columns. This
  63.  *                                 is an array of the form: array(fieldName, ...).
  64.  *                                 This option only affects drivers that support
  65.  *                                 sorting.
  66.  * - encoding:            (string) The content encoding. If the mbstring extension
  67.  *                                 is present the default value is set from
  68.  *                                 mb_internal_encoding(), otherwise it is ISO-8859-1.
  69.  * - extraVars:           (array)  Variables to be added to the generated HTTP
  70.  *                                 queries.
  71.  * - excludeVars:         (array)  Variables to be removed from the generated
  72.  *                                 HTTP queries.
  73.  * - columnAttributes:    (array)  Column cells attributes. This is an array of
  74.  *                                 the form:
  75.  *                                 array(fieldName => array(attribute => value, ...) ...)
  76.  *                                 This option is only used by XML/HTML based
  77.  *                                 drivers.
  78.  * - onMove:              (string) Name of a Javascript function to call on
  79.  *                                 onClick/onSubmit events when the user is either paging
  80.  *                                 or sorting the data. This function
  81.  *                                 receives a single object argument of the
  82.  *                                 form: { page: <page>, sort: [{field: <field>,
  83.  *                                 direction: <direction>}, ...],
  84.  *                                 data: <user_data> }. Remark: setting this
  85.  *                                 option doesn't remove the href attribute,
  86.  *                                 you should return false from your handler
  87.  *                                 function to void it (eg: for AJAX, etc..).
  88.  * - onMoveData:          (string) User data passed in the "data" member of the
  89.  *                                 object argument passed to onMove. No JSON
  90.  *                                 serialization is performed, this is assigned
  91.  *                                 as a raw string to the "data" attribute.
  92.  *                                 It's up to you to add quotes, slashes, etc...
  93.  * 
  94.  * --- DRIVER INTERFACE ---
  95.  *
  96.  * Methods (none required):
  97.  *     - Constructor
  98.  *     - setContainer()
  99.  *     - getContainer()
  100.  *     - init()
  101.  *     - defaultCellFormatter()
  102.  *     - buildHeader()
  103.  *     - buildBody()
  104.  *     - buildRow()
  105.  *     - buildEmptyRow()
  106.  *     - buildFooter()
  107.  *     - finalize()
  108.  *     - flatten()
  109.  *     - render()
  110.  *     - getPaging()  (deprecated)
  111.  * 
  112.  * Properties (all read-only):
  113.  *     - $_columns
  114.  *     - $_columnsNum
  115.  *     - $_currentSort
  116.  *     - $_firstRecord
  117.  *     - $_lastRecord
  118.  *     - $_multiSort
  119.  *     - $_options
  120.  *     - $_page
  121.  *     - $_pageLimit
  122.  *     - $_pagesNum
  123.  *     - $_records
  124.  *     - $_recordsNum
  125.  *     - $_requestPrefix
  126.  *     - $_sortableFields
  127.  *     - $_totalRecordsNum
  128.  *     
  129.  * Options that drivers may handle:
  130.  *     - encoding
  131.  *     - fillWithEmptyRows
  132.  *     - numberAlign
  133.  *     - extraVars
  134.  *     - excludeVars
  135.  * 
  136.  * @version  $Revision: 1.85 $
  137.  * @author   Olivier Guilyardi <olivier@samalyse.com>
  138.  * @author   Mark Wiesemann <wiesemann@php.net>
  139.  * @author   Sascha Grossenbacher <saschagros@bluewin.ch>
  140.  * @access   public
  141.  * @package  Structures_DataGrid
  142.  * @category Structures
  143.  * @abstract
  144.  */ 
  145. {
  146.     /**
  147.      * Columns' fields names and labels
  148.      * 
  149.      * Drivers can read the content of this property but must not change it.
  150.      * 
  151.      * @var array Structure:
  152.      *             array(<columnIndex> => array(field => <fieldName>,
  153.      *                                          label=> <label>), ...)
  154.      *             Where <columnIndex> is zero-based
  155.      * @access protected
  156.      */
  157.     var $_columns = array();
  158.  
  159.     /**
  160.      * Records content
  161.      *
  162.      * Drivers can read the content of this property but must not change it.
  163.      * 
  164.      * @var array Structure:
  165.      *             array(
  166.      *               <rowIndex> => array(
  167.      *                  <columnIndex> => array(<cellValue>, ...),
  168.      *               ...),
  169.      *             ...)
  170.      *             Where <rowIndex> and <columnIndex> are zero-based
  171.      * @access protected
  172.      */
  173.     var $_records = array();
  174.  
  175.     /**
  176.      * Fields/directions the data is currently sorted by
  177.      *
  178.      * Drivers can read the content of this property but must not change it.
  179.      *
  180.      * @var     array       Structure: array(fieldName => direction, ....)
  181.      * @access  protected
  182.      */
  183.     var $_currentSort = array();
  184.  
  185.     /**
  186.      * Whether the backend support sorting by multiple fields
  187.      *
  188.      * Drivers can read the content of this property but must not change it.
  189.      *
  190.      * @var     bool 
  191.      * @access  protected
  192.      */
  193.     var $_multiSort = false;
  194.  
  195.     /**
  196.      * Number of columns
  197.      *
  198.      * Drivers can read the content of this property but must not change it.
  199.      * 
  200.      * @var int 
  201.      * @access protected
  202.      */
  203.     var $_columnsNum;
  204.  
  205.     /**
  206.      * Number of records in the current page
  207.      * 
  208.      * Drivers can read the content of this property but must not change it.
  209.      *
  210.      * @var int 
  211.      * @access protected
  212.      */
  213.     var $_recordsNum = 0;
  214.  
  215.     /**
  216.      * Total number of records as reported by the datasource
  217.      * 
  218.      * Drivers can read the content of this property but must not change it.
  219.      *
  220.      * @var int 
  221.      * @access protected
  222.      */
  223.     var $_totalRecordsNum;
  224.  
  225.     /**
  226.      * First record number (starting from 1), in the current page
  227.      * 
  228.      * Drivers can read the content of this property but must not change it.
  229.      *
  230.      * @var int 
  231.      * @access protected
  232.      */
  233.     var $_firstRecord;
  234.     
  235.     /**
  236.      * Last record number (starting from 1), in the current page
  237.      * 
  238.      * Drivers can read the content of this property but must not change it.
  239.      *
  240.      * @var int 
  241.      * @access protected
  242.      */
  243.     var $_lastRecord;
  244.     
  245.     /**
  246.      * Current page
  247.      * 
  248.      * Page number starting from 1.
  249.      * 
  250.      * Drivers can read the content of this property but must not change it.
  251.      *
  252.      * @var int 
  253.      * @access protected
  254.      */
  255.     var $_page = 1;
  256.  
  257.     /**
  258.      * Number of records per page
  259.      * 
  260.      * Drivers can read the content of this property but must not change it.
  261.      *
  262.      * @var int 
  263.      * @access protected
  264.      */
  265.     var $_pageLimit = null;
  266.  
  267.     /**
  268.      * Number of pages
  269.      * 
  270.      * Drivers can read the content of this property but must not change it.
  271.      *
  272.      * @var int 
  273.      * @access protected
  274.      */
  275.     var $_pagesNum;
  276.     
  277.      /**
  278.      * GET/POST/Cookie parameters prefix
  279.      * 
  280.      * Drivers can read the content of this property but must not change it.
  281.      *
  282.      * @var string 
  283.      * @access protected
  284.      */
  285.     var $_requestPrefix = '';
  286.  
  287.     /**
  288.      * Which fields the datagrid may be sorted by
  289.      * 
  290.      * Drivers can read the content of this property but must not change it.
  291.      *
  292.      * @var array Field names
  293.      * @access protected
  294.      */
  295.     var $_sortableFields = array();
  296.  
  297.     /**
  298.      * The default directions to sort by
  299.      *
  300.      * Drivers can read the content of this property but must not change it.
  301.      * 
  302.      * @var array Structure: array(field => ASC|DESC, ...)
  303.      * @access protected
  304.      */
  305.     var $_defaultDirections = array();
  306.  
  307.     /**
  308.      * Common and driver-specific options
  309.      * 
  310.      * Drivers can read the content of this property but must not change it.
  311.      *
  312.      * @var array 
  313.      * @access protected
  314.      * @see Structures_DataGrid_Renderer::setOption()
  315.      * @see Structures_DataGrid_Renderer::_addDefaultOptions()
  316.      */
  317.     var $_options = array();
  318.  
  319.     /**
  320.      * Special driver features
  321.      *
  322.      * @var array 
  323.      * @access protected
  324.      */
  325.     var $_features = array();
  326.  
  327.     /**
  328.      * Columns objects
  329.      * 
  330.      * Beware: this is a private property, it is not meant to be accessed
  331.      * by drivers. Use the $_columns property instead
  332.      * 
  333.      * @var array 
  334.      * @access private
  335.      * @see Structures_DataGrid_Renderer::_columns
  336.      */
  337.     var $_columnObjects = array();
  338.  
  339.     /**
  340.      * Whether the datagrid has been built or not
  341.      * @var bool 
  342.      * @access private
  343.      * @see Structures_DataGrid_Renderer::isBuilt()
  344.      */
  345.     var $_isBuilt = false;
  346.  
  347.     /**
  348.      * Cache for the GET parameters that are common to all sorting http queries
  349.      * 
  350.      * @var array 
  351.      * @access private
  352.      * @see Structures_DataGrid_Renderer::_buildSortingHttpQuery()
  353.      */
  354.     var $_sortingHttpQueryCommon = null;
  355.  
  356.     /**
  357.      * Whether streaming is enabled or not
  358.      * 
  359.      * @var bool 
  360.      * @access private
  361.      */
  362.     var $_streamingEnabled = false;
  363.     
  364.     /**
  365.      * URL mapper instance, if provided
  366.      * 
  367.      * @var object Net_URL_Mapper 
  368.      * @access protected
  369.      */
  370.     var $_urlMapper = null;
  371.     
  372.     /**
  373.      * Instantiate the driver and set default options and features
  374.      *
  375.      * Drivers may overload this method in order to change/add default options.
  376.      *
  377.      * @access  public
  378.      * @see Structures_DataGrid_Renderer::_addDefaultOptions()
  379.      */
  380.     function Structures_DataGrid_Renderer()
  381.     {
  382.         $this->_options = array(
  383.             
  384.             /* Options that the drivers may/should handle */    
  385.             'encoding'              => 'ISO-8859-1',
  386.             'fillWithEmptyRows'     => false,
  387.             'numberAlign'           => true,
  388.             'extraVars'             => array(),
  389.             'excludeVars'           => array(),
  390.             'columnAttributes'      => array(),
  391.  
  392.             /* Options that must not be accessed by drivers */
  393.             'buildHeader'           => true
  394.             'buildFooter'           => true,  
  395.             'defaultCellValue'      => null,
  396.             'defaultColumnValues'   => array(),
  397.             'hideColumnLinks'       => array()
  398.             'onMove'                => null,
  399.             'onMoveData'            => '',
  400.         );
  401.  
  402.         $this->_features = array(
  403.                 'streaming' => false
  404.                 'outputBuffering' => false,
  405.                 'objectPreserving' => false,
  406.         );
  407.  
  408.         if (function_exists('mb_internal_encoding')) {
  409.             $encoding = mb_internal_encoding();
  410.             if ($encoding != 'pass'{
  411.                 $this->_options['encoding'$encoding;
  412.             }
  413.         }
  414.  
  415.     }
  416.  
  417.     /**
  418.      * Adds some default options.
  419.      *
  420.      * This method is meant to be called by drivers. It allows adding some
  421.      * default options.
  422.      *
  423.      * @access protected
  424.      * @param array $options An associative array of the from:
  425.      *                        array(optionName => optionValue, ...)
  426.      * @return void 
  427.      * @see Structures_DataGrid_Renderer::setOption()
  428.      */
  429.     function _addDefaultOptions($options)
  430.     {
  431.         $this->_options = array_merge($this->_options$options);
  432.     }
  433.  
  434.     /**
  435.      * Add special driver features
  436.      *
  437.      * This method is meant to be called by drivers. It allows specifying
  438.      * the special features that are supported by the current driver.
  439.      *
  440.      * @access protected
  441.      * @param array $features An associative array of the form:
  442.      *                         array(feature => true|false, ...)
  443.      * @return void 
  444.      */
  445.     function _setFeatures($features)
  446.     {
  447.         $this->_features = array_merge($this->_features$features);
  448.     }
  449.  
  450.     /**
  451.      * Set multiple options
  452.      *
  453.      * @param   mixed   $options    An associative array of the form:
  454.      *                               array("option_name" => "option_value",...)
  455.      * @access  public
  456.      */
  457.     function setOptions($options)
  458.     {
  459.         $this->_options = array_merge($this->_options$options);
  460.     }
  461.  
  462.     /**
  463.      * Set a single option
  464.      *
  465.      * @param   string  $name       Option name
  466.      * @param   mixed   $value      Option value
  467.      * @access  public
  468.      */
  469.     function setOption($name$value)
  470.     {
  471.         $this->_options[$name$value;
  472.     }
  473.  
  474.     /**
  475.      * Provide columns
  476.      * 
  477.      * This method is supposed to be called ONLY by the code that loads the
  478.      * driver. In most cases, that'll be the Structures_DataGrid class.
  479.      * 
  480.      * @param array $columns Array of Structures_DataGrid_Column objects
  481.      * @access public
  482.      */
  483.     function setColumns(&$columns)
  484.     {
  485.         $this->_columnObjects &$columns;
  486.     }
  487.   
  488.     /**
  489.      * Specify how the datagrid is currently sorted
  490.      *
  491.      * 
  492.      * This method is supposed to be called ONLY by the code that loads the
  493.      * driver. In most cases, that'll be the Structures_DataGrid class.
  494.      * 
  495.      * The multiSort capabilities is related to the multiSort DataSource
  496.      * feature. In short : the DataGrid checks if the DataSource supports
  497.      * multiSort and informs the Renderer about it.
  498.      * 
  499.      * @param array $currentSort        Structure:
  500.      *                                   array(fieldName => direction, ....)
  501.      * @param bool  $multiSortCapable   Whether the backend support sorting by
  502.      *                                   multiple fields
  503.      * @access public
  504.      */
  505.     function setCurrentSorting($currentSort$multiSortCapable = false)
  506.     {
  507.         $this->_currentSort = $currentSort;
  508.         $this->_multiSort   = $multiSortCapable;
  509.     }
  510.  
  511.     /**
  512.      * Specify page and row limits
  513.      * 
  514.      * This method is supposed to be called ONLY by the code that loads the
  515.      * driver. In most cases, that'll be the Structures_DataGrid class.
  516.      * 
  517.      * @param int $currentPage Current page number
  518.      * @param int $rowsPerPage Maximum number of rows per page
  519.      * @param int $totalRowNum Total number of data rows
  520.      * @access public
  521.      */
  522.     function setLimit($currentPage$rowsPerPage$totalRowNum{
  523.         $this->_page            = $currentPage;
  524.         $this->_pageLimit       = $rowsPerPage;
  525.         $this->_totalRecordsNum = $totalRowNum;
  526.         $this->_pagesNum        = (is_null($rowsPerPageor $totalRowNum == 0?
  527.             1 : ceil($totalRowNum $rowsPerPage);
  528.         $this->_firstRecord     = ($currentPage - 1$rowsPerPage + 1;
  529.         $this->_lastRecord      = (is_null($rowsPerPage))
  530.                                   ? $totalRowNum
  531.                                   : min($this->_firstRecord + $rowsPerPage - 1,
  532.                                         $totalRowNum);
  533.         if ($this->_lastRecord > $totalRowNum{
  534.             $this->_lastRecord  = $totalRowNum;
  535.         }
  536.     }
  537.  
  538.     /**
  539.      * Tell the renderer whether streaming is enabled or not
  540.      * 
  541.      * This method is supposed to be called ONLY by the code that loads the
  542.      * driver. In most cases, that'll be the Structures_DataGrid class.
  543.      * 
  544.      * @param int $status Whether streaming is enabled or not
  545.      * @access public
  546.      */
  547.     function setStreaming($status{
  548.         $this->_streamingEnabled = (boolean)$status;
  549.     }
  550.  
  551.     /**
  552.      * Attach a container object
  553.      *
  554.      * Drivers that provide support for the Structures_DataGrid::fill() method
  555.      * must implement this method.
  556.      *
  557.      * @abstract
  558.      * @param  object Container of the class supported by the driver
  559.      * @access public
  560.      * @return mixed  True or PEAR_Error
  561.      */
  562.     function setContainer(&$container)
  563.     {
  564.         return $this->_noSupport(__FUNCTION__);
  565.     }
  566.  
  567.     /**
  568.      * Return the container used by the driver
  569.      *
  570.      * Drivers should implement this method when they have some kind of support
  571.      * for rendering containers.
  572.      * 
  573.      * @abstract
  574.      * @return object Container of the class supported by the driver
  575.      *                 or PEAR_Error
  576.      * @access public
  577.      */
  578.     function &getContainer()
  579.     {
  580.         return $this->_noSupport(__FUNCTION__);
  581.     }
  582.  
  583.     /**
  584.      * Create or/and prepare the container
  585.      *
  586.      * Drivers may optionally implement this method for any pre-build()
  587.      * operations.
  588.      *
  589.      * For the container support, it is responsible for creating the
  590.      * container if it has not already been provided by the user with
  591.      * the setContainer() method. It is where preliminary container
  592.      * setup should also be done.
  593.      *
  594.      * @abstract
  595.      * @access protected
  596.      */
  597.     function init()
  598.     {
  599.     }
  600.  
  601.     /**
  602.      * Build the header
  603.      *
  604.      * Drivers may optionally implement this method.
  605.      *
  606.      * @abstract
  607.      * 
  608.      * @param   array $columns Columns' fields names and labels (This is a
  609.      *                          convenient reference to the $_columns protected
  610.      *                          property)
  611.      * @access  protected
  612.      * @return  void or PEAR_Error
  613.      */
  614.     function buildHeader(&$columns
  615.     {
  616.     }
  617.  
  618.     /**
  619.      * Stream a chunk of records
  620.      *
  621.      * @param  array    $records   2D array of records
  622.      * @param  integer  $startRow  Starting row number
  623.      * @param  boolean  $eof       Whether the current chunk is the last chunk
  624.      * @access  protected
  625.      * @return  void or PEAR_Error
  626.      */
  627.     function streamBody($records$startRow$eof = false)
  628.     {
  629.         $rowNum count($records);
  630.         for ($row = 0; $row $rowNum$row++{
  631.             $result $this->buildRow($row $startRow$records[$row]);
  632.             if (PEAR::isError($result)) {
  633.                 return $result;
  634.             }
  635.         }
  636.  
  637.         if ($eof && $this->_options['fillWithEmptyRows'&& !is_null($this->_pageLimit)) {
  638.             for ($row $this->_recordsNum$row $this->_pageLimit$row++{
  639.                 $result $this->buildEmptyRow($row);
  640.                 if (PEAR::isError($result)) {
  641.                     return $result;
  642.                 }
  643.             }
  644.         }
  645.     }
  646.  
  647.     /**
  648.      * Build the body
  649.      *
  650.      * Drivers may overload() this method, if buildRow() and buildEmptyRow()
  651.      * are not flexible enough.
  652.      *
  653.      * @access  protected
  654.      * @return  void or PEAR_Error
  655.      */
  656.     function buildBody()
  657.     {
  658.         for ($row = 0; $row $this->_recordsNum$row++{
  659.             $result $this->buildRow($row$this->_records[$row]);
  660.             if (PEAR::isError($result)) {
  661.                 return $result;
  662.             }
  663.         }
  664.  
  665.         if ($this->_options['fillWithEmptyRows'&& !is_null($this->_pageLimit)) {
  666.             for ($row $this->_recordsNum$row $this->_pageLimit$row++{
  667.                 $result $this->buildEmptyRow($row);
  668.                 if (PEAR::isError($result)) {
  669.                     return $result;
  670.                 }
  671.             }
  672.         }
  673.     }
  674.  
  675.     /**
  676.      * Build a body row
  677.      *
  678.      * This is a very simple method for drivers to build a row.
  679.      * For more flexibility, drivers should overload buildBody()
  680.      *
  681.      * @param int   $index Row index (zero-based)
  682.      * @param array $data  Record data.
  683.      *                      Structure: array(0 => <value0>, 1 => <value1>, ...)
  684.      * @return void or PEAR_Error
  685.      * @access protected
  686.      * @abstract
  687.      */
  688.     function buildRow($index$data)
  689.     {
  690.     }
  691.   
  692.     /**
  693.      * Build an empty row
  694.      *
  695.      * Drivers must overload this method if they need to do something with
  696.      * empty rows that remain at the end of the body.
  697.      * 
  698.      * This method will only be called if the "fillWithEmptyRows" option is
  699.      * enabled.
  700.      * 
  701.      * @param int   $index Row index (zero-based)
  702.      * @return void or PEAR_Error
  703.      * @access protected
  704.      * @abstract
  705.      */
  706.     function buildEmptyRow($index)
  707.     {
  708.     }
  709.     
  710.     /**
  711.      * Build the footer
  712.      *
  713.      * Drivers may optionally implement this method.
  714.      *
  715.      * @abstract
  716.      * @access  protected
  717.      * @return  void or PEAR_Error
  718.      */
  719.     function buildFooter(
  720.     {
  721.     }
  722.  
  723.     /**
  724.      * Finish building the datagrid.
  725.      *
  726.      * Drivers may optionally implement this method for any post-build()
  727.      * operations.
  728.      *
  729.      * @abstract
  730.      * @access  protected
  731.      * @return  void or PEAR_Error
  732.      */
  733.     function finalize()
  734.     {
  735.     }
  736.  
  737.     /**
  738.      * Retrieve output from the container object
  739.      * 
  740.      * Drivers may optionally implement this method.
  741.      *
  742.      * This method is meant to retrieve final output from the container.
  743.      * 
  744.      * Usually the container is an object (ex: HTMLTable instance),
  745.      * and the final output a string.
  746.      *
  747.      * The driver knows how to retrieve such final output from a given
  748.      * container (ex: HTMLTable::toHTML()), and this is where to do it.
  749.      *
  750.      * Sometimes the container may not be an object, but the final output
  751.      * itself. In this case, this method should simply return the container.
  752.      * 
  753.      * This method mustn't output anything directly to the standard output.
  754.      *  
  755.      * @abstract
  756.      * @return mixed Output
  757.      * @access protected
  758.      */
  759.     function flatten()
  760.     {
  761.         return $this->_noSupport(__FUNCTION__);
  762.     }
  763.  
  764.     /**
  765.      * Default formatter for all cells
  766.      * 
  767.      * Drivers may optionally implement this method.
  768.      *
  769.      * @abstract
  770.      * @param string  Cell value
  771.      * @return string Formatted cell value
  772.      * @access protected
  773.      */
  774.     function defaultCellFormatter($value)
  775.     {
  776.         return $value;
  777.     }
  778.  
  779.     /**
  780.      * Build the grid
  781.      *
  782.      * Drivers must not overload this method. Pre and post-build operations
  783.      * can be performed in init() and finalize()
  784.      * 
  785.      * @param  array    $chunk     2D array of records
  786.      * @param  integer  $startRow  Starting row number of current chunk
  787.      * @param  boolean  $eof       Whether the current chunk is the last chunk
  788.      * @access public
  789.      * @return void 
  790.      */
  791.     function build($chunk$startRow$eof = false)
  792.     {
  793.         // on first call of build(): initialize the columns and prepare the header
  794.         if (empty($this->_columns)) {
  795.             $this->_columns = array();
  796.             foreach ($this->_columnObjects as $index => $column{
  797.                 if (!is_null($column->orderBy)) {
  798.                     $field $column->orderBy;
  799.                     if (!in_array($field,$this->_sortableFieldsand 
  800.                         !in_array($field$this->_options['hideColumnLinks'])
  801.                        {
  802.                         $this->_sortableFields[$field;
  803.                     }
  804.                 else if (!is_null($column->fieldName)) {
  805.                     $field $column->fieldName;
  806.                 else {
  807.                     $field $column->columnName;
  808.                 }
  809.  
  810.                 $label $column->columnName;
  811.  
  812.                 if (isset($this->_options['defaultColumnValues'][$field])) {
  813.                     $column->setAutoFillValue($this->_options['defaultColumnValues'][$field]);
  814.                 else if (!is_null($this->_options['defaultCellValue'])) {
  815.                     $column->setAutoFillValue($this->_options['defaultCellValue']);
  816.                 }
  817.  
  818.                 if (is_array($column->attribs)) {
  819.                     if (!array_key_exists($field$this->_options['columnAttributes'])) {
  820.                         $this->_options['columnAttributes'][$field= array();
  821.                     }
  822.                     $this->_options['columnAttributes'][$field=
  823.                         array_merge($this->_options['columnAttributes'][$field],
  824.                                     $column->attribs);
  825.                 }
  826.  
  827.                 $this->_defaultDirections[$field$column->defaultDirection;
  828.  
  829.                 $this->_columns[$indexcompact('field','label');
  830.             }
  831.  
  832.             $this->_columnsNum = count($this->_columns);
  833.  
  834.             $result $this->init();
  835.             if (PEAR::isError($result)) {
  836.                 return $result;
  837.             }
  838.  
  839.             if ($this->_options['buildHeader']{
  840.                 $result $this->buildHeader($this->_columns);
  841.                 if (PEAR::isError($result)) {
  842.                     return $result;
  843.                 }
  844.             }
  845.         }
  846.  
  847.         $chunkSize count($chunk);
  848.         $this->_recordsNum += $chunkSize;
  849.  
  850.         $row = 0;
  851.         for ($rec = 0; $rec $chunkSize$rec++{
  852.             // Currently, no formatting is performed on object records.
  853.             // These are not converted to indexed arrays, so that some
  854.             // renderer drivers might fail to process them.
  855.             if (is_array($chunk[$rec]or !$this->hasFeature('objectPreserving')) {
  856.                 $content = array();
  857.                 $col = 0;
  858.                 foreach ($this->_columnObjects as $column{
  859.                     $content[$this->recordToCell($column$chunk[$rec],
  860.                                                      $row $startRow$col);
  861.                     $col++;
  862.                 }
  863.                 $chunk[$rec$content;
  864.             }
  865.             $row++;
  866.         }
  867.  
  868.         if (!$this->hasFeature('streaming')) {
  869.             $this->_records = array_merge($this->_records$chunk);
  870.         else {
  871.             $result $this->streamBody($chunk$startRow$eof);
  872.             if (PEAR::isError($result)) {
  873.                 return $result;
  874.             }
  875.         }
  876.  
  877.         // if this is the last chunk, do some final operations
  878.         if ($eof{
  879.             if (is_null($this->_pageLimit)) {
  880.                 $result $this->_pageLimit = $this->_recordsNum;
  881.                 if (PEAR::isError($result)) {
  882.                     return $result;
  883.                 }
  884.             }
  885.  
  886.             if (!$this->hasFeature('streaming')) {
  887.                 $result $this->buildBody();
  888.                 if (PEAR::isError($result)) {
  889.                     return $result;
  890.                 }
  891.             }
  892.  
  893.             if ($this->_options['buildFooter']{
  894.                 $result $this->buildFooter();
  895.                 if (PEAR::isError($result)) {
  896.                     return $result;
  897.                 }
  898.             }
  899.  
  900.             $result $this->finalize();
  901.             if (PEAR::isError($result)) {
  902.                 return $result;
  903.             }
  904.  
  905.             $this->_isBuilt = true;
  906.         }
  907.     }
  908.  
  909.     /**
  910.      * Returns the output from the renderer (e.g. HTML table, XLS object, ...)
  911.      *
  912.      * Drivers must not overload this method. Output generation has to be
  913.      * implemented in flatten().
  914.      * 
  915.      * @access  public
  916.      * @return  mixed    The output from the renderer
  917.      */
  918.     function getOutput()
  919.     {
  920.         if ($this->_streamingEnabled{
  921.             return PEAR::raiseError('getOutput() cannot be used together with ' .
  922.                                     'streaming.');
  923.         }
  924.  
  925.         if ($this->hasFeature('outputBuffering')) {
  926.             return $this->flatten();
  927.         else {
  928.             return $this->_noSupport(__FUNCTION__);
  929.         }
  930.     }
  931.  
  932.     /**
  933.      * Render to the standard output
  934.      *
  935.      * This method may be overloaded by renderer drivers in order to prepare
  936.      * writing to the standard output (like calling header(), etc...).
  937.      * 
  938.      * @access  public
  939.      * @return  void or object PEAR_Error
  940.      */
  941.     function render()
  942.     {
  943.         if ($this->hasFeature('outputBuffering')) {
  944.             echo $this->flatten();
  945.         else {
  946.             $result $this->build(array()0);
  947.             if (PEAR::isError($result)) {
  948.                 return $result;
  949.             }
  950.         }
  951.     }
  952.  
  953.     /**
  954.      * Return an error related to an unsupported public method
  955.      *
  956.      * When a given public method is not implemented/supported by the driver
  957.      * it must return a PEAR_Error object with code DATAGRID_ERROR_UNSUPPORTED.
  958.      * This is a helper method for generating such PEAR_Error objects.
  959.      *
  960.      * Example:
  961.      * 
  962.      * <code>
  963.      * function anUnsupportedMethod()
  964.      * {
  965.      *     return $this->_noSupport(__FUNCTION__);
  966.      * }
  967.      * </code>
  968.      *
  969.      * @param string $method The name of the unsupported method
  970.      * @return object PEAR_Error with code DATAGRID_ERROR_UNSUPPORTED
  971.      * @access protected
  972.      */
  973.     function _noSupport($method)
  974.     {
  975.         return PEAR::raiseError("The renderer driver class \"" .get_class($this)
  976.                                 "\" does not support the $method() method",
  977.                                 DATAGRID_ERROR_UNSUPPORTED);
  978.     }
  979.     
  980.     /**
  981.      * Sets the rendered status.  This can be used to "flush the cache" in case
  982.      * you need to render the datagrid twice with the second time having changes
  983.      *
  984.      * This is quite an obsolete method...
  985.      * 
  986.      * @access  public
  987.      * @param   bool        $status     The rendered status of the DataGrid
  988.      */
  989.     function setRendered($status = false)
  990.     {
  991.         if (!$status{
  992.             $this->_isBuilt = false;
  993.         }
  994.         /* What are we supposed to do with $status = true ? */
  995.     }   
  996.  
  997.      /**
  998.      * Set the HTTP Request prefix
  999.      * 
  1000.      * @param string $prefix The prefix string
  1001.      * @return void 
  1002.      * @access public
  1003.      */
  1004.     function setRequestPrefix($prefix
  1005.     {
  1006.         $this->_requestPrefix = $prefix;
  1007.     }
  1008.  
  1009.     /**
  1010.      * Perform record/column to cell intersection and formatting
  1011.      * 
  1012.      * @param  object $column The column object
  1013.      * @param  array  $record Array of record values
  1014.      * @param  int    $row    The row number of the cell
  1015.      * @param  int    $col    The column number of the cell
  1016.      * @return string Formatted cell value
  1017.      * @access private
  1018.      */
  1019.     function recordToCell(&$column$record$row = null$col = null)
  1020.     {
  1021.         $value '';
  1022.         if (isset($column->formatterand !empty($column->formatter)) {
  1023.             $value $column->formatter($record$row$col);
  1024.         else if (isset($column->fieldName)) 
  1025.             $record = (array) $record// record might be an object
  1026.             if (isset($record[$column->fieldName])) {
  1027.                 $value $this->defaultCellFormatter($record[$column->fieldName]);
  1028.             }
  1029.         }
  1030.  
  1031.         if (empty($valueand !is_null($column->autoFillValue)) {
  1032.             $value $column->autoFillValue; 
  1033.         }
  1034.  
  1035.         return $value;
  1036.     }
  1037.  
  1038.     /**
  1039.      * Query the grid build status
  1040.      * 
  1041.      * @return bool Whether the grid has already been built or not
  1042.      * @access public
  1043.      */
  1044.     function isBuilt()
  1045.     {
  1046.         return $this->_isBuilt;
  1047.     }
  1048.     
  1049.     /**
  1050.      * Build an HTTP query for sorting a given column
  1051.      * 
  1052.      * This is a handy method that most drivers can use in order to build
  1053.      * the HTTP queries that are used to sort columns.
  1054.      *
  1055.      * It takes the global "extraVars", "excludeVars" options as well as the
  1056.      * $_requestPrefix property into account and can also convert the ampersand
  1057.      * to XML/HTML entities according to the "encoding" option.
  1058.      *
  1059.      * @param string $field            Sort field name
  1060.      * @param string $direction        Sort direction
  1061.      * @param bool   $convertAmpersand Whether to convert ampersands to
  1062.      *                                  XML/HTML compliant entities
  1063.      * @param array  $extraParameters  Optional extra HTTP parameters
  1064.      * @return string Query string of the
  1065.      * @access protected
  1066.      *             
  1067.      */
  1068.     function _buildSortingHttpQuery($field$direction$convertAmpersand = false
  1069.                                     $extraParameters = array())
  1070.     {
  1071.         $prefix $this->_requestPrefix;
  1072.  
  1073.         if (is_null($this->_sortingHttpQueryCommon)) {
  1074.             // Build and cache the list of common get parameters
  1075.             $this->_sortingHttpQueryCommon $this->_options['extraVars'];
  1076.             $ignore   $this->_options['excludeVars'];
  1077.             $ignore[$prefix 'orderBy';
  1078.             $ignore[$prefix 'direction';
  1079.             foreach ($extraParameters as $var => $value{
  1080.                 $ignore[$prefix $var;
  1081.             }
  1082.             foreach ($_GET as $key => $val{
  1083.                 if (!in_array($key$ignore)) {
  1084.                     $this->_sortingHttpQueryCommon[$key$val;
  1085.                 }
  1086.             }
  1087.         }
  1088.  
  1089.         // Build list of GET variables
  1090.         $get = array();
  1091.         $get[$prefix 'orderBy'$field;
  1092.         $get[$prefix 'direction'$direction;
  1093.         foreach ($extraParameters as $var => $value{
  1094.             $get[$prefix $var$value;
  1095.         }
  1096.  
  1097.         // Merge common and column-specific GET variables
  1098.         $get array_merge($this->_sortingHttpQueryCommon$get);
  1099.  
  1100.         // Build query
  1101.         if ($convertAmpersand and ini_get('arg_separator.output'== '&'{
  1102.             $query htmlentities(http_build_query($get),ENT_QUOTES,
  1103.                                   $this->_options['encoding']);
  1104.         else {
  1105.             $query http_build_query($get);
  1106.         }
  1107.  
  1108.         return $query;
  1109.     }
  1110.     
  1111.     /**
  1112.      * Builds a HTTP URL for sorting and paging.
  1113.      * 
  1114.      * It uses NUM and optionally adds a query string with extraVars/GET
  1115.      *
  1116.      * @param string $field     Sort field name
  1117.      * @param string $direction Sort direction
  1118.      * @param int    $page      Pager index
  1119.      * 
  1120.      * @return string generated HTTP URL
  1121.      */
  1122.     function _buildMapperURL($field$direction$page = 1
  1123.     {
  1124.         if (!empty($direction)) {
  1125.             $direction strtolower($direction);
  1126.         }
  1127.  
  1128.         $params = array('page' => $page,
  1129.                         'orderBy' => $field,
  1130.                         'direction' => $direction);
  1131.         
  1132.         if (is_null($this->_sortingHttpQueryCommon)) {
  1133.             // Build and cache the list of common get parameters
  1134.             $prefix $this->_requestPrefix;
  1135.             $this->_sortingHttpQueryCommon $this->_options['extraVars'];
  1136.             $ignore   $this->_options['excludeVars'];
  1137.             $ignore[$prefix 'orderBy';
  1138.             $ignore[$prefix 'direction';
  1139.             foreach ($_GET as $key => $val{
  1140.                 if (!in_array($key$ignore)) {
  1141.                     $this->_sortingHttpQueryCommon[$key$val;
  1142.                 }
  1143.             }
  1144.         }
  1145.             
  1146.         return $this->_urlMapper->generate($params$this->_sortingHttpQueryCommon);
  1147.     }
  1148.  
  1149.     /**
  1150.      * Build a Javascript handler call for a given page and sorting spec
  1151.      *
  1152.      * @param  string   $page     Page number (can also be "%d" for replacement
  1153.      *                             by Pager, etc...)
  1154.      * @param  mixed    $sortSpec Array of fields and directions, or raw
  1155.      *                             javascript string
  1156.      * @return string             JS function string, semi-colon included
  1157.      * @access protected
  1158.      */
  1159.     function _buildOnMoveCall($page$sortSpec)
  1160.     {
  1161.         $handler '';
  1162.         if ($this->_options['onMove']{
  1163.             if (is_array($sortSpec)) {
  1164.                 $sort = array();
  1165.                 foreach ($sortSpec as $field => $direction{
  1166.                     $sort["{field: '" addslashes($field"', " .
  1167.                               "direction:'$direction'}";
  1168.                 }
  1169.                 $sort "[" join(','$sort"]";
  1170.             else {
  1171.                 $sort $sortSpec;
  1172.             }
  1173.             $data $this->_options['onMoveData'or $data "''";
  1174.             $handler $this->_options['onMove'.
  1175.                 "({ page: $page, sort: $sort, data: $data });";
  1176.         }
  1177.         return $handler;
  1178.     }
  1179.  
  1180.     /**
  1181.      * List special driver features
  1182.      *
  1183.      * @return array Of the form: array(feature => true|false, etc...)
  1184.      * @access public
  1185.      */
  1186.     function getFeatures()
  1187.     {
  1188.         return $this->_features;
  1189.     }
  1190.    
  1191.     /**
  1192.      * Tell if the driver as a specific feature
  1193.      *
  1194.      * @param  string $name Feature name
  1195.      * @return bool 
  1196.      * @access public
  1197.      */
  1198.     function hasFeature($name)
  1199.     {
  1200.         return $this->_features[$name];
  1201.     }
  1202.  
  1203.     /**
  1204.      * Set the URL mapper
  1205.      *
  1206.      * @param object $instance Net_URL_Mapper instance
  1207.      * @return void 
  1208.      * @access public
  1209.      */
  1210.     function setUrlMapper($instance)
  1211.     {
  1212.         $this->_urlMapper = $instance;
  1213.     }
  1214.  
  1215.     /**
  1216.      * Return the URL mapper
  1217.      *
  1218.      * @return object Net_URL_Mapper instance or null
  1219.      * @access public
  1220.      */
  1221.     function getUrlMapper()
  1222.     {
  1223.         return $this->_urlMapper;
  1224.     }
  1225.  
  1226. }
  1227.  
  1228. // This function is here because we can't depend on PHP_Compat
  1229. if (!function_exists('http_build_query')) {
  1230.     function http_build_query($formdata$numeric_prefix = null)
  1231.     {
  1232.         // If $formdata is an object, convert it to an array
  1233.         if (is_object($formdata)) {
  1234.             $formdata get_object_vars($formdata);
  1235.         }
  1236.  
  1237.         // Check we have an array to work with
  1238.         if (!is_array($formdata)) {
  1239.             user_error('http_build_query() Parameter 1 expected to be Array or Object. Incorrect value given.',
  1240.                 E_USER_WARNING);
  1241.             return false;
  1242.         }
  1243.  
  1244.         // If the array is empty, return null
  1245.         if (empty($formdata)) {
  1246.             return;
  1247.         }
  1248.  
  1249.         // Argument seperator
  1250.         $separator ini_get('arg_separator.output');
  1251.         if (strlen($separator== 0{
  1252.             $separator '&';
  1253.         }
  1254.  
  1255.         // Start building the query
  1256.         $tmp = array ();
  1257.         foreach ($formdata as $key => $val{
  1258.             if (is_null($val)) {
  1259.                 continue;
  1260.             }
  1261.  
  1262.             if (is_integer($key&& $numeric_prefix != null{
  1263.                 $key $numeric_prefix $key;
  1264.             }
  1265.  
  1266.             if (is_scalar($val)) {
  1267.                 array_push($tmpurlencode($key'=' urlencode($val));
  1268.                 continue;
  1269.             }
  1270.  
  1271.             // If the value is an array, recursively parse it
  1272.             if (is_array($val|| is_object($val)) {
  1273.                 array_push($tmphttp_build_query_helper($valurlencode($key)));
  1274.                 continue;
  1275.             }
  1276.  
  1277.             // The value is a resource
  1278.             return null;
  1279.         }
  1280.  
  1281.         return implode($separator$tmp);
  1282.     }
  1283.  
  1284.     // Helper function
  1285.     function http_build_query_helper($array$name)
  1286.     {
  1287.         $tmp = array ();
  1288.         foreach ($array as $key => $value{
  1289.             if (is_array($value)) {
  1290.                 array_push($tmphttp_build_query_helper($valuesprintf('%s[%s]'$name$key)));
  1291.             elseif (is_scalar($value)) {
  1292.                 array_push($tmpsprintf('%s[%s]=%s'$nameurlencode($key)urlencode($value)));
  1293.             elseif (is_object($value)) {
  1294.                 array_push($tmphttp_build_query_helper(get_object_vars($value)sprintf('%s[%s]'$name$key)));
  1295.             }
  1296.         }
  1297.  
  1298.         // Argument seperator
  1299.         $separator ini_get('arg_separator.output');
  1300.         if (strlen($separator== 0{
  1301.             $separator '&';
  1302.         }
  1303.  
  1304.         return implode($separator$tmp);
  1305.     }
  1306. }
  1307.  
  1308. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  1309. ?>

Documentation generated on Tue, 18 Dec 2007 11:30:15 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.