1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Spatie\Dropbox; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use GuzzleHttp\Psr7\StreamWrapper; |
7
|
|
|
use GuzzleHttp\Client as GuzzleClient; |
8
|
|
|
use Psr\Http\Message\ResponseInterface; |
9
|
|
|
use GuzzleHttp\Exception\ClientException; |
10
|
|
|
use Spatie\Dropbox\Exceptions\BadRequest; |
11
|
|
|
|
12
|
|
|
class Client |
13
|
|
|
{ |
14
|
|
|
const THUMBNAIL_FORMAT_JPEG = 'jpeg'; |
15
|
|
|
const THUMBNAIL_FORMAT_PNG = 'png'; |
16
|
|
|
|
17
|
|
|
const THUMBNAIL_SIZE_XS = 'w32h32'; |
18
|
|
|
const THUMBNAIL_SIZE_S = 'w64h64'; |
19
|
|
|
const THUMBNAIL_SIZE_M = 'w128h128'; |
20
|
|
|
const THUMBNAIL_SIZE_L = 'w640h480'; |
21
|
|
|
const THUMBNAIL_SIZE_XL = 'w1024h768'; |
22
|
|
|
|
23
|
|
|
/** @var string */ |
24
|
|
|
protected $accessToken; |
25
|
|
|
|
26
|
|
|
/** @var \GuzzleHttp\Client */ |
27
|
|
|
protected $client; |
28
|
|
|
|
29
|
|
|
public function __construct(string $accessToken, GuzzleClient $client = null) |
30
|
|
|
{ |
31
|
|
|
$this->accessToken = $accessToken; |
32
|
|
|
|
33
|
|
|
$this->client = $client ?? new GuzzleClient([ |
34
|
|
|
'headers' => [ |
35
|
|
|
'Authorization' => "Bearer {$this->accessToken}", |
36
|
|
|
], |
37
|
|
|
]); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Copy a file or folder to a different location in the user's Dropbox. |
42
|
|
|
* |
43
|
|
|
* If the source path is a folder all its contents will be copied. |
44
|
|
|
* |
45
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy |
46
|
|
|
*/ |
47
|
|
View Code Duplication |
public function copy(string $fromPath, string $toPath): array |
|
|
|
|
48
|
|
|
{ |
49
|
|
|
$parameters = [ |
50
|
|
|
'from_path' => $this->normalizePath($fromPath), |
51
|
|
|
'to_path' => $this->normalizePath($toPath), |
52
|
|
|
]; |
53
|
|
|
|
54
|
|
|
return $this->rpcEndpointRequest('files/copy', $parameters); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Create a folder at a given path. |
59
|
|
|
* |
60
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder |
61
|
|
|
*/ |
62
|
|
|
public function createFolder(string $path): array |
63
|
|
|
{ |
64
|
|
|
$parameters = [ |
65
|
|
|
'path' => $this->normalizePath($path), |
66
|
|
|
]; |
67
|
|
|
|
68
|
|
|
$object = $this->rpcEndpointRequest('files/create_folder', $parameters); |
69
|
|
|
|
70
|
|
|
$object['.tag'] = 'folder'; |
71
|
|
|
|
72
|
|
|
return $object; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Create a shared link with custom settings. |
77
|
|
|
* |
78
|
|
|
* If no settings are given then the default visibility is RequestedVisibility.public. |
79
|
|
|
* The resolved visibility, though, may depend on other aspects such as team and |
80
|
|
|
* shared folder settings). Only for paid users. |
81
|
|
|
* |
82
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-create_shared_link_with_settings |
83
|
|
|
*/ |
84
|
|
View Code Duplication |
public function createSharedLinkWithSettings(string $path, array $settings = []) |
|
|
|
|
85
|
|
|
{ |
86
|
|
|
$parameters = [ |
87
|
|
|
'path' => $this->normalizePath($path), |
88
|
|
|
'settings' => $settings, |
89
|
|
|
]; |
90
|
|
|
|
91
|
|
|
return $this->rpcEndpointRequest('sharing/create_shared_link_with_settings', $parameters); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* List shared links. |
96
|
|
|
* |
97
|
|
|
* For empty path returns a list of all shared links. For non-empty path |
98
|
|
|
* returns a list of all shared links with access to the given path. |
99
|
|
|
* |
100
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links |
101
|
|
|
*/ |
102
|
|
View Code Duplication |
public function listSharedLinks(string $path): array |
|
|
|
|
103
|
|
|
{ |
104
|
|
|
$parameters = [ |
105
|
|
|
'path' => $this->normalizePath($path), |
106
|
|
|
]; |
107
|
|
|
|
108
|
|
|
$body = $this->rpcEndpointRequest('sharing/list_shared_links', $parameters); |
109
|
|
|
|
110
|
|
|
return $body['links']; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Delete the file or folder at a given path. |
115
|
|
|
* |
116
|
|
|
* If the path is a folder, all its contents will be deleted too. |
117
|
|
|
* A successful response indicates that the file or folder was deleted. |
118
|
|
|
* |
119
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete |
120
|
|
|
*/ |
121
|
|
|
public function delete(string $path): array |
122
|
|
|
{ |
123
|
|
|
$parameters = [ |
124
|
|
|
'path' => $this->normalizePath($path), |
125
|
|
|
]; |
126
|
|
|
|
127
|
|
|
return $this->rpcEndpointRequest('files/delete', $parameters); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Download a file from a user's Dropbox. |
132
|
|
|
* |
133
|
|
|
* @param string $path |
134
|
|
|
* |
135
|
|
|
* @return resource |
136
|
|
|
* |
137
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-download |
138
|
|
|
*/ |
139
|
|
|
public function download(string $path) |
140
|
|
|
{ |
141
|
|
|
$arguments = [ |
142
|
|
|
'path' => $this->normalizePath($path), |
143
|
|
|
]; |
144
|
|
|
|
145
|
|
|
$response = $this->contentEndpointRequest('files/download', $arguments); |
146
|
|
|
|
147
|
|
|
return StreamWrapper::getResource($response->getBody()); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Returns the metadata for a file or folder. |
152
|
|
|
* |
153
|
|
|
* Note: Metadata for the root folder is unsupported. |
154
|
|
|
* |
155
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata |
156
|
|
|
*/ |
157
|
|
|
public function getMetadata(string $path): array |
158
|
|
|
{ |
159
|
|
|
$parameters = [ |
160
|
|
|
'path' => $this->normalizePath($path), |
161
|
|
|
]; |
162
|
|
|
|
163
|
|
|
return $this->rpcEndpointRequest('files/get_metadata', $parameters); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Get a temporary link to stream content of a file. |
168
|
|
|
* |
169
|
|
|
* This link will expire in four hours and afterwards you will get 410 Gone. |
170
|
|
|
* Content-Type of the link is determined automatically by the file's mime type. |
171
|
|
|
* |
172
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link |
173
|
|
|
*/ |
174
|
|
View Code Duplication |
public function getTemporaryLink(string $path): string |
|
|
|
|
175
|
|
|
{ |
176
|
|
|
$parameters = [ |
177
|
|
|
'path' => $this->normalizePath($path), |
178
|
|
|
]; |
179
|
|
|
|
180
|
|
|
$body = $this->rpcEndpointRequest('files/get_temporary_link', $parameters); |
181
|
|
|
|
182
|
|
|
return $body['link']; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Get a thumbnail for an image. |
187
|
|
|
* |
188
|
|
|
* This method currently supports files with the following file extensions: |
189
|
|
|
* jpg, jpeg, png, tiff, tif, gif and bmp. |
190
|
|
|
* |
191
|
|
|
* Photos that are larger than 20MB in size won't be converted to a thumbnail. |
192
|
|
|
* |
193
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_thumbnail |
194
|
|
|
*/ |
195
|
|
|
public function getThumbnail(string $path, string $format = 'jpeg', string $size = 'w64h64'): string |
196
|
|
|
{ |
197
|
|
|
$arguments = [ |
198
|
|
|
'path' => $this->normalizePath($path), |
199
|
|
|
'format' => $format, |
200
|
|
|
'size' => $size, |
201
|
|
|
]; |
202
|
|
|
|
203
|
|
|
$response = $this->contentEndpointRequest('files/get_thumbnail', $arguments); |
204
|
|
|
|
205
|
|
|
return (string) $response->getBody(); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Starts returning the contents of a folder. |
210
|
|
|
* |
211
|
|
|
* If the result's ListFolderResult.has_more field is true, call |
212
|
|
|
* list_folder/continue with the returned ListFolderResult.cursor to retrieve more entries. |
213
|
|
|
* |
214
|
|
|
* Note: auth.RateLimitError may be returned if multiple list_folder or list_folder/continue calls |
215
|
|
|
* with same parameters are made simultaneously by same API app for same user. If your app implements |
216
|
|
|
* retry logic, please hold off the retry until the previous request finishes. |
217
|
|
|
* |
218
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder |
219
|
|
|
*/ |
220
|
|
View Code Duplication |
public function listFolder(string $path = '', bool $recursive = false): array |
|
|
|
|
221
|
|
|
{ |
222
|
|
|
$parameters = [ |
223
|
|
|
'path' => $this->normalizePath($path), |
224
|
|
|
'recursive' => $recursive, |
225
|
|
|
]; |
226
|
|
|
|
227
|
|
|
return $this->rpcEndpointRequest('files/list_folder', $parameters); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Once a cursor has been retrieved from list_folder, use this to paginate through all files and |
232
|
|
|
* retrieve updates to the folder, following the same rules as documented for list_folder. |
233
|
|
|
* |
234
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue |
235
|
|
|
*/ |
236
|
|
|
public function listFolderContinue(string $cursor = ''): array |
237
|
|
|
{ |
238
|
|
|
return $this->rpcEndpointRequest('files/list_folder/continue', compact('cursor')); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Move a file or folder to a different location in the user's Dropbox. |
243
|
|
|
* |
244
|
|
|
* If the source path is a folder all its contents will be moved. |
245
|
|
|
* |
246
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-move |
247
|
|
|
*/ |
248
|
|
View Code Duplication |
public function move(string $fromPath, string $toPath): array |
|
|
|
|
249
|
|
|
{ |
250
|
|
|
$parameters = [ |
251
|
|
|
'from_path' => $this->normalizePath($fromPath), |
252
|
|
|
'to_path' => $this->normalizePath($toPath), |
253
|
|
|
]; |
254
|
|
|
|
255
|
|
|
return $this->rpcEndpointRequest('files/move', $parameters); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Create a new file with the contents provided in the request. |
260
|
|
|
* |
261
|
|
|
* Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start. |
262
|
|
|
* |
263
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload |
264
|
|
|
* |
265
|
|
|
* @param string $path |
266
|
|
|
* @param string|resource $contents |
267
|
|
|
* @param string|array $mode |
268
|
|
|
* |
269
|
|
|
* @return array |
270
|
|
|
*/ |
271
|
|
|
public function upload(string $path, $contents, $mode = 'add'): array |
272
|
|
|
{ |
273
|
|
|
$arguments = [ |
274
|
|
|
'path' => $this->normalizePath($path), |
275
|
|
|
'mode' => $mode, |
276
|
|
|
]; |
277
|
|
|
|
278
|
|
|
$response = $this->contentEndpointRequest('files/upload', $arguments, $contents); |
279
|
|
|
|
280
|
|
|
$metadata = json_decode($response->getBody(), true); |
281
|
|
|
|
282
|
|
|
$metadata['.tag'] = 'file'; |
283
|
|
|
|
284
|
|
|
return $metadata; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Get Account Info for current authenticated user. |
289
|
|
|
* |
290
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account |
291
|
|
|
* |
292
|
|
|
* @return array |
293
|
|
|
*/ |
294
|
|
|
public function getAccountInfo(): array |
295
|
|
|
{ |
296
|
|
|
return $this->rpcEndpointRequest('users/get_current_account'); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Revoke current access token. |
301
|
|
|
* |
302
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke |
303
|
|
|
*/ |
304
|
|
|
public function revokeToken() |
305
|
|
|
{ |
306
|
|
|
$this->rpcEndpointRequest('auth/token/revoke'); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
protected function normalizePath(string $path): string |
310
|
|
|
{ |
311
|
|
|
$path = trim($path, '/'); |
312
|
|
|
|
313
|
|
|
return ($path === '') ? '' : '/'.$path; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* @param string $endpoint |
318
|
|
|
* @param array $arguments |
319
|
|
|
* @param string|resource $body |
320
|
|
|
* |
321
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
322
|
|
|
* |
323
|
|
|
* @throws \Exception |
324
|
|
|
*/ |
325
|
|
|
public function contentEndpointRequest(string $endpoint, array $arguments, $body = ''): ResponseInterface |
326
|
|
|
{ |
327
|
|
|
$headers['Dropbox-API-Arg'] = json_encode($arguments); |
|
|
|
|
328
|
|
|
|
329
|
|
|
if ($body !== '') { |
330
|
|
|
$headers['Content-Type'] = 'application/octet-stream'; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
try { |
334
|
|
|
$response = $this->client->post("https://content.dropboxapi.com/2/{$endpoint}", [ |
335
|
|
|
'headers' => $headers, |
336
|
|
|
'body' => $body, |
337
|
|
|
]); |
338
|
|
|
} catch (ClientException $exception) { |
339
|
|
|
throw $this->determineException($exception); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
return $response; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
public function rpcEndpointRequest(string $endpoint, array $parameters = null): array |
346
|
|
|
{ |
347
|
|
|
try { |
348
|
|
|
$options = []; |
349
|
|
|
|
350
|
|
|
if ($parameters) { |
351
|
|
|
$options['json'] = $parameters; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
$response = $this->client->post("https://api.dropboxapi.com/2/{$endpoint}", $options); |
355
|
|
|
} catch (ClientException $exception) { |
356
|
|
|
throw $this->determineException($exception); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
$response = json_decode($response->getBody(), true); |
360
|
|
|
|
361
|
|
|
return $response ?? []; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
protected function determineException(ClientException $exception): Exception |
365
|
|
|
{ |
366
|
|
|
if (in_array($exception->getResponse()->getStatusCode(), [400, 409])) { |
367
|
|
|
return new BadRequest($exception->getResponse()); |
|
|
|
|
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
return $exception; |
371
|
|
|
} |
372
|
|
|
} |
373
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.