1<?php
2
3namespace dokuwiki\plugin\filelist;
4
5class Output
6{
7    /** @var \Doku_Renderer */
8    protected $renderer;
9
10    /** @var string */
11    protected $basedir;
12
13    /** @var string */
14    protected $webdir;
15
16    /** @var array */
17    protected $files;
18
19
20    public function __construct(\Doku_Renderer $renderer, $basedir, $webdir, $files)
21    {
22        $this->renderer = $renderer;
23        $this->basedir = $basedir;
24        $this->webdir = $webdir;
25        $this->files = $files;
26    }
27
28    public function renderAsList($params)
29    {
30        if ($this->renderer instanceof \Doku_Renderer_xhtml) {
31            $this->renderer->doc .= '<div class="filelist-plugin">';
32        }
33
34        $this->renderListItems($this->files, $params);
35
36        if ($this->renderer instanceof \Doku_Renderer_xhtml) {
37            $this->renderer->doc .= '</div>';
38        }
39    }
40
41    /**
42     * Renders the files as a table, including details if configured that way.
43     *
44     * @param array $params the parameters of the filelist call
45     */
46    public function renderAsTable($params)
47    {
48        if ($this->renderer instanceof \Doku_Renderer_xhtml) {
49            $this->renderer->doc .= '<div class="filelist-plugin">';
50        }
51
52        $items = $this->flattenResultTree($this->files);
53        $this->renderTableItems($items, $params);
54
55        if ($this->renderer instanceof \Doku_Renderer_xhtml) {
56            $this->renderer->doc .= '</div>';
57        }
58    }
59
60
61    /**
62     * Renders the files as a table, including details if configured that way.
63     *
64     * @param array $params the parameters of the filelist call
65     */
66    protected function renderTableItems($items, $params)
67    {
68
69        $renderer = $this->renderer;
70
71
72        // count the columns
73        $columns = 1;
74        if ($params['showsize']) {
75            $columns++;
76        }
77        if ($params['showdate']) {
78            $columns++;
79        }
80
81        $renderer->table_open($columns);
82
83        if ($params['tableheader']) {
84            $renderer->tablethead_open();
85            $renderer->tablerow_open();
86
87            $renderer->tableheader_open();
88            $renderer->cdata($this->getLang('filename'));
89            $renderer->tableheader_close();
90
91            if ($params['showsize']) {
92                $renderer->tableheader_open();
93                $renderer->cdata($this->getLang('filesize'));
94                $renderer->tableheader_close();
95            }
96
97            if ($params['showdate']) {
98                $renderer->tableheader_open();
99                $renderer->cdata($this->getLang('lastmodified'));
100                $renderer->tableheader_close();
101            }
102
103            $renderer->tablerow_close();
104            $renderer->tablethead_close();
105        }
106
107        $renderer->tabletbody_open();
108        foreach ($items as $item) {
109            $renderer->tablerow_open();
110            $renderer->tablecell_open();
111            $this->renderItemLink($item, $params['randlinks']);
112            $renderer->tablecell_close();
113
114            if ($params['showsize']) {
115                $renderer->tablecell_open(1, 'right');
116                $renderer->cdata(filesize_h($item['size']));
117                $renderer->tablecell_close();
118            }
119
120            if ($params['showdate']) {
121                $renderer->tablecell_open();
122                $renderer->cdata(dformat($item['mtime']));
123                $renderer->tablecell_close();
124            }
125
126            $renderer->tablerow_close();
127        }
128        $renderer->tabletbody_close();
129        $renderer->table_close();
130    }
131
132
133    /**
134     * Recursively renders a tree of files as list items.
135     *
136     * @param array $items the files to render
137     * @param array $params the parameters of the filelist call
138     * @param int $level the level to render
139     * @return void
140     */
141    protected function renderListItems($items, $params, $level = 1)
142    {
143        if ($params['style'] == 'olist') {
144            $this->renderer->listo_open();
145        } else {
146            $this->renderer->listu_open();
147        }
148
149        foreach ($items as $file) {
150            if ($file['children'] === false && $file['treesize'] === 0) continue; // empty directory
151
152            $this->renderer->listitem_open($level);
153            $this->renderer->listcontent_open();
154
155            if ($file['children'] !== false && $file['treesize'] > 0) {
156                // render the directory and its subtree
157                $this->renderer->cdata($file['name']);
158                $this->renderListItems($file['children'], $params, $level + 1);
159            } elseif ($file['children'] === false) {
160                // render the file link
161                $this->renderItemLink($file, $params['randlinks']);
162
163                // render filesize
164                if ($params['showsize']) {
165                    $this->renderer->cdata($params['listsep'] . filesize_h($file['size']));
166                }
167                // render lastmodified
168                if ($params['showdate']) {
169                    $this->renderer->cdata($params['listsep'] . dformat($file['mtime']));
170                }
171            }
172
173            $this->renderer->listcontent_close();
174            $this->renderer->listitem_close();
175        }
176
177        if ($params['style'] == 'olist') {
178            $this->renderer->listo_close();
179        } else {
180            $this->renderer->listu_close();
181        }
182    }
183
184    protected function renderItemLink($item, $cachebuster = false)
185    {
186        if ($this->renderer instanceof \Doku_Renderer_xhtml) {
187            $this->renderItemLinkXHTML($item, $cachebuster);
188        } else {
189            $this->renderItemLinkAny($item, $cachebuster);
190        }
191    }
192
193    /**
194     * Render a file link on the XHTML renderer
195     */
196    protected function renderItemLinkXHTML($item, $cachebuster = false)
197    {
198        global $conf;
199        /** @var \Doku_Renderer_xhtml $renderer */
200        $renderer = $this->renderer;
201
202        //prepare for formating
203        $link['target'] = $conf['target']['extern'];
204        $link['style'] = '';
205        $link['pre'] = '';
206        $link['suf'] = '';
207        $link['more'] = '';
208        $link['url'] = $this->itemWebUrl($item, $cachebuster);
209        $link['name'] = $item['name'];
210        $link['title'] = $renderer->_xmlEntities($link['url']);
211        if ($conf['relnofollow']) $link['more'] .= ' rel="nofollow"';
212        [$ext,] = mimetype(basename($item['local']));
213        $link['class'] = 'media mediafile mf_' . $ext;
214        $renderer->doc .= $renderer->_formatLink($link);
215    }
216
217    /**
218     * Render a file link on any Renderer
219     * @param array $item
220     * @param bool $cachebuster
221     * @return void
222     */
223    protected function renderItemLinkAny($item, $cachebuster = false)
224    {
225        $this->renderer->externalmedialink($this->itemWebUrl($item, $cachebuster), $item['name']);
226    }
227
228    /**
229     * Construct the Web URL for a given item
230     *
231     * @param array $item The item data as returned by the Crawler
232     * @param bool $cachbuster add a cachebuster to the URL?
233     * @return string
234     */
235    protected function itemWebUrl($item, $cachbuster = false)
236    {
237        if (str_ends_with($this->webdir, '=')) {
238            $url = $this->webdir . rawurlencode($item['local']);
239        } else {
240            $url = $this->webdir . $item['local'];
241        }
242
243        if ($cachbuster) {
244            if (strpos($url, '?') === false) {
245                $url .= '?t=' . $item['mtime'];
246            } else {
247                $url .= '&t=' . $item['mtime'];
248            }
249        }
250        return $url;
251    }
252
253    /**
254     * Flattens the filelist by recursively walking through all subtrees and
255     * merging them with a prefix attached to the filenames.
256     *
257     * @param array $items the tree to flatten
258     * @param string $prefix the prefix to attach to all processed nodes
259     * @return array a flattened representation of the tree
260     */
261    protected function flattenResultTree($items, $prefix = '')
262    {
263        $result = [];
264        foreach ($items as $file) {
265            if ($file['children'] !== false) {
266                $result = array_merge(
267                    $result,
268                    $this->flattenResultTree($file['children'], $prefix . $file['name'] . '/')
269                );
270            } else {
271                $file['name'] = $prefix . $file['name'];
272                $result[] = $file;
273            }
274        }
275        return $result;
276    }
277
278    protected function getLang($key)
279    {
280        $syntax = plugin_load('syntax', 'filelist');
281        return $syntax->getLang($key);
282    }
283}
284