1<?php
2/**
3 * DokuWiki Plugin filelisting (Helper Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Szymon Olewniczak <dokuwiki@cosmocode.de>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class helper_plugin_filelisting extends DokuWiki_Plugin {
13
14    /**
15     * Display the file listing for the current page
16     *
17     * @param bool $print Should the HTML be printed or returned?
18     *
19     * @return string
20     */
21    public function tpl_filelisting($print = true, $ns = NULL) {
22        global $INFO;
23        global $lang;
24
25        if (is_null($ns)) $ns = getNS($INFO['id']);
26
27        if ($ns == false) {
28            $ns = ':';
29            $ns_string = '[' . $lang['mediaroot'] . ']';
30        } else {
31            $ns_string = $ns;
32        }
33
34        $colgroup = '<colgroup>';
35        $colgroup .= '<col style="width: 22px;">';
36        $colgroup .= '<col style="width: 25px;">';
37        $colgroup .= '<col style="width: 50%;">';
38        $colgroup .= '<col style="width: 15%;">';
39        $colgroup .= '<col style="width: 35%;">';
40        $colgroup .= '</colgroup>';
41
42        $ret = '<div class="plugin__filelisting" data-namespace="' . $ns . '">';
43
44        $ret .= '<div class="plugin__filelisting_capiton">';
45        $ret .= sprintf($this->getLang('files_in_namespace'), $ns_string);
46        $ret .= '</div>';
47
48        //collapsible is for filter box (added dynamicly by JS)
49        $ret .= '<div class="plugin__filelisting_collapsible">';
50
51        //form for file deletion
52        $form = new dokuwiki\Form\Form(['action' => wl($INFO['id'], '', false, '&')]);
53        $form->addHTML('<div class="plugin__filelisting_content">');
54
55        $form->addHTML('<div class="plugin__filelisting_headertable">');
56        $form->addHTML('<table>');
57        $form->addHTML($colgroup);
58        $form->addHTML('<thead>');
59        $form->addHTML('<tr>');
60        //colspan for delete checkbox and icon
61        $form->addHTML('<th colspan="2"></th>');
62        $form->addHTML('<th>' . $this->getLang('header filename') .'</th>');
63        $form->addHTML('<th>' . $this->getLang('header filesize') .'</th>');
64        $form->addHTML('<th>' . $this->getLang('header filedate') .'</th>');
65        $form->addHTML('</tr>');
66        $form->addHTML('</thead>');
67        $form->addHTML('</table>');
68        $form->addHTML('</div>');
69
70        $form->addHTML('<div class="plugin__filelisting_bodytable">');
71        $form->addHTML('<table>');
72        $form->addHTML($colgroup);
73        $form->addHTML('<tbody>');
74
75        $rowElements = $this->getFilesRows($ns);
76        foreach($rowElements as $element) {
77            $form->addElement($element);
78        }
79        $form->addHTML('</tbody>');
80        $form->addHTML('</table>');
81        $form->addHTML('</div>');
82
83        //div.plugin__filelisting_content
84        $form->addHTML('</div>');
85
86        $form->addHTML('<div class="plugin__filelisting_footer">');
87        //user can delete on this namespace
88        $form->addButton('do[plugin_filelisting_delete]', $this->getLang('delete_selected'));
89        $form->addHTML('</div>');
90
91        $ret .= $form->toHTML();
92
93        //div.plugin__filelisting_collapsible
94        $ret .= '</div>';
95        //div.plugin__filelisting
96        $ret .= '</div>';
97
98        if ($print) {
99            echo $ret;
100        }
101
102        return $ret;
103    }
104
105    /**
106     * Return namespace files as html table rows
107     * @param string    $ns
108     * @param int       $lvl
109     * @param bool      $filesOnly if true, then the directories in a namespace are ignored
110     * @return array    of \dokuwiki\Form\Element
111     */
112    public function getFilesRows($ns, $lvl=0, $filesOnly = false) {
113        $files = $this->getFiles($ns);
114        $elements = array();
115        foreach ($files as $file) {
116            //skip dirs
117            if ($filesOnly && $file['isdir']) continue;
118
119            //empty $ns means root
120            $trOpen = new \dokuwiki\Form\TagOpenElement('tr', array('data-childOf' => $ns));
121            if ($file['isdir']) {
122                $trOpen->attr('data-namespace', $file['id']);
123            }
124            $elements[] = $trOpen;
125
126            //delete checkbox
127            $elements[] = new \dokuwiki\Form\TagOpenElement('td');
128            if (!$file['isdir'] && $file['perm'] >= AUTH_DELETE) {
129                $name = 'delete[' . $file['id'] . ']';
130                $elements[] = new \dokuwiki\Form\CheckableElement('checkbox', $name, '');
131            }
132            $elements[] = new \dokuwiki\Form\TagCloseElement('td');
133
134            $html = '<td class="plugin__filelisting_cell_icon">' . $file['icon'] . '</td>';
135            $elements[] = new \dokuwiki\Form\HTMLElement($html);
136
137            $td_name = new \dokuwiki\Form\TagOpenElement('td', array('data-sort' => $file['file']));
138            $td_name->addClass('plugin__filelisting_cell_name');
139            $elements[] = $td_name;
140
141            if ($lvl > 0) {
142                $html = '<span style="margin-left: ' . $lvl * 10 . 'px;">↳ </span>';
143                $elements[] = new \dokuwiki\Form\HTMLElement($html);
144            }
145            $elements[] = new \dokuwiki\Form\HTMLElement($file['link']);
146            $elements[] = new \dokuwiki\Form\TagCloseElement('td');
147
148            if ($file['isdir']) {
149                $elements[] = new \dokuwiki\Form\HTMLElement('<td data-sort=""> — </td>');
150                $elements[] = new \dokuwiki\Form\HTMLElement('<td data-sort=""> — </td>');
151            } else {
152                $html = '<td data-sort="' . $file['size'] . '">' . filesize_h($file['size']) . '</td>';
153                $elements[] = new \dokuwiki\Form\HTMLElement($html);
154                $html = '<td data-sort="' . $file['mtime'] . '">' . dformat($file['mtime']) . '</td>';
155                $elements[] = new \dokuwiki\Form\HTMLElement($html);
156            }
157            $elements[] = new \dokuwiki\Form\TagCloseElement('tr');
158        }
159        return $elements;
160    }
161
162    /**
163     * Get a list of namespace files
164     *
165     * Suppress error messages for invalid file names: nothing to be done here,
166     * they should be fixed in the media manager.
167     *
168     * @param $ns
169     * @return array
170     */
171    public function getFiles($ns) {
172        global $conf;
173
174        $ns = cleanId($ns);
175
176        $dir = utf8_encodeFN(str_replace(':','/',$ns));
177        $data = array();
178        search($data,$conf['mediadir'],array($this, 'search_media_and_namespaces'),
179               array('showmsg'=>false,'depth'=>1),$dir,1, false);
180
181        return array_map(array($this, 'fileInfo'), $data);
182    }
183
184    /**
185     * Add additional info to search() function result item
186     *
187     * @param $item
188     * @return mixed
189     */
190    protected function fileInfo($item) {
191
192        // Prepare filename
193        $item['file'] = utf8_decodeFN($item['file']);
194
195        //handle directory diffirently
196        if (isset($item['isdir'])) {
197            $item['icon'] = $this->dirClosedIcon();
198
199            $item['link'] = '<a href="'.wl($item['id']. ':start').'">' . $item['file'] . '</a>';
200        } else {
201            // Prepare fileicons
202            list($ext) = mimetype($item['file'],false);
203            $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
204            $class = 'mf_' . $class;
205            $item['icon'] = '<div style="width: 16px; height:16px;" class="' . $class . '"></div>';
206
207            $item['link'] = '<a href="'.ml($item['id']).'" target="_blank">' . $item['file'] . '</a>';
208        }
209
210        return $item;
211    }
212
213    /**
214     * Html for closed dir icon
215     *
216     * @return false|string
217     */
218    public function dirClosedIcon() {
219        return inlineSVG(dirname(__FILE__) . '/images/folder.svg');
220    }
221
222    /**
223     * Html for opened dir icon
224     *
225     * @return false|string
226     */
227    public function dirOpenedIcon() {
228        return inlineSVG(dirname(__FILE__) . '/images/folder-open.svg');
229    }
230
231    /**
232     * Html for loading icon
233     *
234     * @return string
235     */
236    public function loadingIcon() {
237        $file = dirname(__FILE__) . '/images/loading.gif';
238        $contents = file_get_contents($file);
239        $base64 = base64_encode($contents);
240        return '<img src="data:image/gif;base64,'.$base64.'">';
241    }
242
243    /**
244     * List all mediafiles in a namespace
245     *   $opts['depth']     recursion level, 0 for all
246     *   $opts['showmsg']   shows message if invalid media id is used
247     *   $opts['skipacl']   skip acl checking
248     *   $opts['pattern']   check given pattern
249     *   $opts['hash']      add hashes to result list
250     *
251     *
252     * @param array $data
253     * @param string $base
254     * @param string $file
255     * @param string $type
256     * @param integer $lvl
257     * @param array $opts
258     *
259     * @return bool
260     */
261    public function search_media_and_namespaces(&$data,$base,$file,$type,$lvl,$opts){
262
263        if ($type == 'd') {
264            $info = array();
265
266            $info['id']    = pathID($file,true);
267            $info['file']  = utf8_basename($file);
268            $info['isdir'] = true;
269
270            //check ACL for namespace
271            $info['perm'] = auth_quickaclcheck($info['id'].':*');
272
273            if(!empty($opts['skipacl']) || $info['perm'] >= AUTH_READ){
274                $data[] = $info;
275            }
276        }
277
278        return search_media($data, $base, $file, $type, $lvl, $opts);
279    }
280
281    public function delete_files($files_to_delete) {
282        global $lang;
283        global $INFO;
284
285        $msgs = array();
286        foreach ($files_to_delete as $DEL) {
287            $res = media_delete($DEL, $INFO['perm']);
288            if ($res & DOKU_MEDIA_DELETED) {
289                $msg = sprintf($lang['deletesucc'], noNS($DEL));
290                $msgs[] = array('message' => $msg, 'lvl' => 1);
291            } elseif ($res & DOKU_MEDIA_INUSE) {
292                    $msg = sprintf($lang['mediainuse'],noNS($DEL));
293                    $msgs[] = array('message' => $msg, 'lvl' => 0);
294            } else {
295                $msg = sprintf($lang['deletefail'],noNS($DEL));
296                $msgs[] = array('message' => $msg, 'lvl' => -1);
297            }
298        }
299
300        return $msgs;
301    }
302
303}
304
305// vim:ts=4:sw=4:et:
306