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