xref: /plugin/struct/meta/AggregationList.php (revision 5bc00e117f7d6c42d7b1b5918894e63f3f847966)
1<?php
2
3namespace dokuwiki\plugin\struct\meta;
4
5/**
6 * Class AggregationList
7 *
8 * @package dokuwiki\plugin\struct\meta
9 */
10class AggregationList
11{
12    /**
13     * @var string the page id of the page this is rendered to
14     */
15    protected $id;
16    /**
17     * @var string the Type of renderer used
18     */
19    protected $mode;
20    /**
21     * @var \Doku_Renderer the DokuWiki renderer used to create the output
22     */
23    protected $renderer;
24    /**
25     * @var SearchConfig the configured search - gives access to columns etc.
26     */
27    protected $searchConfig;
28
29    /**
30     * @var Column[] the list of columns to be displayed
31     */
32    protected $columns;
33
34    /**
35     * @var  Value[][] the search result
36     */
37    protected $result;
38
39    /**
40     * @var int number of all results
41     */
42    protected $resultColumnCount;
43
44    /**
45     * Initialize the Aggregation renderer and executes the search
46     *
47     * You need to call @param string $id
48     * @param string $mode
49     * @param \Doku_Renderer $renderer
50     * @param SearchConfig $searchConfig
51     * @see render() on the resulting object.
52     *
53     */
54    public function __construct($id, $mode, \Doku_Renderer $renderer, SearchConfig $searchConfig)
55    {
56        $this->id = $id;
57        $this->mode = $mode;
58        $this->renderer = $renderer;
59        $this->searchConfig = $searchConfig;
60        $this->data = $searchConfig->getConf();
61        $this->columns = $searchConfig->getColumns();
62
63        $this->result = $this->searchConfig->execute();
64        $this->resultColumnCount = count($this->columns);
65        $this->resultPIDs = $this->searchConfig->getPids();
66    }
67
68    /**
69     * Create the list on the renderer
70     */
71    public function render()
72    {
73        $nestedResult = new NestedResult($this->result);
74        $root = $nestedResult->getRoot($this->data['nesting']);
75
76        $this->startScope();
77        $this->renderNode($root);
78        $this->finishScope();
79    }
80
81    /**
82     * Recursively render the result tree
83     *
84     * @param NestedValue $node
85     * @return void
86     */
87    protected function renderNode(NestedValue $node)
88    {
89        $self = $node->getValueObject(); // null for root node
90        $children = $node->getChildren();
91        $results = $node->getResultRows();
92
93        // all our content is in a listitem, unless we are the root node
94        if ($self) {
95            $this->renderer->listitem_open($node->getDepth() + 1); // levels are 1 based
96        }
97
98        // render own value if available
99        if ($self) {
100            $this->renderer->listcontent_open();
101            $this->renderListItem([$self], $node->getDepth()); // zero based depth
102            $this->renderer->listcontent_close();
103        }
104
105        // render children or results as sub-list
106        if ($children || $results) {
107            $this->renderer->listu_open();
108
109            foreach ($children as $child) {
110                $this->renderNode($child);
111            }
112
113            foreach ($results as $result) {
114                $this->renderer->listitem_open($node->getDepth() + 2); // levels are 1 based, this is one deeper
115                $this->renderer->listcontent_open();
116                $this->renderListItem($result, $node->getDepth() + 1); // zero based depth, one deeper
117                $this->renderer->listcontent_close();
118                $this->renderer->listitem_close();
119            }
120
121            $this->renderer->listu_close();
122        }
123
124        // close listitem if opened
125        if ($self) {
126            $this->renderer->listitem_close();
127        }
128    }
129
130    /**
131     * Adds additional info to document and renderer in XHTML mode
132     *
133     * @see finishScope()
134     */
135    protected function startScope()
136    {
137        // wrapping div
138        if ($this->mode != 'xhtml') return;
139        $this->renderer->doc .= "<div class=\"structaggregation listaggregation\">";
140    }
141
142    /**
143     * Closes anything opened in startScope()
144     *
145     * @see startScope()
146     */
147    protected function finishScope()
148    {
149        // wrapping div
150        if ($this->mode != 'xhtml') return;
151        $this->renderer->doc .= '</div>';
152    }
153
154
155    /**
156     * Render the content of a single list item
157     *
158     * @param Value[] $resultrow
159     * @param int $depth The current nesting depth (zero based)
160     */
161    protected function renderListItem($resultrow, $depth)
162    {
163        $sepbyheaders = $this->searchConfig->getConf()['sepbyheaders'];
164        $headers = $this->searchConfig->getConf()['headers'];
165
166        foreach ($resultrow as $index => $value) {
167            if ($value->isEmpty()) continue;
168            $column = $index + $depth; // the resultrow is shifted by the nesting depth
169            if ($sepbyheaders && !empty($headers[$column])) {
170                $header = $headers[$column];
171            } else {
172                $header = '';
173            }
174
175            if ($this->mode === 'xhtml') {
176                $this->renderValueXHTML($value, $header);
177            } else {
178                $this->renderValueGeneric($value, $header);
179            }
180        }
181    }
182
183    /**
184     * Render the given Value in a XHTML renderer
185     * @param Value $value
186     * @param string $header
187     * @return void
188     */
189    protected function renderValueXHTML($value, $header)
190    {
191        $attributes = [
192            'data-struct-column' => strtolower($value->getColumn()->getFullQualifiedLabel()),
193            'data-struct-type' => strtolower($value->getColumn()->getType()->getClass()),
194            'class' => 'li', // default dokuwiki content wrapper
195        ];
196
197        $this->renderer->doc .= sprintf('<div %s>', buildAttributes($attributes)); // wrapper
198        if ($header !== '') {
199            $this->renderer->doc .= sprintf('<span class="struct_header">%s</span> ', hsc($header));
200        }
201        $this->renderer->doc .= '<div class="struct_value">';
202        $value->render($this->renderer, $this->mode);
203        $this->renderer->doc .= '</div>';
204        $this->renderer->doc .= '</div> '; // wrapper
205    }
206
207    /**
208     * Render the given Value in any non-XHTML renderer
209     * @param Value $value
210     * @param string $header
211     * @return void
212     */
213    protected function renderValueGeneric($value, $header)
214    {
215        $this->renderer->listcontent_open();
216        if ($header !== '') $this->renderer->cdata($header . ' ');
217        $value->render($this->renderer, $this->mode);
218        $this->renderer->listcontent_close();
219    }
220}
221