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