drg_elfinder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/drg_elfinder_manifest.js +1 -0
  6. data/app/assets/javascripts/drg_elfinder.js +15 -0
  7. data/app/assets/javascripts/elfinder/Changelog +1283 -0
  8. data/app/assets/javascripts/elfinder/LICENSE.md +15 -0
  9. data/app/assets/javascripts/elfinder/README.md +216 -0
  10. data/app/assets/javascripts/elfinder/bower.json +28 -0
  11. data/app/assets/javascripts/elfinder/composer.json +39 -0
  12. data/app/assets/javascripts/elfinder/css/elfinder.full.css +5350 -0
  13. data/app/assets/javascripts/elfinder/css/elfinder.min.css +9 -0
  14. data/app/assets/javascripts/elfinder/css/theme.css +430 -0
  15. data/app/assets/javascripts/elfinder/elfinder.html +74 -0
  16. data/app/assets/javascripts/elfinder/elfinder.legacy.html +94 -0
  17. data/app/assets/javascripts/elfinder/img/arrows-active.png +0 -0
  18. data/app/assets/javascripts/elfinder/img/arrows-normal.png +0 -0
  19. data/app/assets/javascripts/elfinder/img/crop.gif +0 -0
  20. data/app/assets/javascripts/elfinder/img/dialogs.png +0 -0
  21. data/app/assets/javascripts/elfinder/img/edit_aceeditor.png +0 -0
  22. data/app/assets/javascripts/elfinder/img/edit_ckeditor.png +0 -0
  23. data/app/assets/javascripts/elfinder/img/edit_ckeditor5.png +0 -0
  24. data/app/assets/javascripts/elfinder/img/edit_codemirror.png +0 -0
  25. data/app/assets/javascripts/elfinder/img/edit_creativecloud.png +0 -0
  26. data/app/assets/javascripts/elfinder/img/edit_onlineconvert.png +0 -0
  27. data/app/assets/javascripts/elfinder/img/edit_pixlreditor.png +0 -0
  28. data/app/assets/javascripts/elfinder/img/edit_pixlrexpress.png +0 -0
  29. data/app/assets/javascripts/elfinder/img/edit_simplemde.png +0 -0
  30. data/app/assets/javascripts/elfinder/img/edit_tinymce.png +0 -0
  31. data/app/assets/javascripts/elfinder/img/edit_tuiimgedit.png +0 -0
  32. data/app/assets/javascripts/elfinder/img/edit_zohooffice.png +0 -0
  33. data/app/assets/javascripts/elfinder/img/editor-icons.png +0 -0
  34. data/app/assets/javascripts/elfinder/img/icons-big.png +0 -0
  35. data/app/assets/javascripts/elfinder/img/icons-big.svg +1 -0
  36. data/app/assets/javascripts/elfinder/img/icons-small.png +0 -0
  37. data/app/assets/javascripts/elfinder/img/logo.png +0 -0
  38. data/app/assets/javascripts/elfinder/img/progress.gif +0 -0
  39. data/app/assets/javascripts/elfinder/img/quicklook-bg.png +0 -0
  40. data/app/assets/javascripts/elfinder/img/quicklook-icons.png +0 -0
  41. data/app/assets/javascripts/elfinder/img/resize.png +0 -0
  42. data/app/assets/javascripts/elfinder/img/spinner-mini.gif +0 -0
  43. data/app/assets/javascripts/elfinder/img/toolbar.png +0 -0
  44. data/app/assets/javascripts/elfinder/img/trashmesh.png +0 -0
  45. data/app/assets/javascripts/elfinder/img/tui-icon-a.svg +235 -0
  46. data/app/assets/javascripts/elfinder/img/tui-icon-b.svg +224 -0
  47. data/app/assets/javascripts/elfinder/img/tui-icon-c.svg +224 -0
  48. data/app/assets/javascripts/elfinder/img/tui-icon-d.svg +224 -0
  49. data/app/assets/javascripts/elfinder/img/ui-icons_ffffff_256x240.png +0 -0
  50. data/app/assets/javascripts/elfinder/img/volume_icon_box.png +0 -0
  51. data/app/assets/javascripts/elfinder/img/volume_icon_box.svg +1 -0
  52. data/app/assets/javascripts/elfinder/img/volume_icon_dropbox.png +0 -0
  53. data/app/assets/javascripts/elfinder/img/volume_icon_dropbox.svg +1 -0
  54. data/app/assets/javascripts/elfinder/img/volume_icon_ftp.png +0 -0
  55. data/app/assets/javascripts/elfinder/img/volume_icon_ftp.svg +1 -0
  56. data/app/assets/javascripts/elfinder/img/volume_icon_googledrive.png +0 -0
  57. data/app/assets/javascripts/elfinder/img/volume_icon_googledrive.svg +1 -0
  58. data/app/assets/javascripts/elfinder/img/volume_icon_local.png +0 -0
  59. data/app/assets/javascripts/elfinder/img/volume_icon_local.svg +1 -0
  60. data/app/assets/javascripts/elfinder/img/volume_icon_network.png +0 -0
  61. data/app/assets/javascripts/elfinder/img/volume_icon_network.svg +1 -0
  62. data/app/assets/javascripts/elfinder/img/volume_icon_onedrive.png +0 -0
  63. data/app/assets/javascripts/elfinder/img/volume_icon_onedrive.svg +1 -0
  64. data/app/assets/javascripts/elfinder/img/volume_icon_sql.png +0 -0
  65. data/app/assets/javascripts/elfinder/img/volume_icon_sql.svg +1 -0
  66. data/app/assets/javascripts/elfinder/img/volume_icon_trash.png +0 -0
  67. data/app/assets/javascripts/elfinder/img/volume_icon_trash.svg +1 -0
  68. data/app/assets/javascripts/elfinder/img/volume_icon_zip.png +0 -0
  69. data/app/assets/javascripts/elfinder/img/volume_icon_zip.svg +1 -0
  70. data/app/assets/javascripts/elfinder/js/elfinder.full.js +36019 -0
  71. data/app/assets/javascripts/elfinder/js/elfinder.min.js +25 -0
  72. data/app/assets/javascripts/elfinder/js/extras/editors.default.js +2643 -0
  73. data/app/assets/javascripts/elfinder/js/extras/editors.default.min.js +2 -0
  74. data/app/assets/javascripts/elfinder/js/extras/quicklook.googledocs.js +75 -0
  75. data/app/assets/javascripts/elfinder/js/extras/quicklook.googledocs.min.js +1 -0
  76. data/app/assets/javascripts/elfinder/js/i18n/elfinder.LANG.js +587 -0
  77. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ar.js +580 -0
  78. data/app/assets/javascripts/elfinder/js/i18n/elfinder.bg.js +559 -0
  79. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ca.js +375 -0
  80. data/app/assets/javascripts/elfinder/js/i18n/elfinder.cs.js +581 -0
  81. data/app/assets/javascripts/elfinder/js/i18n/elfinder.da.js +580 -0
  82. data/app/assets/javascripts/elfinder/js/i18n/elfinder.de.js +582 -0
  83. data/app/assets/javascripts/elfinder/js/i18n/elfinder.el.js +374 -0
  84. data/app/assets/javascripts/elfinder/js/i18n/elfinder.es.js +546 -0
  85. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fa.js +580 -0
  86. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fallback.js +11 -0
  87. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fo.js +419 -0
  88. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fr.js +578 -0
  89. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fr_CA.js +580 -0
  90. data/app/assets/javascripts/elfinder/js/i18n/elfinder.he.js +375 -0
  91. data/app/assets/javascripts/elfinder/js/i18n/elfinder.hr.js +434 -0
  92. data/app/assets/javascripts/elfinder/js/i18n/elfinder.hu.js +580 -0
  93. data/app/assets/javascripts/elfinder/js/i18n/elfinder.id.js +498 -0
  94. data/app/assets/javascripts/elfinder/js/i18n/elfinder.it.js +552 -0
  95. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ja.js +581 -0
  96. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ko.js +582 -0
  97. data/app/assets/javascripts/elfinder/js/i18n/elfinder.nl.js +581 -0
  98. data/app/assets/javascripts/elfinder/js/i18n/elfinder.no.js +374 -0
  99. data/app/assets/javascripts/elfinder/js/i18n/elfinder.pl.js +580 -0
  100. data/app/assets/javascripts/elfinder/js/i18n/elfinder.pt_BR.js +580 -0
  101. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ro.js +417 -0
  102. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ru.js +582 -0
  103. data/app/assets/javascripts/elfinder/js/i18n/elfinder.si.js +537 -0
  104. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sk.js +581 -0
  105. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sl.js +374 -0
  106. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sr.js +374 -0
  107. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sv.js +375 -0
  108. data/app/assets/javascripts/elfinder/js/i18n/elfinder.tr.js +583 -0
  109. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ug_CN.js +374 -0
  110. data/app/assets/javascripts/elfinder/js/i18n/elfinder.uk.js +580 -0
  111. data/app/assets/javascripts/elfinder/js/i18n/elfinder.vi.js +579 -0
  112. data/app/assets/javascripts/elfinder/js/i18n/elfinder.zh_CN.js +585 -0
  113. data/app/assets/javascripts/elfinder/js/i18n/elfinder.zh_TW.js +582 -0
  114. data/app/assets/javascripts/elfinder/js/i18n/help/cs.html.js +10 -0
  115. data/app/assets/javascripts/elfinder/js/i18n/help/de.html.js +15 -0
  116. data/app/assets/javascripts/elfinder/js/i18n/help/en.html.js +10 -0
  117. data/app/assets/javascripts/elfinder/js/i18n/help/es.html.js +10 -0
  118. data/app/assets/javascripts/elfinder/js/i18n/help/ja.html.js +10 -0
  119. data/app/assets/javascripts/elfinder/js/i18n/help/ko.html.js +10 -0
  120. data/app/assets/javascripts/elfinder/js/i18n/help/pl.html.js +10 -0
  121. data/app/assets/javascripts/elfinder/js/i18n/help/ru.html.js +10 -0
  122. data/app/assets/javascripts/elfinder/js/i18n/help/sk.html.js +10 -0
  123. data/app/assets/javascripts/elfinder/js/i18n/help/tr.html.js +10 -0
  124. data/app/assets/javascripts/elfinder/js/proxy/elFinderSupportVer1.js +408 -0
  125. data/app/assets/javascripts/elfinder/js/worker/calcfilehash.js +20 -0
  126. data/app/assets/javascripts/elfinder/js/worker/quicklook.tiff.js +7 -0
  127. data/app/assets/javascripts/elfinder/js/worker/quicklook.unzip.js +55 -0
  128. data/app/assets/javascripts/elfinder/main.default.js +175 -0
  129. data/app/assets/javascripts/elfinder/package.json +13 -0
  130. data/app/assets/javascripts/elfinder/php/MySQLStorage.sql +47 -0
  131. data/app/assets/javascripts/elfinder/php/autoload.php +56 -0
  132. data/app/assets/javascripts/elfinder/php/connector.maximal.php-dist +449 -0
  133. data/app/assets/javascripts/elfinder/php/connector.minimal.php-dist +180 -0
  134. data/app/assets/javascripts/elfinder/php/editors/OnlineConvert/editor.php +113 -0
  135. data/app/assets/javascripts/elfinder/php/editors/ZipArchive/editor.php +12 -0
  136. data/app/assets/javascripts/elfinder/php/editors/ZohoOffice/editor.php +206 -0
  137. data/app/assets/javascripts/elfinder/php/editors/editor.php +79 -0
  138. data/app/assets/javascripts/elfinder/php/elFinder.class.php +5401 -0
  139. data/app/assets/javascripts/elfinder/php/elFinderConnector.class.php +380 -0
  140. data/app/assets/javascripts/elfinder/php/elFinderFlysystemGoogleDriveNetmount.php +380 -0
  141. data/app/assets/javascripts/elfinder/php/elFinderPlugin.php +113 -0
  142. data/app/assets/javascripts/elfinder/php/elFinderSession.php +335 -0
  143. data/app/assets/javascripts/elfinder/php/elFinderSessionInterface.php +57 -0
  144. data/app/assets/javascripts/elfinder/php/elFinderVolumeBox.class.php +1972 -0
  145. data/app/assets/javascripts/elfinder/php/elFinderVolumeDriver.class.php +7651 -0
  146. data/app/assets/javascripts/elfinder/php/elFinderVolumeDropbox.class.php +1464 -0
  147. data/app/assets/javascripts/elfinder/php/elFinderVolumeDropbox2.class.php +1516 -0
  148. data/app/assets/javascripts/elfinder/php/elFinderVolumeFTP.class.php +1810 -0
  149. data/app/assets/javascripts/elfinder/php/elFinderVolumeGoogleDrive.class.php +2163 -0
  150. data/app/assets/javascripts/elfinder/php/elFinderVolumeGroup.class.php +315 -0
  151. data/app/assets/javascripts/elfinder/php/elFinderVolumeLocalFileSystem.class.php +1477 -0
  152. data/app/assets/javascripts/elfinder/php/elFinderVolumeMySQL.class.php +1022 -0
  153. data/app/assets/javascripts/elfinder/php/elFinderVolumeOneDrive.class.php +2131 -0
  154. data/app/assets/javascripts/elfinder/php/elFinderVolumeSFTPphpseclib.class.php +838 -0
  155. data/app/assets/javascripts/elfinder/php/elFinderVolumeTrash.class.php +51 -0
  156. data/app/assets/javascripts/elfinder/php/elFinderVolumeTrashMySQL.class.php +51 -0
  157. data/app/assets/javascripts/elfinder/php/libs/GdBmp.php +473 -0
  158. data/app/assets/javascripts/elfinder/php/mime.types +781 -0
  159. data/app/assets/javascripts/elfinder/php/plugins/AutoResize/plugin.php +151 -0
  160. data/app/assets/javascripts/elfinder/php/plugins/AutoRotate/plugin.php +148 -0
  161. data/app/assets/javascripts/elfinder/php/plugins/Normalizer/plugin.php +204 -0
  162. data/app/assets/javascripts/elfinder/php/plugins/Sanitizer/plugin.php +157 -0
  163. data/app/assets/javascripts/elfinder/php/plugins/Watermark/logo.png +0 -0
  164. data/app/assets/javascripts/elfinder/php/plugins/Watermark/plugin.php +432 -0
  165. data/app/assets/javascripts/elfinder/php/plugins/WinRemoveTailDots/plugin.php +114 -0
  166. data/app/assets/javascripts/elfinder/php/resources/image.png +0 -0
  167. data/app/assets/javascripts/elfinder/php/resources/video.png +0 -0
  168. data/app/assets/javascripts/elfinder/sounds/rm.wav +0 -0
  169. data/app/controllers/dc_elfinder_controller.rb +63 -0
  170. data/app/models/drgcms_form_fields/elfinder.rb +85 -0
  171. data/changelog.md +0 -0
  172. data/lib/drg_elfinder/engine.rb +5 -0
  173. data/lib/drg_elfinder/version.rb +3 -0
  174. data/lib/drg_elfinder.rb +9 -0
  175. data/lib/el_finder/action.rb +19 -0
  176. data/lib/el_finder/base64.rb +24 -0
  177. data/lib/el_finder/connector.rb +625 -0
  178. data/lib/el_finder/image.rb +33 -0
  179. data/lib/el_finder/mime_type.rb +86 -0
  180. data/lib/el_finder/pathname.rb +185 -0
  181. data/lib/el_finder/version.rb +5 -0
  182. data/lib/el_finder.rb +8 -0
  183. data/lib/patches/patch_for_elfinder.rb +115 -0
  184. data/lib/tasks/drg_elfinder_tasks.rake +10 -0
  185. metadata +245 -0
@@ -0,0 +1,1516 @@
1
+ <?php
2
+
3
+ use Kunnu\Dropbox\Dropbox;
4
+ use Kunnu\Dropbox\DropboxApp;
5
+ use Kunnu\Dropbox\DropboxFile;
6
+ use Kunnu\Dropbox\Exceptions\DropboxClientException;
7
+ use Kunnu\Dropbox\Models\FileMetadata;
8
+ use Kunnu\Dropbox\Models\FolderMetadata;
9
+
10
+ /**
11
+ * Simple elFinder driver for Dropbox
12
+ * kunalvarma05/dropbox-php-sdk:0.1.5 or above.
13
+ *
14
+ * @author Naoki Sawada
15
+ **/
16
+ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
17
+ {
18
+ /**
19
+ * Driver id
20
+ * Must be started from letter and contains [a-z0-9]
21
+ * Used as part of volume id.
22
+ *
23
+ * @var string
24
+ **/
25
+ protected $driverId = 'db';
26
+
27
+ /**
28
+ * Dropbox service object.
29
+ *
30
+ * @var object
31
+ **/
32
+ protected $service = null;
33
+
34
+ /**
35
+ * Fetch options.
36
+ *
37
+ * @var string
38
+ */
39
+ private $FETCH_OPTIONS = [];
40
+
41
+ /**
42
+ * Directory for tmp files
43
+ * If not set driver will try to use tmbDir as tmpDir.
44
+ *
45
+ * @var string
46
+ **/
47
+ protected $tmp = '';
48
+
49
+ /**
50
+ * Net mount key.
51
+ *
52
+ * @var string
53
+ **/
54
+ public $netMountKey = '';
55
+
56
+ /**
57
+ * Constructor
58
+ * Extend options with required fields.
59
+ *
60
+ * @author Naoki Sawada
61
+ **/
62
+ public function __construct()
63
+ {
64
+ $opts = [
65
+ 'app_key' => '',
66
+ 'app_secret' => '',
67
+ 'access_token' => '',
68
+ 'aliasFormat' => '%s@Dropbox',
69
+ 'path' => '/',
70
+ 'separator' => '/',
71
+ 'acceptedName' => '#^[^\\\/]+$#',
72
+ 'rootCssClass' => 'elfinder-navbar-root-dropbox',
73
+ 'publishPermission' => [
74
+ 'requested_visibility' => 'public',
75
+ //'link_password' => '',
76
+ //'expires' => '',
77
+ ],
78
+ 'getThumbSize' => 'medium', // Available sizes: 'thumb', 'small', 'medium', 'large', 'huge'
79
+ ];
80
+ $this->options = array_merge($this->options, $opts);
81
+ $this->options['mimeDetect'] = 'internal';
82
+ }
83
+
84
+ /*********************************************************************/
85
+ /* ORIGINAL FUNCTIONS */
86
+ /*********************************************************************/
87
+
88
+ /**
89
+ * Get Parent ID, Item ID, Parent Path as an array from path.
90
+ *
91
+ * @param string $path
92
+ *
93
+ * @return array
94
+ */
95
+ protected function _db_splitPath($path)
96
+ {
97
+ $path = trim($path, '/');
98
+ if ($path === '') {
99
+ $dirname = '/';
100
+ $basename = '';
101
+ } else {
102
+ $pos = strrpos($path, '/');
103
+ if ($pos === false) {
104
+ $dirname = '/';
105
+ $basename = $path;
106
+ } else {
107
+ $dirname = '/' . substr($path, 0, $pos);
108
+ $basename = substr($path, $pos + 1);
109
+ }
110
+ }
111
+
112
+ return [$dirname, $basename];
113
+ }
114
+
115
+ /**
116
+ * Get dat(Dropbox metadata) from Dropbox.
117
+ *
118
+ * @param string $path
119
+ *
120
+ * @return boolean|object Dropbox metadata
121
+ */
122
+ private function _db_getFile($path)
123
+ {
124
+ if ($path === '/') {
125
+ return true;
126
+ }
127
+
128
+ $res = false;
129
+ try {
130
+ $file = $this->service->getMetadata($path, $this->FETCH_OPTIONS);
131
+ if ($file instanceof FolderMetadata || $file instanceof FileMetadata) {
132
+ $res = $file;
133
+ }
134
+
135
+ return $res;
136
+ } catch (DropboxClientException $e) {
137
+ return false;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Parse line from Dropbox metadata output and return file stat (array).
143
+ *
144
+ * @param object $raw line from ftp_rawlist() output
145
+ *
146
+ * @return array
147
+ * @author Naoki Sawada
148
+ **/
149
+ protected function _db_parseRaw($raw)
150
+ {
151
+ $stat = [];
152
+ $isFolder = false;
153
+ if ($raw === true) {
154
+ // root folder
155
+ $isFolder = true;
156
+ $stat['name'] = '';
157
+ $stat['iid'] = '0';
158
+ }
159
+
160
+ $data = [];
161
+ if (is_object($raw)) {
162
+ $isFolder = $raw instanceof FolderMetadata;
163
+ $data = $raw->getData();
164
+ } elseif (is_array($raw)) {
165
+ $isFolder = $raw['.tag'] === 'folder';
166
+ $data = $raw;
167
+ }
168
+
169
+ if (isset($data['path_lower'])) {
170
+ $stat['path'] = $data['path_lower'];
171
+ }
172
+
173
+ if (isset($data['name'])) {
174
+ $stat['name'] = $data['name'];
175
+ }
176
+
177
+ if (isset($data['id'])) {
178
+ $stat['iid'] = substr($data['id'], 3);
179
+ }
180
+
181
+ if ($isFolder) {
182
+ $stat['mime'] = 'directory';
183
+ $stat['size'] = 0;
184
+ $stat['ts'] = 0;
185
+ $stat['dirs'] = -1;
186
+ } else {
187
+ $stat['size'] = isset($data['size']) ? (int)$data['size'] : 0;
188
+ if (isset($data['server_modified'])) {
189
+ $stat['ts'] = strtotime($data['server_modified']);
190
+ } elseif (isset($data['client_modified'])) {
191
+ $stat['ts'] = strtotime($data['client_modified']);
192
+ } else {
193
+ $stat['ts'] = 0;
194
+ }
195
+ $stat['url'] = '1';
196
+ }
197
+
198
+ return $stat;
199
+ }
200
+
201
+ /**
202
+ * Get thumbnail from Dropbox.
203
+ *
204
+ * @param string $path
205
+ * @param string $size
206
+ *
207
+ * @return string | boolean
208
+ */
209
+ protected function _db_getThumbnail($path)
210
+ {
211
+ try {
212
+ return $this->service->getThumbnail($path, $this->options['getThumbSize'])->getContents();
213
+ } catch (DropboxClientException $e) {
214
+ return false;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Join dir name and file name(display name) and retur full path.
220
+ *
221
+ * @param string $dir
222
+ * @param string $displayName
223
+ *
224
+ * @return string
225
+ */
226
+ protected function _db_joinName($dir, $displayName)
227
+ {
228
+ return rtrim($dir, '/') . '/' . $displayName;
229
+ }
230
+
231
+ /**
232
+ * Get OAuth2 access token form OAuth1 tokens.
233
+ *
234
+ * @param string $app_key
235
+ * @param string $app_secret
236
+ * @param string $oauth1_token
237
+ * @param string $oauth1_secret
238
+ *
239
+ * @return string|false
240
+ */
241
+ public static function getTokenFromOauth1($app_key, $app_secret, $oauth1_token, $oauth1_secret)
242
+ {
243
+ $data = [
244
+ 'oauth1_token' => $oauth1_token,
245
+ 'oauth1_token_secret' => $oauth1_secret,
246
+ ];
247
+ $auth = base64_encode($app_key . ':' . $app_secret);
248
+
249
+ $ch = curl_init('https://api.dropboxapi.com/2/auth/token/from_oauth1');
250
+ curl_setopt($ch, CURLOPT_POST, true);
251
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
252
+ curl_setopt($ch, CURLOPT_HTTPHEADER, [
253
+ 'Content-Type: application/json',
254
+ 'Authorization: Basic ' . $auth,
255
+ ]);
256
+ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
257
+ $result = curl_exec($ch);
258
+ curl_close($ch);
259
+
260
+ $res = $result ? json_decode($result, true) : [];
261
+
262
+ return isset($res['oauth2_token']) ? $res['oauth2_token'] : false;
263
+ }
264
+
265
+ /*********************************************************************/
266
+ /* EXTENDED FUNCTIONS */
267
+ /*********************************************************************/
268
+
269
+ /**
270
+ * Prepare
271
+ * Call from elFinder::netmout() before volume->mount().
272
+ *
273
+ * @return array
274
+ * @author Naoki Sawada
275
+ **/
276
+ public function netmountPrepare($options)
277
+ {
278
+ if (empty($options['app_key']) && defined('ELFINDER_DROPBOX_APPKEY')) {
279
+ $options['app_key'] = ELFINDER_DROPBOX_APPKEY;
280
+ }
281
+ if (empty($options['app_secret']) && defined('ELFINDER_DROPBOX_APPSECRET')) {
282
+ $options['app_secret'] = ELFINDER_DROPBOX_APPSECRET;
283
+ }
284
+
285
+ if (!isset($options['pass'])) {
286
+ $options['pass'] = '';
287
+ }
288
+
289
+ try {
290
+ $app = new DropboxApp($options['app_key'], $options['app_secret']);
291
+ $dropbox = new Dropbox($app);
292
+ $authHelper = $dropbox->getAuthHelper();
293
+
294
+ if ($options['pass'] === 'reauth') {
295
+ $options['pass'] = '';
296
+ $this->session->set('Dropbox2AuthParams', [])->set('Dropbox2Tokens', []);
297
+ } elseif ($options['pass'] === 'dropbox2') {
298
+ $options['pass'] = '';
299
+ }
300
+
301
+ $options = array_merge($this->session->get('Dropbox2AuthParams', []), $options);
302
+
303
+ if (!isset($options['tokens'])) {
304
+ $options['tokens'] = $this->session->get('Dropbox2Tokens', []);
305
+ $this->session->remove('Dropbox2Tokens');
306
+ }
307
+ $aToken = $options['tokens'];
308
+ if (!is_array($aToken) || !isset($aToken['access_token'])) {
309
+ $aToken = [];
310
+ }
311
+
312
+ $service = null;
313
+ if ($aToken) {
314
+ try {
315
+ $dropbox->setAccessToken($aToken['access_token']);
316
+ $this->session->set('Dropbox2AuthParams', $options);
317
+ } catch (DropboxClientException $e) {
318
+ $aToken = [];
319
+ $options['tokens'] = [];
320
+ if ($options['user'] !== 'init') {
321
+ $this->session->set('Dropbox2AuthParams', $options);
322
+
323
+ return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
324
+ }
325
+ }
326
+ }
327
+
328
+ if ((isset($options['user']) && $options['user'] === 'init') || (isset($_GET['host']) && $_GET['host'] == '1')) {
329
+ if (empty($options['url'])) {
330
+ $options['url'] = elFinder::getConnectorUrl();
331
+ }
332
+
333
+ if (!empty($options['id'])) {
334
+ $callback = $options['url']
335
+ . (strpos($options['url'], '?') !== false? '&' : '?') . 'cmd=netmount&protocol=dropbox2&host=' . ($options['id'] === 'elfinder'? '1' : $options['id']);
336
+ }
337
+
338
+ $itpCare = isset($options['code']);
339
+ $code = $itpCare? $options['code'] : (isset($_GET['code'])? $_GET['code'] : '');
340
+ $state = $itpCare? $options['state'] : (isset($_GET['state'])? $_GET['state'] : '');
341
+ if (!$aToken && empty($code)) {
342
+ $url = $authHelper->getAuthUrl($callback);
343
+
344
+ $html = '<input id="elf-volumedriver-dropbox2-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button">';
345
+ $html .= '<script>
346
+ $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "dropbox2", mode: "makebtn", url: "' . $url . '"});
347
+ </script>';
348
+ if (empty($options['pass']) && $options['host'] !== '1') {
349
+ $options['pass'] = 'return';
350
+ $this->session->set('Dropbox2AuthParams', $options);
351
+
352
+ return ['exit' => true, 'body' => $html];
353
+ } else {
354
+ $out = [
355
+ 'node' => $options['id'],
356
+ 'json' => '{"protocol": "dropbox2", "mode": "makebtn", "body" : "' . str_replace($html, '"', '\\"') . '", "error" : "' . elFinder::ERROR_ACCESS_DENIED . '"}',
357
+ 'bind' => 'netmount',
358
+ ];
359
+
360
+ return ['exit' => 'callback', 'out' => $out];
361
+ }
362
+ } else {
363
+ if ($code && $state) {
364
+ if (!empty($options['id'])) {
365
+ // see https://github.com/kunalvarma05/dropbox-php-sdk/issues/115
366
+ $authHelper->getPersistentDataStore()->set('state', filter_var($state, FILTER_SANITIZE_STRING));
367
+ $tokenObj = $authHelper->getAccessToken($code, $state, $callback);
368
+ $options['tokens'] = [
369
+ 'access_token' => $tokenObj->getToken(),
370
+ 'uid' => $tokenObj->getUid(),
371
+ ];
372
+ unset($options['code'], $options['state']);
373
+ $this->session->set('Dropbox2Tokens', $options['tokens'])->set('Dropbox2AuthParams', $options);
374
+ $out = [
375
+ 'node' => $options['id'],
376
+ 'json' => '{"protocol": "dropbox2", "mode": "done", "reset": 1}',
377
+ 'bind' => 'netmount',
378
+ ];
379
+ } else {
380
+ $nodeid = ($_GET['host'] === '1')? 'elfinder' : $_GET['host'];
381
+ $out = [
382
+ 'node' => $nodeid,
383
+ 'json' => json_encode(array(
384
+ 'protocol' => 'dropbox2',
385
+ 'host' => $nodeid,
386
+ 'mode' => 'redirect',
387
+ 'options' => array(
388
+ 'id' => $nodeid,
389
+ 'code' => $code,
390
+ 'state' => $state
391
+ )
392
+ )),
393
+ 'bind' => 'netmount'
394
+ ];
395
+ }
396
+ if (!$itpCare) {
397
+ return array('exit' => 'callback', 'out' => $out);
398
+ } else {
399
+ return array('exit' => true, 'body' => $out['json']);
400
+ }
401
+ }
402
+ $path = $options['path'];
403
+ $folders = [];
404
+ $listFolderContents = $dropbox->listFolder($path);
405
+ $items = $listFolderContents->getItems();
406
+ foreach ($items as $item) {
407
+ $data = $item->getData();
408
+ if ($data['.tag'] === 'folder') {
409
+ $folders[$data['path_lower']] = $data['name'];
410
+ }
411
+ }
412
+ natcasesort($folders);
413
+
414
+ if ($options['pass'] === 'folders') {
415
+ return ['exit' => true, 'folders' => $folders];
416
+ }
417
+
418
+ $folders = ['/' => '/'] + $folders;
419
+ $folders = json_encode($folders);
420
+ $json = '{"protocol": "dropbox2", "mode": "done", "folders": ' . $folders . '}';
421
+ $options['pass'] = 'return';
422
+ $html = 'Dropbox.com';
423
+ $html .= '<script>
424
+ $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
425
+ </script>';
426
+ $this->session->set('Dropbox2AuthParams', $options);
427
+
428
+ return ['exit' => true, 'body' => $html];
429
+ }
430
+ }
431
+ } catch (DropboxClientException $e) {
432
+ $this->session->remove('Dropbox2AuthParams')->remove('Dropbox2Tokens');
433
+ if (empty($options['pass'])) {
434
+ return ['exit' => true, 'body' => '{msg:' . elFinder::ERROR_ACCESS_DENIED . '}' . ' ' . $e->getMessage()];
435
+ } else {
436
+ return ['exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]];
437
+ }
438
+ }
439
+
440
+ if (!$aToken) {
441
+ return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
442
+ }
443
+
444
+ if ($options['path'] === 'root') {
445
+ $options['path'] = '/';
446
+ }
447
+
448
+ try {
449
+ if ($options['path'] !== '/') {
450
+ $file = $dropbox->getMetadata($options['path']);
451
+ $name = $file->getName();
452
+ } else {
453
+ $name = 'root';
454
+ }
455
+ $options['alias'] = sprintf($this->options['aliasFormat'], $name);
456
+ } catch (DropboxClientException $e) {
457
+ return ['exit' => true, 'error' => $e->getMessage()];
458
+ }
459
+
460
+ foreach (['host', 'user', 'pass', 'id', 'offline'] as $key) {
461
+ unset($options[$key]);
462
+ }
463
+
464
+ return $options;
465
+ }
466
+
467
+ /**
468
+ * process of on netunmount
469
+ * Drop `Dropbox` & rm thumbs.
470
+ *
471
+ * @param array $options
472
+ *
473
+ * @return bool
474
+ */
475
+ public function netunmount($netVolumes, $key)
476
+ {
477
+ if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->driverId . '_' . $this->options['tokens']['uid'] . '*.png')) {
478
+ foreach ($tmbs as $file) {
479
+ unlink($file);
480
+ }
481
+ }
482
+
483
+ return true;
484
+ }
485
+
486
+ /*********************************************************************/
487
+ /* INIT AND CONFIGURE */
488
+ /*********************************************************************/
489
+
490
+ /**
491
+ * Prepare Dropbox connection
492
+ * Connect to remote server and check if credentials are correct, if so, store the connection id in $this->service.
493
+ *
494
+ * @return bool
495
+ * @author Naoki Sawada
496
+ **/
497
+ protected function init()
498
+ {
499
+ if (empty($this->options['app_key'])) {
500
+ if (defined('ELFINDER_DROPBOX_APPKEY') && ELFINDER_DROPBOX_APPKEY) {
501
+ $this->options['app_key'] = ELFINDER_DROPBOX_APPKEY;
502
+ } else {
503
+ return $this->setError('Required option "app_key" is undefined.');
504
+ }
505
+ }
506
+ if (empty($this->options['app_secret'])) {
507
+ if (defined('ELFINDER_DROPBOX_APPSECRET') && ELFINDER_DROPBOX_APPSECRET) {
508
+ $this->options['app_secret'] = ELFINDER_DROPBOX_APPSECRET;
509
+ } else {
510
+ return $this->setError('Required option "app_secret" is undefined.');
511
+ }
512
+ }
513
+ if (isset($this->options['tokens']) && is_array($this->options['tokens']) && !empty($this->options['tokens']['access_token'])) {
514
+ $this->options['access_token'] = $this->options['tokens']['access_token'];
515
+ }
516
+ if (!$this->options['access_token']) {
517
+ return $this->setError('Required option "access_token" or "refresh_token" is undefined.');
518
+ }
519
+
520
+ try {
521
+ // make net mount key for network mount
522
+ $aToken = $this->options['access_token'];
523
+ $this->netMountKey = md5($aToken . '-' . $this->options['path']);
524
+
525
+ $errors = [];
526
+ if ($this->needOnline && !$this->service) {
527
+ $app = new DropboxApp($this->options['app_key'], $this->options['app_secret'], $aToken);
528
+ $this->service = new Dropbox($app);
529
+ // to check access_token
530
+ $this->service->getCurrentAccount();
531
+ }
532
+ } catch (DropboxClientException $e) {
533
+ $errors[] = 'Dropbox error: ' . $e->getMessage();
534
+ } catch (Exception $e) {
535
+ $errors[] = $e->getMessage();
536
+ }
537
+
538
+ if ($this->needOnline && !$this->service) {
539
+ $errors[] = 'Dropbox Service could not be loaded.';
540
+ }
541
+
542
+ if ($errors) {
543
+ return $this->setError($errors);
544
+ }
545
+
546
+ // normalize root path
547
+ $this->options['path'] = strtolower($this->options['path']);
548
+ if ($this->options['path'] == 'root') {
549
+ $this->options['path'] = '/';
550
+ }
551
+ $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
552
+
553
+ if (empty($this->options['alias'])) {
554
+ $this->options['alias'] = sprintf($this->options['aliasFormat'], ($this->options['path'] === '/') ? 'Root' : $this->_basename($this->options['path']));
555
+ if (!empty($this->options['netkey'])) {
556
+ elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
557
+ }
558
+ }
559
+
560
+ $this->rootName = $this->options['alias'];
561
+
562
+ if (!empty($this->options['tmpPath'])) {
563
+ if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
564
+ $this->tmp = $this->options['tmpPath'];
565
+ }
566
+ }
567
+
568
+ if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
569
+ $this->tmp = $tmp;
570
+ }
571
+
572
+ // This driver dose not support `syncChkAsTs`
573
+ $this->options['syncChkAsTs'] = false;
574
+
575
+ // 'lsPlSleep' minmum 10 sec
576
+ $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
577
+
578
+ // enable command archive
579
+ $this->options['useRemoteArchive'] = true;
580
+
581
+ return true;
582
+ }
583
+
584
+ /**
585
+ * Configure after successfull mount.
586
+ *
587
+ * @author Naoki Sawada
588
+ * @throws elFinderAbortException
589
+ */
590
+ protected function configure()
591
+ {
592
+ parent::configure();
593
+
594
+ // fallback of $this->tmp
595
+ if (!$this->tmp && $this->tmbPathWritable) {
596
+ $this->tmp = $this->tmbPath;
597
+ }
598
+
599
+ if ($this->isMyReload()) {
600
+ //$this->_db_getDirectoryData(false);
601
+ }
602
+ }
603
+
604
+ /*********************************************************************/
605
+ /* FS API */
606
+ /*********************************************************************/
607
+
608
+ /**
609
+ * Close opened connection.
610
+ **/
611
+ public function umount()
612
+ {
613
+ }
614
+
615
+ /**
616
+ * Cache dir contents.
617
+ *
618
+ * @param string $path dir path
619
+ *
620
+ * @return
621
+ * @author Naoki Sawada
622
+ */
623
+ protected function cacheDir($path)
624
+ {
625
+ $this->dirsCache[$path] = [];
626
+ $hasDir = false;
627
+
628
+ $res = $this->service->listFolder($path, $this->FETCH_OPTIONS);
629
+
630
+ if ($res) {
631
+ $items = $res->getItems()->all();
632
+ foreach ($items as $raw) {
633
+ if ($stat = $this->_db_parseRaw($raw)) {
634
+ $mountPath = $this->_joinPath($path, $stat['name']);
635
+ $stat = $this->updateCache($mountPath, $stat);
636
+ if (empty($stat['hidden']) && $path !== $mountPath) {
637
+ if (!$hasDir && $stat['mime'] === 'directory') {
638
+ $hasDir = true;
639
+ }
640
+ $this->dirsCache[$path][] = $mountPath;
641
+ }
642
+ }
643
+ }
644
+ }
645
+
646
+ if (isset($this->sessionCache['subdirs'])) {
647
+ $this->sessionCache['subdirs'][$path] = $hasDir;
648
+ }
649
+
650
+ return $this->dirsCache[$path];
651
+ }
652
+
653
+ /**
654
+ * Recursive files search.
655
+ *
656
+ * @param string $path dir path
657
+ * @param string $q search string
658
+ * @param array $mimes
659
+ *
660
+ * @return array
661
+ * @throws elFinderAbortException
662
+ * @author Naoki Sawada
663
+ */
664
+ protected function doSearch($path, $q, $mimes)
665
+ {
666
+ if (!empty($this->doSearchCurrentQuery['matchMethod']) || $mimes) {
667
+ // has custom match method or mimes, use elFinderVolumeDriver::doSearch()
668
+ return parent::doSearch($path, $q, $mimes);
669
+ }
670
+
671
+ $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
672
+
673
+ $searchRes = $this->service->search($path, $q, ['start' => 0, 'max_results' => 1000]);
674
+ $items = $searchRes->getItems();
675
+ $more = $searchRes->hasMoreItems();
676
+ while ($more) {
677
+ if ($timeout && $timeout < time()) {
678
+ $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->_path($path));
679
+ break;
680
+ }
681
+ $searchRes = $this->service->search($path, $q, ['start' => $searchRes->getCursor(), 'max_results' => 1000]);
682
+ $more = $searchRes->hasMoreItems();
683
+ $items = $items->merge($searchRes->getItems());
684
+ }
685
+
686
+ $result = [];
687
+ foreach ($items as $raw) {
688
+ if ($stat = $this->_db_parseRaw($raw->getMetadata())) {
689
+ $stat = $this->updateCache($stat['path'], $stat);
690
+ if (empty($stat['hidden'])) {
691
+ $result[] = $stat;
692
+ }
693
+ }
694
+ }
695
+
696
+ return $result;
697
+ }
698
+
699
+ /**
700
+ * Copy file/recursive copy dir only in current volume.
701
+ * Return new file path or false.
702
+ *
703
+ * @param string $src source path
704
+ * @param string $dst destination dir path
705
+ * @param string $name new file name (optionaly)
706
+ *
707
+ * @return string|false
708
+ * @throws elFinderAbortException
709
+ * @author Naoki Sawada
710
+ */
711
+ protected function copy($src, $dst, $name)
712
+ {
713
+ $srcStat = $this->stat($src);
714
+ $target = $this->_joinPath($dst, $name);
715
+ $tgtStat = $this->stat($target);
716
+ if ($tgtStat) {
717
+ if ($srcStat['mime'] === 'directory') {
718
+ return parent::copy($src, $dst, $name);
719
+ } else {
720
+ $this->_unlink($target);
721
+ }
722
+ }
723
+ $this->clearcache();
724
+ if ($res = $this->_copy($src, $dst, $name)) {
725
+ $this->added[] = $this->stat($target);
726
+ $res = $target;
727
+ }
728
+
729
+ return $res;
730
+ }
731
+
732
+ /**
733
+ * Remove file/ recursive remove dir.
734
+ *
735
+ * @param string $path file path
736
+ * @param bool $force try to remove even if file locked
737
+ * @param bool $recursive
738
+ *
739
+ * @return bool
740
+ * @throws elFinderAbortException
741
+ * @author Naoki Sawada
742
+ */
743
+ protected function remove($path, $force = false, $recursive = false)
744
+ {
745
+ $stat = $this->stat($path);
746
+ $stat['realpath'] = $path;
747
+ $this->rmTmb($stat);
748
+ $this->clearcache();
749
+
750
+ if (empty($stat)) {
751
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
752
+ }
753
+
754
+ if (!$force && !empty($stat['locked'])) {
755
+ return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
756
+ }
757
+
758
+ if ($stat['mime'] == 'directory') {
759
+ if (!$recursive && !$this->_rmdir($path)) {
760
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path));
761
+ }
762
+ } else {
763
+ if (!$recursive && !$this->_unlink($path)) {
764
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path));
765
+ }
766
+ }
767
+
768
+ $this->removed[] = $stat;
769
+
770
+ return true;
771
+ }
772
+
773
+ /**
774
+ * Create thumnbnail and return it's URL on success.
775
+ *
776
+ * @param string $path file path
777
+ * @param $stat
778
+ *
779
+ * @return string|false
780
+ * @throws ImagickException
781
+ * @throws elFinderAbortException
782
+ * @author Naoki Sawada
783
+ */
784
+ protected function createTmb($path, $stat)
785
+ {
786
+ if (!$stat || !$this->canCreateTmb($path, $stat)) {
787
+ return false;
788
+ }
789
+
790
+ $name = $this->tmbname($stat);
791
+ $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
792
+
793
+ // copy image into tmbPath so some drivers does not store files on local fs
794
+ if (!$data = $this->_db_getThumbnail($path)) {
795
+ return false;
796
+ }
797
+ if (!file_put_contents($tmb, $data)) {
798
+ return false;
799
+ }
800
+
801
+ $tmbSize = $this->tmbSize;
802
+
803
+ if (($s = getimagesize($tmb)) == false) {
804
+ return false;
805
+ }
806
+
807
+ $result = true;
808
+
809
+ /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
810
+ if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
811
+ $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
812
+ } else {
813
+ if ($this->options['tmbCrop']) {
814
+
815
+ /* Resize and crop if image bigger than thumbnail */
816
+ if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
817
+ $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
818
+ }
819
+
820
+ if ($result && ($s = getimagesize($tmb)) != false) {
821
+ $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
822
+ $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
823
+ $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
824
+ }
825
+ } else {
826
+ $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
827
+ }
828
+
829
+ if ($result) {
830
+ $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
831
+
832
+ }
833
+ }
834
+
835
+ if (!$result) {
836
+ unlink($tmb);
837
+
838
+ return false;
839
+ }
840
+
841
+ return $name;
842
+ }
843
+
844
+ /**
845
+ * Return thumbnail file name for required file.
846
+ *
847
+ * @param array $stat file stat
848
+ *
849
+ * @return string
850
+ * @author Naoki Sawada
851
+ **/
852
+ protected function tmbname($stat)
853
+ {
854
+ $name = $this->driverId . '_';
855
+ if (isset($this->options['tokens']) && is_array($this->options['tokens'])) {
856
+ $name .= $this->options['tokens']['uid'];
857
+ }
858
+
859
+ return $name . md5($stat['iid']) . $stat['ts'] . '.png';
860
+ }
861
+
862
+ /**
863
+ * Return content URL (for netmout volume driver)
864
+ * If file.url == 1 requests from JavaScript client with XHR.
865
+ *
866
+ * @param string $hash file hash
867
+ * @param array $options options array
868
+ *
869
+ * @return bool|string
870
+ * @author Naoki Sawada
871
+ */
872
+ public function getContentUrl($hash, $options = [])
873
+ {
874
+ if (!empty($options['onetime']) && $this->options['onetimeUrl']) {
875
+ return parent::getContentUrl($hash, $options);
876
+ }
877
+ if (!empty($options['temporary'])) {
878
+ // try make temporary file
879
+ $url = parent::getContentUrl($hash, $options);
880
+ if ($url) {
881
+ return $url;
882
+ }
883
+ }
884
+ $file = $this->file($hash);
885
+ if (($file = $this->file($hash)) !== false && (!$file['url'] || $file['url'] == 1)) {
886
+ $path = $this->decode($hash);
887
+ $url = '';
888
+ try {
889
+ $res = $this->service->postToAPI('/sharing/list_shared_links', ['path' => $path, 'direct_only' => true])->getDecodedBody();
890
+ if ($res && !empty($res['links'])) {
891
+ foreach ($res['links'] as $link) {
892
+ if (isset($link['link_permissions'])
893
+ && isset($link['link_permissions']['requested_visibility'])
894
+ && $link['link_permissions']['requested_visibility']['.tag'] === $this->options['publishPermission']['requested_visibility']) {
895
+ $url = $link['url'];
896
+ break;
897
+ }
898
+ }
899
+ }
900
+ if (!$url) {
901
+ $res = $this->service->postToAPI('/sharing/create_shared_link_with_settings', ['path' => $path, 'settings' => $this->options['publishPermission']])->getDecodedBody();
902
+ if (isset($res['url'])) {
903
+ $url = $res['url'];
904
+ }
905
+ }
906
+ if ($url) {
907
+ $url = str_replace('www.dropbox.com', 'dl.dropboxusercontent.com', $url);
908
+ $url = str_replace('?dl=0', '', $url);
909
+
910
+ return $url;
911
+ }
912
+ } catch (DropboxClientException $e) {
913
+ return $this->setError('Dropbox error: ' . $e->getMessage());
914
+ }
915
+ }
916
+
917
+ return false;
918
+ }
919
+
920
+ /**
921
+ * Return debug info for client.
922
+ *
923
+ * @return array
924
+ **/
925
+ public function debug()
926
+ {
927
+ $res = parent::debug();
928
+ if (!empty($this->options['netkey']) && isset($this->options['tokens']) && !empty($this->options['tokens']['uid'])) {
929
+ $res['Dropbox uid'] = $this->options['tokens']['uid'];
930
+ $res['access_token'] = $this->options['tokens']['access_token'];
931
+ }
932
+
933
+ return $res;
934
+ }
935
+
936
+ /*********************** paths/urls *************************/
937
+
938
+ /**
939
+ * Return parent directory path.
940
+ *
941
+ * @param string $path file path
942
+ *
943
+ * @return string
944
+ * @author Naoki Sawada
945
+ **/
946
+ protected function _dirname($path)
947
+ {
948
+ list($dirname) = $this->_db_splitPath($path);
949
+
950
+ return $dirname;
951
+ }
952
+
953
+ /**
954
+ * Return file name.
955
+ *
956
+ * @param string $path file path
957
+ *
958
+ * @return string
959
+ * @author Naoki Sawada
960
+ **/
961
+ protected function _basename($path)
962
+ {
963
+ list(, $basename) = $this->_db_splitPath($path);
964
+
965
+ return $basename;
966
+ }
967
+
968
+ /**
969
+ * Join dir name and file name and retur full path.
970
+ *
971
+ * @param string $dir
972
+ * @param string $name
973
+ *
974
+ * @return string
975
+ * @author Dmitry (dio) Levashov
976
+ **/
977
+ protected function _joinPath($dir, $name)
978
+ {
979
+ return rtrim($dir, '/') . '/' . strtolower($name);
980
+ }
981
+
982
+ /**
983
+ * Return normalized path, this works the same as os.path.normpath() in Python.
984
+ *
985
+ * @param string $path path
986
+ *
987
+ * @return string
988
+ * @author Naoki Sawada
989
+ **/
990
+ protected function _normpath($path)
991
+ {
992
+ return '/' . ltrim($path, '/');
993
+ }
994
+
995
+ /**
996
+ * Return file path related to root dir.
997
+ *
998
+ * @param string $path file path
999
+ *
1000
+ * @return string
1001
+ * @author Dmitry (dio) Levashov
1002
+ **/
1003
+ protected function _relpath($path)
1004
+ {
1005
+ if ($path === $this->root) {
1006
+ return '';
1007
+ } else {
1008
+ return ltrim(substr($path, strlen($this->root)), '/');
1009
+ }
1010
+ }
1011
+
1012
+ /**
1013
+ * Convert path related to root dir into real path.
1014
+ *
1015
+ * @param string $path file path
1016
+ *
1017
+ * @return string
1018
+ * @author Naoki Sawada
1019
+ **/
1020
+ protected function _abspath($path)
1021
+ {
1022
+ if ($path === '/') {
1023
+ return $this->root;
1024
+ } else {
1025
+ return $this->_joinPath($this->root, $path);
1026
+ }
1027
+ }
1028
+
1029
+ /**
1030
+ * Return fake path started from root dir.
1031
+ *
1032
+ * @param string $path file path
1033
+ *
1034
+ * @return string
1035
+ * @author Naoki Sawada
1036
+ **/
1037
+ protected function _path($path)
1038
+ {
1039
+ $path = $this->_normpath(substr($path, strlen($this->root)));
1040
+
1041
+ return $path;
1042
+ }
1043
+
1044
+ /**
1045
+ * Return true if $path is children of $parent.
1046
+ *
1047
+ * @param string $path path to check
1048
+ * @param string $parent parent path
1049
+ *
1050
+ * @return bool
1051
+ * @author Naoki Sawada
1052
+ **/
1053
+ protected function _inpath($path, $parent)
1054
+ {
1055
+ return $path == $parent || strpos($path, $parent . '/') === 0;
1056
+ }
1057
+
1058
+ /***************** file stat ********************/
1059
+ /**
1060
+ * Return stat for given path.
1061
+ * Stat contains following fields:
1062
+ * - (int) size file size in b. required
1063
+ * - (int) ts file modification time in unix time. required
1064
+ * - (string) mime mimetype. required for folders, others - optionally
1065
+ * - (bool) read read permissions. required
1066
+ * - (bool) write write permissions. required
1067
+ * - (bool) locked is object locked. optionally
1068
+ * - (bool) hidden is object hidden. optionally
1069
+ * - (string) alias for symlinks - link target path relative to root path. optionally
1070
+ * - (string) target for symlinks - link target path. optionally.
1071
+ * If file does not exists - returns empty array or false.
1072
+ *
1073
+ * @param string $path file path
1074
+ *
1075
+ * @return array|false
1076
+ * @author Dmitry (dio) Levashov
1077
+ **/
1078
+ protected function _stat($path)
1079
+ {
1080
+ if ($raw = $this->_db_getFile($path)) {
1081
+ return $this->_db_parseRaw($raw);
1082
+ }
1083
+
1084
+ return false;
1085
+ }
1086
+
1087
+ /**
1088
+ * Return true if path is dir and has at least one childs directory.
1089
+ *
1090
+ * @param string $path dir path
1091
+ *
1092
+ * @return bool
1093
+ * @author Naoki Sawada
1094
+ **/
1095
+ protected function _subdirs($path)
1096
+ {
1097
+ $hasdir = false;
1098
+
1099
+ try {
1100
+ $res = $this->service->listFolder($path);
1101
+ if ($res) {
1102
+ $items = $res->getItems();
1103
+ foreach ($items as $raw) {
1104
+ if ($raw instanceof FolderMetadata) {
1105
+ $hasdir = true;
1106
+ break;
1107
+ }
1108
+ }
1109
+ }
1110
+ } catch (DropboxClientException $e) {
1111
+ $this->setError('Dropbox error: ' . $e->getMessage());
1112
+ }
1113
+
1114
+ return $hasdir;
1115
+ }
1116
+
1117
+ /**
1118
+ * Return object width and height
1119
+ * Ususaly used for images, but can be realize for video etc...
1120
+ *
1121
+ * @param string $path file path
1122
+ * @param string $mime file mime type
1123
+ *
1124
+ * @return string
1125
+ * @throws ImagickException
1126
+ * @throws elFinderAbortException
1127
+ * @author Naoki Sawada
1128
+ */
1129
+ protected function _dimensions($path, $mime)
1130
+ {
1131
+ if (strpos($mime, 'image') !== 0) {
1132
+ return '';
1133
+ }
1134
+ $ret = '';
1135
+
1136
+ if ($data = $this->_getContents($path)) {
1137
+ $tmp = $this->getTempFile();
1138
+ file_put_contents($tmp, $data);
1139
+ $size = getimagesize($tmp);
1140
+ if ($size) {
1141
+ $ret = array('dim' => $size[0] . 'x' . $size[1]);
1142
+ $srcfp = fopen($tmp, 'rb');
1143
+ $target = isset(elFinder::$currentArgs['target'])? elFinder::$currentArgs['target'] : '';
1144
+ if ($subImgLink = $this->getSubstituteImgLink($target, $size, $srcfp)) {
1145
+ $ret['url'] = $subImgLink;
1146
+ }
1147
+ }
1148
+ }
1149
+
1150
+ return $ret;
1151
+ }
1152
+
1153
+ /******************** file/dir content *********************/
1154
+
1155
+ /**
1156
+ * Return files list in directory.
1157
+ *
1158
+ * @param string $path dir path
1159
+ *
1160
+ * @return array
1161
+ * @author Naoki Sawada
1162
+ **/
1163
+ protected function _scandir($path)
1164
+ {
1165
+ return isset($this->dirsCache[$path])
1166
+ ? $this->dirsCache[$path]
1167
+ : $this->cacheDir($path);
1168
+ }
1169
+
1170
+ /**
1171
+ * Open file and return file pointer.
1172
+ *
1173
+ * @param string $path file path
1174
+ * @param bool $write open file for writing
1175
+ *
1176
+ * @return resource|false
1177
+ * @author Naoki Sawada
1178
+ **/
1179
+ protected function _fopen($path, $mode = 'rb')
1180
+ {
1181
+ if ($mode === 'rb' || $mode === 'r') {
1182
+ if ($link = $this->service->getTemporaryLink($path)) {
1183
+ $access_token = $this->service->getAccessToken();
1184
+ if ($access_token) {
1185
+ $data = array(
1186
+ 'target' => $link->getLink(),
1187
+ 'headers' => array('Authorization: Bearer ' . $access_token),
1188
+ );
1189
+
1190
+ // to support range request
1191
+ if (func_num_args() > 2) {
1192
+ $opts = func_get_arg(2);
1193
+ } else {
1194
+ $opts = array();
1195
+ }
1196
+ if (!empty($opts['httpheaders'])) {
1197
+ $data['headers'] = array_merge($opts['httpheaders'], $data['headers']);
1198
+ }
1199
+
1200
+ return elFinder::getStreamByUrl($data);
1201
+ }
1202
+ }
1203
+ }
1204
+
1205
+ return false;
1206
+ }
1207
+
1208
+ /**
1209
+ * Close opened file.
1210
+ *
1211
+ * @param resource $fp file pointer
1212
+ *
1213
+ * @return bool
1214
+ * @author Naoki Sawada
1215
+ **/
1216
+ protected function _fclose($fp, $path = '')
1217
+ {
1218
+ is_resource($fp) && fclose($fp);
1219
+ }
1220
+
1221
+ /******************** file/dir manipulations *************************/
1222
+
1223
+ /**
1224
+ * Create dir and return created dir path or false on failed.
1225
+ *
1226
+ * @param string $path parent dir path
1227
+ * @param string $name new directory name
1228
+ *
1229
+ * @return string|bool
1230
+ * @author Naoki Sawada
1231
+ **/
1232
+ protected function _mkdir($path, $name)
1233
+ {
1234
+ try {
1235
+ return $this->service->createFolder($this->_db_joinName($path, $name))->getPathLower();
1236
+ } catch (DropboxClientException $e) {
1237
+ return $this->setError('Dropbox error: ' . $e->getMessage());
1238
+ }
1239
+ }
1240
+
1241
+ /**
1242
+ * Create file and return it's path or false on failed.
1243
+ *
1244
+ * @param string $path parent dir path
1245
+ * @param string $name new file name
1246
+ *
1247
+ * @return string|bool
1248
+ * @author Naoki Sawada
1249
+ **/
1250
+ protected function _mkfile($path, $name)
1251
+ {
1252
+ return $this->_save($this->tmpfile(), $path, $name, []);
1253
+ }
1254
+
1255
+ /**
1256
+ * Create symlink. FTP driver does not support symlinks.
1257
+ *
1258
+ * @param string $target link target
1259
+ * @param string $path symlink path
1260
+ *
1261
+ * @return bool
1262
+ * @author Naoki Sawada
1263
+ **/
1264
+ protected function _symlink($target, $path, $name)
1265
+ {
1266
+ return false;
1267
+ }
1268
+
1269
+ /**
1270
+ * Copy file into another file.
1271
+ *
1272
+ * @param string $source source file path
1273
+ * @param string $targetDir target directory path
1274
+ * @param string $name new file name
1275
+ *
1276
+ * @return bool
1277
+ * @author Naoki Sawada
1278
+ **/
1279
+ protected function _copy($source, $targetDir, $name)
1280
+ {
1281
+ try {
1282
+ $this->service->copy($source, $this->_db_joinName($targetDir, $name))->getPathLower();
1283
+ } catch (DropboxClientException $e) {
1284
+ return $this->setError('Dropbox error: ' . $e->getMessage());
1285
+ }
1286
+
1287
+ return true;
1288
+ }
1289
+
1290
+ /**
1291
+ * Move file into another parent dir.
1292
+ * Return new file path or false.
1293
+ *
1294
+ * @param string $source source file path
1295
+ * @param string $target target dir path
1296
+ * @param string $name file name
1297
+ *
1298
+ * @return string|bool
1299
+ * @author Naoki Sawada
1300
+ **/
1301
+ protected function _move($source, $targetDir, $name)
1302
+ {
1303
+ try {
1304
+ return $this->service->move($source, $this->_db_joinName($targetDir, $name))->getPathLower();
1305
+ } catch (DropboxClientException $e) {
1306
+ return $this->setError('Dropbox error: ' . $e->getMessage());
1307
+ }
1308
+ }
1309
+
1310
+ /**
1311
+ * Remove file.
1312
+ *
1313
+ * @param string $path file path
1314
+ *
1315
+ * @return bool
1316
+ * @author Naoki Sawada
1317
+ **/
1318
+ protected function _unlink($path)
1319
+ {
1320
+ try {
1321
+ $this->service->delete($path);
1322
+
1323
+ return true;
1324
+ } catch (DropboxClientException $e) {
1325
+ return $this->setError('Dropbox error: ' . $e->getMessage());
1326
+ }
1327
+
1328
+ return true;
1329
+ }
1330
+
1331
+ /**
1332
+ * Remove dir.
1333
+ *
1334
+ * @param string $path dir path
1335
+ *
1336
+ * @return bool
1337
+ * @author Naoki Sawada
1338
+ **/
1339
+ protected function _rmdir($path)
1340
+ {
1341
+ return $this->_unlink($path);
1342
+ }
1343
+
1344
+ /**
1345
+ * Create new file and write into it from file pointer.
1346
+ * Return new file path or false on error.
1347
+ *
1348
+ * @param resource $fp file pointer
1349
+ * @param string $dir target dir path
1350
+ * @param string $name file name
1351
+ * @param array $stat file stat (required by some virtual fs)
1352
+ *
1353
+ * @return bool|string
1354
+ * @author Naoki Sawada
1355
+ **/
1356
+ protected function _save($fp, $path, $name, $stat)
1357
+ {
1358
+ try {
1359
+ $info = stream_get_meta_data($fp);
1360
+ if (empty($info['uri']) || preg_match('#^[a-z0-9.-]+://#', $info['uri'])) {
1361
+ if ($filepath = $this->getTempFile()) {
1362
+ $_fp = fopen($filepath, 'wb');
1363
+ stream_copy_to_stream($fp, $_fp);
1364
+ fclose($_fp);
1365
+ }
1366
+ } else {
1367
+ $filepath = $info['uri'];
1368
+ }
1369
+ $dropboxFile = new DropboxFile($filepath);
1370
+ if ($name === '') {
1371
+ $fullpath = $path;
1372
+ } else {
1373
+ $fullpath = $this->_db_joinName($path, $name);
1374
+ }
1375
+
1376
+ return $this->service->upload($dropboxFile, $fullpath, ['mode' => 'overwrite'])->getPathLower();
1377
+ } catch (DropboxClientException $e) {
1378
+ return $this->setError('Dropbox error: ' . $e->getMessage());
1379
+ }
1380
+ }
1381
+
1382
+ /**
1383
+ * Get file contents.
1384
+ *
1385
+ * @param string $path file path
1386
+ *
1387
+ * @return string|false
1388
+ * @author Naoki Sawada
1389
+ **/
1390
+ protected function _getContents($path)
1391
+ {
1392
+ $contents = '';
1393
+
1394
+ try {
1395
+ $file = $this->service->download($path);
1396
+ $contents = $file->getContents();
1397
+ } catch (Exception $e) {
1398
+ return $this->setError('Dropbox error: ' . $e->getMessage());
1399
+ }
1400
+
1401
+ return $contents;
1402
+ }
1403
+
1404
+ /**
1405
+ * Write a string to a file.
1406
+ *
1407
+ * @param string $path file path
1408
+ * @param string $content new file content
1409
+ *
1410
+ * @return bool
1411
+ * @author Naoki Sawada
1412
+ **/
1413
+ protected function _filePutContents($path, $content)
1414
+ {
1415
+ $res = false;
1416
+
1417
+ if ($local = $this->getTempFile($path)) {
1418
+ if (file_put_contents($local, $content, LOCK_EX) !== false
1419
+ && ($fp = fopen($local, 'rb'))) {
1420
+ clearstatcache();
1421
+ $name = '';
1422
+ $stat = $this->stat($path);
1423
+ if ($stat) {
1424
+ // keep real name
1425
+ $path = $this->_dirname($path);
1426
+ $name = $stat['name'];
1427
+ }
1428
+ $res = $this->_save($fp, $path, $name, []);
1429
+ fclose($fp);
1430
+ }
1431
+ file_exists($local) && unlink($local);
1432
+ }
1433
+
1434
+ return $res;
1435
+ }
1436
+
1437
+ /**
1438
+ * Detect available archivers.
1439
+ **/
1440
+ protected function _checkArchivers()
1441
+ {
1442
+ // die('Not yet implemented. (_checkArchivers)');
1443
+ return [];
1444
+ }
1445
+
1446
+ /**
1447
+ * chmod implementation.
1448
+ *
1449
+ * @return bool
1450
+ **/
1451
+ protected function _chmod($path, $mode)
1452
+ {
1453
+ return false;
1454
+ }
1455
+
1456
+ /**
1457
+ * Unpack archive.
1458
+ *
1459
+ * @param string $path archive path
1460
+ * @param array $arc archiver command and arguments (same as in $this->archivers)
1461
+ *
1462
+ * @return true
1463
+ * @author Dmitry (dio) Levashov
1464
+ * @author Alexey Sukhotin
1465
+ **/
1466
+ protected function _unpack($path, $arc)
1467
+ {
1468
+ die('Not yet implemented. (_unpack)');
1469
+ //return false;
1470
+ }
1471
+
1472
+ /**
1473
+ * Recursive symlinks search.
1474
+ *
1475
+ * @param string $path file/dir path
1476
+ *
1477
+ * @return bool
1478
+ * @author Dmitry (dio) Levashov
1479
+ **/
1480
+ protected function _findSymlinks($path)
1481
+ {
1482
+ die('Not yet implemented. (_findSymlinks)');
1483
+ }
1484
+
1485
+ /**
1486
+ * Extract files from archive.
1487
+ *
1488
+ * @param string $path archive path
1489
+ * @param array $arc archiver command and arguments (same as in $this->archivers)
1490
+ *
1491
+ * @return true
1492
+ * @author Dmitry (dio) Levashov,
1493
+ * @author Alexey Sukhotin
1494
+ **/
1495
+ protected function _extract($path, $arc)
1496
+ {
1497
+ die('Not yet implemented. (_extract)');
1498
+ }
1499
+
1500
+ /**
1501
+ * Create archive and return its path.
1502
+ *
1503
+ * @param string $dir target dir
1504
+ * @param array $files files names list
1505
+ * @param string $name archive name
1506
+ * @param array $arc archiver options
1507
+ *
1508
+ * @return string|bool
1509
+ * @author Dmitry (dio) Levashov,
1510
+ * @author Alexey Sukhotin
1511
+ **/
1512
+ protected function _archive($dir, $files, $name, $arc)
1513
+ {
1514
+ die('Not yet implemented. (_archive)');
1515
+ }
1516
+ } // END class