elfinder.drupal.inc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <?php
  2. /**
  3. * elFinder Integration
  4. *
  5. * Copyright (c) 2010-2019, Alexey Sukhotin. All rights reserved.
  6. */
  7. /**
  8. * @file
  9. *
  10. * elFinder conenctor class
  11. */
  12. class elFinderDrupal extends elFinder {
  13. protected $elFinderDrupalLibVersion;
  14. protected $elFinderDrupalLibRevision;
  15. public function __construct($opts) {
  16. if (is_callable(array($this, 'version'))) {
  17. $this->elFinderDrupalLibVersion = floatval($this->version());
  18. if (isset(elFinder::$ApiVersion)) {
  19. $this->elFinderDrupalLibVersion = elFinder::$ApiVersion;
  20. }
  21. if (is_callable(array($this, 'revision'))) {
  22. $this->elFinderDrupalLibRevision = $this->revision();
  23. }
  24. if ($this->elFinderDrupalLibVersion == 2.1 && is_callable(array($this, 'revision')) && $this->revision() < 37) {
  25. $this->connector_unsupported_error();
  26. }
  27. parent::__construct($opts);
  28. $this->commands['desc'] = array('target' => TRUE, 'content' => FALSE);
  29. $this->commands['owner'] = array('target' => TRUE, 'content' => FALSE);
  30. $this->commands['downloadcount'] = array('target' => TRUE);
  31. } else {
  32. $this->connector_unsupported_error();
  33. }
  34. }
  35. public function connector_unsupported_error() {
  36. $this->connector_error(t('Unsupported elFinder library version. Please upgrade.'));
  37. }
  38. public function connector_error($message) {
  39. exit(drupal_json_encode(array('error' => array(strip_tags($message)))));
  40. }
  41. /* Overriding search query argument name 'q' since it's already used in Drupal */
  42. public function commandArgsList($cmd) {
  43. $this->commands['search']['elfinder_search_q'] = TRUE;
  44. return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
  45. }
  46. protected function search($args) {
  47. $q = trim($args['elfinder_search_q']);
  48. $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
  49. $target = !empty($args['target']) ? $args['target'] : null;
  50. $result = array();
  51. $errors = array();
  52. if ($target) {
  53. if ($volume = $this->volume($target)) {
  54. $result = $volume->search($q, $mimes, $target);
  55. $errors = array_merge($errors, $volume->error());
  56. }
  57. } else {
  58. foreach ($this->volumes as $volume) {
  59. $result = array_merge($result, $volume->search($q, $mimes));
  60. $errors = array_merge($errors, $volume->error());
  61. }
  62. }
  63. // Exclude hidden folders from search results.
  64. $hidden_folders = variable_get('elfinder_settings_misc_hidden_folders', '');
  65. if($hidden_folders) {
  66. $hidden_folders = explode(',', $hidden_folders);
  67. foreach($result AS $key => $file) {
  68. $parts = explode('/', $file['path']);
  69. if(in_array($parts[1], $hidden_folders)) {
  70. unset($result[$key]);
  71. }
  72. }
  73. }
  74. $result = array('files' => $result);
  75. if ($errors) {
  76. $result['warning'] = $errors;
  77. }
  78. return $result;
  79. }
  80. protected function desc($args) {
  81. $target = $args['target'];
  82. $desc = $args['content'];
  83. $error = array(self::ERROR_UNKNOWN, '#' . $target);
  84. if (($volume = $this->volume($target)) == FALSE
  85. || ($file = $volume->file($target)) == FALSE) {
  86. return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND));
  87. }
  88. $error[1] = $file['name'];
  89. if ($volume->driverId() == 'f') {
  90. return array('desc' => '');
  91. }
  92. if ($volume->commandDisabled('desc')) {
  93. return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED));
  94. }
  95. if (($desc = $volume->desc($target, $desc)) == -1) {
  96. return array('error' => $this->error($error, $volume->error()));
  97. }
  98. return array('desc' => $desc);
  99. }
  100. protected function owner($args) {
  101. $target = $args['target'];
  102. $error = array(self::ERROR_UNKNOWN, '#' . $target);
  103. if (($volume = $this->volume($target)) == FALSE
  104. || ($file = $volume->file($target)) == FALSE) {
  105. return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND));
  106. }
  107. $error[1] = $file['name'];
  108. if ($volume->driverId() == 'f') {
  109. return array('owner' => '');
  110. }
  111. if ($volume->commandDisabled('owner')) {
  112. return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED));
  113. }
  114. if (($owner = $volume->owner($target)) == FALSE) {
  115. return array('error' => $this->error($error, $volume->error()));
  116. }
  117. return array('owner' => $owner);
  118. }
  119. protected function downloadcount($args) {
  120. $target = $args['target'];
  121. $error = array(self::ERROR_UNKNOWN, '#' . $target);
  122. if (($volume = $this->volume($target)) == FALSE
  123. || ($file = $volume->file($target)) == FALSE) {
  124. return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND));
  125. }
  126. $error[1] = $file['name'];
  127. if ($volume->driverId() == 'f') {
  128. return array('downloadcount' => '');
  129. }
  130. if ($volume->commandDisabled('downloadcount')) {
  131. return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED));
  132. }
  133. if (($downloadcount = $volume->downloadcount($target)) == FALSE) {
  134. return array('error' => $this->error($error, $volume->error()));
  135. }
  136. return array('downloadcount' => $downloadcount);
  137. }
  138. /**
  139. * Required to output file in browser when volume URL is not set
  140. * Return array contains opened file pointer, root itself and required headers
  141. *
  142. * @param array command arguments
  143. * @return array
  144. * @author Dmitry (dio) Levashov
  145. * */
  146. protected function file($args) {
  147. if ($this->elFinderDrupalLibVersion >= 2.1 && $this->elFinderDrupalLibRevision > 38) {
  148. return parent::file($args);
  149. }
  150. $target = $args['target'];
  151. $download = !empty($args['download']);
  152. $h403 = 'HTTP/1.x 403 Access Denied';
  153. $h404 = 'HTTP/1.x 404 Not Found';
  154. if (($volume = $this->volume($target)) == FALSE) {
  155. return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE);
  156. }
  157. if (($file = $volume->file($target)) == FALSE) {
  158. return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE);
  159. }
  160. if (!$file['read']) {
  161. return array('error' => self::$errors[self::ERROR_ACCESS_DENIED], 'header' => $h403, 'raw' => TRUE);
  162. }
  163. if ($volume->driverId() != 'f' && (($fp = $volume->open($target)) == FALSE)) {
  164. return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE);
  165. }
  166. $mime = ($download) ? 'application/octet-stream' : $file['mime'];
  167. $result = array(
  168. 'volume' => $volume,
  169. 'pointer' => $fp,
  170. 'info' => $file,
  171. 'header' => array(
  172. "Content-Type: " . $mime,
  173. "Content-Disposition: " . $this->GetContentDisposition($file['name'], $mime, $download),
  174. "Content-Location: " . $file['name'],
  175. 'Content-Transfer-Encoding: binary',
  176. "Content-Length: " . $file['size'],
  177. "Connection: close"
  178. )
  179. );
  180. $real_path = $this->realpath($target);
  181. module_invoke_all('file_download', $volume->drupalpathtouri($real_path));
  182. return $result;
  183. }
  184. /**
  185. * Generating Content-Disposition HTTP header
  186. *
  187. * @param string $file Filename
  188. * @param string $filemime MIME Type
  189. * @param bool $download Disposition type (true = download file, false = open file in browser)
  190. * @return string
  191. * @author Dmitry (dio) Levashov, Alexey Sukhotin
  192. * */
  193. public static function GetContentDisposition($file, $filemime, $download = FALSE) {
  194. $disp = '';
  195. $filename = $file;
  196. $ua = $_SERVER["HTTP_USER_AGENT"];
  197. $mime = $filemime;
  198. if ($download) {
  199. $disp = 'attachment';
  200. $mime = 'application/octet-stream';
  201. } else {
  202. $disp = preg_match('/^(image|text)/i', $mime)
  203. || $mime == 'application/x-shockwave-flash' ? 'inline' : 'attachment';
  204. }
  205. $disp .= '; ';
  206. if (preg_match("/MSIE ([0-9]{1,}[\.0-9]{0,})/", $ua)) {
  207. $filename = rawurlencode($filename);
  208. $filename = str_replace("+", "%20", $filename);
  209. //$filename = str_replace(" ", "%20", $filename);
  210. $disp .= "filename=" . $filename;
  211. } elseif (preg_match("/Firefox\/(\d+)/", $ua, $m)) {
  212. if ($m[1] >= 8) {
  213. $disp .= "filename*=?UTF-8''" . rawurlencode($filename);
  214. } else {
  215. $disp .= "filename*=\"?UTF-8''" . rawurlencode($filename) . "\";";
  216. }
  217. } elseif (preg_match("/Chrome\/(\d+)/", $ua, $m)) {
  218. $disp .= "filename=\"" . $filename . "\"";
  219. } else {
  220. $disp .= "filename=" . $filename;
  221. }
  222. return $disp;
  223. }
  224. }