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,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