1<?php 2 3namespace dokuwiki\plugin\struct\meta; 4 5use dokuwiki\Form\Form; 6use dokuwiki\Utf8\Sort; 7 8/** 9 * Struct filter class 10 */ 11class AggregationFilter extends Aggregation 12{ 13 /** 14 * Render the filter form. 15 * Reuses the structure of advanced search tools to leverage 16 * the core grouping styles and scripts. 17 * 18 * @param bool $showNotFound Inherited from parent method 19 * @return void 20 */ 21 public function render($showNotFound = false) 22 { 23 $colValues = $this->getAllColumnValues($this->searchConfig->getResult()->getRows()); 24 25 // column dropdowns 26 foreach ($colValues as $num => $colData) { 27 /** @var Column $column */ 28 $column = $colData['column']; 29 $label = $this->data['headers'][$num] ?? $colData['label']; 30 31 $this->renderer->doc .= '<details>'; 32 $this->renderer->doc .= '<summary>' . hsc($label) . '</summary>'; 33 $this->renderer->doc .= '<ul>'; 34 foreach ($colData['values'] as $value => $displayValue) { 35 $current = false; 36 $dyn = $this->searchConfig->getDynamicParameters(); 37 $allFilters = $dyn->getFilters(); 38 if (isset($allFilters[$column->getFullQualifiedLabel()])) { 39 if ($allFilters[$column->getFullQualifiedLabel()][1] == $displayValue) { 40 $current = true; 41 } 42 $dyn->removeFilter($column); // remove previous filter for this column 43 } 44 if (!$current) { 45 // add new filter unless it's the current item 46 $dyn->addFilter($column, '=', $displayValue); 47 } 48 $params = $dyn->getURLParameters(); 49 $filter = buildURLparams($params); 50 51 $this->renderer->doc .= '<li ' . ($current ? 'class="active"' : '') . '><div class="li">'; 52 $column->getType()->renderTagCloudLink($value, $this->renderer, $this->mode, $this->id, $filter, 100); 53 $this->renderer->doc .= '</div></li>'; 54 } 55 $this->renderer->doc .= '</ul>'; 56 $this->renderer->doc .= '</details>'; 57 } 58 } 59 60 /** 61 * Get all values from given search result grouped by column 62 * 63 * @return array 64 */ 65 protected function getAllColumnValues($result) 66 { 67 $colValues = []; 68 69 foreach ($result as $row) { 70 foreach ($row as $value) { 71 /** @var Value $value */ 72 $colName = $value->getColumn()->getFullQualifiedLabel(); 73 $colValues[$colName]['column'] = $value->getColumn(); 74 $colValues[$colName]['label'] = $value->getColumn()->getTranslatedLabel(); 75 $colValues[$colName]['values'] ??= []; 76 77 if (empty($value->getDisplayValue())) continue; 78 79 // create an array with [value => displayValue] pairs 80 // the cast to array will handle single and multi-value fields the same 81 // using the full value as key will make sure we don't have duplicates 82 // 83 // because a value might be interpreted as integer in the array key, we pad 84 // each key with a space at the end to enforce string keys. The space will 85 // be ignored when parsing JSON values and trimmed for all other types. 86 // This is a work around for #665 87 $pairs = array_combine( 88 array_map( 89 static fn($v) => "$v ", 90 (array)$value->getValue() 91 ), 92 (array)$value->getDisplayValue() 93 ); 94 $colValues[$colName]['values'] = array_merge($colValues[$colName]['values'], $pairs); 95 } 96 } 97 98 // sort by display value 99 array_walk($colValues, function (&$col) { 100 Sort::asort($col['values']); 101 }); 102 103 return array_values($colValues); // reindex 104 } 105} 106