Passed
Push — master ( ff58cd...f7cca5 )
by John
17:34 queued 12s
created

ApiController::getStorageStats()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Daniel Kesselberg <[email protected]>
7
 * @author Felix Nüsse <[email protected]>
8
 * @author fnuesse <[email protected]>
9
 * @author fnuesse <[email protected]>
10
 * @author Joas Schilling <[email protected]>
11
 * @author John Molakvoæ <[email protected]>
12
 * @author Julius Härtl <[email protected]>
13
 * @author Lukas Reschke <[email protected]>
14
 * @author Max Kovalenko <[email protected]>
15
 * @author Morris Jobke <[email protected]>
16
 * @author Nina Pypchenko <[email protected]>
17
 * @author Richard Steinmetz <[email protected]>
18
 * @author Robin Appelman <[email protected]>
19
 * @author Roeland Jago Douma <[email protected]>
20
 * @author Tobias Kaminsky <[email protected]>
21
 * @author Vincent Petry <[email protected]>
22
 *
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program. If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
namespace OCA\Files\Controller;
39
40
use OC\Files\Node\Node;
41
use OCA\Files\Service\TagService;
42
use OCA\Files\Service\UserConfig;
43
use OCA\Files\Service\ViewConfig;
44
use OCP\AppFramework\Controller;
45
use OCP\AppFramework\Http;
46
use OCP\AppFramework\Http\ContentSecurityPolicy;
47
use OCP\AppFramework\Http\DataResponse;
48
use OCP\AppFramework\Http\FileDisplayResponse;
49
use OCP\AppFramework\Http\JSONResponse;
50
use OCP\AppFramework\Http\Response;
51
use OCP\AppFramework\Http\StreamResponse;
52
use OCP\Files\File;
53
use OCP\Files\Folder;
54
use OCP\Files\NotFoundException;
55
use OCP\IConfig;
56
use OCP\IPreview;
57
use OCP\IRequest;
58
use OCP\IUserSession;
59
use OCP\Share\IManager;
60
use OCP\Share\IShare;
61
62
/**
63
 * Class ApiController
64
 *
65
 * @package OCA\Files\Controller
66
 */
67
class ApiController extends Controller {
68
	private TagService $tagService;
69
	private IManager $shareManager;
70
	private IPreview $previewManager;
71
	private IUserSession $userSession;
72
	private IConfig $config;
73
	private Folder $userFolder;
74
	private UserConfig $userConfig;
75
	private ViewConfig $viewConfig;
76
77
	/**
78
	 * @param string $appName
79
	 * @param IRequest $request
80
	 * @param IUserSession $userSession
81
	 * @param TagService $tagService
82
	 * @param IPreview $previewManager
83
	 * @param IManager $shareManager
84
	 * @param IConfig $config
85
	 * @param Folder $userFolder
86
	 */
87
	public function __construct($appName,
88
								IRequest $request,
89
								IUserSession $userSession,
90
								TagService $tagService,
91
								IPreview $previewManager,
92
								IManager $shareManager,
93
								IConfig $config,
94
								Folder $userFolder,
95
								UserConfig $userConfig,
96
								ViewConfig $viewConfig) {
97
		parent::__construct($appName, $request);
98
		$this->userSession = $userSession;
99
		$this->tagService = $tagService;
100
		$this->previewManager = $previewManager;
101
		$this->shareManager = $shareManager;
102
		$this->config = $config;
103
		$this->userFolder = $userFolder;
104
		$this->userConfig = $userConfig;
105
		$this->viewConfig = $viewConfig;
106
	}
107
108
	/**
109
	 * Gets a thumbnail of the specified file
110
	 *
111
	 * @since API version 1.0
112
	 *
113
	 * @NoAdminRequired
114
	 * @NoCSRFRequired
115
	 * @StrictCookieRequired
116
	 *
117
	 * @param int $x
118
	 * @param int $y
119
	 * @param string $file URL-encoded filename
120
	 * @return DataResponse|FileDisplayResponse
121
	 */
122
	public function getThumbnail($x, $y, $file) {
123
		if ($x < 1 || $y < 1) {
124
			return new DataResponse(['message' => 'Requested size must be numeric and a positive value.'], Http::STATUS_BAD_REQUEST);
125
		}
126
127
		try {
128
			$file = $this->userFolder->get($file);
129
			if ($file instanceof Folder) {
130
				throw new NotFoundException();
131
			}
132
133
			/** @var File $file */
134
			$preview = $this->previewManager->getPreview($file, $x, $y, true);
135
136
			return new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]);
137
		} catch (NotFoundException $e) {
138
			return new DataResponse(['message' => 'File not found.'], Http::STATUS_NOT_FOUND);
139
		} catch (\Exception $e) {
140
			return new DataResponse([], Http::STATUS_BAD_REQUEST);
141
		}
142
	}
143
144
	/**
145
	 * Updates the info of the specified file path
146
	 * The passed tags are absolute, which means they will
147
	 * replace the actual tag selection.
148
	 *
149
	 * @NoAdminRequired
150
	 *
151
	 * @param string $path path
152
	 * @param array|string $tags array of tags
153
	 * @return DataResponse
154
	 */
155
	public function updateFileTags($path, $tags = null) {
156
		$result = [];
157
		// if tags specified or empty array, update tags
158
		if (!is_null($tags)) {
159
			try {
160
				$this->tagService->updateFileTags($path, $tags);
161
			} catch (\OCP\Files\NotFoundException $e) {
162
				return new DataResponse([
163
					'message' => $e->getMessage()
164
				], Http::STATUS_NOT_FOUND);
165
			} catch (\OCP\Files\StorageNotAvailableException $e) {
166
				return new DataResponse([
167
					'message' => $e->getMessage()
168
				], Http::STATUS_SERVICE_UNAVAILABLE);
169
			} catch (\Exception $e) {
170
				return new DataResponse([
171
					'message' => $e->getMessage()
172
				], Http::STATUS_NOT_FOUND);
173
			}
174
			$result['tags'] = $tags;
175
		}
176
		return new DataResponse($result);
177
	}
178
179
	/**
180
	 * @param \OCP\Files\Node[] $nodes
181
	 * @return array
182
	 */
183
	private function formatNodes(array $nodes) {
184
		$shareTypesForNodes = $this->getShareTypesForNodes($nodes);
185
		return array_values(array_map(function (Node $node) use ($shareTypesForNodes) {
186
			$shareTypes = $shareTypesForNodes[$node->getId()] ?? [];
187
			$file = \OCA\Files\Helper::formatFileInfo($node->getFileInfo());
188
			$file['hasPreview'] = $this->previewManager->isAvailable($node);
189
			$parts = explode('/', dirname($node->getPath()), 4);
190
			if (isset($parts[3])) {
191
				$file['path'] = '/' . $parts[3];
192
			} else {
193
				$file['path'] = '/';
194
			}
195
			if (!empty($shareTypes)) {
196
				$file['shareTypes'] = $shareTypes;
197
			}
198
			return $file;
199
		}, $nodes));
200
	}
201
202
	/**
203
	 * Get the share types for each node
204
	 *
205
	 * @param \OCP\Files\Node[] $nodes
206
	 * @return array<int, int[]> list of share types for each fileid
207
	 */
208
	private function getShareTypesForNodes(array $nodes): array {
209
		$userId = $this->userSession->getUser()->getUID();
210
		$requestedShareTypes = [
211
			IShare::TYPE_USER,
212
			IShare::TYPE_GROUP,
213
			IShare::TYPE_LINK,
214
			IShare::TYPE_REMOTE,
215
			IShare::TYPE_EMAIL,
216
			IShare::TYPE_ROOM,
217
			IShare::TYPE_DECK,
218
			IShare::TYPE_SCIENCEMESH,
219
		];
220
		$shareTypes = [];
221
222
		$nodeIds = array_map(function (Node $node) {
223
			return $node->getId();
224
		}, $nodes);
225
226
		foreach ($requestedShareTypes as $shareType) {
227
			$nodesLeft = array_combine($nodeIds, array_fill(0, count($nodeIds), true));
228
			$offset = 0;
229
230
			// fetch shares until we've either found shares for all nodes or there are no more shares left
231
			while (count($nodesLeft) > 0) {
232
				$shares = $this->shareManager->getSharesBy($userId, $shareType, null, false, 100, $offset);
233
				foreach ($shares as $share) {
234
					$fileId = $share->getNodeId();
235
					if (isset($nodesLeft[$fileId])) {
236
						if (!isset($shareTypes[$fileId])) {
237
							$shareTypes[$fileId] = [];
238
						}
239
						$shareTypes[$fileId][] = $shareType;
240
						unset($nodesLeft[$fileId]);
241
					}
242
				}
243
244
				if (count($shares) < 100) {
245
					break;
246
				} else {
247
					$offset += count($shares);
248
				}
249
			}
250
		}
251
		return $shareTypes;
252
	}
253
254
	/**
255
	 * Returns a list of recently modified files.
256
	 *
257
	 * @NoAdminRequired
258
	 *
259
	 * @return DataResponse
260
	 */
261
	public function getRecentFiles() {
262
		$nodes = $this->userFolder->getRecent(100);
263
		$files = $this->formatNodes($nodes);
264
		return new DataResponse(['files' => $files]);
265
	}
266
267
268
	/**
269
	 * Returns the current logged-in user's storage stats.
270
	 *
271
	 * @NoAdminRequired
272
	 *
273
	 * @param ?string $dir the directory to get the storage stats from
274
	 * @return JSONResponse
275
	 */
276
	public function getStorageStats($dir = '/'): JSONResponse {
277
		$storageInfo = \OC_Helper::getStorageInfo($dir ?: '/');
278
		return new JSONResponse(['message' => 'ok', 'data' => $storageInfo]);
279
	}
280
281
	/**
282
	 * Set a user view config
283
	 *
284
	 * @NoAdminRequired
285
	 *
286
	 * @param string $view
287
	 * @param string $key
288
	 * @param string|bool $value
289
	 * @return JSONResponse
290
	 */
291
	public function setViewConfig(string $view, string $key, $value): JSONResponse {
292
		try {
293
			$this->viewConfig->setConfig($view, $key, (string)$value);
294
		} catch (\InvalidArgumentException $e) {
295
			return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
296
		}
297
298
		return new JSONResponse(['message' => 'ok', 'data' => $this->viewConfig->getConfig($view)]);
299
	}
300
301
302
	/**
303
	 * Get the user view config
304
	 *
305
	 * @NoAdminRequired
306
	 *
307
	 * @return JSONResponse
308
	 */
309
	public function getViewConfigs(): JSONResponse {
310
		return new JSONResponse(['message' => 'ok', 'data' => $this->viewConfig->getConfigs()]);
311
	}
312
313
	/**
314
	 * Set a user config
315
	 *
316
	 * @NoAdminRequired
317
	 *
318
	 * @param string $key
319
	 * @param string|bool $value
320
	 * @return JSONResponse
321
	 */
322
	public function setConfig(string $key, $value): JSONResponse {
323
		try {
324
			$this->userConfig->setConfig($key, (string)$value);
325
		} catch (\InvalidArgumentException $e) {
326
			return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
327
		}
328
329
		return new JSONResponse(['message' => 'ok', 'data' => ['key' => $key, 'value' => $value]]);
330
	}
331
332
333
	/**
334
	 * Get the user config
335
	 *
336
	 * @NoAdminRequired
337
	 *
338
	 * @return JSONResponse
339
	 */
340
	public function getConfigs(): JSONResponse {
341
		return new JSONResponse(['message' => 'ok', 'data' => $this->userConfig->getConfigs()]);
342
	}
343
344
	/**
345
	 * Toggle default for showing/hiding hidden files
346
	 *
347
	 * @NoAdminRequired
348
	 *
349
	 * @param bool $value
350
	 * @return Response
351
	 * @throws \OCP\PreConditionNotMetException
352
	 */
353
	public function showHiddenFiles(bool $value): Response {
354
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', $value ? '1' : '0');
355
		return new Response();
356
	}
357
358
	/**
359
	 * Toggle default for cropping preview images
360
	 *
361
	 * @NoAdminRequired
362
	 *
363
	 * @param bool $value
364
	 * @return Response
365
	 * @throws \OCP\PreConditionNotMetException
366
	 */
367
	public function cropImagePreviews(bool $value): Response {
368
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'crop_image_previews', $value ? '1' : '0');
369
		return new Response();
370
	}
371
372
	/**
373
	 * Toggle default for files grid view
374
	 *
375
	 * @NoAdminRequired
376
	 *
377
	 * @param bool $show
378
	 * @return Response
379
	 * @throws \OCP\PreConditionNotMetException
380
	 */
381
	public function showGridView(bool $show): Response {
382
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'show_grid', $show ? '1' : '0');
383
		return new Response();
384
	}
385
386
	/**
387
	 * Get default settings for the grid view
388
	 *
389
	 * @NoAdminRequired
390
	 */
391
	public function getGridView() {
392
		$status = $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_grid', '0') === '1';
393
		return new JSONResponse(['gridview' => $status]);
394
	}
395
396
	/**
397
	 * Get sorting-order for custom sorting
398
	 *
399
	 * @NoAdminRequired
400
	 *
401
	 * @param string $folderpath
402
	 * @return string
403
	 * @throws \OCP\Files\NotFoundException
404
	 */
405
	public function getNodeType($folderpath) {
406
		$node = $this->userFolder->get($folderpath);
407
		return $node->getType();
408
	}
409
	
410
	/**
411
	 * @NoAdminRequired
412
	 * @NoCSRFRequired
413
	 */
414
	public function serviceWorker(): StreamResponse {
415
		$response = new StreamResponse(__DIR__ . '/../../../../dist/preview-service-worker.js');
416
		$response->setHeaders([
417
			'Content-Type' => 'application/javascript',
418
			'Service-Worker-Allowed' => '/'
419
		]);
420
		$policy = new ContentSecurityPolicy();
421
		$policy->addAllowedWorkerSrcDomain("'self'");
422
		$policy->addAllowedScriptDomain("'self'");
423
		$policy->addAllowedConnectDomain("'self'");
424
		$response->setContentSecurityPolicy($policy);
425
		return $response;
426
	}
427
}
428