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 // consistent info needed for rendering 202 $item['isdir'] = false; 203 // Prepare fileicons 204 list($ext) = mimetype($item['file'],false); 205 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); 206 $class = 'mf_' . $class; 207 $item['icon'] = '<div style="width: 16px; height:16px;" class="' . $class . '"></div>'; 208 209 $item['link'] = '<a href="'.ml($item['id']).'" target="_blank">' . $item['file'] . '</a>'; 210 } 211 212 return $item; 213 } 214 215 /** 216 * Html for closed dir icon 217 * 218 * @return false|string 219 */ 220 public function dirClosedIcon() { 221 return inlineSVG(dirname(__FILE__) . '/images/folder.svg'); 222 } 223 224 /** 225 * Html for opened dir icon 226 * 227 * @return false|string 228 */ 229 public function dirOpenedIcon() { 230 return inlineSVG(dirname(__FILE__) . '/images/folder-open.svg'); 231 } 232 233 /** 234 * Html for loading icon 235 * 236 * @return string 237 */ 238 public function loadingIcon() { 239 $file = dirname(__FILE__) . '/images/loading.gif'; 240 $contents = file_get_contents($file); 241 $base64 = base64_encode($contents); 242 return '<img src="data:image/gif;base64,'.$base64.'">'; 243 } 244 245 /** 246 * List all mediafiles in a namespace 247 * $opts['depth'] recursion level, 0 for all 248 * $opts['showmsg'] shows message if invalid media id is used 249 * $opts['skipacl'] skip acl checking 250 * $opts['pattern'] check given pattern 251 * $opts['hash'] add hashes to result list 252 * 253 * 254 * @param array $data 255 * @param string $base 256 * @param string $file 257 * @param string $type 258 * @param integer $lvl 259 * @param array $opts 260 * 261 * @return bool 262 */ 263 public function search_media_and_namespaces(&$data,$base,$file,$type,$lvl,$opts){ 264 265 if ($type == 'd') { 266 $info = array(); 267 268 $info['id'] = pathID($file,true); 269 $info['file'] = utf8_basename($file); 270 $info['isdir'] = true; 271 272 //check ACL for namespace 273 $info['perm'] = auth_quickaclcheck($info['id'].':*'); 274 275 if(!empty($opts['skipacl']) || $info['perm'] >= AUTH_READ){ 276 $data[] = $info; 277 } 278 } 279 280 return search_media($data, $base, $file, $type, $lvl, $opts); 281 } 282 283 public function delete_files($files_to_delete) { 284 global $lang; 285 global $INFO; 286 287 $msgs = array(); 288 foreach ($files_to_delete as $DEL) { 289 $res = media_delete($DEL, $INFO['perm']); 290 if ($res & DOKU_MEDIA_DELETED) { 291 $msg = sprintf($lang['deletesucc'], noNS($DEL)); 292 $msgs[] = array('message' => $msg, 'lvl' => 1); 293 } elseif ($res & DOKU_MEDIA_INUSE) { 294 $msg = sprintf($lang['mediainuse'],noNS($DEL)); 295 $msgs[] = array('message' => $msg, 'lvl' => 0); 296 } else { 297 $msg = sprintf($lang['deletefail'],noNS($DEL)); 298 $msgs[] = array('message' => $msg, 'lvl' => -1); 299 } 300 } 301 302 return $msgs; 303 } 304 305} 306 307// vim:ts=4:sw=4:et: 308