1<?php 2/** 3 * Smarty Rendering Driver 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 * CVS file id: $Id: Smarty.php 259488 2008-05-10 15:08:26Z olivierg $ 40 * 41 * @version $Revision: 259488 $ 42 * @package Structures_DataGrid_Renderer_Smarty 43 * @category Structures 44 * @license http://opensource.org/licenses/bsd-license.php New BSD License 45 */ 46 47require_once 'Structures/DataGrid.php'; 48require_once 'Structures/DataGrid/Renderer.php'; 49 50/** 51 * Smarty Rendering Driver 52 * 53 * SUPPORTED OPTIONS: 54 * 55 * - selfPath: (string) The complete path for sorting and paging links. 56 * (default: $_SERVER['PHP_SELF']) 57 * - sortingResetsPaging: (bool) Whether sorting HTTP queries reset paging. 58 * - convertEntities: (bool) Whether or not to convert html entities. 59 * This calls htmlspecialchars(). 60 * - varPrefix: (string) Prefix for smarty variables and functions 61 * assigned by this driver. Can be used in 62 * conjunction with 63 * Structure_DataGrid::setRequestPrefix() for 64 * displaying several grids on a single page. 65 * - associative: (bool) By default the column set and the records 66 * are numerically indexed arrays. By setting 67 * this option to true the keys will be field 68 * names instead. 69 * 70 * SUPPORTED OPERATION MODES: 71 * 72 * - Container Support: yes 73 * - Output Buffering: yes 74 * - Direct Rendering: no 75 * - Streaming: no 76 * - Object Preserving: yes 77 * 78 * GENERAL NOTES: 79 * 80 * To use this driver you need the Smarty template engine from 81 * http://smarty.php.net 82 * 83 * This driver does not support the render() method, it is only able to: 84 * 85 * Either fill() a Smarty object by assigning variables and registering 86 * the {getPaging} smarty function. It's up to you to call Smarty::display() 87 * after the Smarty object has been filled. 88 * 89 * Or return all variables as a PHP array from getOutput(), for maximum 90 * flexibility, so that you can assign them the way you like to your Smarty 91 * instance. 92 * 93 * This driver assigns the following Smarty variables: 94 * <code> 95 * - $columnSet: array of columns specifications 96 * structure: 97 * array ( 98 * 0 => array ( 99 * 'name' => field name, 100 * 'label' => column label, 101 * 'link' => sorting link, 102 * 'attributes' => attributes string, 103 * 'direction' => 'ASC', 'DESC' or '', 104 * 'onclick' => onMove call 105 * ), 106 * ... 107 * ) 108 * - $recordSet: array of records values 109 * - $currentPage: current page (starting from 1) 110 * - $nextPage: next page 111 * - $previousPage: previous page 112 * - $recordLimit: number of rows per page 113 * - $pagesNum: number of pages 114 * - $columnsNum: number of columns 115 * - $recordsNum: number of records in the current page 116 * - $totalRecordsNum: total number of records 117 * - $firstRecord: first record number (starting from 1) 118 * - $lastRecord: last record number (starting from 1) 119 * - $currentSort: array with column names and the directions used for sorting 120 * - $datagrid: a reference that you can pass to {getPaging} 121 * </code> 122 * 123 * This driver registers a Smarty custom function named getPaging 124 * that can be called from Smarty templates with {getPaging} in order 125 * to print paging links. This function accepts the same parameters as the 126 * pagerOptions option of Structures_DataGrid_Renderer_Pager. 127 * 128 * {getPaging} accepts an optional "datagrid" parameter 129 * which you can pass the $datagrid variable, to display paging for an 130 * arbitrary datagrid (useful with multiple dynamic datagrids on a single page). 131 * 132 * Object Records : this drivers preserves object records if provided. This means 133 * that if your datasource provides objects instead of associative arrays as 134 * records, you can access their properties and methods in your smarty template, 135 * with something like: {$recordSet[col]->getSomeInformation()}. 136 * 137 * @version $Revision: 259488 $ 138 * @example smarty-simple.php Using the Smarty renderer 139 * @example smarty-simple.tpl Smarty template with sorting and paging (smarty-simple.tpl) 140 * @author Andrew S. Nagy <asnagy@webitecture.org> 141 * @author Olivier Guilyardi <olivier@samalyse.com> 142 * @author Sascha Grossenbacher <saschagros@bluewin.ch> 143 * @access public 144 * @package Structures_DataGrid_Renderer_Smarty 145 * @see Structures_DataGrid_Renderer_Pager 146 * @category Structures 147 */ 148class Structures_DataGrid_Renderer_Smarty extends Structures_DataGrid_Renderer 149{ 150 /** 151 * Variables that get assigned into the Smarty container 152 * @var array Associative array with smarty var names as keys 153 */ 154 var $_data; 155 156 /** 157 * Smarty container 158 * @var object Smarty object 159 */ 160 var $_smarty = null; 161 162 /** 163 * Constructor 164 * 165 * @access public 166 */ 167 function Structures_DataGrid_Renderer_Smarty() 168 { 169 parent::Structures_DataGrid_Renderer(); 170 $this->_addDefaultOptions( 171 array( 172 'selfPath' => htmlspecialchars($_SERVER['PHP_SELF']), 173 'convertEntities' => true, 174 'sortingResetsPaging' => true, 175 'varPrefix' => '', 176 'associative' => false, 177 ) 178 ); 179 180 $this->_setFeatures( 181 array( 182 'outputBuffering' => true, 183 'objectPreserving' => true, 184 ) 185 ); 186 } 187 188 /** 189 * Attach an already instantiated Smarty object 190 * 191 * @param object $smarty Smarty container 192 * @return mixed True or PEAR_Error 193 */ 194 function setContainer(&$smarty) 195 { 196 $this->_smarty =& $smarty; 197 return true; 198 } 199 200 /** 201 * Attach a Smarty instance 202 * 203 * @deprecated Use setContainer() instead 204 * @param object Smarty instance 205 * @access public 206 */ 207 function setSmarty(&$smarty) 208 { 209 return $this->setContainer($smarty); 210 } 211 212 /** 213 * Return the currently used Smarty object 214 * 215 * @return object Smarty or PEAR_Error object 216 */ 217 function &getContainer() 218 { 219 return $this->_smarty; 220 } 221 222 /** 223 * Initialize the Smarty container 224 * 225 * @access protected 226 */ 227 function init() 228 { 229 $p = $this->_options['varPrefix']; 230 $this->_data = array( 231 "{$p}currentPage" => $this->_page, 232 "{$p}nextPage" => ($this->_page < $this->_pagesNum) ? $this->_page + 1 : null, 233 "{$p}previousPage" => ($this->_page > 1) ? $this->_page - 1 : null, 234 "{$p}recordLimit" => $this->_pageLimit, 235 "{$p}columnsNum" => $this->_columnsNum, 236 "{$p}recordsNum" => $this->_recordsNum, 237 "{$p}totalRecordsNum" => $this->_totalRecordsNum, 238 "{$p}pagesNum" => $this->_pagesNum, 239 "{$p}firstRecord" => $this->_firstRecord, 240 "{$p}lastRecord" => $this->_lastRecord, 241 "{$p}currentSort" => $this->_currentSort, 242 ); 243 } 244 245 /** 246 * Build the header 247 * 248 * @param array $columns Columns' fields names and labels 249 * @access protected 250 * @return void 251 */ 252 function buildHeader(&$columns) 253 { 254 $prepared = array(); 255 foreach ($columns as $index => $spec) { 256 $key = $this->_options['associative'] ? $spec['field'] : $index; 257 if (in_array($spec['field'], $this->_sortableFields)) { 258 reset($this->_currentSort); 259 if ((list($currentField, $currentDirection) = each($this->_currentSort)) 260 && isset($currentField) 261 && $currentField == $spec['field'] 262 ) { 263 if ($currentDirection == 'ASC') { 264 $direction = 'DESC'; 265 } else { 266 $direction = 'ASC'; 267 } 268 $prepared[$key]['direction'] = $currentDirection; 269 } else { 270 $prepared[$key]['direction'] = ''; 271 $direction = $this->_defaultDirections[$spec['field']]; 272 } 273 $page = $this->_options['sortingResetsPaging'] ? 1 : $this->_page; 274 $extra = array('page' => $page); 275 // Check if NUM is enabled 276 if ($this->_urlMapper) { 277 $prepared[$key]['link'] = $this->_buildMapperURL($spec['field'], 278 $direction, 279 $page); 280 } else { 281 $query = $this->_buildSortingHttpQuery($spec['field'], 282 $direction, true, $extra); 283 $prepared[$key]['link'] = "{$this->_options['selfPath']}?$query"; 284 } 285 $prepared[$key]['onclick'] = $this->_buildOnMoveCall($page, 286 array($spec['field'] => $direction)); 287 } else { 288 $query = ''; 289 $prepared[$key]['link'] = ""; 290 } 291 $prepared[$key]['name'] = $spec['field']; 292 $prepared[$key]['label'] = $spec['label']; 293 294 $prepared[$key]['attributes'] = ""; 295 if (isset($this->_options['columnAttributes'][$spec['field']])) { 296 foreach ($this->_options['columnAttributes'][$spec['field']] 297 as $name => $value) { 298 $value = htmlspecialchars($value, ENT_COMPAT, 299 $this->_options['encoding']); 300 $prepared[$key]['attributes'] .= "$name=\"$value\" "; 301 } 302 } 303 } 304 305 $this->_data[$this->_options['varPrefix'] . 'columnSet'] = $prepared; 306 } 307 308 /** 309 * Handles building the body of the table 310 * 311 * @access protected 312 * @return void 313 */ 314 function buildBody() 315 { 316 if ($this->_options['associative']) { 317 $associative = array(); 318 foreach ($this->_records as $row => $rec) { 319 if (is_array($rec)) { 320 $associative[$row] = array(); 321 foreach ($this->_columns as $col => $spec) { 322 $associative[$row][$spec['field']] = $rec[$col]; 323 } 324 } else { 325 // object records are left untouched 326 $associative[$row] =& $this->_records[$row]; 327 } 328 } 329 $this->_data[$this->_options['varPrefix'] . 'recordSet'] 330 = $associative; 331 } else { 332 $this->_data[$this->_options['varPrefix'] . 'recordSet'] 333 = $this->_records; 334 } 335 } 336 337 /** 338 * Assign the computed variables to the Smarty container, if any 339 * 340 * @access protected 341 * @return void 342 */ 343 function finalize() 344 { 345 $p = $this->_options['varPrefix']; 346 347 if ($this->_smarty) { 348 foreach ($this->_data as $key => $val) { 349 $this->_smarty->assign($key, $val); 350 } 351 352 $this->_smarty->assign("{$p}datagrid", $this->_getReference()); 353 354 $this->_smarty->register_function("{$p}getPaging", 355 array(&$this, 'smartyGetPaging')); 356 } else { 357 $this->_data["{$p}datagrid"] = $this->_getReference(); 358 } 359 } 360 361 /** 362 * Return the computed variables 363 * 364 * @access protected 365 * @return array Array with smarty variable names as keys 366 */ 367 function flatten() 368 { 369 return $this->_data; 370 } 371 372 /** 373 * Discard the unsupported render() method 374 * 375 * This Smarty driver does not support the render() method. 376 * It is required to use the setContainer() (or 377 * Structures_DataGrid::fill()) method in order to do anything 378 * with this driver. 379 * 380 */ 381 function render() 382 { 383 return $this->_noSupport(__FUNCTION__); 384 } 385 386 /** 387 * Smarty custom function "getPaging" 388 * 389 * This is only meant to be called from a smarty template, using the 390 * expression: {getPaging <options>} 391 * 392 * <options> are any Pager::factory() options 393 * 394 * @param array $params Options passed from the Smarty template 395 * @param object $smarty Smarty object 396 * @return string Paging HTML links 397 * @access public 398 */ 399 function smartyGetPaging($params, &$smarty) 400 { 401 // Load and get output from the Pager rendering driver 402 $driver =& Structures_DataGrid::loadDriver('Structures_DataGrid_Renderer_Pager'); 403 404 // Propagate the selfPath option. Do not override user params 405 if (!isset($params['path']) && !isset($params['filename'])) { 406 $params['path'] = dirname($this->_options['selfPath']); 407 $params['fileName'] = basename($this->_options['selfPath']); 408 $params['fixFileName'] = false; 409 } 410 411 // Use a different renderer if provided 412 if (isset($params['datagrid'])) { 413 $renderer =& $this->_getReference($params['datagrid']); 414 unset($params['datagrid']); 415 } else { 416 $renderer =& $this; 417 } 418 419 $driver->setupAs($renderer, $params); 420 $driver->build(array(), 0); 421 return $driver->getOutput(); 422 } 423 424 /** 425 * Return a renderer reference by id or create a new id 426 * 427 * @param int Renderer id 428 * @return mixed New id or renderer object 429 */ 430 function &_getReference($id = null) 431 { 432 static $references = array(); 433 434 if (!is_null($id)) { 435 return $references[$id - 1]; 436 } else { 437 $references[] =& $this; 438 $id = count($references); 439 return $id; 440 } 441 } 442 443 /** 444 * Default formatter for all cells 445 * 446 * @param string Cell value 447 * @return string Formatted cell value 448 * @access protected 449 */ 450 function defaultCellFormatter($value) 451 { 452 return $this->_options['convertEntities'] 453 ? htmlspecialchars($value, ENT_COMPAT, $this->_options['encoding']) 454 : $value; 455 } 456} 457 458/* vim: set expandtab tabstop=4 shiftwidth=4: */ 459?> 460