Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / web / public_php / ams / misc / elfinder-connector / elFinder.class.php
blob5b15085050a05cb4ec9aba02053a0b794c598093
1 <?php
3 /**
4 * elFinder - file manager for web.
5 * Core class.
7 * @package elfinder
8 * @author Dmitry (dio) Levashov
9 * @author Troex Nevelin
10 * @author Alexey Sukhotin
11 **/
12 class elFinder {
14 /**
15 * API version number
17 * @var string
18 **/
19 protected $version = '2.0';
21 /**
22 * Storages (root dirs)
24 * @var array
25 **/
26 protected $volumes = array();
28 /**
29 * Mounted volumes count
30 * Required to create unique volume id
32 * @var int
33 **/
34 public static $volumesCnt = 1;
36 /**
37 * Default root (storage)
39 * @var elFinderStorageDriver
40 **/
41 protected $default = null;
43 /**
44 * Commands and required arguments list
46 * @var array
47 **/
48 protected $commands = array(
49 'open' => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false),
50 'ls' => array('target' => true, 'mimes' => false),
51 'tree' => array('target' => true),
52 'parents' => array('target' => true),
53 'tmb' => array('targets' => true),
54 'file' => array('target' => true, 'download' => false),
55 'size' => array('targets' => true),
56 'mkdir' => array('target' => true, 'name' => true),
57 'mkfile' => array('target' => true, 'name' => true, 'mimes' => false),
58 'rm' => array('targets' => true),
59 'rename' => array('target' => true, 'name' => true, 'mimes' => false),
60 'duplicate' => array('targets' => true, 'suffix' => false),
61 'paste' => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false),
62 'upload' => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false),
63 'get' => array('target' => true),
64 'put' => array('target' => true, 'content' => '', 'mimes' => false),
65 'archive' => array('targets' => true, 'type' => true, 'mimes' => false),
66 'extract' => array('target' => true, 'mimes' => false),
67 'search' => array('q' => true, 'mimes' => false),
68 'info' => array('targets' => true),
69 'dim' => array('target' => true),
70 'resize' => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false)
73 /**
74 * Commands listeners
76 * @var array
77 **/
78 protected $listeners = array();
80 /**
81 * script work time for debug
83 * @var string
84 **/
85 protected $time = 0;
86 /**
87 * Is elFinder init correctly?
89 * @var bool
90 **/
91 protected $loaded = false;
92 /**
93 * Send debug to client?
95 * @var string
96 **/
97 protected $debug = false;
99 /**
100 * undocumented class variable
102 * @var string
104 protected $uploadDebug = '';
107 * Errors from not mounted volumes
109 * @var array
111 public $mountErrors = array();
113 // Errors messages
114 const ERROR_UNKNOWN = 'errUnknown';
115 const ERROR_UNKNOWN_CMD = 'errUnknownCmd';
116 const ERROR_CONF = 'errConf';
117 const ERROR_CONF_NO_JSON = 'errJSON';
118 const ERROR_CONF_NO_VOL = 'errNoVolumes';
119 const ERROR_INV_PARAMS = 'errCmdParams';
120 const ERROR_OPEN = 'errOpen';
121 const ERROR_DIR_NOT_FOUND = 'errFolderNotFound';
122 const ERROR_FILE_NOT_FOUND = 'errFileNotFound'; // 'File not found.'
123 const ERROR_TRGDIR_NOT_FOUND = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.'
124 const ERROR_NOT_DIR = 'errNotFolder';
125 const ERROR_NOT_FILE = 'errNotFile';
126 const ERROR_PERM_DENIED = 'errPerm';
127 const ERROR_LOCKED = 'errLocked'; // '"$1" is locked and can not be renamed, moved or removed.'
128 const ERROR_EXISTS = 'errExists'; // 'File named "$1" already exists.'
129 const ERROR_INVALID_NAME = 'errInvName'; // 'Invalid file name.'
130 const ERROR_MKDIR = 'errMkdir';
131 const ERROR_MKFILE = 'errMkfile';
132 const ERROR_RENAME = 'errRename';
133 const ERROR_COPY = 'errCopy';
134 const ERROR_MOVE = 'errMove';
135 const ERROR_COPY_FROM = 'errCopyFrom';
136 const ERROR_COPY_TO = 'errCopyTo';
137 const ERROR_COPY_ITSELF = 'errCopyInItself';
138 const ERROR_REPLACE = 'errReplace'; // 'Unable to replace "$1".'
139 const ERROR_RM = 'errRm'; // 'Unable to remove "$1".'
140 const ERROR_RM_SRC = 'errRmSrc'; // 'Unable remove source file(s)'
141 const ERROR_UPLOAD = 'errUpload'; // 'Upload error.'
142 const ERROR_UPLOAD_FILE = 'errUploadFile'; // 'Unable to upload "$1".'
143 const ERROR_UPLOAD_NO_FILES = 'errUploadNoFiles'; // 'No files found for upload.'
144 const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize'; // 'Data exceeds the maximum allowed size.'
145 const ERROR_UPLOAD_FILE_SIZE = 'errUploadFileSize'; // 'File exceeds maximum allowed size.'
146 const ERROR_UPLOAD_FILE_MIME = 'errUploadMime'; // 'File type not allowed.'
147 const ERROR_UPLOAD_TRANSFER = 'errUploadTransfer'; // '"$1" transfer error.'
148 // const ERROR_ACCESS_DENIED = 'errAccess';
149 const ERROR_NOT_REPLACE = 'errNotReplace'; // Object "$1" already exists at this location and can not be replaced with object of another type.
150 const ERROR_SAVE = 'errSave';
151 const ERROR_EXTRACT = 'errExtract';
152 const ERROR_ARCHIVE = 'errArchive';
153 const ERROR_NOT_ARCHIVE = 'errNoArchive';
154 const ERROR_ARCHIVE_TYPE = 'errArcType';
155 const ERROR_ARC_SYMLINKS = 'errArcSymlinks';
156 const ERROR_ARC_MAXSIZE = 'errArcMaxSize';
157 const ERROR_RESIZE = 'errResize';
158 const ERROR_UNSUPPORT_TYPE = 'errUsupportType';
159 const ERROR_NOT_UTF8_CONTENT = 'errNotUTF8Content';
162 * Constructor
164 * @param array elFinder and roots configurations
165 * @return void
166 * @author Dmitry (dio) Levashov
168 public function __construct($opts) {
170 $this->time = $this->utime();
171 $this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
173 setlocale(LC_ALL, !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8');
175 // bind events listeners
176 if (!empty($opts['bind']) && is_array($opts['bind'])) {
177 foreach ($opts['bind'] as $cmd => $handler) {
178 $this->bind($cmd, $handler);
182 // "mount" volumes
183 if (isset($opts['roots']) && is_array($opts['roots'])) {
185 foreach ($opts['roots'] as $i => $o) {
186 $class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : '');
188 if (class_exists($class)) {
189 $volume = new $class();
191 if ($volume->mount($o)) {
192 // unique volume id (ends on "_") - used as prefix to files hash
193 $id = $volume->id();
195 $this->volumes[$id] = $volume;
196 if (!$this->default && $volume->isReadable()) {
197 $this->default = $this->volumes[$id];
199 } else {
200 $this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
202 } else {
203 $this->mountErrors[] = 'Driver "'.$class.'" does not exists';
207 // if at least one redable volume - ii desu >_<
208 $this->loaded = !empty($this->default);
212 * Return true if fm init correctly
214 * @return bool
215 * @author Dmitry (dio) Levashov
217 public function loaded() {
218 return $this->loaded;
222 * Return version (api) number
224 * @return string
225 * @author Dmitry (dio) Levashov
227 public function version() {
228 return $this->version;
232 * Add handler to elFinder command
234 * @param string command name
235 * @param string|array callback name or array(object, method)
236 * @return elFinder
237 * @author Dmitry (dio) Levashov
239 public function bind($cmd, $handler) {
240 $cmds = array_map('trim', explode(' ', $cmd));
242 foreach ($cmds as $cmd) {
243 if ($cmd) {
244 if (!isset($this->listeners[$cmd])) {
245 $this->listeners[$cmd] = array();
248 if ((is_array($handler) && count($handler) == 2 && is_object($handler[0]) && method_exists($handler[0], $handler[1]))
249 || function_exists($handler)) {
250 $this->listeners[$cmd][] = $handler;
255 return $this;
259 * Remove event (command exec) handler
261 * @param string command name
262 * @param string|array callback name or array(object, method)
263 * @return elFinder
264 * @author Dmitry (dio) Levashov
266 public function unbind($cmd, $handler) {
267 if (!empty($this->listeners[$cmd])) {
268 foreach ($this->listeners[$cmd] as $i => $h) {
269 if ($h === $handler) {
270 unset($this->listeners[$cmd][$i]);
271 return $this;
275 return $this;
279 * Return true if command exists
281 * @param string command name
282 * @return bool
283 * @author Dmitry (dio) Levashov
285 public function commandExists($cmd) {
286 return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
290 * Return command required arguments info
292 * @param string command name
293 * @return array
294 * @author Dmitry (dio) Levashov
296 public function commandArgsList($cmd) {
297 return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
301 * Exec command and return result
303 * @param string $cmd command name
304 * @param array $args command arguments
305 * @return array
306 * @author Dmitry (dio) Levashov
308 public function exec($cmd, $args) {
310 if (!$this->loaded) {
311 return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
314 if (!$this->commandExists($cmd)) {
315 return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
318 if (!empty($args['mimes']) && is_array($args['mimes'])) {
319 foreach ($this->volumes as $id => $v) {
320 $this->volumes[$id]->setMimesFilter($args['mimes']);
324 $result = $this->$cmd($args);
326 if (isset($result['removed'])) {
327 foreach ($this->volumes as $volume) {
328 $result['removed'] = array_merge($result['removed'], $volume->removed());
329 $volume->resetRemoved();
333 // call handlers for this command
334 if (!empty($this->listeners[$cmd])) {
335 foreach ($this->listeners[$cmd] as $handler) {
336 if ((is_array($handler) && $handler[0]->{$handler[1]}($cmd, $result, $args, $this))
337 || (!is_array($handler) && $handler($cmd, $result, $args, $this))) {
338 // handler return true to force sync client after command completed
339 $result['sync'] = true;
344 // replace removed files info with removed files hashes
345 if (!empty($result['removed'])) {
346 $removed = array();
347 foreach ($result['removed'] as $file) {
348 $removed[] = $file['hash'];
350 $result['removed'] = array_unique($removed);
352 // remove hidden files and filter files by mimetypes
353 if (!empty($result['added'])) {
354 $result['added'] = $this->filter($result['added']);
356 // remove hidden files and filter files by mimetypes
357 if (!empty($result['changed'])) {
358 $result['changed'] = $this->filter($result['changed']);
361 if ($this->debug || !empty($args['debug'])) {
362 $result['debug'] = array(
363 'connector' => 'php',
364 'phpver' => PHP_VERSION,
365 'time' => $this->utime() - $this->time,
366 'memory' => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
367 'upload' => $this->uploadDebug,
368 'volumes' => array(),
369 'mountErrors' => $this->mountErrors
372 foreach ($this->volumes as $id => $volume) {
373 $result['debug']['volumes'][] = $volume->debug();
377 foreach ($this->volumes as $volume) {
378 $volume->umount();
381 return $result;
385 * Return file real path
387 * @param string $hash file hash
388 * @return string
389 * @author Dmitry (dio) Levashov
391 public function realpath($hash) {
392 if (($volume = $this->volume($hash)) == false) {
393 return false;
395 return $volume->realpath($hash);
398 /***************************************************************************/
399 /* commands */
400 /***************************************************************************/
403 * Normalize error messages
405 * @return array
406 * @author Dmitry (dio) Levashov
408 public function error() {
409 $errors = array();
411 foreach (func_get_args() as $msg) {
412 if (is_array($msg)) {
413 $errors = array_merge($errors, $msg);
414 } else {
415 $errors[] = $msg;
419 return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
423 * "Open" directory
424 * Return array with following elements
425 * - cwd - opened dir info
426 * - files - opened dir content [and dirs tree if $args[tree]]
427 * - api - api version (if $args[init])
428 * - uplMaxSize - if $args[init]
429 * - error - on failed
431 * @param array command arguments
432 * @return array
433 * @author Dmitry (dio) Levashov
435 protected function open($args) {
436 $target = $args['target'];
437 $init = !empty($args['init']);
438 $tree = !empty($args['tree']);
439 $volume = $this->volume($target);
440 $cwd = $volume ? $volume->dir($target, true) : false;
441 $hash = $init ? 'default folder' : '#'.$target;
443 // on init request we can get invalid dir hash -
444 // dir which can not be opened now, but remembered by client,
445 // so open default dir
446 if ((!$cwd || !$cwd['read']) && $init) {
447 $volume = $this->default;
448 $cwd = $volume->dir($volume->defaultPath(), true);
451 if (!$cwd) {
452 return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
454 if (!$cwd['read']) {
455 return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
458 $files = array();
460 // get folders trees
461 if ($args['tree']) {
462 foreach ($this->volumes as $id => $v) {
464 if (($tree = $v->tree('', 0, $cwd['hash'])) != false) {
465 $files = array_merge($files, $tree);
470 // get current working directory files list and add to $files if not exists in it
471 if (($ls = $volume->scandir($cwd['hash'])) === false) {
472 return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
475 foreach ($ls as $file) {
476 if (!in_array($file, $files)) {
477 $files[] = $file;
481 $result = array(
482 'cwd' => $cwd,
483 'options' => $volume->options($cwd['hash']),
484 'files' => $files
487 if (!empty($args['init'])) {
488 $result['api'] = $this->version;
489 $result['uplMaxSize'] = ini_get('upload_max_filesize');
492 return $result;
496 * Return dir files names list
498 * @param array command arguments
499 * @return array
500 * @author Dmitry (dio) Levashov
502 protected function ls($args) {
503 $target = $args['target'];
505 if (($volume = $this->volume($target)) == false
506 || ($list = $volume->ls($target)) === false) {
507 return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
509 return array('list' => $list);
513 * Return subdirs for required directory
515 * @param array command arguments
516 * @return array
517 * @author Dmitry (dio) Levashov
519 protected function tree($args) {
520 $target = $args['target'];
522 if (($volume = $this->volume($target)) == false
523 || ($tree = $volume->tree($target)) == false) {
524 return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
527 return array('tree' => $tree);
531 * Return parents dir for required directory
533 * @param array command arguments
534 * @return array
535 * @author Dmitry (dio) Levashov
537 protected function parents($args) {
538 $target = $args['target'];
540 if (($volume = $this->volume($target)) == false
541 || ($tree = $volume->parents($target)) == false) {
542 return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
545 return array('tree' => $tree);
549 * Return new created thumbnails list
551 * @param array command arguments
552 * @return array
553 * @author Dmitry (dio) Levashov
555 protected function tmb($args) {
557 $result = array('images' => array());
558 $targets = $args['targets'];
560 foreach ($targets as $target) {
561 if (($volume = $this->volume($target)) != false
562 && (($tmb = $volume->tmb($target)) != false)) {
563 $result['images'][$target] = $tmb;
566 return $result;
570 * Required to output file in browser when volume URL is not set
571 * Return array contains opened file pointer, root itself and required headers
573 * @param array command arguments
574 * @return array
575 * @author Dmitry (dio) Levashov
577 protected function file($args) {
578 $target = $args['target'];
579 $download = !empty($args['download']);
580 $h403 = 'HTTP/1.x 403 Access Denied';
581 $h404 = 'HTTP/1.x 404 Not Found';
583 if (($volume = $this->volume($target)) == false) {
584 return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
587 if (($file = $volume->file($target)) == false) {
588 return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
591 if (!$file['read']) {
592 return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
595 if (($fp = $volume->open($target)) == false) {
596 return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
599 if ($download) {
600 $disp = 'attachment';
601 $mime = 'application/octet-stream';
602 } else {
603 $disp = preg_match('/^(image|text)/i', $file['mime']) || $file['mime'] == 'application/x-shockwave-flash'
604 ? 'inline'
605 : 'attachment';
606 $mime = $file['mime'];
609 $filenameEncoded = rawurlencode($file['name']);
610 if (strpos($filenameEncoded, '%') === false) { // ASCII only
611 $filename = 'filename="'.$file['name'].'"';
612 } else {
613 $ua = $_SERVER["HTTP_USER_AGENT"];
614 if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
615 $filename = 'filename="'.$filenameEncoded.'"';
616 } else { // RFC 6266 (RFC 2231/RFC 5987)
617 $filename = 'filename*=UTF-8\'\''.$filenameEncoded;
621 $result = array(
622 'volume' => $volume,
623 'pointer' => $fp,
624 'info' => $file,
625 'header' => array(
626 'Content-Type: '.$mime,
627 'Content-Disposition: '.$disp.'; '.$filename,
628 'Content-Location: '.$file['name'],
629 'Content-Transfer-Encoding: binary',
630 'Content-Length: '.$file['size'],
631 'Connection: close'
634 return $result;
638 * Count total files size
640 * @param array command arguments
641 * @return array
642 * @author Dmitry (dio) Levashov
644 protected function size($args) {
645 $size = 0;
647 foreach ($args['targets'] as $target) {
648 if (($volume = $this->volume($target)) == false
649 || ($file = $volume->file($target)) == false
650 || !$file['read']) {
651 return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
654 $size += $volume->size($target);
656 return array('size' => $size);
660 * Create directory
662 * @param array command arguments
663 * @return array
664 * @author Dmitry (dio) Levashov
666 protected function mkdir($args) {
667 $target = $args['target'];
668 $name = $args['name'];
670 if (($volume = $this->volume($target)) == false) {
671 return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
674 return ($dir = $volume->mkdir($target, $name)) == false
675 ? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
676 : array('added' => array($dir));
680 * Create empty file
682 * @param array command arguments
683 * @return array
684 * @author Dmitry (dio) Levashov
686 protected function mkfile($args) {
687 $target = $args['target'];
688 $name = $args['name'];
690 if (($volume = $this->volume($target)) == false) {
691 return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
694 return ($file = $volume->mkfile($target, $args['name'])) == false
695 ? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
696 : array('added' => array($file));
700 * Rename file
702 * @param array $args
703 * @return array
704 * @author Dmitry (dio) Levashov
706 protected function rename($args) {
707 $target = $args['target'];
708 $name = $args['name'];
710 if (($volume = $this->volume($target)) == false
711 || ($rm = $volume->file($target)) == false) {
712 return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
714 $rm['realpath'] = $volume->realpath($target);
716 return ($file = $volume->rename($target, $name)) == false
717 ? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
718 : array('added' => array($file), 'removed' => array($rm));
722 * Duplicate file - create copy with "copy %d" suffix
724 * @param array $args command arguments
725 * @return array
726 * @author Dmitry (dio) Levashov
728 protected function duplicate($args) {
729 $targets = is_array($args['targets']) ? $args['targets'] : array();
730 $result = array('added' => array());
731 $suffix = empty($args['suffix']) ? 'copy' : $args['suffix'];
733 foreach ($targets as $target) {
734 if (($volume = $this->volume($target)) == false
735 || ($src = $volume->file($target)) == false) {
736 $result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
737 break;
740 if (($file = $volume->duplicate($target, $suffix)) == false) {
741 $result['warning'] = $this->error($volume->error());
742 break;
745 $result['added'][] = $file;
748 return $result;
752 * Remove dirs/files
754 * @param array command arguments
755 * @return array
756 * @author Dmitry (dio) Levashov
758 protected function rm($args) {
759 $targets = is_array($args['targets']) ? $args['targets'] : array();
760 $result = array('removed' => array());
762 foreach ($targets as $target) {
763 if (($volume = $this->volume($target)) == false) {
764 $result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
765 return $result;
767 if (!$volume->rm($target)) {
768 $result['warning'] = $this->error($volume->error());
769 return $result;
773 return $result;
777 * Save uploaded files
779 * @param array
780 * @return array
781 * @author Dmitry (dio) Levashov
783 protected function upload($args) {
784 $target = $args['target'];
785 $volume = $this->volume($target);
786 $files = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
787 $result = array('added' => array(), 'header' => empty($args['html']) ? false : 'Content-Type: text/html; charset=utf-8');
789 if (empty($files)) {
790 return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES), 'header' => $header);
793 if (!$volume) {
794 return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target), 'header' => $header);
797 foreach ($files['name'] as $i => $name) {
798 if (($error = $files['error'][$i]) > 0) {
799 $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER);
800 $this->uploadDebug = 'Upload error code: '.$error;
801 break;
804 $tmpname = $files['tmp_name'][$i];
806 if (($fp = fopen($tmpname, 'rb')) == false) {
807 $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
808 $this->uploadDebug = 'Upload error: unable open tmp file';
809 break;
812 if (($file = $volume->upload($fp, $target, $name, $tmpname)) === false) {
813 $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
814 fclose($fp);
815 break;
818 fclose($fp);
819 $result['added'][] = $file;
822 return $result;
826 * Copy/move files into new destination
828 * @param array command arguments
829 * @return array
830 * @author Dmitry (dio) Levashov
832 protected function paste($args) {
833 $dst = $args['dst'];
834 $targets = is_array($args['targets']) ? $args['targets'] : array();
835 $cut = !empty($args['cut']);
836 $error = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
837 $result = array('added' => array(), 'removed' => array());
839 if (($dstVolume = $this->volume($dst)) == false) {
840 return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
843 foreach ($targets as $target) {
844 if (($srcVolume = $this->volume($target)) == false) {
845 $result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
846 break;
849 if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
850 $result['warning'] = $this->error($dstVolume->error());
851 break;
854 $result['added'][] = $file;
856 return $result;
860 * Return file content
862 * @param array $args command arguments
863 * @return array
864 * @author Dmitry (dio) Levashov
866 protected function get($args) {
867 $target = $args['target'];
868 $volume = $this->volume($target);
870 if (!$volume || ($file = $volume->file($target)) == false) {
871 return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
874 if (($content = $volume->getContents($target)) === false) {
875 return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
878 $json = json_encode($content);
880 if ($json == 'null' && strlen($json) < strlen($content)) {
881 return array('error' => $this->error(self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
884 return array('content' => $content);
888 * Save content into text file
890 * @return array
891 * @author Dmitry (dio) Levashov
893 protected function put($args) {
894 $target = $args['target'];
896 if (($volume = $this->volume($target)) == false
897 || ($file = $volume->file($target)) == false) {
898 return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
901 if (($file = $volume->putContents($target, $args['content'])) == false) {
902 return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
905 return array('changed' => array($file));
909 * Extract files from archive
911 * @param array $args command arguments
912 * @return array
913 * @author Dmitry (dio) Levashov,
914 * @author Alexey Sukhotin
916 protected function extract($args) {
917 $target = $args['target'];
918 $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
919 $error = array(self::ERROR_EXTRACT, '#'.$target);
921 if (($volume = $this->volume($target)) == false
922 || ($file = $volume->file($target)) == false) {
923 return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
926 return ($file = $volume->extract($target))
927 ? array('added' => array($file))
928 : array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
932 * Create archive
934 * @param array $args command arguments
935 * @return array
936 * @author Dmitry (dio) Levashov,
937 * @author Alexey Sukhotin
939 protected function archive($args) {
940 $type = $args['type'];
941 $targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
943 if (($volume = $this->volume($targets[0])) == false) {
944 return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
947 return ($file = $volume->archive($targets, $args['type']))
948 ? array('added' => array($file))
949 : array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
953 * Search files
955 * @param array $args command arguments
956 * @return array
957 * @author Dmitry Levashov
959 protected function search($args) {
960 $q = trim($args['q']);
961 $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
962 $result = array();
964 foreach ($this->volumes as $volume) {
965 $result = array_merge($result, $volume->search($q, $mimes));
968 return array('files' => $result);
972 * Return file info (used by client "places" ui)
974 * @param array $args command arguments
975 * @return array
976 * @author Dmitry Levashov
978 protected function info($args) {
979 $files = array();
981 foreach ($args['targets'] as $hash) {
982 if (($volume = $this->volume($hash)) != false
983 && ($info = $volume->file($hash)) != false) {
984 $files[] = $info;
988 return array('files' => $files);
992 * Return image dimmensions
994 * @param array $args command arguments
995 * @return array
996 * @author Dmitry (dio) Levashov
998 protected function dim($args) {
999 $target = $args['target'];
1001 if (($volume = $this->volume($target)) != false) {
1002 $dim = $volume->dimensions($target);
1003 return $dim ? array('dim' => $dim) : array();
1005 return array();
1009 * Resize image
1011 * @param array command arguments
1012 * @return array
1013 * @author Dmitry (dio) Levashov
1014 * @author Alexey Sukhotin
1016 protected function resize($args) {
1017 $target = $args['target'];
1018 $width = $args['width'];
1019 $height = $args['height'];
1020 $x = (int)$args['x'];
1021 $y = (int)$args['y'];
1022 $mode = $args['mode'];
1023 $bg = null;
1024 $degree = (int)$args['degree'];
1026 if (($volume = $this->volume($target)) == false
1027 || ($file = $volume->file($target)) == false) {
1028 return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1031 return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree))
1032 ? array('changed' => array($file))
1033 : array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
1036 /***************************************************************************/
1037 /* utils */
1038 /***************************************************************************/
1041 * Return root - file's owner
1043 * @param string file hash
1044 * @return elFinderStorageDriver
1045 * @author Dmitry (dio) Levashov
1047 protected function volume($hash) {
1048 foreach ($this->volumes as $id => $v) {
1049 if (strpos(''.$hash, $id) === 0) {
1050 return $this->volumes[$id];
1053 return false;
1057 * Return files info array
1059 * @param array $data one file info or files info
1060 * @return array
1061 * @author Dmitry (dio) Levashov
1063 protected function toArray($data) {
1064 return isset($data['hash']) || !is_array($data) ? array($data) : $data;
1068 * Return fils hashes list
1070 * @param array $files files info
1071 * @return array
1072 * @author Dmitry (dio) Levashov
1074 protected function hashes($files) {
1075 $ret = array();
1076 foreach ($files as $file) {
1077 $ret[] = $file['hash'];
1079 return $ret;
1083 * Remove from files list hidden files and files with required mime types
1085 * @param array $files files info
1086 * @return array
1087 * @author Dmitry (dio) Levashov
1089 protected function filter($files) {
1090 foreach ($files as $i => $file) {
1091 if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
1092 unset($files[$i]);
1095 return array_merge($files, array());
1098 protected function utime() {
1099 $time = explode(" ", microtime());
1100 return (double)$time[1] + (double)$time[0];
1103 } // END class