drg_elfinder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,1972 @@
1
+ <?php
2
+
3
+ /**
4
+ * Simple elFinder driver for BoxDrive
5
+ * Box.com API v2.0.
6
+ *
7
+ * @author Dmitry (dio) Levashov
8
+ * @author Cem (discofever)
9
+ **/
10
+ class elFinderVolumeBox extends elFinderVolumeDriver
11
+ {
12
+ /**
13
+ * Driver id
14
+ * Must be started from letter and contains [a-z0-9]
15
+ * Used as part of volume id.
16
+ *
17
+ * @var string
18
+ **/
19
+ protected $driverId = 'bd';
20
+
21
+ /**
22
+ * @var string The base URL for API requests
23
+ */
24
+ const API_URL = 'https://api.box.com/2.0';
25
+
26
+ /**
27
+ * @var string The base URL for authorization requests
28
+ */
29
+ const AUTH_URL = 'https://account.box.com/api/oauth2/authorize';
30
+
31
+ /**
32
+ * @var string The base URL for token requests
33
+ */
34
+ const TOKEN_URL = 'https://api.box.com/oauth2/token';
35
+
36
+ /**
37
+ * @var string The base URL for upload requests
38
+ */
39
+ const UPLOAD_URL = 'https://upload.box.com/api/2.0';
40
+
41
+ /**
42
+ * Fetch fields list.
43
+ *
44
+ * @var string
45
+ */
46
+ const FETCHFIELDS = 'type,id,name,created_at,modified_at,description,size,parent,permissions,file_version,shared_link';
47
+
48
+ /**
49
+ * Box.com token object.
50
+ *
51
+ * @var object
52
+ **/
53
+ protected $token = null;
54
+
55
+ /**
56
+ * Directory for tmp files
57
+ * If not set driver will try to use tmbDir as tmpDir.
58
+ *
59
+ * @var string
60
+ **/
61
+ protected $tmp = '';
62
+
63
+ /**
64
+ * Net mount key.
65
+ *
66
+ * @var string
67
+ **/
68
+ public $netMountKey = '';
69
+
70
+ /**
71
+ * Thumbnail prefix.
72
+ *
73
+ * @var string
74
+ **/
75
+ private $tmbPrefix = '';
76
+
77
+ /**
78
+ * Path to access token file for permanent mount
79
+ *
80
+ * @var string
81
+ */
82
+ private $aTokenFile = '';
83
+
84
+ /**
85
+ * hasCache by folders.
86
+ *
87
+ * @var array
88
+ **/
89
+ protected $HasdirsCache = array();
90
+
91
+ /**
92
+ * Constructor
93
+ * Extend options with required fields.
94
+ *
95
+ * @author Dmitry (dio) Levashov
96
+ * @author Cem (DiscoFever)
97
+ **/
98
+ public function __construct()
99
+ {
100
+ $opts = array(
101
+ 'client_id' => '',
102
+ 'client_secret' => '',
103
+ 'accessToken' => '',
104
+ 'root' => 'Box.com',
105
+ 'path' => '/',
106
+ 'separator' => '/',
107
+ 'tmbPath' => '',
108
+ 'tmbURL' => '',
109
+ 'tmpPath' => '',
110
+ 'acceptedName' => '#^[^\\\/]+$#',
111
+ 'rootCssClass' => 'elfinder-navbar-root-box',
112
+ );
113
+ $this->options = array_merge($this->options, $opts);
114
+ $this->options['mimeDetect'] = 'internal';
115
+ }
116
+
117
+ /*********************************************************************/
118
+ /* ORIGINAL FUNCTIONS */
119
+ /*********************************************************************/
120
+
121
+ /**
122
+ * Get Parent ID, Item ID, Parent Path as an array from path.
123
+ *
124
+ * @param string $path
125
+ *
126
+ * @return array
127
+ */
128
+ protected function _bd_splitPath($path)
129
+ {
130
+ $path = trim($path, '/');
131
+ $pid = '';
132
+ if ($path === '') {
133
+ $id = '0';
134
+ $parent = '';
135
+ } else {
136
+ $paths = explode('/', trim($path, '/'));
137
+ $id = array_pop($paths);
138
+ if ($paths) {
139
+ $parent = '/' . implode('/', $paths);
140
+ $pid = array_pop($paths);
141
+ } else {
142
+ $pid = '0';
143
+ $parent = '/';
144
+ }
145
+ }
146
+
147
+ return array($pid, $id, $parent);
148
+ }
149
+
150
+ /**
151
+ * Obtains a new access token from OAuth. This token is valid for one hour.
152
+ *
153
+ * @param string $clientSecret The Box client secret
154
+ * @param string $code The code returned by Box after
155
+ * successful log in
156
+ * @param string $redirectUri Must be the same as the redirect URI passed
157
+ * to LoginUrl
158
+ *
159
+ * @return bool|object
160
+ * @throws \Exception Thrown if this Client instance's clientId is not set
161
+ * @throws \Exception Thrown if the redirect URI of this Client instance's
162
+ * state is not set
163
+ */
164
+ protected function _bd_obtainAccessToken($client_id, $client_secret, $code)
165
+ {
166
+ if (null === $client_id) {
167
+ return $this->setError('The client ID must be set to call obtainAccessToken()');
168
+ }
169
+
170
+ if (null === $client_secret) {
171
+ return $this->setError('The client Secret must be set to call obtainAccessToken()');
172
+ }
173
+
174
+ if (null === $code) {
175
+ return $this->setError('Authorization code must be set to call obtainAccessToken()');
176
+ }
177
+
178
+ $url = self::TOKEN_URL;
179
+
180
+ $curl = curl_init();
181
+
182
+ $fields = http_build_query(
183
+ array(
184
+ 'client_id' => $client_id,
185
+ 'client_secret' => $client_secret,
186
+ 'code' => $code,
187
+ 'grant_type' => 'authorization_code',
188
+ )
189
+ );
190
+
191
+ curl_setopt_array($curl, array(
192
+ // General options.
193
+ CURLOPT_RETURNTRANSFER => true,
194
+ CURLOPT_POST => true,
195
+ CURLOPT_POSTFIELDS => $fields,
196
+ CURLOPT_URL => $url,
197
+ ));
198
+
199
+ $decoded = $this->_bd_curlExec($curl, true, array('Content-Length: ' . strlen($fields)));
200
+
201
+ $res = (object)array(
202
+ 'expires' => time() + $decoded->expires_in - 30,
203
+ 'initialToken' => '',
204
+ 'data' => $decoded
205
+ );
206
+ if (!empty($decoded->refresh_token)) {
207
+ $res->initialToken = md5($client_id . $decoded->refresh_token);
208
+ }
209
+ return $res;
210
+ }
211
+
212
+ /**
213
+ * Get token and auto refresh.
214
+ *
215
+ * @return true|string error message
216
+ * @throws Exception
217
+ */
218
+ protected function _bd_refreshToken()
219
+ {
220
+ if (!property_exists($this->token, 'expires') || $this->token->expires < time()) {
221
+ if (!$this->options['client_id']) {
222
+ $this->options['client_id'] = ELFINDER_BOX_CLIENTID;
223
+ }
224
+
225
+ if (!$this->options['client_secret']) {
226
+ $this->options['client_secret'] = ELFINDER_BOX_CLIENTSECRET;
227
+ }
228
+
229
+ if (empty($this->token->data->refresh_token)) {
230
+ throw new \Exception(elFinder::ERROR_REAUTH_REQUIRE);
231
+ } else {
232
+ $refresh_token = $this->token->data->refresh_token;
233
+ $initialToken = $this->_bd_getInitialToken();
234
+ }
235
+
236
+ $lock = '';
237
+ $aTokenFile = $this->aTokenFile? $this->aTokenFile : $this->_bd_getATokenFile();
238
+ if ($aTokenFile && is_file($aTokenFile)) {
239
+ $lock = $aTokenFile . '.lock';
240
+ if (file_exists($lock)) {
241
+ // Probably updating on other instance
242
+ return true;
243
+ }
244
+ touch($lock);
245
+ $GLOBALS['elFinderTempFiles'][$lock] = true;
246
+ }
247
+
248
+ $postData = array(
249
+ 'client_id' => $this->options['client_id'],
250
+ 'client_secret' => $this->options['client_secret'],
251
+ 'grant_type' => 'refresh_token',
252
+ 'refresh_token' => $refresh_token
253
+ );
254
+
255
+ $url = self::TOKEN_URL;
256
+
257
+ $curl = curl_init();
258
+
259
+ curl_setopt_array($curl, array(
260
+ // General options.
261
+ CURLOPT_RETURNTRANSFER => true,
262
+ CURLOPT_POST => true, // i am sending post data
263
+ CURLOPT_POSTFIELDS => http_build_query($postData),
264
+ CURLOPT_URL => $url,
265
+ ));
266
+
267
+ $decoded = $error = '';
268
+ try {
269
+ $decoded = $this->_bd_curlExec($curl, true, array(), $postData);
270
+ } catch (Exception $e) {
271
+ $error = $e->getMessage();
272
+ }
273
+ if (!$decoded && !$error) {
274
+ $error = 'Tried to renew the access token, but did not get a response from the Box server.';
275
+ }
276
+ if ($error) {
277
+ $lock && unlink($lock);
278
+ throw new \Exception('Box access token update failed. ('.$error.') If this message appears repeatedly, please notify the administrator.');
279
+ }
280
+
281
+ if (empty($decoded->access_token)) {
282
+ if ($aTokenFile) {
283
+ if (is_file($aTokenFile)) {
284
+ unlink($aTokenFile);
285
+ }
286
+ }
287
+ $err = property_exists($decoded, 'error')? ' ' . $decoded->error : '';
288
+ $err .= property_exists($decoded, 'error_description')? ' ' . $decoded->error_description : '';
289
+ throw new \Exception($err? $err : elFinder::ERROR_REAUTH_REQUIRE);
290
+ }
291
+
292
+ $token = (object)array(
293
+ 'expires' => time() + $decoded->expires_in - 300,
294
+ 'initialToken' => $initialToken,
295
+ 'data' => $decoded,
296
+ );
297
+
298
+ $this->token = $token;
299
+ $json = json_encode($token);
300
+
301
+ if (!empty($decoded->refresh_token)) {
302
+ if (empty($this->options['netkey']) && $aTokenFile) {
303
+ file_put_contents($aTokenFile, json_encode($token), LOCK_EX);
304
+ $this->options['accessToken'] = $json;
305
+ } else if (!empty($this->options['netkey'])) {
306
+ // OAuth2 refresh token can be used only once,
307
+ // so update it if it is the same as the token file
308
+ if ($aTokenFile && is_file($aTokenFile)) {
309
+ if ($_token = json_decode(file_get_contents($aTokenFile))) {
310
+ if ($_token->data->refresh_token === $refresh_token) {
311
+ file_put_contents($aTokenFile, $json, LOCK_EX);
312
+ }
313
+ }
314
+ }
315
+ $this->options['accessToken'] = $json;
316
+ // update session value
317
+ elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'accessToken', $json);
318
+ $this->session->set('BoxTokens', $token);
319
+ } else {
320
+ throw new \Exception(ERROR_CREATING_TEMP_DIR);
321
+ }
322
+ }
323
+ $lock && unlink($lock);
324
+ }
325
+
326
+ return true;
327
+ }
328
+
329
+ /**
330
+ * Creates a base cURL object which is compatible with the Box.com API.
331
+ *
332
+ * @param array $options cURL options
333
+ *
334
+ * @return resource A compatible cURL object
335
+ */
336
+ protected function _bd_prepareCurl($options = array())
337
+ {
338
+ $curl = curl_init();
339
+
340
+ $defaultOptions = array(
341
+ // General options.
342
+ CURLOPT_RETURNTRANSFER => true,
343
+ );
344
+
345
+ curl_setopt_array($curl, $options + $defaultOptions);
346
+
347
+ return $curl;
348
+ }
349
+
350
+ /**
351
+ * Creates a base cURL object which is compatible with the Box.com API.
352
+ *
353
+ * @param $url
354
+ * @param bool $contents
355
+ *
356
+ * @return boolean|array
357
+ * @throws Exception
358
+ */
359
+ protected function _bd_fetch($url, $contents = false)
360
+ {
361
+ $curl = curl_init($url);
362
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
363
+
364
+ if ($contents) {
365
+ return $this->_bd_curlExec($curl, false);
366
+ } else {
367
+ $result = $this->_bd_curlExec($curl);
368
+
369
+ if (isset($result->entries)) {
370
+ $res = $result->entries;
371
+ $cnt = count($res);
372
+ $total = $result->total_count;
373
+ $offset = $result->offset;
374
+ $single = ($result->limit == 1) ? true : false;
375
+ if (!$single && $total > ($offset + $cnt)) {
376
+ $offset = $offset + $cnt;
377
+ if (strpos($url, 'offset=') === false) {
378
+ $url .= '&offset=' . $offset;
379
+ } else {
380
+ $url = preg_replace('/^(.+?offset=)\d+(.*)$/', '${1}' . $offset . '$2', $url);
381
+ }
382
+ $more = $this->_bd_fetch($url);
383
+ if (is_array($more)) {
384
+ $res = array_merge($res, $more);
385
+ }
386
+ }
387
+
388
+ return $res;
389
+ } else {
390
+ if (isset($result->type) && $result->type === 'error') {
391
+ return false;
392
+ } else {
393
+ return $result;
394
+ }
395
+ }
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Call curl_exec().
401
+ *
402
+ * @param resource $curl
403
+ * @param bool|string $decodeOrParent
404
+ * @param array $headers
405
+ *
406
+ * @throws \Exception
407
+ * @return mixed
408
+ */
409
+ protected function _bd_curlExec($curl, $decodeOrParent = true, $headers = array(), $postData = array())
410
+ {
411
+ if ($this->token) {
412
+ $headers = array_merge(array(
413
+ 'Authorization: Bearer ' . $this->token->data->access_token,
414
+ ), $headers);
415
+ }
416
+
417
+ $result = elFinder::curlExec($curl, array(), $headers, $postData);
418
+
419
+ if (!$decodeOrParent) {
420
+ return $result;
421
+ }
422
+
423
+ $decoded = json_decode($result);
424
+
425
+ if ($error = !empty($decoded->error_code)) {
426
+ $errmsg = $decoded->error_code;
427
+ if (!empty($decoded->message)) {
428
+ $errmsg .= ': ' . $decoded->message;
429
+ }
430
+ throw new \Exception($errmsg);
431
+ } else if ($error = !empty($decoded->error)) {
432
+ $errmsg = $decoded->error;
433
+ if (!empty($decoded->error_description)) {
434
+ $errmsg .= ': ' . $decoded->error_description;
435
+ }
436
+ throw new \Exception($errmsg);
437
+ }
438
+
439
+ // make catch
440
+ if ($decodeOrParent && $decodeOrParent !== true) {
441
+ $raws = null;
442
+ if (isset($decoded->entries)) {
443
+ $raws = $decoded->entries;
444
+ } elseif (isset($decoded->id)) {
445
+ $raws = array($decoded);
446
+ }
447
+ if ($raws) {
448
+ foreach ($raws as $raw) {
449
+ if (isset($raw->id)) {
450
+ $stat = $this->_bd_parseRaw($raw);
451
+ $itemPath = $this->_joinPath($decodeOrParent, $raw->id);
452
+ $this->updateCache($itemPath, $stat);
453
+ }
454
+ }
455
+ }
456
+ }
457
+
458
+ return $decoded;
459
+ }
460
+
461
+ /**
462
+ * Drive query and fetchAll.
463
+ *
464
+ * @param $itemId
465
+ * @param bool $fetch_self
466
+ * @param bool $recursive
467
+ *
468
+ * @return bool|object
469
+ * @throws Exception
470
+ */
471
+ protected function _bd_query($itemId, $fetch_self = false, $recursive = false)
472
+ {
473
+ $result = [];
474
+
475
+ if (null === $itemId) {
476
+ $itemId = '0';
477
+ }
478
+
479
+ if ($fetch_self) {
480
+ $path = '/folders/' . $itemId . '?fields=' . self::FETCHFIELDS;
481
+ } else {
482
+ $path = '/folders/' . $itemId . '/items?limit=1000&fields=' . self::FETCHFIELDS;
483
+ }
484
+
485
+ $url = self::API_URL . $path;
486
+
487
+ if ($recursive) {
488
+ foreach ($this->_bd_fetch($url) as $file) {
489
+ if ($file->type == 'folder') {
490
+ $result[] = $file;
491
+ $result = array_merge($result, $this->_bd_query($file->id, $fetch_self = false, $recursive = true));
492
+ } elseif ($file->type == 'file') {
493
+ $result[] = $file;
494
+ }
495
+ }
496
+ } else {
497
+ $result = $this->_bd_fetch($url);
498
+ if ($fetch_self && !$result) {
499
+ $path = '/files/' . $itemId . '?fields=' . self::FETCHFIELDS;
500
+ $url = self::API_URL . $path;
501
+ $result = $this->_bd_fetch($url);
502
+ }
503
+ }
504
+
505
+ return $result;
506
+ }
507
+
508
+ /**
509
+ * Get dat(box metadata) from Box.com.
510
+ *
511
+ * @param string $path
512
+ *
513
+ * @return object box metadata
514
+ * @throws Exception
515
+ */
516
+ protected function _bd_getRawItem($path)
517
+ {
518
+ if ($path == '/') {
519
+ return $this->_bd_query('0', $fetch_self = true);
520
+ }
521
+
522
+ list(, $itemId) = $this->_bd_splitPath($path);
523
+
524
+ try {
525
+ return $this->_bd_query($itemId, $fetch_self = true);
526
+ } catch (Exception $e) {
527
+ $empty = new stdClass;
528
+ return $empty;
529
+ }
530
+ }
531
+
532
+ /**
533
+ * Parse line from box metadata output and return file stat (array).
534
+ *
535
+ * @param object $raw line from ftp_rawlist() output
536
+ *
537
+ * @return array
538
+ * @author Dmitry Levashov
539
+ **/
540
+ protected function _bd_parseRaw($raw)
541
+ {
542
+ $stat = array();
543
+
544
+ $stat['rev'] = isset($raw->id) ? $raw->id : 'root';
545
+ $stat['name'] = $raw->name;
546
+ if (!empty($raw->modified_at)) {
547
+ $stat['ts'] = strtotime($raw->modified_at);
548
+ }
549
+
550
+ if ($raw->type === 'folder') {
551
+ $stat['mime'] = 'directory';
552
+ $stat['size'] = 0;
553
+ $stat['dirs'] = -1;
554
+ } else {
555
+ $stat['size'] = (int)$raw->size;
556
+ if (!empty($raw->shared_link->url) && $raw->shared_link->access == 'open') {
557
+ if ($url = $this->getSharedWebContentLink($raw)) {
558
+ $stat['url'] = $url;
559
+ }
560
+ } elseif (!$this->disabledGetUrl) {
561
+ $stat['url'] = '1';
562
+ }
563
+ }
564
+
565
+ return $stat;
566
+ }
567
+
568
+ /**
569
+ * Get thumbnail from Box.com.
570
+ *
571
+ * @param string $path
572
+ * @param string $size
573
+ *
574
+ * @return string | boolean
575
+ */
576
+ protected function _bd_getThumbnail($path)
577
+ {
578
+ list(, $itemId) = $this->_bd_splitPath($path);
579
+
580
+ try {
581
+ $url = self::API_URL . '/files/' . $itemId . '/thumbnail.png?min_height=' . $this->tmbSize . '&min_width=' . $this->tmbSize;
582
+
583
+ $contents = $this->_bd_fetch($url, true);
584
+ return $contents;
585
+ } catch (Exception $e) {
586
+ return false;
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Remove item.
592
+ *
593
+ * @param string $path file path
594
+ *
595
+ * @return bool
596
+ **/
597
+ protected function _bd_unlink($path, $type = null)
598
+ {
599
+ try {
600
+ list(, $itemId) = $this->_bd_splitPath($path);
601
+
602
+ if ($type == 'folders') {
603
+ $url = self::API_URL . '/' . $type . '/' . $itemId . '?recursive=true';
604
+ } else {
605
+ $url = self::API_URL . '/' . $type . '/' . $itemId;
606
+ }
607
+
608
+ $curl = $this->_bd_prepareCurl(array(
609
+ CURLOPT_URL => $url,
610
+ CURLOPT_CUSTOMREQUEST => 'DELETE',
611
+ ));
612
+
613
+ //unlink or delete File or Folder in the Parent
614
+ $this->_bd_curlExec($curl);
615
+ } catch (Exception $e) {
616
+ return $this->setError('Box error: ' . $e->getMessage());
617
+ }
618
+
619
+ return true;
620
+ }
621
+
622
+ /**
623
+ * Get AccessToken file path
624
+ *
625
+ * @return string ( description_of_the_return_value )
626
+ */
627
+ protected function _bd_getATokenFile()
628
+ {
629
+ $tmp = $aTokenFile = '';
630
+ if (!empty($this->token->data->refresh_token)) {
631
+ if (!$this->tmp) {
632
+ $tmp = elFinder::getStaticVar('commonTempPath');
633
+ if (!$tmp) {
634
+ $tmp = $this->getTempPath();
635
+ }
636
+ $this->tmp = $tmp;
637
+ }
638
+ if ($tmp) {
639
+ $aTokenFile = $tmp . DIRECTORY_SEPARATOR . $this->_bd_getInitialToken() . '.btoken';
640
+ }
641
+ }
642
+ return $aTokenFile;
643
+ }
644
+
645
+ /**
646
+ * Get Initial Token (MD5 hash)
647
+ *
648
+ * @return string
649
+ */
650
+ protected function _bd_getInitialToken()
651
+ {
652
+ return (empty($this->token->initialToken)? md5($this->options['client_id'] . (!empty($this->token->data->refresh_token)? $this->token->data->refresh_token : $this->token->data->access_token)) : $this->token->initialToken);
653
+ }
654
+
655
+ /*********************************************************************/
656
+ /* OVERRIDE FUNCTIONS */
657
+ /*********************************************************************/
658
+
659
+ /**
660
+ * Prepare
661
+ * Call from elFinder::netmout() before volume->mount().
662
+ *
663
+ * @return array
664
+ * @author Naoki Sawada
665
+ * @author Raja Sharma updating for Box
666
+ **/
667
+ public function netmountPrepare($options)
668
+ {
669
+ if (empty($options['client_id']) && defined('ELFINDER_BOX_CLIENTID')) {
670
+ $options['client_id'] = ELFINDER_BOX_CLIENTID;
671
+ }
672
+ if (empty($options['client_secret']) && defined('ELFINDER_BOX_CLIENTSECRET')) {
673
+ $options['client_secret'] = ELFINDER_BOX_CLIENTSECRET;
674
+ }
675
+
676
+ if (isset($options['pass']) && $options['pass'] === 'reauth') {
677
+ $options['user'] = 'init';
678
+ $options['pass'] = '';
679
+ $this->session->remove('BoxTokens');
680
+ }
681
+
682
+ if (isset($options['id'])) {
683
+ $this->session->set('nodeId', $options['id']);
684
+ } else if ($_id = $this->session->get('nodeId')) {
685
+ $options['id'] = $_id;
686
+ $this->session->set('nodeId', $_id);
687
+ }
688
+
689
+ if (!empty($options['tmpPath'])) {
690
+ if ((is_dir($options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($options['tmpPath'])) {
691
+ $this->tmp = $options['tmpPath'];
692
+ }
693
+ }
694
+
695
+ try {
696
+ if (empty($options['client_id']) || empty($options['client_secret'])) {
697
+ return array('exit' => true, 'body' => '{msg:errNetMountNoDriver}');
698
+ }
699
+
700
+ $itpCare = isset($options['code']);
701
+ $code = $itpCare? $options['code'] : (isset($_GET['code'])? $_GET['code'] : '');
702
+ if ($code) {
703
+ try {
704
+ if (!empty($options['id'])) {
705
+ // Obtain the token using the code received by the Box.com API
706
+ $this->session->set('BoxTokens',
707
+ $this->_bd_obtainAccessToken($options['client_id'], $options['client_secret'], $code));
708
+
709
+ $out = array(
710
+ 'node' => $options['id'],
711
+ 'json' => '{"protocol": "box", "mode": "done", "reset": 1}',
712
+ 'bind' => 'netmount'
713
+ );
714
+ } else {
715
+ $nodeid = ($_GET['host'] === '1')? 'elfinder' : $_GET['host'];
716
+ $out = array(
717
+ 'node' => $nodeid,
718
+ 'json' => json_encode(array(
719
+ 'protocol' => 'box',
720
+ 'host' => $nodeid,
721
+ 'mode' => 'redirect',
722
+ 'options' => array(
723
+ 'id' => $nodeid,
724
+ 'code'=> $code
725
+ )
726
+ )),
727
+ 'bind' => 'netmount'
728
+ );
729
+ }
730
+ if (!$itpCare) {
731
+ return array('exit' => 'callback', 'out' => $out);
732
+ } else {
733
+ return array('exit' => true, 'body' => $out['json']);
734
+ }
735
+ } catch (Exception $e) {
736
+ $out = array(
737
+ 'node' => $options['id'],
738
+ 'json' => json_encode(array('error' => $e->getMessage())),
739
+ );
740
+
741
+ return array('exit' => 'callback', 'out' => $out);
742
+ }
743
+ } elseif (!empty($_GET['error'])) {
744
+ $out = array(
745
+ 'node' => $options['id'],
746
+ 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED)),
747
+ );
748
+
749
+ return array('exit' => 'callback', 'out' => $out);
750
+ }
751
+
752
+ if ($options['user'] === 'init') {
753
+ $this->token = $this->session->get('BoxTokens');
754
+
755
+ if ($this->token) {
756
+ try {
757
+ $this->_bd_refreshToken();
758
+ } catch (Exception $e) {
759
+ $this->setError($e->getMessage());
760
+ $this->token = null;
761
+ $this->session->remove('BoxTokens');
762
+ }
763
+ }
764
+
765
+ if (empty($this->token)) {
766
+ $result = false;
767
+ } else {
768
+ $path = $options['path'];
769
+ if ($path === '/' || $path === 'root') {
770
+ $path = '0';
771
+ }
772
+ $result = $this->_bd_query($path, $fetch_self = false, $recursive = false);
773
+ }
774
+
775
+ if ($result === false) {
776
+ $redirect = elFinder::getConnectorUrl();
777
+ $redirect .= (strpos($redirect, '?') !== false? '&' : '?') . 'cmd=netmount&protocol=box&host=' . ($options['id'] === 'elfinder'? '1' : $options['id']);
778
+
779
+ try {
780
+ $this->session->set('BoxTokens', (object)array('token' => null));
781
+ $url = self::AUTH_URL . '?' . http_build_query(array('response_type' => 'code', 'client_id' => $options['client_id'], 'redirect_uri' => $redirect));
782
+ } catch (Exception $e) {
783
+ return array('exit' => true, 'body' => '{msg:errAccess}');
784
+ }
785
+
786
+ $html = '<input id="elf-volumedriver-box-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button">';
787
+ $html .= '<script>
788
+ $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "box", mode: "makebtn", url: "' . $url . '"});
789
+ </script>';
790
+
791
+ return array('exit' => true, 'body' => $html);
792
+ } else {
793
+ $folders = [];
794
+
795
+ if ($result) {
796
+ foreach ($result as $res) {
797
+ if ($res->type == 'folder') {
798
+ $folders[$res->id . ' '] = $res->name;
799
+ }
800
+ }
801
+ natcasesort($folders);
802
+ }
803
+
804
+ if ($options['pass'] === 'folders') {
805
+ return ['exit' => true, 'folders' => $folders];
806
+ }
807
+
808
+ $folders = ['root' => 'My Box'] + $folders;
809
+ $folders = json_encode($folders);
810
+
811
+ $expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0;
812
+ $mnt2res = empty($this->token->data->refresh_token) ? '' : ', "mnt2res": 1';
813
+ $json = '{"protocol": "box", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . $mnt2res . '}';
814
+ $html = 'Box.com';
815
+ $html .= '<script>
816
+ $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
817
+ </script>';
818
+
819
+ return array('exit' => true, 'body' => $html);
820
+ }
821
+ }
822
+ } catch (Exception $e) {
823
+ return array('exit' => true, 'body' => '{msg:errNetMountNoDriver}');
824
+ }
825
+
826
+ if ($_aToken = $this->session->get('BoxTokens')) {
827
+ $options['accessToken'] = json_encode($_aToken);
828
+ if ($this->options['path'] === 'root' || !$this->options['path']) {
829
+ $this->options['path'] = '/';
830
+ }
831
+ } else {
832
+ $this->session->remove('BoxTokens');
833
+ $this->setError(elFinder::ERROR_NETMOUNT, $options['host'], implode(' ', $this->error()));
834
+
835
+ return array('exit' => true, 'error' => $this->error());
836
+ }
837
+
838
+ $this->session->remove('nodeId');
839
+ unset($options['user'], $options['pass'], $options['id']);
840
+
841
+ return $options;
842
+ }
843
+
844
+ /**
845
+ * process of on netunmount
846
+ * Drop `box` & rm thumbs.
847
+ *
848
+ * @param $netVolumes
849
+ * @param $key
850
+ *
851
+ * @return bool
852
+ */
853
+ public function netunmount($netVolumes, $key)
854
+ {
855
+ if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->tmbPrefix . '*.png')) {
856
+ foreach ($tmbs as $file) {
857
+ unlink($file);
858
+ }
859
+ }
860
+
861
+ return true;
862
+ }
863
+
864
+ /**
865
+ * Return debug info for client.
866
+ *
867
+ * @return array
868
+ **/
869
+ public function debug()
870
+ {
871
+ $res = parent::debug();
872
+ if (!empty($this->options['netkey']) && !empty($this->options['accessToken'])) {
873
+ $res['accessToken'] = $this->options['accessToken'];
874
+ }
875
+
876
+ return $res;
877
+ }
878
+
879
+ /*********************************************************************/
880
+ /* INIT AND CONFIGURE */
881
+ /*********************************************************************/
882
+
883
+ /**
884
+ * Prepare FTP connection
885
+ * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn.
886
+ *
887
+ * @return bool
888
+ * @throws Exception
889
+ * @author Dmitry (dio) Levashov
890
+ * @author Cem (DiscoFever)
891
+ */
892
+ protected function init()
893
+ {
894
+ if (!$this->options['accessToken']) {
895
+ return $this->setError('Required option `accessToken` is undefined.');
896
+ }
897
+
898
+ if (!empty($this->options['tmpPath'])) {
899
+ if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
900
+ $this->tmp = $this->options['tmpPath'];
901
+ }
902
+ }
903
+
904
+ $error = false;
905
+ try {
906
+ $this->token = json_decode($this->options['accessToken']);
907
+ if (!is_object($this->token)) {
908
+ throw new Exception('Required option `accessToken` is invalid JSON.');
909
+ }
910
+
911
+ // make net mount key
912
+ if (empty($this->options['netkey'])) {
913
+ $this->netMountKey = $this->_bd_getInitialToken();
914
+ } else {
915
+ $this->netMountKey = $this->options['netkey'];
916
+ }
917
+
918
+ if ($this->aTokenFile = $this->_bd_getATokenFile()) {
919
+ if (empty($this->options['netkey'])) {
920
+ if ($this->aTokenFile) {
921
+ if (is_file($this->aTokenFile)) {
922
+ $this->token = json_decode(file_get_contents($this->aTokenFile));
923
+ if (!is_object($this->token)) {
924
+ unlink($this->aTokenFile);
925
+ throw new Exception('Required option `accessToken` is invalid JSON.');
926
+ }
927
+ } else {
928
+ file_put_contents($this->aTokenFile, json_encode($this->token), LOCK_EX);
929
+ }
930
+ }
931
+ } else if (is_file($this->aTokenFile)) {
932
+ // If the refresh token is the same as the permanent volume
933
+ $this->token = json_decode(file_get_contents($this->aTokenFile));
934
+ }
935
+ }
936
+
937
+ $this->needOnline && $this->_bd_refreshToken();
938
+ } catch (Exception $e) {
939
+ $this->token = null;
940
+ $error = true;
941
+ $this->setError($e->getMessage());
942
+ }
943
+
944
+ if ($this->netMountKey) {
945
+ $this->tmbPrefix = 'box' . base_convert($this->netMountKey, 16, 32);
946
+ }
947
+
948
+ if ($error) {
949
+ if (empty($this->options['netkey']) && $this->tmbPrefix) {
950
+ // for delete thumbnail
951
+ $this->netunmount(null, null);
952
+ }
953
+ return false;
954
+ }
955
+
956
+ // normalize root path
957
+ if ($this->options['path'] == 'root') {
958
+ $this->options['path'] = '/';
959
+ }
960
+
961
+ $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
962
+
963
+ $this->options['root'] = ($this->options['root'] == '')? 'Box.com' : $this->options['root'];
964
+
965
+ if (empty($this->options['alias'])) {
966
+ if ($this->needOnline) {
967
+ list(, $itemId) = $this->_bd_splitPath($this->options['path']);
968
+ $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] :
969
+ $this->_bd_query($itemId, $fetch_self = true)->name . '@Box';
970
+ if (!empty($this->options['netkey'])) {
971
+ elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
972
+ }
973
+ } else {
974
+ $this->options['alias'] = $this->options['root'];
975
+ }
976
+ }
977
+
978
+ $this->rootName = $this->options['alias'];
979
+
980
+ // This driver dose not support `syncChkAsTs`
981
+ $this->options['syncChkAsTs'] = false;
982
+
983
+ // 'lsPlSleep' minmum 10 sec
984
+ $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
985
+
986
+ // enable command archive
987
+ $this->options['useRemoteArchive'] = true;
988
+
989
+ return true;
990
+ }
991
+
992
+ /**
993
+ * Configure after successfull mount.
994
+ *
995
+ * @author Dmitry (dio) Levashov
996
+ * @throws elFinderAbortException
997
+ */
998
+ protected function configure()
999
+ {
1000
+ parent::configure();
1001
+
1002
+ // fallback of $this->tmp
1003
+ if (!$this->tmp && $this->tmbPathWritable) {
1004
+ $this->tmp = $this->tmbPath;
1005
+ }
1006
+ }
1007
+
1008
+ /*********************************************************************/
1009
+ /* FS API */
1010
+ /*********************************************************************/
1011
+
1012
+ /**
1013
+ * Close opened connection.
1014
+ *
1015
+ * @author Dmitry (dio) Levashov
1016
+ **/
1017
+ public function umount()
1018
+ {
1019
+ }
1020
+
1021
+ /**
1022
+ * Return fileinfo based on filename
1023
+ * For item ID based path file system
1024
+ * Please override if needed on each drivers.
1025
+ *
1026
+ * @param string $path file cache
1027
+ *
1028
+ * @return array|boolean
1029
+ * @throws elFinderAbortException
1030
+ */
1031
+ protected function isNameExists($path)
1032
+ {
1033
+ list(, $name, $parent) = $this->_bd_splitPath($path);
1034
+
1035
+ // We can not use it because the search of Box.com there is a time lag.
1036
+ // ref. https://docs.box.com/reference#searching-for-content
1037
+ // > Note: If an item is added to Box then it becomes accessible through the search endpoint after ten minutes.
1038
+
1039
+ /***
1040
+ * $url = self::API_URL.'/search?limit=1&offset=0&content_types=name&ancestor_folder_ids='.rawurlencode($pid)
1041
+ * .'&query='.rawurlencode('"'.$name.'"')
1042
+ * .'fields='.self::FETCHFIELDS;
1043
+ * $raw = $this->_bd_fetch($url);
1044
+ * if (is_array($raw) && count($raw)) {
1045
+ * return $this->_bd_parseRaw($raw);
1046
+ * }
1047
+ ***/
1048
+
1049
+ $phash = $this->encode($parent);
1050
+
1051
+ // do not recursive search
1052
+ $searchExDirReg = $this->options['searchExDirReg'];
1053
+ $this->options['searchExDirReg'] = '/.*/';
1054
+ $search = $this->search($name, array(), $phash);
1055
+ $this->options['searchExDirReg'] = $searchExDirReg;
1056
+
1057
+ if ($search) {
1058
+ $f = false;
1059
+ foreach($search as $f) {
1060
+ if ($f['name'] !== $name) {
1061
+ $f = false;
1062
+ }
1063
+ if ($f) {
1064
+ break;
1065
+ }
1066
+ }
1067
+ return $f;
1068
+ }
1069
+
1070
+ return false;
1071
+ }
1072
+
1073
+ /**
1074
+ * Cache dir contents.
1075
+ *
1076
+ * @param string $path dir path
1077
+ *
1078
+ * @return
1079
+ * @throws Exception
1080
+ * @author Dmitry Levashov
1081
+ */
1082
+ protected function cacheDir($path)
1083
+ {
1084
+ $this->dirsCache[$path] = array();
1085
+ $hasDir = false;
1086
+
1087
+ if ($path == '/') {
1088
+ $items = $this->_bd_query('0', $fetch_self = true); // get root directory with folder & files
1089
+ $itemId = $items->id;
1090
+ } else {
1091
+ list(, $itemId) = $this->_bd_splitPath($path);
1092
+ }
1093
+
1094
+ $res = $this->_bd_query($itemId);
1095
+
1096
+ if ($res) {
1097
+ foreach ($res as $raw) {
1098
+ if ($stat = $this->_bd_parseRaw($raw)) {
1099
+ $itemPath = $this->_joinPath($path, $raw->id);
1100
+ $stat = $this->updateCache($itemPath, $stat);
1101
+ if (empty($stat['hidden'])) {
1102
+ if (!$hasDir && $stat['mime'] === 'directory') {
1103
+ $hasDir = true;
1104
+ }
1105
+ $this->dirsCache[$path][] = $itemPath;
1106
+ }
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ if (isset($this->sessionCache['subdirs'])) {
1112
+ $this->sessionCache['subdirs'][$path] = $hasDir;
1113
+ }
1114
+
1115
+ return $this->dirsCache[$path];
1116
+ }
1117
+
1118
+ /**
1119
+ * Copy file/recursive copy dir only in current volume.
1120
+ * Return new file path or false.
1121
+ *
1122
+ * @param string $src source path
1123
+ * @param string $dst destination dir path
1124
+ * @param string $name new file name (optionaly)
1125
+ *
1126
+ * @return string|false
1127
+ * @author Dmitry (dio) Levashov
1128
+ * @author Naoki Sawada
1129
+ **/
1130
+ protected function copy($src, $dst, $name)
1131
+ {
1132
+ if ($res = $this->_copy($src, $dst, $name)) {
1133
+ $this->added[] = $this->stat($res);
1134
+ return $res;
1135
+ } else {
1136
+ return $this->setError(elFinder::ERROR_COPY, $this->_path($src));
1137
+ }
1138
+ }
1139
+
1140
+ /**
1141
+ * Remove file/ recursive remove dir.
1142
+ *
1143
+ * @param string $path file path
1144
+ * @param bool $force try to remove even if file locked
1145
+ *
1146
+ * @return bool
1147
+ * @throws elFinderAbortException
1148
+ * @author Dmitry (dio) Levashov
1149
+ * @author Naoki Sawada
1150
+ */
1151
+ protected function remove($path, $force = false)
1152
+ {
1153
+ $stat = $this->stat($path);
1154
+ $stat['realpath'] = $path;
1155
+ $this->rmTmb($stat);
1156
+ $this->clearcache();
1157
+
1158
+ if (empty($stat)) {
1159
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
1160
+ }
1161
+
1162
+ if (!$force && !empty($stat['locked'])) {
1163
+ return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
1164
+ }
1165
+
1166
+ if ($stat['mime'] == 'directory') {
1167
+ if (!$this->_rmdir($path)) {
1168
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1169
+ }
1170
+ } else {
1171
+ if (!$this->_unlink($path)) {
1172
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1173
+ }
1174
+ }
1175
+
1176
+ $this->removed[] = $stat;
1177
+
1178
+ return true;
1179
+ }
1180
+
1181
+ /**
1182
+ * Create thumnbnail and return it's URL on success.
1183
+ *
1184
+ * @param string $path file path
1185
+ * @param $stat
1186
+ *
1187
+ * @return string|false
1188
+ * @throws ImagickException
1189
+ * @throws elFinderAbortException
1190
+ * @author Dmitry (dio) Levashov
1191
+ * @author Naoki Sawada
1192
+ */
1193
+ protected function createTmb($path, $stat)
1194
+ {
1195
+ if (!$stat || !$this->canCreateTmb($path, $stat)) {
1196
+ return false;
1197
+ }
1198
+
1199
+ $name = $this->tmbname($stat);
1200
+ $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
1201
+
1202
+ // copy image into tmbPath so some drivers does not store files on local fs
1203
+ if (!$data = $this->_bd_getThumbnail($path)) {
1204
+ // try get full contents as fallback
1205
+ if (!$data = $this->_getContents($path)) {
1206
+ return false;
1207
+ }
1208
+ }
1209
+ if (!file_put_contents($tmb, $data)) {
1210
+ return false;
1211
+ }
1212
+
1213
+ $tmbSize = $this->tmbSize;
1214
+
1215
+ if (($s = getimagesize($tmb)) == false) {
1216
+ return false;
1217
+ }
1218
+
1219
+ $result = true;
1220
+ /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
1221
+ if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
1222
+ $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1223
+ } else {
1224
+ if ($this->options['tmbCrop']) {
1225
+
1226
+ /* Resize and crop if image bigger than thumbnail */
1227
+ if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
1228
+ $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
1229
+ }
1230
+
1231
+ if ($result && ($s = getimagesize($tmb)) != false) {
1232
+ $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
1233
+ $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
1234
+ $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
1235
+ }
1236
+ } else {
1237
+ $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
1238
+ }
1239
+
1240
+ if ($result) {
1241
+ $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1242
+ }
1243
+ }
1244
+
1245
+ if (!$result) {
1246
+ unlink($tmb);
1247
+
1248
+ return false;
1249
+ }
1250
+
1251
+ return $name;
1252
+ }
1253
+
1254
+ /**
1255
+ * Return thumbnail file name for required file.
1256
+ *
1257
+ * @param array $stat file stat
1258
+ *
1259
+ * @return string
1260
+ * @author Dmitry (dio) Levashov
1261
+ **/
1262
+ protected function tmbname($stat)
1263
+ {
1264
+ return $this->tmbPrefix . $stat['rev'] . $stat['ts'] . '.png';
1265
+ }
1266
+
1267
+ /**
1268
+ * Return content URL.
1269
+ *
1270
+ * @param object $raw data
1271
+ *
1272
+ * @return string
1273
+ * @author Naoki Sawada
1274
+ **/
1275
+ protected function getSharedWebContentLink($raw)
1276
+ {
1277
+ if ($raw->shared_link->url) {
1278
+ return sprintf('https://app.box.com/index.php?rm=box_download_shared_file&shared_name=%s&file_id=f_%s', basename($raw->shared_link->url), $raw->id);
1279
+ } elseif ($raw->shared_link->download_url) {
1280
+ return $raw->shared_link->download_url;
1281
+ }
1282
+
1283
+ return false;
1284
+ }
1285
+
1286
+ /**
1287
+ * Return content URL.
1288
+ *
1289
+ * @param string $hash file hash
1290
+ * @param array $options options
1291
+ *
1292
+ * @return string
1293
+ * @throws Exception
1294
+ * @author Naoki Sawada
1295
+ */
1296
+ public function getContentUrl($hash, $options = array())
1297
+ {
1298
+ if (!empty($options['onetime']) && $this->options['onetimeUrl']) {
1299
+ return parent::getContentUrl($hash, $options);
1300
+ }
1301
+ if (!empty($options['temporary'])) {
1302
+ // try make temporary file
1303
+ $url = parent::getContentUrl($hash, $options);
1304
+ if ($url) {
1305
+ return $url;
1306
+ }
1307
+ }
1308
+ if (($file = $this->file($hash)) == false || !$file['url'] || $file['url'] == 1) {
1309
+ $path = $this->decode($hash);
1310
+
1311
+ list(, $itemId) = $this->_bd_splitPath($path);
1312
+ $params['shared_link']['access'] = 'open'; //open|company|collaborators
1313
+
1314
+ $url = self::API_URL . '/files/' . $itemId;
1315
+
1316
+ $curl = $this->_bd_prepareCurl(array(
1317
+ CURLOPT_URL => $url,
1318
+ CURLOPT_CUSTOMREQUEST => 'PUT',
1319
+ CURLOPT_POSTFIELDS => json_encode($params),
1320
+ ));
1321
+ $res = $this->_bd_curlExec($curl, true, array(
1322
+ // The data is sent as JSON as per Box documentation.
1323
+ 'Content-Type: application/json',
1324
+ ));
1325
+
1326
+ if ($url = $this->getSharedWebContentLink($res)) {
1327
+ return $url;
1328
+ }
1329
+ }
1330
+
1331
+ return '';
1332
+ }
1333
+
1334
+ /*********************** paths/urls *************************/
1335
+
1336
+ /**
1337
+ * Return parent directory path.
1338
+ *
1339
+ * @param string $path file path
1340
+ *
1341
+ * @return string
1342
+ * @author Dmitry (dio) Levashov
1343
+ **/
1344
+ protected function _dirname($path)
1345
+ {
1346
+ list(, , $dirname) = $this->_bd_splitPath($path);
1347
+
1348
+ return $dirname;
1349
+ }
1350
+
1351
+ /**
1352
+ * Return file name.
1353
+ *
1354
+ * @param string $path file path
1355
+ *
1356
+ * @return string
1357
+ * @author Dmitry (dio) Levashov
1358
+ **/
1359
+ protected function _basename($path)
1360
+ {
1361
+ list(, $basename) = $this->_bd_splitPath($path);
1362
+
1363
+ return $basename;
1364
+ }
1365
+
1366
+ /**
1367
+ * Join dir name and file name and retur full path.
1368
+ *
1369
+ * @param string $dir
1370
+ * @param string $name
1371
+ *
1372
+ * @return string
1373
+ * @author Dmitry (dio) Levashov
1374
+ **/
1375
+ protected function _joinPath($dir, $name)
1376
+ {
1377
+ if (strval($dir) === '0') {
1378
+ $dir = '';
1379
+ }
1380
+
1381
+ return $this->_normpath($dir . '/' . $name);
1382
+ }
1383
+
1384
+ /**
1385
+ * Return normalized path, this works the same as os.path.normpath() in Python.
1386
+ *
1387
+ * @param string $path path
1388
+ *
1389
+ * @return string
1390
+ * @author Troex Nevelin
1391
+ **/
1392
+ protected function _normpath($path)
1393
+ {
1394
+ if (DIRECTORY_SEPARATOR !== '/') {
1395
+ $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
1396
+ }
1397
+ $path = '/' . ltrim($path, '/');
1398
+
1399
+ return $path;
1400
+ }
1401
+
1402
+ /**
1403
+ * Return file path related to root dir.
1404
+ *
1405
+ * @param string $path file path
1406
+ *
1407
+ * @return string
1408
+ * @author Dmitry (dio) Levashov
1409
+ **/
1410
+ protected function _relpath($path)
1411
+ {
1412
+ return $path;
1413
+ }
1414
+
1415
+ /**
1416
+ * Convert path related to root dir into real path.
1417
+ *
1418
+ * @param string $path file path
1419
+ *
1420
+ * @return string
1421
+ * @author Dmitry (dio) Levashov
1422
+ **/
1423
+ protected function _abspath($path)
1424
+ {
1425
+ return $path;
1426
+ }
1427
+
1428
+ /**
1429
+ * Return fake path started from root dir.
1430
+ *
1431
+ * @param string $path file path
1432
+ *
1433
+ * @return string
1434
+ * @author Dmitry (dio) Levashov
1435
+ **/
1436
+ protected function _path($path)
1437
+ {
1438
+ return $this->rootName . $this->_normpath(substr($path, strlen($this->root)));
1439
+ }
1440
+
1441
+ /**
1442
+ * Return true if $path is children of $parent.
1443
+ *
1444
+ * @param string $path path to check
1445
+ * @param string $parent parent path
1446
+ *
1447
+ * @return bool
1448
+ * @author Dmitry (dio) Levashov
1449
+ **/
1450
+ protected function _inpath($path, $parent)
1451
+ {
1452
+ return $path == $parent || strpos($path, $parent . '/') === 0;
1453
+ }
1454
+
1455
+ /***************** file stat ********************/
1456
+ /**
1457
+ * Return stat for given path.
1458
+ * Stat contains following fields:
1459
+ * - (int) size file size in b. required
1460
+ * - (int) ts file modification time in unix time. required
1461
+ * - (string) mime mimetype. required for folders, others - optionally
1462
+ * - (bool) read read permissions. required
1463
+ * - (bool) write write permissions. required
1464
+ * - (bool) locked is object locked. optionally
1465
+ * - (bool) hidden is object hidden. optionally
1466
+ * - (string) alias for symlinks - link target path relative to root path. optionally
1467
+ * - (string) target for symlinks - link target path. optionally.
1468
+ * If file does not exists - returns empty array or false.
1469
+ *
1470
+ * @param string $path file path
1471
+ *
1472
+ * @return array|false
1473
+ * @throws Exception
1474
+ * @author Dmitry (dio) Levashov
1475
+ */
1476
+ protected function _stat($path)
1477
+ {
1478
+ if ($raw = $this->_bd_getRawItem($path)) {
1479
+ return $this->_bd_parseRaw($raw);
1480
+ }
1481
+
1482
+ return false;
1483
+ }
1484
+
1485
+ /**
1486
+ * Return true if path is dir and has at least one childs directory.
1487
+ *
1488
+ * @param string $path dir path
1489
+ *
1490
+ * @return bool
1491
+ * @throws Exception
1492
+ * @author Dmitry (dio) Levashov
1493
+ */
1494
+ protected function _subdirs($path)
1495
+ {
1496
+ list(, $itemId) = $this->_bd_splitPath($path);
1497
+
1498
+ $path = '/folders/' . $itemId . '/items?limit=1&offset=0&fields=' . self::FETCHFIELDS;
1499
+
1500
+ $url = self::API_URL . $path;
1501
+
1502
+ if ($res = $this->_bd_fetch($url)) {
1503
+ if ($res[0]->type == 'folder') {
1504
+ return true;
1505
+ }
1506
+ }
1507
+
1508
+ return false;
1509
+ }
1510
+
1511
+ /**
1512
+ * Return object width and height
1513
+ * Ususaly used for images, but can be realize for video etc...
1514
+ *
1515
+ * @param string $path file path
1516
+ * @param string $mime file mime type
1517
+ *
1518
+ * @return string
1519
+ * @throws ImagickException
1520
+ * @throws elFinderAbortException
1521
+ * @author Dmitry (dio) Levashov
1522
+ */
1523
+ protected function _dimensions($path, $mime)
1524
+ {
1525
+ if (strpos($mime, 'image') !== 0) {
1526
+ return '';
1527
+ }
1528
+
1529
+ $ret = '';
1530
+ if ($work = $this->getWorkFile($path)) {
1531
+ if ($size = @getimagesize($work)) {
1532
+ $cache['width'] = $size[0];
1533
+ $cache['height'] = $size[1];
1534
+ $ret = array('dim' => $size[0] . 'x' . $size[1]);
1535
+ $srcfp = fopen($work, 'rb');
1536
+ $target = isset(elFinder::$currentArgs['target'])? elFinder::$currentArgs['target'] : '';
1537
+ if ($subImgLink = $this->getSubstituteImgLink($target, $size, $srcfp)) {
1538
+ $ret['url'] = $subImgLink;
1539
+ }
1540
+ }
1541
+ }
1542
+ is_file($work) && @unlink($work);
1543
+
1544
+ return $ret;
1545
+ }
1546
+
1547
+ /******************** file/dir content *********************/
1548
+
1549
+ /**
1550
+ * Return files list in directory.
1551
+ *
1552
+ * @param string $path dir path
1553
+ *
1554
+ * @return array
1555
+ * @throws Exception
1556
+ * @author Dmitry (dio) Levashov
1557
+ * @author Cem (DiscoFever)
1558
+ */
1559
+ protected function _scandir($path)
1560
+ {
1561
+ return isset($this->dirsCache[$path])
1562
+ ? $this->dirsCache[$path]
1563
+ : $this->cacheDir($path);
1564
+ }
1565
+
1566
+ /**
1567
+ * Open file and return file pointer.
1568
+ *
1569
+ * @param string $path file path
1570
+ * @param string $mode
1571
+ *
1572
+ * @return resource|false
1573
+ * @author Dmitry (dio) Levashov
1574
+ */
1575
+ protected function _fopen($path, $mode = 'rb')
1576
+ {
1577
+ if ($mode === 'rb' || $mode === 'r') {
1578
+ list(, $itemId) = $this->_bd_splitPath($path);
1579
+ $data = array(
1580
+ 'target' => self::API_URL . '/files/' . $itemId . '/content',
1581
+ 'headers' => array('Authorization: Bearer ' . $this->token->data->access_token),
1582
+ );
1583
+
1584
+ // to support range request
1585
+ if (func_num_args() > 2) {
1586
+ $opts = func_get_arg(2);
1587
+ } else {
1588
+ $opts = array();
1589
+ }
1590
+ if (!empty($opts['httpheaders'])) {
1591
+ $data['headers'] = array_merge($opts['httpheaders'], $data['headers']);
1592
+ }
1593
+
1594
+ return elFinder::getStreamByUrl($data);
1595
+ }
1596
+
1597
+ return false;
1598
+ }
1599
+
1600
+ /**
1601
+ * Close opened file.
1602
+ *
1603
+ * @param resource $fp file pointer
1604
+ * @param string $path
1605
+ *
1606
+ * @return void
1607
+ * @author Dmitry (dio) Levashov
1608
+ */
1609
+ protected function _fclose($fp, $path = '')
1610
+ {
1611
+ is_resource($fp) && fclose($fp);
1612
+ if ($path) {
1613
+ unlink($this->getTempFile($path));
1614
+ }
1615
+ }
1616
+
1617
+ /******************** file/dir manipulations *************************/
1618
+
1619
+ /**
1620
+ * Create dir and return created dir path or false on failed.
1621
+ *
1622
+ * @param string $path parent dir path
1623
+ * @param string $name new directory name
1624
+ *
1625
+ * @return string|bool
1626
+ * @author Dmitry (dio) Levashov
1627
+ **/
1628
+ protected function _mkdir($path, $name)
1629
+ {
1630
+ try {
1631
+ list(, $parentId) = $this->_bd_splitPath($path);
1632
+ $params = array('name' => $name, 'parent' => array('id' => $parentId));
1633
+
1634
+ $url = self::API_URL . '/folders';
1635
+
1636
+ $curl = $this->_bd_prepareCurl(array(
1637
+ CURLOPT_URL => $url,
1638
+ CURLOPT_POST => true,
1639
+ CURLOPT_POSTFIELDS => json_encode($params),
1640
+ ));
1641
+
1642
+ //create the Folder in the Parent
1643
+ $folder = $this->_bd_curlExec($curl, $path);
1644
+
1645
+ return $this->_joinPath($path, $folder->id);
1646
+ } catch (Exception $e) {
1647
+ return $this->setError('Box error: ' . $e->getMessage());
1648
+ }
1649
+ }
1650
+
1651
+ /**
1652
+ * Create file and return it's path or false on failed.
1653
+ *
1654
+ * @param string $path parent dir path
1655
+ * @param string $name new file name
1656
+ *
1657
+ * @return string|bool
1658
+ * @author Dmitry (dio) Levashov
1659
+ **/
1660
+ protected function _mkfile($path, $name)
1661
+ {
1662
+ return $this->_save($this->tmpfile(), $path, $name, array());
1663
+ }
1664
+
1665
+ /**
1666
+ * Create symlink. FTP driver does not support symlinks.
1667
+ *
1668
+ * @param string $target link target
1669
+ * @param string $path symlink path
1670
+ *
1671
+ * @return bool
1672
+ * @author Dmitry (dio) Levashov
1673
+ **/
1674
+ protected function _symlink($target, $path, $name)
1675
+ {
1676
+ return false;
1677
+ }
1678
+
1679
+ /**
1680
+ * Copy file into another file.
1681
+ *
1682
+ * @param string $source source file path
1683
+ * @param string $targetDir target directory path
1684
+ * @param string $name new file name
1685
+ *
1686
+ * @return string|false
1687
+ * @author Dmitry (dio) Levashov
1688
+ **/
1689
+ protected function _copy($source, $targetDir, $name)
1690
+ {
1691
+ try {
1692
+ //Set the Parent id
1693
+ list(, $parentId) = $this->_bd_splitPath($targetDir);
1694
+ list(, $srcId) = $this->_bd_splitPath($source);
1695
+
1696
+ $srcItem = $this->_bd_getRawItem($source);
1697
+
1698
+ $properties = array('name' => $name, 'parent' => array('id' => $parentId));
1699
+ $data = (object)$properties;
1700
+
1701
+ $type = ($srcItem->type === 'folder') ? 'folders' : 'files';
1702
+ $url = self::API_URL . '/' . $type . '/' . $srcId . '/copy';
1703
+
1704
+ $curl = $this->_bd_prepareCurl(array(
1705
+ CURLOPT_URL => $url,
1706
+ CURLOPT_POST => true,
1707
+ CURLOPT_POSTFIELDS => json_encode($data),
1708
+ ));
1709
+
1710
+ //copy File in the Parent
1711
+ $result = $this->_bd_curlExec($curl, $targetDir);
1712
+
1713
+ if (isset($result->id)) {
1714
+ if ($type === 'folders' && isset($this->sessionCache['subdirs'])) {
1715
+ $this->sessionCache['subdirs'][$targetDir] = true;
1716
+ }
1717
+
1718
+ return $this->_joinPath($targetDir, $result->id);
1719
+ }
1720
+
1721
+ return false;
1722
+ } catch (Exception $e) {
1723
+ return $this->setError('Box error: ' . $e->getMessage());
1724
+ }
1725
+ }
1726
+
1727
+ /**
1728
+ * Move file into another parent dir.
1729
+ * Return new file path or false.
1730
+ *
1731
+ * @param string $source source file path
1732
+ * @param string $target target dir path
1733
+ * @param string $name file name
1734
+ *
1735
+ * @return string|bool
1736
+ * @author Dmitry (dio) Levashov
1737
+ **/
1738
+ protected function _move($source, $targetDir, $name)
1739
+ {
1740
+ try {
1741
+ //moving and renaming a file or directory
1742
+ //Set new Parent and remove old parent
1743
+ list(, $parentId) = $this->_bd_splitPath($targetDir);
1744
+ list(, $itemId) = $this->_bd_splitPath($source);
1745
+
1746
+ $srcItem = $this->_bd_getRawItem($source);
1747
+
1748
+ //rename or move file or folder in destination target
1749
+ $properties = array('name' => $name, 'parent' => array('id' => $parentId));
1750
+
1751
+ $type = ($srcItem->type === 'folder') ? 'folders' : 'files';
1752
+ $url = self::API_URL . '/' . $type . '/' . $itemId;
1753
+ $data = (object)$properties;
1754
+
1755
+ $curl = $this->_bd_prepareCurl(array(
1756
+ CURLOPT_URL => $url,
1757
+ CURLOPT_CUSTOMREQUEST => 'PUT',
1758
+ CURLOPT_POSTFIELDS => json_encode($data),
1759
+ ));
1760
+
1761
+ $result = $this->_bd_curlExec($curl, $targetDir, array(
1762
+ // The data is sent as JSON as per Box documentation.
1763
+ 'Content-Type: application/json',
1764
+ ));
1765
+
1766
+ if ($result && isset($result->id)) {
1767
+ return $this->_joinPath($targetDir, $result->id);
1768
+ }
1769
+
1770
+ return false;
1771
+ } catch (Exception $e) {
1772
+ return $this->setError('Box error: ' . $e->getMessage());
1773
+ }
1774
+ }
1775
+
1776
+ /**
1777
+ * Remove file.
1778
+ *
1779
+ * @param string $path file path
1780
+ *
1781
+ * @return bool
1782
+ * @author Dmitry (dio) Levashov
1783
+ **/
1784
+ protected function _unlink($path)
1785
+ {
1786
+ return $this->_bd_unlink($path, 'files');
1787
+ }
1788
+
1789
+ /**
1790
+ * Remove dir.
1791
+ *
1792
+ * @param string $path dir path
1793
+ *
1794
+ * @return bool
1795
+ * @author Dmitry (dio) Levashov
1796
+ **/
1797
+ protected function _rmdir($path)
1798
+ {
1799
+ return $this->_bd_unlink($path, 'folders');
1800
+ }
1801
+
1802
+ /**
1803
+ * Create new file and write into it from file pointer.
1804
+ * Return new file path or false on error.
1805
+ *
1806
+ * @param resource $fp file pointer
1807
+ * @param string $dir target dir path
1808
+ * @param string $name file name
1809
+ * @param array $stat file stat (required by some virtual fs)
1810
+ *
1811
+ * @return bool|string
1812
+ * @author Dmitry (dio) Levashov
1813
+ **/
1814
+ protected function _save($fp, $path, $name, $stat)
1815
+ {
1816
+ $itemId = '';
1817
+ if ($name === '') {
1818
+ list($parentId, $itemId, $parent) = $this->_bd_splitPath($path);
1819
+ } else {
1820
+ if ($stat) {
1821
+ if (isset($stat['name'])) {
1822
+ $name = $stat['name'];
1823
+ }
1824
+ if (isset($stat['rev']) && strpos($stat['hash'], $this->id) === 0) {
1825
+ $itemId = $stat['rev'];
1826
+ }
1827
+ }
1828
+ list(, $parentId) = $this->_bd_splitPath($path);
1829
+ $parent = $path;
1830
+ }
1831
+
1832
+ try {
1833
+ //Create or Update a file
1834
+ $metaDatas = stream_get_meta_data($fp);
1835
+ $tmpFilePath = isset($metaDatas['uri']) ? $metaDatas['uri'] : '';
1836
+ // remote contents
1837
+ if (!$tmpFilePath || empty($metaDatas['seekable'])) {
1838
+ $tmpHandle = $this->tmpfile();
1839
+ stream_copy_to_stream($fp, $tmpHandle);
1840
+ $metaDatas = stream_get_meta_data($tmpHandle);
1841
+ $tmpFilePath = $metaDatas['uri'];
1842
+ }
1843
+
1844
+ if ($itemId === '') {
1845
+ //upload or create new file in destination target
1846
+ $properties = array('name' => $name, 'parent' => array('id' => $parentId));
1847
+ $url = self::UPLOAD_URL . '/files/content';
1848
+ } else {
1849
+ //update existing file in destination target
1850
+ $properties = array('name' => $name);
1851
+ $url = self::UPLOAD_URL . '/files/' . $itemId . '/content';
1852
+ }
1853
+
1854
+ if (class_exists('CURLFile')) {
1855
+ $cfile = new CURLFile($tmpFilePath);
1856
+ } else {
1857
+ $cfile = '@' . $tmpFilePath;
1858
+ }
1859
+ $params = array('attributes' => json_encode($properties), 'file' => $cfile);
1860
+ $curl = $this->_bd_prepareCurl(array(
1861
+ CURLOPT_URL => $url,
1862
+ CURLOPT_POST => true,
1863
+ CURLOPT_POSTFIELDS => $params,
1864
+ ));
1865
+
1866
+ $file = $this->_bd_curlExec($curl, $parent);
1867
+
1868
+ return $this->_joinPath($parent, $file->entries[0]->id);
1869
+ } catch (Exception $e) {
1870
+ return $this->setError('Box error: ' . $e->getMessage());
1871
+ }
1872
+ }
1873
+
1874
+ /**
1875
+ * Get file contents.
1876
+ *
1877
+ * @param string $path file path
1878
+ *
1879
+ * @return string|false
1880
+ * @author Dmitry (dio) Levashov
1881
+ **/
1882
+ protected function _getContents($path)
1883
+ {
1884
+ try {
1885
+ list(, $itemId) = $this->_bd_splitPath($path);
1886
+ $url = self::API_URL . '/files/' . $itemId . '/content';
1887
+
1888
+ $contents = $this->_bd_fetch($url, true);
1889
+ } catch (Exception $e) {
1890
+ return $this->setError('Box error: ' . $e->getMessage());
1891
+ }
1892
+
1893
+ return $contents;
1894
+ }
1895
+
1896
+ /**
1897
+ * Write a string to a file.
1898
+ *
1899
+ * @param string $path file path
1900
+ * @param string $content new file content
1901
+ *
1902
+ * @return bool
1903
+ * @author Dmitry (dio) Levashov
1904
+ **/
1905
+ protected function _filePutContents($path, $content)
1906
+ {
1907
+ $res = false;
1908
+
1909
+ if ($local = $this->getTempFile($path)) {
1910
+ if (file_put_contents($local, $content, LOCK_EX) !== false
1911
+ && ($fp = fopen($local, 'rb'))) {
1912
+ clearstatcache();
1913
+ $res = $this->_save($fp, $path, '', array());
1914
+ fclose($fp);
1915
+ }
1916
+ file_exists($local) && unlink($local);
1917
+ }
1918
+
1919
+ return $res;
1920
+ }
1921
+
1922
+ /**
1923
+ * Detect available archivers.
1924
+ **/
1925
+ protected function _checkArchivers()
1926
+ {
1927
+ // die('Not yet implemented. (_checkArchivers)');
1928
+ return array();
1929
+ }
1930
+
1931
+ /**
1932
+ * chmod implementation.
1933
+ *
1934
+ * @return bool
1935
+ **/
1936
+ protected function _chmod($path, $mode)
1937
+ {
1938
+ return false;
1939
+ }
1940
+
1941
+ /**
1942
+ * Extract files from archive.
1943
+ *
1944
+ * @param string $path archive path
1945
+ * @param array $arc archiver command and arguments (same as in $this->archivers)
1946
+ *
1947
+ * @return true
1948
+ * @author Dmitry (dio) Levashov,
1949
+ * @author Alexey Sukhotin
1950
+ **/
1951
+ protected function _extract($path, $arc)
1952
+ {
1953
+ die('Not yet implemented. (_extract)');
1954
+ }
1955
+
1956
+ /**
1957
+ * Create archive and return its path.
1958
+ *
1959
+ * @param string $dir target dir
1960
+ * @param array $files files names list
1961
+ * @param string $name archive name
1962
+ * @param array $arc archiver options
1963
+ *
1964
+ * @return string|bool
1965
+ * @author Dmitry (dio) Levashov,
1966
+ * @author Alexey Sukhotin
1967
+ **/
1968
+ protected function _archive($dir, $files, $name, $arc)
1969
+ {
1970
+ die('Not yet implemented. (_archive)');
1971
+ }
1972
+ } // END class