PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/php/plugins/filebrowser/classes/filebrowser.php

https://bitbucket.org/chiamingyen/cmsimple-and-plugins
PHP | 431 lines | 348 code | 68 blank | 15 comment | 56 complexity | c59c4f7daa89f01fd0796bbd733f8743 MD5 | raw file
  1. <?php
  2. /**
  3. * @version $Id: filebrowser.php 292 2012-09-21 16:24:11Z cmb69 $
  4. */
  5. /* utf-8 marker: äöü */
  6. setlocale(LC_ALL, 'en_US.UTF8');
  7. class XHFileBrowser {
  8. var $linkPrefix = '';
  9. var $browseBase = '';
  10. var $baseDirectory;
  11. var $currentDirectory;
  12. var $linkType;
  13. var $folders = array();
  14. var $files = array();
  15. var $baseDirectories = array();
  16. var $allowedExtensions = array();
  17. var $maxFilesizes = array();
  18. var $view;
  19. var $message = '';
  20. var $browserPath = '';
  21. function XHFileBrowser() {
  22. global $pth, $plugin_cf, $cf;
  23. $image_extensions = array();
  24. $temp = explode(',', $plugin_cf['filebrowser']['extensions_images']);
  25. foreach ($temp as $ext) {
  26. $extension = trim($ext, ' ./');
  27. if ((bool) $extension) {
  28. $image_extensions[] = strtolower($extension);
  29. }
  30. }
  31. $download_extensions = array();
  32. $temp = explode(',', $plugin_cf['filebrowser']['extensions_downloads']);
  33. foreach ($temp as $ext) {
  34. $extension = trim($ext, ' ./');
  35. if ((bool) $extension) {
  36. $download_extensions[] = strtolower($extension);
  37. }
  38. }
  39. $userfiles_extensions = array();
  40. $temp = explode(',', $plugin_cf['filebrowser']['extensions_userfiles']);
  41. foreach ($temp as $ext) {
  42. $extension = trim($ext, ' ./');
  43. if ((bool) $extension) {
  44. $userfiles_extensions[] = strtolower($extension);
  45. }
  46. }
  47. $media_extensions = array();
  48. $temp = explode(',', $plugin_cf['filebrowser']['extensions_media']);
  49. foreach ($temp as $ext) {
  50. $extension = trim($ext, ' ./');
  51. if ((bool) $extension) {
  52. $media_extensions[] = strtolower($extension);
  53. }
  54. }
  55. $this->browserPath = $pth['folder']['plugins'] . basename(dirname(dirname(__FILE__))) . '/';
  56. $this->view = new XHFileBrowserView();
  57. $this->baseDirectories['images'] = rtrim($cf['folders']['images'], '/') . '/';
  58. $this->baseDirectories['downloads'] = rtrim($cf['folders']['downloads'], '/') . '/';
  59. $this->baseDirectories['userfiles'] = rtrim($cf['folders']['userfiles'], '/') . '/';
  60. $this->baseDirectories['media'] = rtrim($cf['folders']['media'], '/') . '/';
  61. $this->allowedExtensions['images'] = $image_extensions;
  62. $this->allowedExtensions['downloads'] = $download_extensions;
  63. $this->allowedExtensions['userfiles'] = $userfiles_extensions;
  64. $this->allowedExtensions['media'] = $media_extensions;
  65. }
  66. // 上傳檔案是否被用於頁面的查驗函式
  67. function fileIsLinked($file) {
  68. global $h, $c, $u;
  69. // 將檔案的空白轉為 %20 其餘保留 utf8 格式
  70. $file=str_replace(" ","%20",$file);
  71. $i = 0;
  72. $usages = array();
  73. // TODO: improve regex for better performance
  74. $regex = '#<.*(?:src|href|download)=(["\']).*' . preg_quote($file, '#') . '\\1.*>#is';
  75. foreach ($c as $page) {
  76. if (preg_match($regex, $page) > 0) {
  77. $usages[] = '<a href="?' . $u[$i] . '">' . $h[$i] . '</a>';
  78. }
  79. $i++;
  80. }
  81. $usages = array_unique($usages);
  82. if (count($usages) > 0) {
  83. return $usages;
  84. }
  85. return false;
  86. }
  87. // 上傳影像檔案是否被用於頁面的查驗函式
  88. function usedImages()
  89. {
  90. global $c, $h, $cl;
  91. $images = array();
  92. for ($i = 0; $i < $cl; $i++) {
  93. preg_match_all('/<img.*?src=(["\'])(.*?)\\1.*?>/is', $c[$i], $m);
  94. foreach ($m[2] as $fn) {
  95. if ($fn{0} == '.' && $fn{1} == '/') {
  96. $fn = substr($fn, 2);
  97. }
  98. if (array_key_exists($fn, $images)) {
  99. if (!in_array($h[$i], $images[$fn])) {
  100. $images[$fn][] = $h[$i];
  101. }
  102. } else {
  103. $images[$fn] = array($h[$i]);
  104. }
  105. }
  106. }
  107. return $images;
  108. }
  109. function readDirectory() {
  110. $dir = $this->browseBase . $this->currentDirectory;
  111. $this->files = array();
  112. $handle = opendir($dir);
  113. if ($handle) {
  114. while (false !== ($file = readdir($handle))) {
  115. if (strpos($file, '.') === 0) {
  116. continue;
  117. }
  118. if (is_dir($dir . $file)) {
  119. $this->folders[] = $this->currentDirectory . $file;
  120. continue;
  121. }
  122. if ($this->isAllowedFile($file)) {
  123. $this->files[] = $file;
  124. }
  125. }
  126. closedir($handle);
  127. natcasesort($this->folders);
  128. natcasesort($this->files);
  129. }
  130. }
  131. function getFolders($directory) {
  132. $folders = array();
  133. $handle = opendir($directory);
  134. if ($handle) {
  135. while (false !== ($file = readdir($handle))) {
  136. if (strpos($file, '.') === 0) {
  137. continue;
  138. }
  139. if (is_dir($directory . $file)) {
  140. $folders[] = str_replace($this->browseBase, '', $directory . $file);
  141. foreach ($this->getFolders($directory . $file . '/') as $subfolder) {
  142. $folders[] = $subfolder;
  143. }
  144. }
  145. }
  146. closedir($handle);
  147. natcasesort($folders);
  148. }
  149. return $folders;
  150. }
  151. function isAllowedFile($file) {
  152. $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
  153. if ($extension == $file) {
  154. return false;
  155. }
  156. if (!in_array($extension, $this->allowedExtensions[$this->linkType])
  157. && !in_array('*', $this->allowedExtensions[$this->linkType])) {
  158. return false;
  159. }
  160. return true;
  161. }
  162. function foldersArray($all = true) {
  163. $folders = array();
  164. $temp = $this->getFolders($this->browseBase . $this->baseDirectory);
  165. $baseDepth = count(explode('/', $this->baseDirectory)) - 2;
  166. foreach ($temp as $i => $folder) {
  167. $ar = explode('/', $folder);
  168. $level = count($ar);
  169. $parent = '';
  170. for ($i = 0; $i < $level - 1; $i++) {
  171. $parent .= '/' . $ar[$i];
  172. }
  173. $parent = substr($parent, 1);
  174. $folders[$folder]['level'] = count($ar) - $baseDepth;
  175. $folders[$folder]['parent'] = $parent;
  176. $folders[$folder]['children'] = array();
  177. $linkList = '';
  178. }
  179. foreach ($folders as $folder => $data) {
  180. $folders[$folder]['children'] = $this->gatherChildren($folder, $folders);
  181. }
  182. $this->view->currentDirectory = $this->currentDirectory;
  183. foreach ($folders as $folder => $data) {
  184. $folders[$folder]['linkList'] = $this->view->folderLink($folder, $folders);
  185. }
  186. return $folders;
  187. }
  188. function gatherChildren($parent, $folders) {
  189. $children = array();
  190. foreach ($folders as $key => $folder) {
  191. if ($folder['parent'] == $parent) {
  192. $children[] = $key;
  193. }
  194. }
  195. return $children;
  196. }
  197. function deleteFile($file) {
  198. $file = $this->browseBase . $this->currentDirectory . basename($file);
  199. $pages = $this->fileIsLinked($file);
  200. if (is_array($pages)) {
  201. $this->view->error('error_not_deleted', $file);
  202. $this->view->error('error_file_is_used', $file);
  203. foreach ($pages as $page) {
  204. $this->view->message .= '<li>' . $page . '</li>';
  205. }
  206. $this->view->message .= '</ul>';
  207. return;
  208. }
  209. // 設法在 Windows 能夠儲存中文命名檔案
  210. if (substr(php_uname(), 0, 7) == "Windows")
  211. {
  212. $yenname=iconv("utf-8","big-5",$file);
  213. }
  214. else
  215. {
  216. $yenname=$file;
  217. }
  218. if (@unlink($yenname)) {
  219. $this->view->success('success_deleted', $file);
  220. } else {
  221. $this->view->error('error_not_deleted', $file);
  222. }
  223. }
  224. function uploadFile() {
  225. $file = $_FILES['fbupload'];
  226. if ($file['error'] != 0) {
  227. switch ($file['error']) {
  228. case UPLOAD_ERR_INI_SIZE:
  229. $this->view->error('error_not_uploaded', $file['name']);
  230. $this->view->error('error_file_too_big', array('?', ini_get('upload_max_filesize')));
  231. return;
  232. default:
  233. $this->view->error('error_not_uploaded', $file['name']);
  234. return;
  235. }
  236. }
  237. $type = @getimagesize($file['tmp_name']) !== FALSE ? 'images' : 'downloads';
  238. // alternatively the following might be used:
  239. // $type = $this->linkType == 'images' ? 'images' : 'downloads';
  240. if (isset($this->maxFilesizes[$type])) {
  241. if ($file['size'] > $this->maxFilesizes[$type]) {
  242. $this->view->error('error_not_uploaded', $file['name']);
  243. $this->view->error('error_file_too_big', array(number_format($file['size']/1000, 2), number_format($this->maxFilesizes[$type]/1000, 2) . ' kb'));
  244. return;
  245. }
  246. }
  247. if ($this->isAllowedFile($file['name']) == false) {
  248. $this->view->error('error_not_uploaded', $file['name']);
  249. $this->view->error('error_no_proper_extension', pathinfo($file['name'], PATHINFO_EXTENSION));
  250. return;
  251. }
  252. $filename = $this->browseBase . $this->currentDirectory . basename($file['name']);
  253. if (file_exists($filename)) {
  254. $this->view->error('error_not_uploaded', $file['name']);
  255. $this->view->error('error_file_already_exists', $filename);
  256. return;
  257. }
  258. // 設法在 Windows 能夠儲存中文命名檔案
  259. if (substr(php_uname(), 0, 7) == "Windows")
  260. {
  261. $yenname=iconv("utf-8","big-5",$filename);
  262. }
  263. else
  264. {
  265. $yenname=$filename;
  266. }
  267. if (move_uploaded_file($_FILES['fbupload']['tmp_name'], $yenname)) {
  268. chmod($filename, 0644);
  269. $this->view->success('success_uploaded', $file['name']);
  270. return;
  271. }
  272. $this->view->error('error_not_uploaded', $file['name']);
  273. }
  274. function createFolder() {
  275. $folder = basename($_POST['createFolder']);
  276. $folder = str_replace(array(':', '*', '?', '"', '<', '>', '|', ' '), '', $folder);
  277. $folder = $this->browseBase . $this->currentDirectory . $folder;
  278. if (is_dir($folder)) {
  279. $this->view->error('error_folder_already_exists', basename($folder));
  280. return;
  281. }
  282. if (!mkdir($folder)) {
  283. $this->view->error('error_unknown');
  284. }
  285. $this->view->success('success_folder_created', basename($folder));
  286. return;
  287. }
  288. function deleteFolder() {
  289. $folder = $this->browseBase . $this->currentDirectory . basename($_POST['folder']);
  290. if (!rmdir($folder)) {
  291. $this->view->error('error_not_deleted', basename($folder));
  292. return;
  293. }
  294. $this->view->success('success_deleted', basename($folder));
  295. return;
  296. }
  297. function renameFile() {
  298. $newName = str_replace(array('..', '<', '>', ':', '?', ' '), '', basename($_POST['renameFile']));
  299. $oldName = $_POST['oldName'];
  300. if ($oldName == $newName) {
  301. return;
  302. }
  303. if (pathinfo($newName, PATHINFO_EXTENSION) !== pathinfo($oldName, PATHINFO_EXTENSION)) {
  304. $this->view->message = 'You can not change the file extension!';
  305. return;
  306. }
  307. // 這裡也會牽涉 big5 與 utf8 存在檔案的查驗
  308. if (file_exists($this->browseBase . $this->currentDirectory . '/' . $newName)) {
  309. $this->view->error('error_file_already_exists', $newName);
  310. return;
  311. }
  312. $pages = $this->fileIsLinked($oldName);
  313. if (is_array($pages)) {
  314. $this->view->error('error_cant_rename', $oldName);
  315. $this->view->error('error_file_is_used', $oldName);
  316. foreach ($pages as $page) {
  317. $this->view->message .= '<li>' . $page . '</li>';
  318. }
  319. $this->view->message .= '</ul>';
  320. return;
  321. }
  322. // 這裡也會因為檔案更名而存在 big5 與 utf8 格式的問題
  323. if (rename($this->browseBase . $this->currentDirectory . '/' . $oldName, $this->browseBase . $this->currentDirectory . '/' . $newName)) {
  324. $this->view->message = 'Renamed ' . $oldName . ' to ' . $newName . '!';
  325. return;
  326. }
  327. $this->view->message = 'Something went wrong (XHFilebrowser::renameFile())';
  328. return;
  329. }
  330. function render($template) {
  331. $template = str_replace(array('.', '/', '\\', '<', ' '), '', $template);
  332. if (!file_exists($this->browserPath . 'tpl/' . $template . '.html')) {
  333. return "<p>XHFileBrowser::render() - Template not found: {$this->browserPath}tpl/$template.html'</p>";
  334. }
  335. $this->view->baseDirectory = $this->baseDirectory;
  336. // $this->view->basePath = '';
  337. $this->view->baseLink = $this->linkType;
  338. $this->view->folders = $this->foldersArray();
  339. $this->view->subfolders = $this->folders;
  340. $this->view->files = $this->files;
  341. return $this->view->loadTemplate($this->browserPath . 'tpl/' . $template . '.html');
  342. }
  343. function setLinkParams($paramsString) {
  344. $this->view->linkParams = $paramsString;
  345. }
  346. function setLinkPrefix($prefix) {
  347. $this->view->linkPrefix = $prefix;
  348. }
  349. function setBrowseBase($path) {
  350. $this->browseBase = $path;
  351. $this->view->basePath = $path;
  352. }
  353. function setBrowserPath($path) {
  354. $this->view->browserPath = $path;
  355. }
  356. function setMaxFileSize($folder = '', $bytes) {
  357. if (key_exists($folder, $this->baseDirectories)){
  358. $this->maxFilesizes[$folder] = (int) $bytes;
  359. }
  360. }
  361. }