Linux multi-monitor fullscreen support
[ryzomcore.git] / web / public_php / ams / misc / elfinder-connector / elFinderVolumeMySQL.class.php
blobf6f3fef35571da2b08c25e1f23b9d922d7dadab9
1 <?php
3 /**
4 * Simple elFinder driver for MySQL.
6 * @author Dmitry (dio) Levashov
7 **/
8 class elFinderVolumeMySQL extends elFinderVolumeDriver {
10 /**
11 * Driver id
12 * Must be started from letter and contains [a-z0-9]
13 * Used as part of volume id
15 * @var string
16 **/
17 protected $driverId = 'm';
19 /**
20 * Database object
22 * @var mysqli
23 **/
24 protected $db = null;
26 /**
27 * Tables to store files
29 * @var string
30 **/
31 protected $tbf = '';
33 /**
34 * Directory for tmp files
35 * If not set driver will try to use tmbDir as tmpDir
37 * @var string
38 **/
39 protected $tmpPath = '';
41 /**
42 * Numbers of sql requests (for debug)
44 * @var int
45 **/
46 protected $sqlCnt = 0;
48 /**
49 * Last db error message
51 * @var string
52 **/
53 protected $dbError = '';
55 /**
56 * Constructor
57 * Extend options with required fields
59 * @return void
60 * @author Dmitry (dio) Levashov
61 **/
62 public function __construct() {
63 $opts = array(
64 'host' => 'localhost',
65 'user' => '',
66 'pass' => '',
67 'db' => '',
68 'port' => null,
69 'socket' => null,
70 'files_table' => 'elfinder_file',
71 'tmbPath' => '',
72 'tmpPath' => ''
74 $this->options = array_merge($this->options, $opts);
75 $this->options['mimeDetect'] = 'internal';
78 /*********************************************************************/
79 /* INIT AND CONFIGURE */
80 /*********************************************************************/
82 /**
83 * Prepare driver before mount volume.
84 * Connect to db, check required tables and fetch root path
86 * @return bool
87 * @author Dmitry (dio) Levashov
88 **/
89 protected function init() {
91 if (!($this->options['host'] || $this->options['socket'])
92 || !$this->options['user']
93 || !$this->options['pass']
94 || !$this->options['db']
95 || !$this->options['path']
96 || !$this->options['files_table']) {
97 return false;
101 $this->db = new mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket']);
102 if ($this->db->connect_error || @mysqli_connect_error()) {
103 return false;
106 $this->db->set_charset('utf8');
108 if ($res = $this->db->query('SHOW TABLES')) {
109 while ($row = $res->fetch_array()) {
110 if ($row[0] == $this->options['files_table']) {
111 $this->tbf = $this->options['files_table'];
112 break;
117 if (!$this->tbf) {
118 return false;
121 $this->updateCache($this->options['path'], $this->_stat($this->options['path']));
123 return true;
129 * Set tmp path
131 * @return void
132 * @author Dmitry (dio) Levashov
134 protected function configure() {
135 parent::configure();
137 if (($tmp = $this->options['tmpPath'])) {
138 if (!file_exists($tmp)) {
139 if (@mkdir($tmp)) {
140 @chmod($tmp, $this->options['tmbPathMode']);
144 $this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false;
147 if (!$this->tmpPath && $this->tmbPath && $this->tmbPathWritable) {
148 $this->tmpPath = $this->tmbPath;
151 $this->mimeDetect = 'internal';
155 * Close connection
157 * @return void
158 * @author Dmitry (dio) Levashov
160 public function umount() {
161 $this->db->close();
165 * Return debug info for client
167 * @return array
168 * @author Dmitry (dio) Levashov
170 public function debug() {
171 $debug = parent::debug();
172 $debug['sqlCount'] = $this->sqlCnt;
173 if ($this->dbError) {
174 $debug['dbError'] = $this->dbError;
176 return $debug;
180 * Perform sql query and return result.
181 * Increase sqlCnt and save error if occurred
183 * @param string $sql query
184 * @return misc
185 * @author Dmitry (dio) Levashov
187 protected function query($sql) {
188 $this->sqlCnt++;
189 $res = $this->db->query($sql);
190 if (!$res) {
191 $this->dbError = $this->db->error;
193 return $res;
197 * Create empty object with required mimetype
199 * @param string $path parent dir path
200 * @param string $name object name
201 * @param string $mime mime type
202 * @return bool
203 * @author Dmitry (dio) Levashov
205 protected function make($path, $name, $mime) {
206 $sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`) VALUES ("%s", "%s", 0, %d, "%s", "", "%d", "%d")';
207 $sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write']);
208 // echo $sql;
209 return $this->query($sql) && $this->db->affected_rows > 0;
213 * Return temporary file path for required file
215 * @param string $path file path
216 * @return string
217 * @author Dmitry (dio) Levashov
219 protected function tmpname($path) {
220 return $this->tmpPath.DIRECTORY_SEPARATOR.md5($path);
224 * Resize image
226 * @param string $hash image file
227 * @param int $width new width
228 * @param int $height new height
229 * @param bool $crop crop image
230 * @return array|false
231 * @author Dmitry (dio) Levashov
232 * @author Alexey Sukhotin
234 public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
235 if ($this->commandDisabled('resize')) {
236 return $this->setError(elFinder::ERROR_PERM_DENIED);
239 if (($file = $this->file($hash)) == false) {
240 return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
243 if (!$file['write'] || !$file['read']) {
244 return $this->setError(elFinder::ERROR_PERM_DENIED);
247 $path = $this->decode($hash);
249 if (!$this->canResize($path, $file)) {
250 return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
253 $img = $this->tmpname($path);
255 if (!($fp = @fopen($img, 'w+'))) {
256 return false;
259 if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id="'.$path.'"'))
260 && ($r = $res->fetch_assoc())) {
261 fwrite($fp, $r['content']);
262 rewind($fp);
263 fclose($fp);
264 } else {
265 return false;
269 switch($mode) {
271 case 'propresize':
272 $result = $this->imgResize($img, $width, $height, true, true);
273 break;
275 case 'crop':
276 $result = $this->imgCrop($img, $width, $height, $x, $y);
277 break;
279 case 'fitsquare':
280 $result = $this->imgSquareFit($img, $width, $height, 'center', 'middle', $bg ? $bg : $this->options['tmbBgColor']);
281 break;
283 default:
284 $result = $this->imgResize($img, $width, $height, false, true);
285 break;
288 if ($result) {
290 $sql = sprintf('UPDATE %s SET content=LOAD_FILE("%s"), mtime=UNIX_TIMESTAMP() WHERE id=%d', $this->tbf, $this->loadFilePath($img), $path);
292 if (!$this->query($sql)) {
293 $content = file_get_contents($img);
294 $sql = sprintf('UPDATE %s SET content="%s", mtime=UNIX_TIMESTAMP() WHERE id=%d', $this->tbf, $this->db->real_escape_string($content), $path);
295 if (!$this->query($sql)) {
296 @unlink($img);
297 return false;
300 @unlink($img);
301 if (!empty($file['tmb']) && $file['tmb'] != "1") {
302 $this->rmTmb($file['tmb']);
304 $this->clearcache();
305 return $this->stat($path);
308 return false;
312 /*********************************************************************/
313 /* FS API */
314 /*********************************************************************/
317 * Cache dir contents
319 * @param string $path dir path
320 * @return void
321 * @author Dmitry Levashov
323 protected function cacheDir($path) {
324 $this->dirsCache[$path] = array();
326 $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs
327 FROM '.$this->tbf.' AS f
328 LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory"
329 WHERE f.parent_id="'.$path.'"
330 GROUP BY f.id';
332 $res = $this->query($sql);
333 if ($res) {
334 while ($row = $res->fetch_assoc()) {
335 // debug($row);
336 $id = $row['id'];
337 if ($row['parent_id']) {
338 $row['phash'] = $this->encode($row['parent_id']);
341 if ($row['mime'] == 'directory') {
342 unset($row['width']);
343 unset($row['height']);
344 } else {
345 unset($row['dirs']);
348 unset($row['id']);
349 unset($row['parent_id']);
353 if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) {
354 $this->dirsCache[$path][] = $id;
359 return $this->dirsCache[$path];
363 * Return array of parents paths (ids)
365 * @param int $path file path (id)
366 * @return array
367 * @author Dmitry (dio) Levashov
369 protected function getParents($path) {
370 $parents = array();
372 while ($path) {
373 if ($file = $this->stat($path)) {
374 array_unshift($parents, $path);
375 $path = isset($file['phash']) ? $this->decode($file['phash']) : false;
379 if (count($parents)) {
380 array_pop($parents);
382 return $parents;
386 * Return correct file path for LOAD_FILE method
388 * @param string $path file path (id)
389 * @return string
390 * @author Troex Nevelin
392 protected function loadFilePath($path) {
393 $realPath = realpath($path);
394 if (DIRECTORY_SEPARATOR == '\\') { // windows
395 $realPath = str_replace('\\', '\\\\', $realPath);
397 return $this->db->real_escape_string($realPath);
400 /*********************** paths/urls *************************/
403 * Return parent directory path
405 * @param string $path file path
406 * @return string
407 * @author Dmitry (dio) Levashov
409 protected function _dirname($path) {
410 return ($stat = $this->stat($path)) ? ($stat['phash'] ? $this->decode($stat['phash']) : $this->root) : false;
414 * Return file name
416 * @param string $path file path
417 * @return string
418 * @author Dmitry (dio) Levashov
420 protected function _basename($path) {
421 return ($stat = $this->stat($path)) ? $stat['name'] : false;
425 * Join dir name and file name and return full path
427 * @param string $dir
428 * @param string $name
429 * @return string
430 * @author Dmitry (dio) Levashov
432 protected function _joinPath($dir, $name) {
433 $sql = 'SELECT id FROM '.$this->tbf.' WHERE parent_id="'.$dir.'" AND name="'.$this->db->real_escape_string($name).'"';
435 if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) {
436 $this->updateCache($r['id'], $this->_stat($r['id']));
437 return $r['id'];
439 return -1;
443 * Return normalized path, this works the same as os.path.normpath() in Python
445 * @param string $path path
446 * @return string
447 * @author Troex Nevelin
449 protected function _normpath($path) {
450 return $path;
454 * Return file path related to root dir
456 * @param string $path file path
457 * @return string
458 * @author Dmitry (dio) Levashov
460 protected function _relpath($path) {
461 return $path;
465 * Convert path related to root dir into real path
467 * @param string $path file path
468 * @return string
469 * @author Dmitry (dio) Levashov
471 protected function _abspath($path) {
472 return $path;
476 * Return fake path started from root dir
478 * @param string $path file path
479 * @return string
480 * @author Dmitry (dio) Levashov
482 protected function _path($path) {
483 if (($file = $this->stat($path)) == false) {
484 return '';
487 $parentsIds = $this->getParents($path);
488 $path = '';
489 foreach ($parentsIds as $id) {
490 $dir = $this->stat($id);
491 $path .= $dir['name'].$this->separator;
493 return $path.$file['name'];
497 * Return true if $path is children of $parent
499 * @param string $path path to check
500 * @param string $parent parent path
501 * @return bool
502 * @author Dmitry (dio) Levashov
504 protected function _inpath($path, $parent) {
505 return $path == $parent
506 ? true
507 : in_array($parent, $this->getParents($path));
510 /***************** file stat ********************/
512 * Return stat for given path.
513 * Stat contains following fields:
514 * - (int) size file size in b. required
515 * - (int) ts file modification time in unix time. required
516 * - (string) mime mimetype. required for folders, others - optionally
517 * - (bool) read read permissions. required
518 * - (bool) write write permissions. required
519 * - (bool) locked is object locked. optionally
520 * - (bool) hidden is object hidden. optionally
521 * - (string) alias for symlinks - link target path relative to root path. optionally
522 * - (string) target for symlinks - link target path. optionally
524 * If file does not exists - returns empty array or false.
526 * @param string $path file path
527 * @return array|false
528 * @author Dmitry (dio) Levashov
530 protected function _stat($path) {
531 $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs
532 FROM '.$this->tbf.' AS f
533 LEFT JOIN '.$this->tbf.' AS p ON p.id=f.parent_id
534 LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory"
535 WHERE f.id="'.$path.'"
536 GROUP BY f.id';
538 $res = $this->query($sql);
540 if ($res) {
541 $stat = $res->fetch_assoc();
542 if ($stat['parent_id']) {
543 $stat['phash'] = $this->encode($stat['parent_id']);
545 if ($stat['mime'] == 'directory') {
546 unset($stat['width']);
547 unset($stat['height']);
548 } else {
549 unset($stat['dirs']);
551 unset($stat['id']);
552 unset($stat['parent_id']);
553 return $stat;
556 return array();
560 * Return true if path is dir and has at least one childs directory
562 * @param string $path dir path
563 * @return bool
564 * @author Dmitry (dio) Levashov
566 protected function _subdirs($path) {
567 return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false;
571 * Return object width and height
572 * Usualy used for images, but can be realize for video etc...
574 * @param string $path file path
575 * @param string $mime file mime type
576 * @return string
577 * @author Dmitry (dio) Levashov
579 protected function _dimensions($path, $mime) {
580 return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'].'x'.$stat['height'] : '';
583 /******************** file/dir content *********************/
586 * Return files list in directory.
588 * @param string $path dir path
589 * @return array
590 * @author Dmitry (dio) Levashov
592 protected function _scandir($path) {
593 return isset($this->dirsCache[$path])
594 ? $this->dirsCache[$path]
595 : $this->cacheDir($path);
599 * Open file and return file pointer
601 * @param string $path file path
602 * @param string $mode open file mode (ignored in this driver)
603 * @return resource|false
604 * @author Dmitry (dio) Levashov
606 protected function _fopen($path, $mode='rb') {
607 $fp = $this->tmbPath
608 ? @fopen($this->tmpname($path), 'w+')
609 : @tmpfile();
612 if ($fp) {
613 if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id="'.$path.'"'))
614 && ($r = $res->fetch_assoc())) {
615 fwrite($fp, $r['content']);
616 rewind($fp);
617 return $fp;
618 } else {
619 $this->_fclose($fp, $path);
623 return false;
627 * Close opened file
629 * @param resource $fp file pointer
630 * @return bool
631 * @author Dmitry (dio) Levashov
633 protected function _fclose($fp, $path='') {
634 @fclose($fp);
635 if ($path) {
636 @unlink($this->tmpname($path));
640 /******************** file/dir manipulations *************************/
643 * Create dir and return created dir path or false on failed
645 * @param string $path parent dir path
646 * @param string $name new directory name
647 * @return string|bool
648 * @author Dmitry (dio) Levashov
650 protected function _mkdir($path, $name) {
651 return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false;
655 * Create file and return it's path or false on failed
657 * @param string $path parent dir path
658 * @param string $name new file name
659 * @return string|bool
660 * @author Dmitry (dio) Levashov
662 protected function _mkfile($path, $name) {
663 return $this->make($path, $name, 'text/plain') ? $this->_joinPath($path, $name) : false;
667 * Create symlink. FTP driver does not support symlinks.
669 * @param string $target link target
670 * @param string $path symlink path
671 * @return bool
672 * @author Dmitry (dio) Levashov
674 protected function _symlink($target, $path, $name) {
675 return false;
679 * Copy file into another file
681 * @param string $source source file path
682 * @param string $targetDir target directory path
683 * @param string $name new file name
684 * @return bool
685 * @author Dmitry (dio) Levashov
687 protected function _copy($source, $targetDir, $name) {
688 $this->clearcache();
689 $id = $this->_joinPath($targetDir, $name);
691 $sql = $id > 0
692 ? sprintf('REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) (SELECT %d, %d, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d)', $this->tbf, $id, $this->_dirname($id), $this->tbf, $source)
693 : sprintf('INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) SELECT %d, "%s", content, size, %d, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d', $this->tbf, $targetDir, $this->db->real_escape_string($name), time(), $this->tbf, $source);
695 return $this->query($sql);
699 * Move file into another parent dir.
700 * Return new file path or false.
702 * @param string $source source file path
703 * @param string $target target dir path
704 * @param string $name file name
705 * @return string|bool
706 * @author Dmitry (dio) Levashov
708 protected function _move($source, $targetDir, $name) {
709 $sql = 'UPDATE %s SET parent_id=%d, name="%s" WHERE id=%d LIMIT 1';
710 $sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source);
711 return $this->query($sql) && $this->db->affected_rows > 0;
715 * Remove file
717 * @param string $path file path
718 * @return bool
719 * @author Dmitry (dio) Levashov
721 protected function _unlink($path) {
722 return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
726 * Remove dir
728 * @param string $path dir path
729 * @return bool
730 * @author Dmitry (dio) Levashov
732 protected function _rmdir($path) {
733 return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
737 * undocumented function
739 * @return void
740 * @author Dmitry Levashov
742 protected function _setContent($path, $fp) {
743 rewind($fp);
744 $fstat = fstat($fp);
745 $size = $fstat['size'];
751 * Create new file and write into it from file pointer.
752 * Return new file path or false on error.
754 * @param resource $fp file pointer
755 * @param string $dir target dir path
756 * @param string $name file name
757 * @return bool|string
758 * @author Dmitry (dio) Levashov
760 protected function _save($fp, $dir, $name, $mime, $w, $h) {
761 $this->clearcache();
763 $id = $this->_joinPath($dir, $name);
764 rewind($fp);
765 $stat = fstat($fp);
766 $size = $stat['size'];
768 if (($tmpfile = tempnam($this->tmpPath, $this->id))) {
769 if (($trgfp = fopen($tmpfile, 'wb')) == false) {
770 unlink($tmpfile);
771 } else {
772 while (!feof($fp)) {
773 fwrite($trgfp, fread($fp, 8192));
775 fclose($trgfp);
777 $sql = $id > 0
778 ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, "%s", LOAD_FILE("%s"), %d, %d, "%s", %d, %d)'
779 : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", LOAD_FILE("%s"), %d, %d, "%s", %d, %d)';
780 $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, time(), $mime, $w, $h);
782 $res = $this->query($sql);
783 unlink($tmpfile);
785 if ($res) {
786 return $id > 0 ? $id : $this->db->insert_id;
792 $content = '';
793 rewind($fp);
794 while (!feof($fp)) {
795 $content .= fread($fp, 8192);
798 $sql = $id > 0
799 ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, "%s", "%s", %d, %d, "%s", %d, %d)'
800 : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", "%s", %d, %d, "%s", %d, %d)';
801 $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, time(), $mime, $w, $h);
803 unset($content);
805 if ($this->query($sql)) {
806 return $id > 0 ? $id : $this->db->insert_id;
809 return false;
813 * Get file contents
815 * @param string $path file path
816 * @return string|false
817 * @author Dmitry (dio) Levashov
819 protected function _getContents($path) {
820 return ($res = $this->query(sprintf('SELECT content FROM %s WHERE id=%d', $this->tbf, $path))) && ($r = $res->fetch_assoc()) ? $r['content'] : false;
824 * Write a string to a file
826 * @param string $path file path
827 * @param string $content new file content
828 * @return bool
829 * @author Dmitry (dio) Levashov
831 protected function _filePutContents($path, $content) {
832 return $this->query(sprintf('UPDATE %s SET content="%s", size=%d, mtime=%d WHERE id=%d LIMIT 1', $this->tbf, $this->db->real_escape_string($content), strlen($content), time(), $path));
836 * Detect available archivers
838 * @return void
840 protected function _checkArchivers() {
841 return;
845 * Unpack archive
847 * @param string $path archive path
848 * @param array $arc archiver command and arguments (same as in $this->archivers)
849 * @return void
850 * @author Dmitry (dio) Levashov
851 * @author Alexey Sukhotin
853 protected function _unpack($path, $arc) {
854 return;
858 * Recursive symlinks search
860 * @param string $path file/dir path
861 * @return bool
862 * @author Dmitry (dio) Levashov
864 protected function _findSymlinks($path) {
865 return false;
869 * Extract files from archive
871 * @param string $path archive path
872 * @param array $arc archiver command and arguments (same as in $this->archivers)
873 * @return true
874 * @author Dmitry (dio) Levashov,
875 * @author Alexey Sukhotin
877 protected function _extract($path, $arc) {
878 return false;
882 * Create archive and return its path
884 * @param string $dir target dir
885 * @param array $files files names list
886 * @param string $name archive name
887 * @param array $arc archiver options
888 * @return string|bool
889 * @author Dmitry (dio) Levashov,
890 * @author Alexey Sukhotin
892 protected function _archive($dir, $files, $name, $arc) {
893 return false;
896 } // END class