xref: /plugin/struct/meta/AggregationList.php (revision 95a8e37d986b141b2caee5b396741b93cd31c0e6)
1<?php
2
3namespace dokuwiki\plugin\struct\meta;
4
5/**
6 * Class AggregationList
7 *
8 * @package dokuwiki\plugin\struct\meta
9 */
10class AggregationList extends Aggregation
11{
12    /** @var int number of all results */
13    protected $resultColumnCount;
14
15    /**
16     * Set our class property after search has been executed in parent
17     * @return void
18     */
19    public function startScope()
20    {
21        parent::startScope();
22    }
23
24    public function postSearch()
25    {
26        $this->resultColumnCount = count($this->columns);
27    }
28
29    /** @inheritdoc */
30    public function render($showNotFound = false)
31    {
32        if ($this->result) {
33            $nestedResult = new NestedResult($this->result);
34            $root = $nestedResult->getRoot($this->data['nesting'], $this->data['index']);
35            $this->renderNode($root);
36        } elseif ($showNotFound) {
37            $this->renderer->cdata($this->helper->getLang('none'));
38        }
39    }
40
41    /**
42     * Recursively render the result tree
43     *
44     * @param NestedValue $node
45     * @return void
46     */
47    protected function renderNode(NestedValue $node)
48    {
49        $self = $node->getValueObject(); // null for root node
50        $children = $node->getChildren(!$self instanceof Value && $this->data['index']); // sort only for index
51        $results = $node->getResultRows();
52
53        // all our content is in a listitem, unless we are the root node
54        if ($self instanceof Value) {
55            $this->renderer->listitem_open($node->getDepth() + 1); // levels are 1 based
56        }
57
58        // render own value if available
59        if ($self instanceof Value) {
60            $this->renderer->listcontent_open();
61            $this->renderListItem([$self], $node->getDepth(), true); // zero based depth
62            $this->renderer->listcontent_close();
63        }
64
65        // render children or results as sub-list
66        if ($children || $results) {
67            $this->renderer->listu_open();
68
69            foreach ($children as $child) {
70                $this->renderNode($child);
71            }
72
73            foreach ($results as $result) {
74                $this->renderer->listitem_open($node->getDepth() + 2); // levels are 1 based, this is one deeper
75                $this->renderer->listcontent_open();
76                $this->renderListItem($result, $node->getDepth() + 1); // zero based depth, one deeper
77                $this->renderer->listcontent_close();
78                $this->renderer->listitem_close();
79            }
80
81            $this->renderer->listu_close();
82        }
83
84        // close listitem if opened
85        if ($self instanceof Value) {
86            $this->renderer->listitem_close();
87        }
88    }
89
90    /**
91     * Render the content of a single list item
92     *
93     * @param Value[] $resultrow
94     * @param int $depth The current nesting depth (zero based)
95     * @param bool $showEmpty show a placeholder for empty values?
96     */
97    protected function renderListItem($resultrow, $depth, $showEmpty = false)
98    {
99        $config = $this->searchConfig->getConf();
100        $sepbyheaders = $config['sepbyheaders'];
101        $headers = $config['headers'];
102
103        foreach ($resultrow as $index => $value) {
104            // when nesting, the resultrow is shifted by the nesting depth
105            $column = $index;
106            if ($config['nesting']) {
107                $column += $depth;
108            }
109            if ($sepbyheaders && !empty($headers[$column])) {
110                $header = $headers[$column];
111            } else {
112                $header = '';
113            }
114
115            if ($this->mode === 'xhtml') {
116                $this->renderValueXHTML($value, $header, $showEmpty);
117            } else {
118                $this->renderValueGeneric($value, $header, $showEmpty);
119            }
120        }
121    }
122
123    /**
124     * Render the given Value in a XHTML renderer
125     * @param Value $value
126     * @param string $header
127     * @param bool $showEmpty
128     * @return void
129     */
130    protected function renderValueXHTML($value, $header, $showEmpty = false)
131    {
132        $attributes = [
133            'data-struct-column' => strtolower($value->getColumn()->getFullQualifiedLabel()),
134            'data-struct-type' => strtolower($value->getColumn()->getType()->getClass()),
135            'class' => 'li', // default dokuwiki content wrapper
136        ];
137
138        $this->renderer->doc .= sprintf('<div %s>', buildAttributes($attributes)); // wrapper
139        if ($header !== '') {
140            $this->renderer->doc .= sprintf('<span class="struct_header">%s</span> ', hsc($header));
141        }
142        $this->renderer->doc .= '<div class="struct_value">';
143        if ($value->isEmpty() && $showEmpty) {
144            $this->renderer->doc .= '<span class="struct_na">' . $this->helper->getLang('na') . '</span>';
145        } else {
146            $value->render($this->renderer, $this->mode);
147        }
148        $this->renderer->doc .= '</div>';
149        $this->renderer->doc .= '</div> '; // wrapper
150    }
151
152    /**
153     * Render the given Value in any non-XHTML renderer
154     * @param Value $value
155     * @param string $header
156     * @return void
157     */
158    protected function renderValueGeneric($value, $header, $showEmpty = false)
159    {
160        $this->renderer->listcontent_open();
161        if ($header !== '') $this->renderer->cdata($header . ' ');
162        if ($value->isEmpty() && $showEmpty) {
163            $this->renderer->cdata($this->helper->getLang('na'));
164        } else {
165            $value->render($this->renderer, $this->mode);
166        }
167        $this->renderer->listcontent_close();
168    }
169}
170