1<?php
2
3namespace dokuwiki\plugin\struct\meta;
4
5class AggregationCloud
6{
7
8    /**
9     * @var string the page id of the page this is rendered to
10     */
11    protected $id;
12
13    /**
14     * @var string the Type of renderer used
15     */
16    protected $mode;
17
18    /**
19     * @var \Doku_Renderer the DokuWiki renderer used to create the output
20     */
21    protected $renderer;
22
23    /**
24     * @var SearchConfig the configured search - gives access to columns etc.
25     */
26    protected $searchConfig;
27
28    /**
29     * @var Column[] the list of columns to be displayed
30     */
31    protected $columns;
32
33    /**
34     * @var  Value[][] the search result
35     */
36    protected $result;
37
38    /**
39     * @var int number of all results
40     */
41    protected $resultCount;
42
43    /**
44     * Initialize the Aggregation renderer and executes the search
45     *
46     * You need to call @see render() on the resulting object.
47     *
48     * @param string $id
49     * @param string $mode
50     * @param \Doku_Renderer $renderer
51     * @param SearchConfig $searchConfig
52     */
53    public function __construct($id, $mode, \Doku_Renderer $renderer, SearchCloud $searchConfig)
54    {
55        $this->id = $id;
56        $this->mode = $mode;
57        $this->renderer = $renderer;
58        $this->searchConfig = $searchConfig;
59        $this->data = $searchConfig->getConf();
60        $this->columns = $searchConfig->getColumns();
61        $this->result = $this->searchConfig->execute();
62        $this->resultCount = $this->searchConfig->getCount();
63
64        $this->max = $this->result[0]['count'];
65        $this->min = end($this->result)['count'];
66    }
67
68    /**
69     * Create the cloud on the renderer
70     */
71    public function render()
72    {
73
74        $this->sortResults();
75
76        $this->startScope();
77        $this->startList();
78        foreach ($this->result as $result) {
79            $this->renderTag($result);
80        }
81        $this->finishList();
82        $this->finishScope();
83        return;
84    }
85
86    /**
87     * Adds additional info to document and renderer in XHTML mode
88     *
89     * @see finishScope()
90     */
91    protected function startScope()
92    {
93        // wrapping div
94        if ($this->mode != 'xhtml') return;
95        $this->renderer->doc .= "<div class=\"structcloud\">";
96    }
97
98    /**
99     * Closes the table and anything opened in startScope()
100     *
101     * @see startScope()
102     */
103    protected function finishScope()
104    {
105        // wrapping div
106        if ($this->mode != 'xhtml') return;
107        $this->renderer->doc .= '</div>';
108    }
109
110    /**
111     * Render a tag of the cloud
112     *
113     * @param ['tag' => Value, 'count' => int] $result
114     */
115    protected function renderTag($result)
116    {
117        /**
118         * @var Value $value
119         */
120        $value = $result['tag'];
121        $count = $result['count'];
122        if ($value->isEmpty()) {
123            return;
124        }
125
126        $type = strtolower($value->getColumn()->getType()->getClass());
127        $weight = $this->getWeight($count, $this->min, $this->max);
128
129        if (!empty($this->data['target'])) {
130            $target = $this->data['target'];
131        } else {
132            global $INFO;
133            $target = $INFO['id'];
134        }
135
136        $tagValue = $value->getDisplayValue();
137        if (is_array($tagValue)) {
138            $tagValue = $tagValue[0];
139        }
140        $key = $value->getColumn()->getFullQualifiedLabel() . '=';
141        $filter = SearchConfigParameters::$PARAM_FILTER . '[' . urlencode($key) . ']=' . urlencode($tagValue);
142
143        $this->renderer->listitem_open(1);
144        $this->renderer->listcontent_open();
145
146        if ($this->mode == 'xhtml') {
147            $this->renderer->doc .= "<div style='font-size:$weight%' data-count='$count' class='cloudtag struct_$type'>";
148        }
149
150        $value->renderAsTagCloudLink($this->renderer, $this->mode, $target, $filter, $weight);
151
152        if ($this->mode == 'xhtml') {
153            $this->renderer->doc .= '</div>';
154        }
155
156        $this->renderer->listcontent_close();
157        $this->renderer->listitem_close();
158    }
159
160    /**
161     * This interpolates the weight between 70 and 150 based on $min, $max and $current
162     *
163     * @param int $current
164     * @param int $min
165     * @param int $max
166     * @return int
167     */
168    protected function getWeight($current, $min, $max)
169    {
170        if ($min == $max) {
171            return 100;
172        }
173        return round(($current - $min) / ($max - $min) * 80 + 70);
174    }
175
176    /**
177     * Sort the list of results
178     */
179    protected function sortResults()
180    {
181        usort($this->result, function ($a, $b) {
182            $asort = $a['tag']->getColumn()->getType()->getSortString($a['tag']);
183            $bsort = $b['tag']->getColumn()->getType()->getSortString($b['tag']);
184            if ($asort < $bsort) {
185                return -1;
186            }
187            if ($asort > $bsort) {
188                return 1;
189            }
190            return 0;
191        });
192    }
193
194    protected function startList()
195    {
196        $this->renderer->listu_open();
197    }
198
199    protected function finishList()
200    {
201        $this->renderer->listu_close();
202    }
203}
204