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 8function wikiplugin_trackerfilter_info() 9{ 10 require_once 'lib/wiki-plugins/wikiplugin_trackerlist.php'; 11 $list = wikiplugin_trackerlist_info(); 12 $params = array_merge( 13 [ 14 'filters' => [ 15 'required' => true, 16 'name' => tra('Filters'), 17 'description' => tr( 18 'The list of fields that can be used as filters along with their formats. 19 The field number and format are separated by a %0/%1 and multiple fields are separated by %0:%1.', 20 '<code>', 21 '</code>' 22 ) 23 . tr('Format choices are:') . '<br /><code>d</code> - ' . tr('dropdown') 24 . '<br /><code>r</code> - ' . tr('radio buttons') 25 . '<br /><code>m</code> - ' . tr('multiple choice dropdown') 26 . '<br /><code>c</code> - ' . tr('checkbox') 27 . '<br /><code>t</code> - ' . tr('text with wild characters') 28 . '<br /><code>T</code> - ' . tr('exact text match') 29 . '<br /><code>i</code> - ' . tr('initials') 30 . '<br /><code>sqlsearch</code> - ' . tr('advanced search') 31 . '<br /><code>range</code> - ' . tr('range search (from/to)') 32 . '<br /><code>></code>, <code>><</code>, <code>>>=</code>, <code>><=</code> - ' . tr('greater 33 than, less than, greater than or equal, less than or equal.') . '<br />' 34 . tr('Example:') . ' <code>2/d:4/r:5:(6:7)/sqlsearch</code>', 35 'since' => '1', 36 'doctype' => 'filter', 37 'default' => '', 38 'profile_reference' => 'tracker_field_string', 39 ], 40 'action' => [ 41 'required' => false, 42 'name' => tra('Action'), 43 'description' => tr('Label on the submit button. Default: %0Filter%1. Use a space character to omit the 44 button (for use in datachannels etc)', '<code>', '</code>'), 45 'since' => '2.0', 46 'doctype' => 'show', 47 'default' => 'Filter' 48 ], 49 'displayList' => [ 50 'required' => false, 51 'name' => tra('Display List'), 52 'description' => tra('Show the full list (before filtering) initially (filtered list shown by default)'), 53 'since' => '2.0', 54 'doctype' => 'show', 55 'filter' => 'alpha', 56 'default' => 'n', 57 'options' => [ 58 ['text' => '', 'value' => ''], 59 ['text' => tra('Yes'), 'value' => 'y'], 60 ['text' => tra('No'), 'value' => 'n'] 61 ] 62 ], 63 'line' => [ 64 'required' => false, 65 'name' => tra('Line'), 66 'description' => tra('Displays all the filters on the same line (not shown on same line by default)'), 67 'since' => '2.0', 68 'doctype' => 'show', 69 'filter' => 'alpha', 70 'default' => 'n', 71 'options' => [ 72 ['text' => '', 'value' => ''], 73 ['text' => tra('Yes'), 'value' => 'y'], 74 ['text' => tra('Yes with field label in dropdown'), 'value' => 'in'], 75 ['text' => tra('No'), 'value' => 'n'] 76 ] 77 ], 78 'noflipflop' => [ 79 'required' => false, 80 'name' => tra('No Toggle'), 81 'description' => tr('The toggle button to show/hide filters will not be shown if set to Yes (%0y%1). 82 Default is not to show the toggle (default changed from "n" to "y" in Tiki 20.0).', '<code>', '</code>'), 83 'since' => '6.0', 84 'doctype' => 'show', 85 'filter' => 'alpha', 86 'default' => 'y', 87 'options' => [ 88 ['text' => '', 'value' => ''], 89 ['text' => tra('Yes'), 'value' => 'y'], 90 ['text' => tra('No'), 'value' => 'n'] 91 ] 92 ], 93 'export_action' => [ 94 'required' => false, 95 'name' => tra('Export CSV.'), 96 'description' => tra('Label for an export button. Leave blank to show the usual "Filter" button instead.'), 97 'since' => '6.0', 98 'doctype' => 'export', 99 'default' => '', 100 'advanced' => true, 101 ], 102 'export_status' => [ 103 'required' => false, 104 'name' => tra('Export Status Field'), 105 'description' => tra('Export the status field if the Export CSV option is used'), 106 'since' => '11.1', 107 'advanced' => true, 108 'filter' => 'alpha', 109 'doctype' => 'export', 110 'default' => 'n', 111 'options' => [ 112 ['text' => '', 'value' => ''], 113 ['text' => tra('Yes'), 'value' => 'y'], 114 ['text' => tra('No'), 'value' => 'n'] 115 ] 116 ], 117 'export_created' => [ 118 'required' => false, 119 'name' => tra('Export Created Date Field'), 120 'description' => tra('Export the created date field if the Export CSV option is used'), 121 'since' => '11.1', 122 'advanced' => true, 123 'filter' => 'alpha', 124 'doctype' => 'export', 125 'default' => 'n', 126 'options' => [ 127 ['text' => '', 'value' => ''], 128 ['text' => tra('Yes'), 'value' => 'y'], 129 ['text' => tra('No'), 'value' => 'n'] 130 ] 131 ], 132 'export_modif' => [ 133 'required' => false, 134 'name' => tra('Export Modified Date Field'), 135 'description' => tra('Export the modified date field if the Export CSV option is used'), 136 'since' => '11.1', 137 'advanced' => true, 138 'filter' => 'alpha', 139 'doctype' => 'export', 140 'default' => 'n', 141 'options' => [ 142 ['text' => '', 'value' => ''], 143 ['text' => tra('Yes'), 'value' => 'y'], 144 ['text' => tra('No'), 'value' => 'n'] 145 ] 146 ], 147 'export_charset' => [ 148 'required' => false, 149 'name' => tra('Export Character Set'), 150 'description' => tra('Character set to be used if the Export CSV option is used'), 151 'since' => '11.1', 152 'doctype' => 'export', 153 'default' => 'UTF-8', 154 'advanced' => true, 155 ], 156 'mapButtons' => [ 157 'required' => false, 158 'name' => tra('Map View Buttons'), 159 'description' => tra('Display Mapview and Listview buttons'), 160 'since' => '6.0' . tr(' - was %0 until 12.0', '<code>googlemapButtons</code>'), 161 'filter' => 'alpha', 162 'doctype' => 'show', 163 'default' => '', 164 'options' => [ 165 ['text' => '', 'value' => ''], 166 ['text' => tra('Yes'), 'value' => 'y'], 167 ['text' => tra('No'), 'value' => 'n'] 168 ] 169 ], 170 ], 171 $list['params'] 172 ); 173 174 return [ 175 'name' => tra('Tracker Filter'), 176 'documentation' => 'PluginTrackerFilter', 177 'description' => tra('Create a form to filter tracker fields'), 178 'prefs' => [ 'feature_trackers', 'wikiplugin_trackerfilter' ], 179 'body' => tra('notice'), 180 'iconname' => 'filter', 181 'introduced' => 1, 182 'params' => $params, 183 'format' => 'html', 184 'extraparams' => true, 185 ]; 186} 187 188function wikiplugin_trackerfilter($data, $params) 189{ 190 global $prefs; 191 $trklib = TikiLib::lib('trk'); 192 $smarty = TikiLib::lib('smarty'); 193 static $iTrackerFilter = 0; 194 if ($prefs['feature_trackers'] != 'y') { 195 return $smarty->fetch("wiki-plugins/error_tracker.tpl"); 196 } 197 $iTrackerFilter++; 198 $default = ['noflipflop' => 'y', 'action' => 'Filter', 'line' => 'n', 'displayList' => 'n', 'export_action' => '', 199 'export_itemid' => 'y', 'export_status' => 'n', 'export_created' => 'n', 'export_modif' => 'n', 'export_charset' => 'UTF-8', 'status' => 'opc']; 200 201 if (isset($_REQUEST['reset_filter'])) { 202 wikiplugin_trackerFilter_reset_filters($iTrackerFilter); 203 } elseif (! isset($_REQUEST['filter']) && isset($_REQUEST['session_filters']) && $_REQUEST['session_filters'] == 'y') { 204 $params = array_merge($params, wikiplugin_trackerFilter_get_session_filters($iTrackerFilter)); 205 } 206 if (isset($_REQUEST["mapview"]) && $_REQUEST["mapview"] == 'y' && ! isset($_REQUEST["searchmap"]) && ! isset($_REQUEST["searchlist"]) || isset($_REQUEST["searchmap"]) && ! isset($_REQUEST["searchlist"])) { 207 $params["showmap"] = 'y'; 208 $smarty->assign('mapview', true); 209 } 210 if (isset($_REQUEST["mapview"]) && $_REQUEST["mapview"] == 'n' && ! isset($_REQUEST["searchmap"]) && ! isset($_REQUEST["searchlist"]) || isset($_REQUEST["searchlist"]) && ! isset($_REQUEST["searchmap"])) { 211 $params["showmap"] = 'n'; 212 $smarty->assign('mapview', false); 213 } 214 $params = array_merge($default, $params); 215 if ($params['noflipflop'] !== 'n') { 216 $params['noflipflop'] = 'y'; 217 } 218 extract($params, EXTR_SKIP); 219 $dataRes = ''; 220 221 if (isset($_REQUEST['msgTrackerFilter'])) { 222 $smarty->assign('msgTrackerFilter', $_REQUEST['msgTrackerFilter']); 223 } 224 225 $headerlib = TikiLib::lib('header'); 226 227 /** 228 * adding spinner when clicking on the filter button 229 *added by Axel.mwenze on Monday, august 05, 2019 230 */ 231 $headerlib->add_jq_onready( 232 '$("#form-filter").submit(function(r) { 233 $(".trackerfilter_loader").show(); 234 return true; 235 })' 236 ); 237 238 $headerlib->add_jq_onready( 239 '/* Maintain state of other trackerfilter plugin forms */ 240 $(".trackerfilter form").submit( function () { 241 var current_tracker = this; 242 $(current_tracker).append("<input type=\"hidden\" name=\"tracker_filters[]\" value=\"" + $(current_tracker).serialize() + "\" />") 243 $(".trackerfilter form").each( function() { 244 if (current_tracker !== this && $("input[name=count_item]", this).val() > 0) { 245 $(current_tracker).append("<input type=\"hidden\" name=\"tracker_filters[]\" value=\"" + $(this).serialize() + "\" />") 246 } 247 }); 248 return true; 249 });' 250 ); 251 if ($prefs['jquery_ui_chosen'] === 'y') { 252 $headerlib->add_css('@media (min-width: 768px) { .tiki #col1 .trackerfilter form .table-responsive { overflow-x: visible; overflow-y: visible; }} /* jquery_ui_chosen specific: edit this in wikiplugin_trackerfilter.php */'); 253 } // TODO: move the CSS to less and add class html attribute in wikiplugin_trackerfilter.tpl instead 254 255 if (! empty($_REQUEST['tracker_filters']) && count($_REQUEST['tracker_filters']) > 0) { 256 foreach ($_REQUEST['tracker_filters'] as $tf_vals) { 257 parse_str(urldecode($tf_vals), $vals); 258 foreach ($vals as $k => $v) { 259 // if it's me and i had some items 260 if ($k == 'iTrackerFilter' && $v == $iTrackerFilter && isset($vals['count_item']) && $vals['count_item'] > 0) { 261 // unset request params for all the plugins (my one will be array_merged below) 262 foreach ($_REQUEST['tracker_filters'] as $tf_vals2) { 263 parse_str(urldecode($tf_vals2), $vals2); 264 foreach ($vals2 as $k2 => $v2) { 265 unset($GLOBALS['_REQUEST'][$k2]); 266 } 267 } 268 $_REQUEST = array_merge($_REQUEST, $vals); 269 } 270 } 271 } 272 } 273 if (! empty($_REQUEST['filter']) || ! empty($_REQUEST['reset_filter'])) { // If we set a new filter, reset pagination for this plugin 274 unset($GLOBALS['_REQUEST']["tr_offset$iTrackerFilter"]); 275 } 276 277 if (! isset($filters)) { 278 if (empty($export_action)) { 279 return tra('missing parameters') . ' filters'; 280 } else { 281 $listfields = []; 282 $filters = []; 283 $formats = []; 284 } 285 } else { 286 $listfields = wikiplugin_trackerFilter_split_filters($filters); 287 foreach ($listfields as $i => $f) { 288 if (strchr($f, '/')) { 289 list($fieldId, $format) = explode('/', $f); 290 $listfields[$i] = $fieldId; 291 $formats[$fieldId] = $format; 292 } else { 293 $formats[$f] = ''; 294 } 295 } 296 } 297 if (empty($trackerId) && ! empty($_REQUEST['trackerId'])) { 298 $trackerId = $_REQUEST['trackerId']; 299 } 300 301 $tracker_definition = Tracker_Definition::get($trackerId); 302 303 if (empty($_REQUEST['filter']) && empty($export_action)) { // look if not coming from an initial and not exporting 304 foreach ($_REQUEST as $key => $val) { 305 if (substr($key, 0, 2) == 'f_') { 306 $_REQUEST['filter'] = 'y'; 307 break; 308 } 309 } 310 } 311 if (! isset($sortchoice)) { 312 $sortchoice = ''; 313 } else { 314 unset($params['sortchoice']); 315 if (isset($_REQUEST["tr_sort_mode$iTrackerFilter"])) { 316 $params['sort_mode'] = $_REQUEST["tr_sort_mode$iTrackerFilter"]; 317 } 318 foreach ($sortchoice as $i => $sc) { 319 $sc = explode('|', $sc); 320 $sortchoice[$i] = ['value' => $sc[0], 'label' => empty($sc[1]) ? $sc[0] : $sc[1]]; 321 } 322 } 323 if (empty($trackerId) || ! ($tracker = $trklib->get_tracker($trackerId))) { 324 return $smarty->fetch("wiki-plugins/error_tracker.tpl"); 325 } 326 $filters = wikiplugin_trackerFilter_get_filters($trackerId, $listfields, $formats, $status); 327 if (empty($export_action)) { 328 if (! is_array($filters)) { 329 return $filters; 330 } 331 } 332 if (($displayList == 'y' || isset($_REQUEST['filter']) || isset($_REQUEST["tr_offset$iTrackerFilter"]) || isset($_REQUEST['tr_sort_mode'])) && 333 (! isset($_REQUEST['iTrackerFilter']) || $_REQUEST['iTrackerFilter'] == $iTrackerFilter)) { 334 $ffs = []; 335 $values = []; 336 $exactValues = []; 337 wikiplugin_trackerfilter_build_trackerlist_filter($_REQUEST, $formats, $ffs, $values, $exactValues, $tracker_definition); 338 // echo '<pre>BUILD_FILTER'; print_r($ffs); print_r($exactValues); echo '</pre>'; 339 340 $params['fields'] = isset($fields) ? $fields : []; 341 if (empty($params['trackerId'])) { 342 $params['trackerId'] = $trackerId; 343 } 344 if (! empty($ffs)) { 345 if (empty($params['filterfield'])) { 346 $params['filterfield'] = $ffs; 347 $params['exactvalue'] = $exactValues; 348 $params['filtervalue'] = $values; 349 } else { 350 if (! isset($params['exactvalue']) && ! isset($params['filtervalue'])) { 351 Feedback::error(tr('TrackerFilter: Wrong parameter specified - filterfield exists but exactvalue or filtervalue not set.')); 352 } 353 $c = count($params['filterfield']); 354 $params['filterfield'] = array_merge($params['filterfield'], $ffs); 355 for ($i = 0; $i < $c; ++$i) { 356 $params['exactvalue'][$i] = empty($params['exactvalue'][$i]) ? '' : $params['exactvalue'][$i]; 357 $params['filtervalue'][$i] = empty($params['filtervalue'][$i]) ? '' : $params['filtervalue'][$i]; 358 } 359 $params['exactvalue'] = array_merge($params['exactvalue'], $exactValues); 360 $params['filtervalue'] = array_merge($params['filtervalue'], $values); 361 } 362 } 363 if (empty($params['max'])) { 364 $params['max'] = $prefs['maxRecords']; 365 } 366 if (! empty($_REQUEST['f_status'])) { 367 $params['status'] = $_REQUEST['f_status']; 368 } 369 wikiplugin_trackerFilter_save_session_filters($params, $iTrackerFilter); 370 $smarty->assign('urlquery', wikiplugin_trackerFilter_build_urlquery($params)); 371 include_once('lib/wiki-plugins/wikiplugin_trackerlist.php'); 372 $dataRes .= wikiplugin_trackerlist($data, $params); 373 } else { 374 $data = ''; 375 } 376 377 $smarty->assign_by_ref('sortchoice', $sortchoice); 378 $smarty->assign_by_ref('filters', $filters); 379 //echo '<pre>';print_r($filters); echo '</pre>'; 380 $smarty->assign_by_ref('trackerId', $trackerId); 381 $smarty->assign('line', ($line == 'y' || $line == 'in') ? 'y' : 'n'); 382 $smarty->assign('indrop', $line == 'in' ? 'y' : 'n'); 383 $smarty->assign('iTrackerFilter', $iTrackerFilter); 384 if (! empty($export_action)) { 385 $smarty->assign('export_action', $export_action); 386 $smarty->assign('export_fields', implode(':', $fields)); 387 $smarty->assign('export_itemid', $export_itemid == 'y' ? 'on' : ''); 388 $smarty->assign('export_status', $export_status == 'y' ? 'on' : ''); 389 $smarty->assign('export_created', $export_created == 'y' ? 'on' : ''); 390 $smarty->assign('export_modif', $export_modif == 'y' ? 'on' : ''); 391 $smarty->assign('export_charset', $export_charset); 392 if (! empty($_REQUEST['itemId']) && (empty($ignoreRequestItemId) || $ignoreRequestItemId != 'y')) { 393 $smarty->assign('export_itemId', $_REQUEST['itemId']); 394 } 395 396 397 if (empty($params['filters'])) { 398 if (! empty($filterfield)) { // convert param filters to export params 399 $f_fields = []; 400 for ($i = 0, $cfilterfield = count($filterfield); $i < $cfilterfield; $i++) { 401 if (! empty($exactvalue[$i])) { 402 $f_fields['f_' . $filterfield[$i]] = $exactvalue[$i]; 403 } elseif (! empty($filtervalue[$i])) { 404 $f_fields['f_' . $filterfield[$i]] = $filtervalue[$i]; 405 $f_fields['x_' . $filterfield[$i]] = 't'; // x_ is for not exact? 406 } 407 } 408 $smarty->assign_by_ref('f_fields', $f_fields); 409 } 410 $filters = []; // clear out filters set up earlier which default to all fields if not exporting 411 } else { 412 $f_fields = []; 413 foreach ($formats as $fid => $fformat) { 414 $f_fields['x_' . $fid] = $fformat; // x_ is for not exact 415 } 416 $smarty->assign_by_ref('f_fields', $f_fields); 417 } 418 } 419 if ($displayList == 'n' || ! empty($_REQUEST['filter']) || $noflipflop !== 'n' || $prefs['javascript_enabled'] != 'y' || (isset($_SESSION['tiki_cookie_jar']["show_trackerFilter$iTrackerFilter"]) && $_SESSION['tiki_cookie_jar']["show_trackerFilter$iTrackerFilter"] == 'y')) { 420 $open = 'y'; 421 $_SESSION['tiki_cookie_jar']["show_trackerFilter$iTrackerFilter"] = 'y'; 422 } else { 423 $open = 'n'; 424 } 425 $smarty->assign_by_ref('open', $open); 426 $smarty->assign_by_ref('action', $action); 427 $smarty->assign_by_ref('noflipflop', $noflipflop); 428 $smarty->assign_by_ref('dataRes', $dataRes); 429 430 if (isset($mapButtons)) { 431 $smarty->assign('mapButtons', $mapButtons); 432 } 433 434 $dataF = $smarty->fetch('wiki-plugins/wikiplugin_trackerfilter.tpl'); 435 436 static $first = true; 437 438 if ($first) { 439 $first = false; 440 $headerlib->add_jq_onready( 441 '$("a.prevnext", "#trackerFilter' . $iTrackerFilter . ' + .trackerfilter-result").click( function( e ) { 442 e.preventDefault(); 443 $("#trackerFilter' . $iTrackerFilter . ' form") 444 .attr("action", $(this).attr("href")) 445 .submit(); 446 } );' 447 ); 448 } 449 450 return $data . $dataF; 451} 452 453function wikiplugin_trackerfilter_build_trackerlist_filter($input, $formats, &$ffs, &$values, &$exactValues, Tracker_Definition $tracker_definition) 454{ 455 $trklib = TikiLib::lib('trk'); 456 457 foreach ($input as $key => $val) { 458 if (substr($key, 0, 2) == 'f_' && ! empty($val) && (! is_array($val) || ! empty($val[0]))) { 459 if (! is_array($val)) { 460 $val = urldecode($val); 461 } 462 if ($val === '-Blank (no data)-') { 463 $val = ''; 464 } 465 $fieldId = substr($key, 2); 466 $field = $tracker_definition->getField((int)$fieldId); 467 468 if ($fieldId == 'status') { 469 continue; 470 } 471 if (preg_match('/([0-9]+)(Month|Day|Year|Hour|Minute|Second)/', $fieldId, $matches)) { // a date 472 if (! in_array($matches[1], $ffs)) { 473 $fieldId = $matches[1]; 474 $ffs[] = $matches[1]; 475 // TO do optimize get options of the field 476 $date = $trklib->build_date($_REQUEST, $trklib->get_tracker_field($fieldId), 'f_' . $fieldId); 477 if (empty($formats[$fieldId])) { // = date 478 $exactValues[] = $date; 479 } else { // > or < data 480 $exactValues[] = [$formats[$fieldId] => $date]; 481 } 482 } 483 } elseif ($field['type'] == 'F') { 484 // if field type is freetag force the use of $values instead of $exactValues 485 $ffs[] = $fieldId; 486 487 if (is_array($val)) { 488 $val = implode('%', $val); 489 } 490 491 $values[] = "%$val%"; 492 } else { 493 if (preg_match("/\d+_(from|to)(Month|Day|Year|Hour|Minute|Second)?/", $fieldId, $m)) { // range filter 494 $fieldId = (int)$fieldId; 495 $formats[$fieldId] = ( $m[1] == 'from' ? '>=' : '<=' ); 496 497 if (! empty($m[2])) { 498 if ($m[2] != 'Year') { 499 continue; 500 } else { 501 $val = $trklib->build_date($_REQUEST, $trklib->get_tracker_field($fieldId), 'f_' . $fieldId . '_' . $m[1]); 502 } 503 } else { 504 $handler = $trklib->get_field_handler($field); 505 $input['ins_' . $fieldId] = $val; 506 $data = $handler->getFieldData($input); 507 $val = $data['value']; 508 } 509 } 510 if (! is_numeric($fieldId)) { // composite filter 511 $ffs[] = ['sqlsearch' => explode(':', str_replace(['(', ')'], '', $fieldId))]; 512 } else { 513 $ffs[] = $fieldId; 514 } 515 if (isset($formats[$fieldId]) && ($formats[$fieldId] == 't' || $formats[$fieldId] == 'm' || $formats[$fieldId] == 'i')) { 516 $exactValues[] = ''; 517 $values[] = ($formats[$fieldId] == 'i') ? "$val%" : $val; 518 } else { 519 if (! empty($formats[$fieldId]) && preg_match('/[\>\<]+/', $formats[$fieldId])) { 520 $exactValues[] = [$formats[$fieldId] => $val]; 521 } else { 522 $exactValues[] = $val; 523 } 524 $values[] = ''; 525 } 526 } 527 } 528 } 529} 530 531function wikiplugin_trackerFilter_reset_filters($iTrackerFilter = 0) 532{ 533 unset($_SESSION[wikiplugin_trackerFilter_get_session_filters_key($iTrackerFilter)]); 534 unset($_REQUEST['tracker_filters']); 535 536 foreach ($_REQUEST as $key => $val) { 537 if (substr($key, 0, 2) == 'f_') { 538 unset($_REQUEST[$key]); 539 } 540 } 541} 542 543function wikiplugin_trackerFilter_get_session_filters_key($iTrackerFilter = 0) 544{ 545 $trackerId = isset($_REQUEST['trackerId']) ? $_REQUEST['trackerId'] : 0; 546 if (! empty($_REQUEST['page'])) { 547 return 'f_' . $_REQUEST['page'] . '_' . $iTrackerFilter; 548 } 549 return ''; 550} 551 552function wikiplugin_trackerFilter_save_session_filters($filters, $iTrackerFilter = 0) 553{ 554 $_SESSION[wikiplugin_trackerFilter_get_session_filters_key($iTrackerFilter)] = $filters; 555} 556 557function wikiplugin_trackerFilter_get_session_filters($iTrackerFilter = 0) 558{ 559 $key = wikiplugin_trackerFilter_get_session_filters_key($iTrackerFilter); 560 561 if (! isset($_SESSION[$key])) { 562 return []; 563 } 564 565 if (isset($_SESSION[$key]['filterfield'])) { 566 foreach ($_SESSION[$key]['filterfield'] as $idx => $field) { 567 $_REQUEST['f_' . $field] = $_SESSION[$key]['filtervalue'][$idx]; 568 } 569 } 570 571 return $_SESSION[$key]; 572} 573 574function wikiplugin_trackerFilter_split_filters($filters) 575{ 576 if (empty($filters)) { 577 return []; 578 } 579 $in = false; 580 for ($i = 0, $max = strlen($filters); $i < $max; ++$i) { 581 if ($filters[$i] == '(') { 582 $in = true; 583 } elseif ($filters[$i] == ')') { 584 $in = false; 585 } elseif ($in && $filters[$i] == ':') { 586 $filters[$i] = ','; 587 } 588 } 589 $list = explode(':', $filters); 590 foreach ($list as $i => $filter) { 591 $list[$i] = str_replace(',', ':', $filter); 592 } 593 return $list; 594} 595 596function wikiplugin_trackerFilter_get_filters($trackerId = 0, array $listfields = [], &$formats, $status = 'opc') 597{ 598 global $tiki_p_admin_trackers; 599 $trklib = TikiLib::lib('trk'); 600 $filters = []; 601 if (empty($trackerId) && ! empty($listfields[0])) { 602 $field = $trklib->get_tracker_field($listfields[0]); 603 $trackerId = $field['trackerId']; 604 } 605 606 if ($listfields) { 607 $newListFields = []; 608 foreach ($listfields as $id => $field) { 609 /** @var TrackerLib $trklib */ 610 $info = $trklib->get_field_info($field); 611 if (! is_numeric($field) || ($info && $info['trackerId'] == $trackerId)) { 612 $newListFields[] = $field; 613 } 614 } 615 if (count($listfields) != count($newListFields)) { 616 $listfields = $newListFields; 617 if ($tiki_p_admin_trackers == "y") { 618 Feedback::error(tr('TrackerFields: Unknown tracker field used in the parameter "filters"')); 619 } 620 } 621 } 622 623 $fields = $trklib->list_tracker_fields($trackerId, 0, -1, 'position_asc', '', true, empty($listfields) ? '' : ['fieldId' => $listfields]); 624 if (empty($listfields)) { 625 foreach ($fields['data'] as $field) { 626 $listfields[] = $field['fieldId']; 627 } 628 } 629 630 $iField = 0; 631 foreach ($listfields as $fieldId) { 632 if ($fieldId == 'status' || $fieldId == 'Status') { 633 $filter = ['name' => $fieldId, 'fieldId' => 'status', 'format' => 'd', 'opts' => [['id' => 'o', 'name' => 'open', 'selected' => (! empty($_REQUEST['f_status'])&& $_REQUEST['f_status'] == 'o') ? 'y' : 'n'], ['id' => 'p', 'name' => 'pending', 'selected' => (! empty($_REQUEST['f_status'])&& $_REQUEST['f_status'] == 'p') ? 'y' : 'n'], ['id' => 'c', 'name' => 'closed', 'selected' => (! empty($_REQUEST['f_status'])&& $_REQUEST['f_status'] == 'c') ? 'y' : 'n']]]; 634 $filters[] = $filter; 635 continue; 636 } 637 if (! is_numeric($fieldId)) { // composite field 638 $filter = ['name' => 'Text', 'fieldId' => $fieldId, 'format' => 'sqlsearch']; 639 if (! empty($_REQUEST['f_' . $fieldId])) { 640 $filter['selected'] = $_REQUEST['f_' . $fieldId]; 641 } 642 $filters[] = $filter; 643 continue; 644 } 645 foreach ($fields['data'] as $iField => $field) { 646 if ($field['fieldId'] == $fieldId) { 647 break; 648 } 649 } 650 if (($field['isHidden'] == 'y' || $field['isHidden'] == 'c') && $tiki_p_admin_trackers != 'y') { 651 continue; 652 } 653 if ($field['type'] == 'i' || $field['type'] == 'h' || $field['type'] == 'G' || $field['type'] == 'x') { 654 continue; 655 } 656 $fieldId = $field['fieldId']; 657 $res = []; 658 if (empty($formats)) { 659 $formats = []; 660 } 661 if (empty($formats[$fieldId])) { // default format depends on field type 662 switch ($field['type']) { 663 case 'e':// category 664 $res = wikiplugin_trackerfilter_get_categories($field); 665 $formats[$fieldId] = (count($res) >= 6) ? 'd' : 'r'; 666 break; 667 case 'd': // drop down list 668 case 'y': // country 669 case 'g': // group selector 670 case 'M': // Multiple Values 671 $formats[$fieldId] = 'd'; 672 break; 673 case 'REL': 674 $formats[$fieldId] = 'REL'; 675 break; 676 case 'R': // radio 677 $formats[$fieldId] = 'r'; 678 break; 679 case '*': //rating 680 $formats[$fieldId] = '*'; 681 break; 682 case 'f': 683 case 'j': 684 $formats[$fieldId] = $field['type']; 685 break; 686 default: 687 $formats[$fieldId] = 't'; 688 break; 689 } 690 } 691 if ($field['type'] == 'e' && ($formats[$fieldId] == 't' || $formats[$fieldId] == 'T' || $formats[$fieldId] == 'i')) { // do not accept a format text for a categ for the moment 692 if (empty($res)) { 693 $res = wikiplugin_trackerfilter_get_categories($field); 694 } 695 $formats[$fieldId] = (count($res) >= 6) ? 'd' : 'r'; 696 } 697 $opts = []; 698 if ($formats[$fieldId] == 't' || $formats[$fieldId] == 'T' || $formats[$fieldId] == 'i') { 699 $selected = empty($_REQUEST['f_' . $fieldId]) ? '' : $_REQUEST['f_' . $fieldId]; 700 } elseif ($formats[$fieldId] == 'range') { 701 // map f_ID_from/to request vars to ins_ ones for tracker fields to parse them 702 $from_input = $_REQUEST; 703 $to_input = $_REQUEST; 704 foreach (['', 'Month', 'Day', 'Year', 'Hour', 'Minute'] as $suffix) { 705 if (isset($from_input['f_' . $fieldId . '_from' . $suffix])) { 706 $from_input['ins_' . $fieldId . $suffix] = $from_input['f_' . $fieldId . '_from' . $suffix]; 707 } 708 if (isset($to_input['f_' . $fieldId . '_to' . $suffix])) { 709 $to_input['ins_' . $fieldId . $suffix] = $to_input['f_' . $fieldId . '_to' . $suffix]; 710 } 711 } 712 $handler = $trklib->get_field_handler($field); 713 $data = $handler->getFieldData($from_input); 714 $field['ins_id'] = 'f_' . $field['fieldId'] . '_from'; 715 $field['value'] = $data['value']; 716 $opts['from'] = $field; 717 $data = $handler->getFieldData($to_input); 718 $field['ins_id'] = 'f_' . $field['fieldId'] . '_to'; 719 $field['value'] = $data['value']; 720 $opts['to'] = $field; 721 } else { 722 $selected = false; 723 switch ($field['type']) { 724 case 'e': // category 725 if (empty($res)) { 726 $res = wikiplugin_trackerfilter_get_categories($field); 727 } 728 foreach ($res as $opt) { 729 $opt['id'] = $opt['categId']; 730 if (! empty($_REQUEST['f_' . $fieldId]) && ((is_array($_REQUEST['f_' . $fieldId]) && in_array($opt['id'], $_REQUEST['f_' . $fieldId])) || (! is_array($_REQUEST['f_' . $fieldId]) && $opt['id'] == $_REQUEST['f_' . $fieldId]))) { 731 $opt['selected'] = 'y'; 732 $selected = true; 733 } else { 734 $opt['selected'] = 'n'; 735 } 736 $opts[] = $opt; 737 } 738 $opts[] = wikiplugin_trackerFilter_add_empty_option($fieldId); 739 break; 740 case 'd': // drop down list 741 case 'R': // radio buttons 742 case '*': // stars 743 case 'M': // Multiple Values 744 $cumul = ''; 745 foreach ($field['options_array'] as $val) { 746 // check if exists custom label, the format is 'value=label' 747 $delimiterPos = stripos($val, '='); 748 if ($delimiterPos !== false) { 749 $optval = substr($val, 0, $delimiterPos); 750 $sval = substr($val, $delimiterPos + 1); 751 } else { 752 $optval = $val; 753 $sval = $val; 754 } 755 $sval = strip_tags(TikiLib::lib('parser')->parse_data($sval, ['parsetoc' => false])); 756 $opt['id'] = $optval; 757 if ($field['type'] == '*') { 758 $cumul = $opt['name'] = "$cumul*"; 759 } else { 760 $opt['name'] = $sval; 761 } 762 if (! empty($_REQUEST['f_' . $fieldId]) && ((! is_array($_REQUEST['f_' . $fieldId]) && $_REQUEST['f_' . $fieldId] == $optval) || (is_array($_REQUEST['f_' . $fieldId]) && in_array($optval, $_REQUEST['f_' . $fieldId])))) { 763 $opt['selected'] = 'y'; 764 $selected = true; 765 } else { 766 $opt['selected'] = 'n'; 767 } 768 $opts[] = $opt; 769 } 770 $opts[] = wikiplugin_trackerFilter_add_empty_option($fieldId); 771 break; 772 case 'c': // checkbox 773 $opt['id'] = 'y'; 774 $opt['name'] = 'Yes'; 775 if (! empty($_REQUEST['f_' . $fieldId]) && $_REQUEST['f_' . $fieldId] == 'y') { 776 $opt['selected'] = 'y'; 777 $selected = true; 778 } else { 779 $opt['selected'] = 'n'; 780 } 781 $opts[] = $opt; 782 $opt['id'] = 'n'; 783 $opt['name'] = 'No'; 784 if (! empty($_REQUEST['f_' . $fieldId]) && $_REQUEST['f_' . $fieldId] == 'n') { 785 $opt['selected'] = 'y'; 786 $selected = true; 787 } else { 788 $opt['selected'] = 'n'; 789 } 790 $opts[] = $opt; 791 $formats[$fieldId] = 'r'; 792 break; 793 case 'n': // numeric 794 case 'D': // drop down + other 795 case 't': // text 796 case 'i': // text with initial 797 case 'a': // textarea 798 case 'm': // email 799 case 'y': // country 800 case 'k': //page selector 801 case 'u': // user 802 case 'g': // group 803 case 'q': // auto increment 804 if (isset($status)) { 805 $res = $trklib->list_tracker_field_values($trackerId, $fieldId, $status); 806 } else { 807 $res = $trklib->list_tracker_field_values($trackerId, $fieldId); 808 } 809 if ($field['type'] == 'u') { 810 $res = array_unique( 811 call_user_func_array( 812 'array_merge', 813 array_map( 814 function ($users) use ($trklib) { 815 return $trklib->parse_user_field($users); 816 }, 817 $res 818 ) 819 ) 820 ); 821 sort($res, SORT_NATURAL); 822 } 823 foreach ($res as $val) { 824 $sval = strip_tags(TikiLib::lib('parser')->parse_data($val, ['parsetoc' => false])); 825 $opt['id'] = $val; 826 $opt['name'] = $sval; 827 if ($field['type'] == 'y') { // country 828 $opt['name'] = str_replace('_', ' ', $opt['name']); 829 } 830 if (! empty($_REQUEST['f_' . $fieldId]) && ((! is_array($_REQUEST['f_' . $fieldId]) && urldecode($_REQUEST['f_' . $fieldId]) == $val) || (is_array($_REQUEST['f_' . $fieldId]) && in_array($val, $_REQUEST['f_' . $fieldId])))) { 831 $opt['selected'] = 'y'; 832 $selected = true; 833 } else { 834 $opt['selected'] = 'n'; 835 } 836 $opts[] = $opt; 837 } 838 $opts[] = wikiplugin_trackerFilter_add_empty_option($fieldId); 839 break; 840 case 'REL': 841 if (! empty($field['options_map']['filter'])) { 842 parse_str($field['options_map']['filter'], $filter); 843 } else { 844 $filter = []; 845 } 846 $format = '{title}'; 847 848 $opts = []; 849 $opts['field_filter'] = $filter; 850 $opts['field_selection'] = isset($_REQUEST['f_'.$fieldId]) ? $_REQUEST['f_'.$fieldId] : ''; 851 $opts['field_format'] = $format; 852 $opts['other_options'][] = wikiplugin_trackerFilter_add_empty_option($fieldId); 853 854 break; 855 case 'w': //dynamic item lists 856 case 'r': // item link 857 $opts = []; 858 $handler = $trklib->get_field_handler($field); 859 if ($handler) { 860 $list1 = $handler->getItemList(); 861 foreach ($list1 as $id => $option) { 862 $opt['id'] = $id; 863 $opt['name'] = html_entity_decode($option); // this will be escaped by smarty but already escaped from ItemLink 864 if (! empty($_REQUEST['f_' . $fieldId]) && 865 ((! is_array($_REQUEST['f_' . $fieldId]) && 866 urldecode($_REQUEST['f_' . $fieldId]) == $id) || 867 (is_array($_REQUEST['f_' . $fieldId]) && 868 in_array($id, $_REQUEST['f_' . $fieldId])) 869 )) { 870 $opt['selected'] = 'y'; 871 $selected = true; 872 } else { 873 $opt['selected'] = 'n'; 874 } 875 $opts[] = $opt; 876 } 877 } 878 $opts[] = wikiplugin_trackerFilter_add_empty_option($fieldId); 879 break; 880 881 case 'f': 882 case 'j': 883 $field['ins_id'] = 'f_' . $field['fieldId']; 884 break; 885 case 'F': // freetags 886 $freetaglib = TikiLib::lib('freetag'); 887 $opts = []; 888 $tags = []; 889 $items = $trklib->list_items($field['trackerId'], 0, -1, '', [$field]); 890 891 foreach ($items['data'] as $item) { 892 $tags = array_merge($tags, $item['field_values'][0]['freetags']); 893 } 894 895 $tags = array_unique($tags); 896 sort($tags); 897 898 foreach ($tags as $tag) { 899 $selected = false; 900 901 if (isset($_REQUEST['f_' . $fieldId])) { 902 $selection = $_REQUEST['f_' . $fieldId]; 903 904 if ((is_array($selection) && in_array($tag, $selection)) || $selection == $tag) { 905 $selected = true; 906 } 907 } 908 909 $opts[] = [ 910 'id' => $tag, 911 'name' => $tag, 912 'selected' => $selected, 913 ]; 914 } 915 916 break; 917 default: 918 return tra('tracker field type not processed yet') . ' ' . $field['type']; 919 } 920 } 921 $filters[] = ['name' => $field['name'], 'fieldId' => $fieldId, 'format' => $formats[$fieldId], 'opts' => $opts, 'selected' => $selected, 'field' => $field]; 922 } 923 return $filters; 924} 925 926/** get get categories for field 927 * 928 * @param array $field 929 * @return array of category arrays 930 * @throws Exception 931 */ 932function wikiplugin_trackerfilter_get_categories($field) 933{ 934 $handler = TikiLib::lib('trk')->get_field_handler($field); 935 936 if ($handler) { 937 $res = $handler->getFieldData(); 938 // handle full path setting here 939 if ($field['options_map']['descendants'] == 2) { 940 foreach ($res['list'] as & $cat) { 941 $cat['name'] = $cat['categpath']; 942 } 943 } 944 return $res['list']; 945 } else { 946 return []; 947 } 948} 949 950function wikiplugin_trackerFilter_build_urlquery($params) 951{ 952 if (empty($params['filterfield'])) { 953 return []; 954 } 955 $urlquery = []; 956 foreach ($params['filterfield'] as $key => $filter) { 957 $filterfield[] = $filter; 958 if (! empty($params['exactvalue'][$key]) && empty($params['filtervalue'][$key])) { 959 $filtervalue[] = ''; 960 $exactvalue[] = $params['exactvalue'][$key]; 961 } else { 962 $filtervalue[] = $params['filtervalue'][$key]; 963 $exactvalue[] = ''; 964 } 965 } 966 if (! empty($filterfield)) { 967 $urlquery['filterfield'] = implode(':', $filterfield); 968 $urlquery['filtervalue'] = implode(':', $filtervalue); 969 $urlquery['exactvalue'] = implode(':', array_map( 970 function ($ev) { 971 return is_array($ev) ? 972 key($ev) . reset($ev) 973 : $ev; 974 }, 975 $exactvalue 976 )); 977 } 978 if (! empty($params['sort_mode'])) { 979 $urlquery['sort_mode'] = $params['sort_mode']; 980 } 981 return $urlquery; 982} 983 984function wikiplugin_trackerFilter_add_empty_option($fieldId) 985{ 986 $empty = '-Blank (no data)-'; 987 return [ 988 'id' => $empty, 989 'name' => $empty, 990 'selected' => (! empty($_REQUEST['f_' . $fieldId]) && ((! is_array($_REQUEST['f_' . $fieldId]) && $_REQUEST['f_' . $fieldId] === $empty) || (is_array($_REQUEST['f_' . $fieldId]) && in_array($empty, $_REQUEST['f_' . $fieldId])))) ? 'y' : 'n' 991 ]; 992} 993