1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8//this script may only be included - so its better to die if called directly. 9if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) !== false) { 10 header('location: index.php'); 11 exit; 12} 13 14/** 15 * Class Table_Settings_Abstract 16 * 17 * Abstract class to produce default settings for applying the jQuery Tablesorter plugin to a table 18 * 19 * @package Tiki 20 * @subpackage Table 21 */ 22abstract class Table_Settings_Abstract 23{ 24 /** 25 * Generic default settings - portions commented out are to illustrate options 26 * Values here are overriden by table-specific defaults and any user settings 27 * @var array 28 */ 29 protected $default = [ 30 //this is the id of the table 31 'id' => 'tsTable', 32// 'selflinks' => true, //if smarty self_links need to be removed 33 //overall sort settings for the table - individual column settings are under columns below 34 'sorts' => [ 35 'type' => 'reset', //choices: boolean true, boolean false, save, reset, savereset. 36 'group' => true, //overall switch to allow or disallow group headings 37// 'multisort' => false, //multisort on by default - set to false to disable 38// 'sortlist' => [ //to set initial sort icons in standard tables since sortList doesn't use selectors yet 39// 'col' => 0, //id of the th element that the table is sorted by on server side 40// 'dir' => 'asc' //direction of the default initial server-side sort 41// ] 42 ], 43 //overall filter settings for the table or external filters - individual column settings are under columns below 44 'filters' => [ 45 'type' => 'reset', //choices: boolean true, boolean false, reset 46/* 'external' => false, 47 'hide' => false, //to hide filters. choices: true, false (default) 48 //for filters external to the table 49 'external' => array( 50 0 => array( 51 'type' => 'dropdown', 52 'options' => array( 53 //label => url parameter value 54 'Email not confirmed' => 'filterEmailNotConfirmed=on', 55 'User not validated' => 'filterNotValidated=on', 56 'Never logged in' => 'filterNeverLoggedIn=on', 57 ), 58 ), 59 ), 60*/ 61 ], 62/* 63 //to add pagination controls 64 'pager' => array( 65 'type' => true, //choices: boolean true, boolean false 66 'max' => 25, 67 'expand' => array(50, 100, 250, 500), 68 ), 69 //set whether filtering and sorting will be done server-side or client side 70 'ajax' => array( 71 'type' => false, 72 'url' => 'tiki-adminusers.php?{sort:sort}&{filter:filter}', 73 'offset' => 'offset' 74 //url sort and filter params manipulated on the server side if set to false 75 'custom' => false, 76 ), 77 //set whether column, page or subtotals will be added 78 'math' => array( 79 'format' => '$(#,###.00)' //see choices at http://mottie.github.io/tablesorter/docs/example-widget-math.html#mask_examples 80 //add a grand total or amount related to all numbers in the table on the page 81 'page' => 'sum' //see choices at http://mottie.github.io/tablesorter/docs/example-widget-math.html#attribute_settings 82 'pagelabel' => 'Grand total' //custom label 83 'columnlabel' => 'Column total' //custon label for column totals 84 ), 85 //determine whether the code uses columns selectors (e.g., th id) or indexes. With selectors the logic 86 //for which columns are shown doesn't need to be recreated for tables with smarty templates where some 87 //columns aren't shown based on logic. For plugins, indexes will generally need to be used since users set 88 // the columns 89*/ 90 'usecolselector' => false, 91 'colselect' => [ 92 'type' => false, 93 ], 94/* 95 //Set individual sort and filter settings for each column 96 //No need to set if overall sorts and filters settings for the table are set to false above 97 'columns' => array( //zero-based column index or th selector, used only if column-specific settings 98 0 => array( 99 //sort settings for the 1st column 100 'sort' => array( 101 'type' => true, //choices: boolean true, boolean false, text, digit, currency, percent, 102 //usLongDate, shortDate, isoDate, dateFormat-ddmmyyyy, ipAddress, url, time 103 //also string-min (sort strings in a numeric column as large negative number) 104 //empty-top (sorts empty cells to the top) 105 'dir' => 'asc', //asc for ascending and desc for descending 106 'ajax' =>'email', //parameter name that is used when querying the database for this field value 107 //when ajax is used 108 'group' => 'letter' //choices: letter (first letter), word (first word), number, date, date-year, 109 //date-month, date-day, date-week, date-time. letter and word can be 110 //extended, e.g., word-2 shows first 2 words. number-10 will group rows 111 //in blocks of ten. 112 ), 113 //filter settings for the 1st column 114 'filter' => array( 115 //for a text input box where user can type to filter 116 'type' => 'text', //choices: text, dropdown, date, range, non 117 'placeholder' => 'Enter valid email...', //override default placeholder text 118 'ajax' => 'filterEmail', 119 ), 120 //math settings for the 1st column 121 'math' => 'col-sum' //choices: see http://mottie.github.io/tablesorter/docs/example-widget-math.html#attribute_settings 122 ), 123 1 => array( 124 //sort settings for the 2nd column 125 'sort' => array( 126 'type' => false, 127 ), 128 //filter settings for the 2nd column 129 'filter' => array( 130 //for a dropdown list for filtering 131 'type' => 'dropdown', 132 //options are optional - automatically generated from column values if not set 133 //but automatic values will only reflect rows returned from server if ajax is used 134 'options' => array( 135 'first filter', 136 'second filter' 137 ) 138 ), 139 ), 140 2 => array( 141 //sort settings for the 3rd column 142 'sort' => array( 143 'type' => false, 144 ), 145 //filter settings for the 3rd column 146 'filter' => array( 147 //a sliding range filter for numeric fields 148 'type' => 'range', 149 'from' => 10, 150 'to' => 100, 151 'style' => 'popup' //choices: popup or inline 152 ), 153 ), 154 3 => array( 155 //sort settings for the 4th column 156 'sort' => array( 157 'type' => false, 158 ), 159 //filter settings for the 4th column 160 'filter' => array( 161 //produces from and to date fields for filtering 162 'type' => 'date', 163 'from' => '2013-12-15', 164 'to' => '2013-12-16', 165 'format' => 'yy-mm-dd' 166 ), 167 ), 168 ), 169*/ 170 ]; 171 172 /** 173 * Default placeholder text for the different types of filters 174 * @var array 175 */ 176 protected $defaultFilters = [ 177 'text' => [ 178 'type' => 'text', 179 'placeholder' => '' 180 ], 181 //tra('Select a value') 182 'dropdown' => [ 183 'type' => 'dropdown', 184 'placeholder' => '' 185 ], 186 'date' => [ 187 'type' => 'date', 188 'format' => 'yy-mm-dd', 189 'from' => '', 190 'to' => '', 191 ], 192 'range' => [ 193 'type' => 'range', 194 'style' => 'inline', //other option is popup. from and to values can also be set 195 ], 196 ]; 197 198 /** 199 * Strings used to create button and pager control ids and default text 200 * @var array 201 */ 202 protected $ids = [ 203 'sorts' => [ 204 'reset' => [ 205 'id' => '-sortreset', 206 //tra('Unsort') 207 'text' => 'Unsort', 208 ], 209 ], 210 'filters' => [ 211 'reset' => [ 212 'id' => '-filterreset', 213 //tra('Clear Filters') 214 'text' => 'Clear Filters', 215 ], 216 'external' => [ 217 'id' => '-ext', 218 ], 219 ], 220 'pager' => [ 221 'disable' => [ 222 'id' => '-pagerbutton', 223 'text' => [ 224 //tra('Enable Pager') 225 'enable' => 'Enable Pager', 226 //tra('Disable Pager') 227 'disable' => 'Disable Pager', 228 ], 229 ], 230 'controls' => [ 231 'id' => '-pager', 232 ], 233 ], 234 'colselect' => [ 235 'button' => [ 236 'id' => '-colselectbtn', 237 //tra('Show/hide columns') 238 'text' => 'Show/hide columns', 239 ], 240 'div' => [ 241 'id' => '-colselectdiv', 242 ] 243 ], 244 ]; 245 246 /** 247 * Used by a second level of abstract classes extending this class to set different 248 * defaults for plugins vs standard tables 249 * @var null 250 */ 251 protected $default2 = null; 252 /** 253 * Used by table classes extending this class for table-specific default settings 254 * @var null 255 */ 256 protected $ts = null; 257 /** 258 * Used by table classes extending this class to manipulate user-specific settings 259 * @var null 260 */ 261 protected $us = null; 262 /** 263 * Final code settings 264 * @var 265 */ 266 public $s; 267 268 /** 269 * Constructs settings for the table 270 * 271 * @param array $settings user-defined settings array 272 */ 273 public function __construct(array $settings) 274 { 275 //translate default text 276 $this->translateDefault(); 277 278 $this->setUserSettings($settings); 279 280 //override any table settings with user settings 281 $this->ts = $this->overrideSettings($this->ts, $this->us); 282 283 //override second level of default settings 284 $this->ts = $this->overrideSettings($this->default2, $this->ts); 285 286 //get table-specific settings 287 $ts = $this->getTableSettings(); 288 289 //override generic defaults with any table-specific defaults 290 $this->s = $this->overrideSettings($this->default, $ts); 291 292 //set placeholders for filters 293 $this->setPlaceholders(); 294 295 //create id's for any buttons based on table id 296 $this->setIds(); 297 298 //set pager row display levels 299 $this->setMax(); 300 301 //create Ajax arrays for filtering and sorting 302 $this->setAjax(); 303 } 304 305 /** 306 * Translate and filter text 307 */ 308 private function translateDefault() 309 { 310 foreach ($this->ids as $type => $elements) { 311 foreach ($elements as $element => $info) { 312 if (isset($elements[$element]['text'])) { 313 if (is_array($elements[$element]['text'])) { 314 foreach ($elements[$element]['text'] as $each => $text) { 315 $this->default[$type][$element]['text'][$each] = htmlspecialchars(tra($text)); 316 } 317 } else { 318 $this->default[$type][$element]['text'] = htmlspecialchars(tra($elements[$element]['text'])); 319 } 320 } 321 } 322 } 323 324 foreach ($this->defaultFilters as $type => $settings) { 325 foreach ($settings as $each => $text) { 326 if ($each == 'placeholder' || ($type == 'date' && ($each == 'from' || $each == 'to'))) { 327 $this->defaultFilters[$type][$each] = htmlspecialchars(tra($text)); 328 } 329 } 330 } 331 } 332 333 /** 334 * Get table-specific settings 335 * 336 * @return null 337 */ 338 protected function getTableSettings() 339 { 340 return $this->ts; 341 } 342 343 /** 344 * Get user-specific settings 345 * 346 * @return null 347 */ 348 protected function setUserSettings($settings) 349 { 350 $this->us = $settings; 351 } 352 353 /** 354 * Used to override generic default settings with table-specific settings 355 * and to override that result with user settings 356 * 357 * @param $default 358 * @param $settings 359 * 360 * @return array 361 */ 362 protected function overrideSettings($default, $settings) 363 { 364 if (is_array($default) && is_array($settings)) { 365 $ret = array_replace_recursive($default, $settings); 366 } elseif (is_array($settings)) { 367 $ret = $settings; 368 } elseif (is_array($default)) { 369 $ret = $default; 370 } 371 return $ret; 372 } 373 374 /** 375 * Set placeholders for filters 376 */ 377 private function setPlaceholders() 378 { 379 //TODO try array_column here 380 if (isset($this->s['columns'])) { 381 foreach ($this->s['columns'] as $col => $colinfo) { 382 if (isset($colinfo['filter'])) { 383 if (isset($colinfo['filter']['type'])) { 384 $ft = $colinfo['filter']['type']; 385 } 386 //add default placeholder text 387 if (isset($ft) && isset($this->defaultFilters[$ft])) { 388 $this->s['columns'][$col]['filter'] = 389 array_replace_recursive($this->defaultFilters[$ft], $colinfo['filter']); 390 } 391 } 392 } 393 } 394 } 395 396 /** 397 * Automatically set the HTML ids for buttons and pager controls based on the overall table id 398 */ 399 private function setIds() 400 { 401 if (isset($this->s['id']) && isset($this->default['id']) && $this->s['id'] == $this->default['id']) { 402 static $i = 0; 403 ++$i; 404 $this->s['id'] .= $i; 405 } 406 foreach ($this->ids as $type => $elements) { 407 if (isset($this->s[$type]['type']) 408 && $this->s[$type]['type'] !== false 409 && $this->s[$type]['type'] !== 'save') { 410 foreach ($elements as $element => $info) { 411 //for multiple elements 412 if (isset($this->s[$type][$element][0])) { 413 foreach ($this->s[$type][$element] as $key => $info) { 414 if (! isset($this->s[$type][$element][$key]['id'])) { 415 $this->s[$type][$element][$key]['id'] = $this->s['id'] 416 . htmlspecialchars($elements[$element]['id'] . $key); 417 } 418 } 419 } elseif ((! isset($this->s[$type][$element]) || (isset($this->s[$type][$element]) 420 && $this->s[$type][$element] !== false)) && ! isset($this->s[$type][$element]['id'])) { 421 $this->s[$type][$element]['id'] = $this->s['id'] . htmlspecialchars($elements[$element]['id']); 422 } 423 } 424 } 425 } 426 } 427 428 /** 429 * Set levels for pager dropdown that allows for displaying various numbers of rows 430 */ 431 private function setMax() 432 { 433 if (isset($this->s['pager']['type']) && $this->s['pager']['type'] !== false) { 434 if (isset($GLOBALS['maxRecords']) && ! isset($this->s['pager']['max'])) { 435 $this->s['pager']['max'] = $GLOBALS['maxRecords']; 436 } elseif (! isset($this->s['pager']['max'])) { 437 $this->s['pager']['max'] = 25; 438 } 439 if (! isset($this->s['pager']['expand']) && isset($this->s['pager']['max'])) { 440 $this->s['pager']['expand'] = [ 441 $this->s['pager']['max'], 442 2 * $this->s['pager']['max'], 443 4 * $this->s['pager']['max'], 444 12 * $this->s['pager']['max'], 445 ]; 446 } 447 } 448 } 449 450 /** 451 * Correlate Tablesorter sort and filter url parameters to those used in Tiki for database calls 452 * This information will be passed to the jQuery code so that the url parameters generated by 453 * Tablesorter can be changed to their Tiki equivalents for the specific table 454 */ 455 private function setAjax() 456 { 457 if (! empty($this->s['ajax'])) { 458 //sort and filter url parameters 459 if (isset($this->s['columns']) && is_array($this->s['columns'])) { 460 foreach ($this->s['columns'] as $col => $colinfo) { 461 $colpointer = $this->s['usecolselector'] ? substr($col, 1) : $col; 462 if (isset($colinfo['sort']['ajax'])) { 463 //tablesorter url param pattern is sort[0]=0 for ascending sort of first column 464 $this->s['ajax']['sort']['sort-' . $colpointer] = $colinfo['sort']['ajax']; 465 } 466 if (isset($colinfo['filter']['ajax'])) { 467 //tablesorter url param pattern is filter[0]=text for filter on first column 468 $this->s['ajax']['colfilters']['filter-' . $colpointer] = $colinfo['filter']['ajax']; 469 } elseif (isset($colinfo['filter']['options'])) { 470 foreach ($colinfo['filter']['options'] as $label => $value) { 471 $label = rawurlencode($label); 472 $this->s['ajax']['colfilters']['filter-' . $colpointer][$label] = $value; 473 } 474 } 475 } 476 } 477 //external filter params 478 if (isset($this->s['filters']['external']) && is_array($this->s['filters']['external'])) { 479 foreach ($this->s['filters']['external'] as $key => $info) { 480 if (isset($info['options']) && is_array($info['options'])) { 481 foreach ($info['options'] as $opt => $value) { 482 $this->s['ajax']['extfilters'][] = rawurlencode($value); 483 } 484 } 485 } 486 } 487 } 488 } 489} 490