1 | <?php |
||
2 | |||
3 | namespace League\Flysystem\Dropbox; |
||
4 | |||
5 | use Dropbox\Client; |
||
6 | use Dropbox\Exception; |
||
7 | use Dropbox\Exception_BadResponseCode; |
||
8 | use Dropbox\WriteMode; |
||
9 | use League\Flysystem\Adapter\AbstractAdapter; |
||
10 | use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait; |
||
11 | use League\Flysystem\Config; |
||
12 | use League\Flysystem\Util; |
||
13 | |||
14 | class DropboxAdapter extends AbstractAdapter |
||
15 | { |
||
16 | use NotSupportingVisibilityTrait; |
||
17 | |||
18 | /** |
||
19 | * @var array |
||
20 | */ |
||
21 | protected static $resultMap = [ |
||
22 | 'bytes' => 'size', |
||
23 | 'mime_type' => 'mimetype', |
||
24 | ]; |
||
25 | |||
26 | /** |
||
27 | * @var Client |
||
28 | */ |
||
29 | protected $client; |
||
30 | |||
31 | /** |
||
32 | * Constructor. |
||
33 | * |
||
34 | * @param Client $client |
||
35 | * @param string $prefix |
||
36 | */ |
||
37 | 21 | public function __construct(Client $client, $prefix = null) |
|
38 | { |
||
39 | 21 | $this->client = $client; |
|
40 | 21 | $this->setPathPrefix($prefix); |
|
41 | 21 | } |
|
42 | |||
43 | /** |
||
44 | * {@inheritdoc} |
||
45 | */ |
||
46 | 9 | public function has($path) |
|
47 | { |
||
48 | 9 | return $this->getMetadata($path); |
|
49 | } |
||
50 | |||
51 | /** |
||
52 | * {@inheritdoc} |
||
53 | */ |
||
54 | 3 | public function write($path, $contents, Config $config) |
|
55 | { |
||
56 | 3 | return $this->upload($path, $contents, WriteMode::add()); |
|
57 | } |
||
58 | |||
59 | /** |
||
60 | * {@inheritdoc} |
||
61 | */ |
||
62 | 3 | public function writeStream($path, $resource, Config $config) |
|
63 | { |
||
64 | 3 | return $this->uploadStream($path, $resource, WriteMode::add()); |
|
65 | } |
||
66 | |||
67 | /** |
||
68 | * {@inheritdoc} |
||
69 | */ |
||
70 | 3 | public function update($path, $contents, Config $config) |
|
71 | { |
||
72 | 3 | return $this->upload($path, $contents, WriteMode::force()); |
|
73 | } |
||
74 | |||
75 | /** |
||
76 | * {@inheritdoc} |
||
77 | */ |
||
78 | 3 | public function updateStream($path, $resource, Config $config) |
|
79 | { |
||
80 | 3 | return $this->uploadStream($path, $resource, WriteMode::force()); |
|
81 | } |
||
82 | |||
83 | /** |
||
84 | * {@inheritdoc} |
||
85 | */ |
||
86 | 3 | public function read($path) |
|
87 | { |
||
88 | 3 | if ( ! $object = $this->readStream($path)) { |
|
89 | 3 | return false; |
|
90 | } |
||
91 | |||
92 | 3 | $object['contents'] = stream_get_contents($object['stream']); |
|
93 | 3 | fclose($object['stream']); |
|
94 | 3 | unset($object['stream']); |
|
95 | |||
96 | 3 | return $object; |
|
97 | } |
||
98 | |||
99 | /** |
||
100 | * {@inheritdoc} |
||
101 | */ |
||
102 | 6 | public function readStream($path) |
|
103 | { |
||
104 | 6 | $stream = fopen('php://temp', 'w+'); |
|
105 | 6 | $location = $this->applyPathPrefix($path); |
|
106 | |||
107 | 6 | if ( ! $this->client->getFile($location, $stream)) { |
|
108 | 6 | fclose($stream); |
|
109 | |||
110 | 6 | return false; |
|
111 | } |
||
112 | |||
113 | 6 | rewind($stream); |
|
114 | |||
115 | 6 | return compact('stream'); |
|
116 | } |
||
117 | |||
118 | /** |
||
119 | * {@inheritdoc} |
||
120 | */ |
||
121 | 6 | public function rename($path, $newpath) |
|
122 | { |
||
123 | 6 | $path = $this->applyPathPrefix($path); |
|
124 | 6 | $newpath = $this->applyPathPrefix($newpath); |
|
125 | |||
126 | try { |
||
127 | 6 | $this->client->move($path, $newpath); |
|
128 | 6 | } catch (Exception $e) { |
|
129 | 3 | return false; |
|
130 | } |
||
131 | |||
132 | 3 | return true; |
|
133 | } |
||
134 | |||
135 | /** |
||
136 | * {@inheritdoc} |
||
137 | */ |
||
138 | 6 | public function copy($path, $newpath) |
|
139 | { |
||
140 | 6 | $path = $this->applyPathPrefix($path); |
|
141 | 6 | $newpath = $this->applyPathPrefix($newpath); |
|
142 | |||
143 | try { |
||
144 | 6 | $this->client->copy($path, $newpath); |
|
145 | 6 | } catch (Exception $e) { |
|
146 | 3 | return false; |
|
147 | } |
||
148 | |||
149 | 3 | return true; |
|
150 | } |
||
151 | |||
152 | /** |
||
153 | * {@inheritdoc} |
||
154 | */ |
||
155 | 3 | public function delete($path) |
|
156 | { |
||
157 | 3 | $location = $this->applyPathPrefix($path); |
|
158 | 3 | $result = $this->client->delete($location); |
|
159 | |||
160 | 3 | return isset($result['is_deleted']) ? $result['is_deleted'] : false; |
|
161 | } |
||
162 | |||
163 | /** |
||
164 | * {@inheritdoc} |
||
165 | */ |
||
166 | 3 | public function deleteDir($path) |
|
167 | { |
||
168 | 3 | return $this->delete($path); |
|
169 | } |
||
170 | |||
171 | /** |
||
172 | * {@inheritdoc} |
||
173 | */ |
||
174 | 3 | public function createDir($path, Config $config) |
|
175 | { |
||
176 | 3 | $location = $this->applyPathPrefix($path); |
|
177 | 3 | $result = $this->client->createFolder($location); |
|
178 | |||
179 | 3 | if ($result === null) { |
|
180 | 3 | return false; |
|
181 | } |
||
182 | |||
183 | 3 | return $this->normalizeResponse($result, $path); |
|
184 | } |
||
185 | |||
186 | /** |
||
187 | * {@inheritdoc} |
||
188 | */ |
||
189 | 21 | public function getMetadata($path) |
|
190 | { |
||
191 | 21 | $location = $this->applyPathPrefix($path); |
|
192 | |||
193 | try { |
||
194 | 21 | $object = $this->client->getMetadata($location); |
|
195 | 21 | } catch(Exception_BadResponseCode $e) { |
|
196 | 6 | if ($e->getStatusCode() === 301) { |
|
197 | 3 | return false; |
|
198 | } |
||
199 | |||
200 | 3 | throw $e; |
|
201 | } |
||
202 | |||
203 | 15 | if ( ! $object) { |
|
204 | 15 | return false; |
|
205 | } |
||
206 | |||
207 | 15 | return $this->normalizeResponse($object, $path); |
|
208 | } |
||
209 | |||
210 | /** |
||
211 | * {@inheritdoc} |
||
212 | */ |
||
213 | 3 | public function getMimetype($path) |
|
214 | { |
||
215 | 3 | return $this->getMetadata($path); |
|
216 | } |
||
217 | |||
218 | /** |
||
219 | * {@inheritdoc} |
||
220 | */ |
||
221 | 3 | public function getSize($path) |
|
222 | { |
||
223 | 3 | return $this->getMetadata($path); |
|
224 | } |
||
225 | |||
226 | /** |
||
227 | * {@inheritdoc} |
||
228 | */ |
||
229 | 3 | public function getTimestamp($path) |
|
230 | { |
||
231 | 3 | return $this->getMetadata($path); |
|
232 | } |
||
233 | |||
234 | /** |
||
235 | * {@inheritdoc} |
||
236 | */ |
||
237 | 3 | public function getClient() |
|
238 | { |
||
239 | 3 | return $this->client; |
|
240 | } |
||
241 | |||
242 | /** |
||
243 | * {@inheritdoc} |
||
244 | */ |
||
245 | 3 | public function listContents($directory = '', $recursive = false) |
|
246 | { |
||
247 | 3 | $listing = []; |
|
248 | 3 | $directory = trim($directory, '/.'); |
|
249 | 3 | $location = $this->applyPathPrefix($directory); |
|
250 | |||
251 | 3 | if ( ! $result = $this->client->getMetadataWithChildren($location)) { |
|
252 | 3 | return []; |
|
253 | } |
||
254 | |||
255 | 3 | foreach ($result['contents'] as $object) { |
|
256 | 3 | $path = $this->removePathPrefix($object['path']); |
|
257 | 3 | $listing[] = $this->normalizeResponse($object, $path); |
|
258 | |||
259 | 3 | if ($recursive && $object['is_dir']) { |
|
260 | 3 | $listing = array_merge($listing, $this->listContents($path, true)); |
|
261 | 3 | } |
|
262 | 3 | } |
|
263 | |||
264 | 3 | return $listing; |
|
265 | } |
||
266 | |||
267 | /** |
||
268 | * Apply the path prefix. |
||
269 | * |
||
270 | * @param string $path |
||
271 | * |
||
272 | * @return string prefixed path |
||
273 | */ |
||
274 | 60 | public function applyPathPrefix($path) |
|
275 | { |
||
276 | |||
277 | 60 | $path = parent::applyPathPrefix($path); |
|
278 | |||
279 | 60 | return '/' . ltrim(rtrim($path, '/'), '/'); |
|
280 | } |
||
281 | |||
282 | /** |
||
283 | * Do the actual upload of a string file. |
||
284 | * |
||
285 | * @param string $path |
||
286 | * @param string $contents |
||
287 | * @param WriteMode $mode |
||
288 | * |
||
289 | * @return array|false file metadata |
||
290 | */ |
||
291 | 6 | protected function upload($path, $contents, WriteMode $mode) |
|
292 | { |
||
293 | 6 | $location = $this->applyPathPrefix($path); |
|
294 | |||
295 | 6 | if ( ! $result = $this->client->uploadFileFromString($location, $mode, $contents)) { |
|
296 | 6 | return false; |
|
297 | } |
||
298 | |||
299 | 6 | return $this->normalizeResponse($result, $path); |
|
300 | } |
||
301 | |||
302 | /** |
||
303 | * Do the actual upload of a file resource. |
||
304 | * |
||
305 | * @param string $path |
||
306 | * @param resource $resource |
||
307 | * @param WriteMode $mode |
||
308 | * |
||
309 | * @return array|false file metadata |
||
310 | */ |
||
311 | 6 | protected function uploadStream($path, $resource, WriteMode $mode) |
|
312 | { |
||
313 | 6 | $location = $this->applyPathPrefix($path); |
|
314 | |||
315 | // If size is zero, consider it unknown. |
||
316 | 6 | $size = Util::getStreamSize($resource) ?: null; |
|
317 | |||
318 | 6 | if ( ! $result = $this->client->uploadFile($location, $mode, $resource, $size)) { |
|
319 | 6 | return false; |
|
320 | } |
||
321 | |||
322 | 6 | return $this->normalizeResponse($result, $path); |
|
323 | } |
||
324 | |||
325 | /** |
||
326 | * Normalize a Dropbox response. |
||
327 | * |
||
328 | * @param array $response |
||
329 | * |
||
330 | * @return array |
||
331 | */ |
||
332 | 33 | protected function normalizeResponse(array $response) |
|
333 | { |
||
334 | 33 | $result = ['path' => ltrim($this->removePathPrefix($response['path']), '/')]; |
|
335 | |||
336 | 33 | if (isset($response['modified'])) { |
|
337 | 27 | $result['timestamp'] = strtotime($response['modified']); |
|
338 | 27 | } |
|
339 | |||
340 | 33 | $result = array_merge($result, Util::map($response, static::$resultMap)); |
|
341 | 33 | $result['type'] = $response['is_dir'] ? 'dir' : 'file'; |
|
342 | |||
343 | 33 | return $result; |
|
344 | } |
||
345 | } |
||
346 |