Passed
Push — master ( de702b...3e9331 )
by Pauli
02:18
created

ApiController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 16
dl 0
loc 31
ccs 16
cts 16
cp 1
crap 1
rs 9.7666
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Morris Jobke <[email protected]>
10
 * @author Pauli Järvinen <[email protected]>
11
 * @copyright Morris Jobke 2013, 2014
12
 * @copyright Pauli Järvinen 2017 - 2020
13
 */
14
15
namespace OCA\Music\Controller;
16
17
use \OCP\AppFramework\Controller;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Controller was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use \OCP\AppFramework\Http;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use \OCP\AppFramework\Http\DataDisplayResponse;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http\DataDisplayResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use \OCP\AppFramework\Http\JSONResponse;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http\JSONResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use \OCP\AppFramework\Http\RedirectResponse;
0 ignored issues
show
Bug introduced by
The type OCP\AppFramework\Http\RedirectResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use \OCP\Files\Folder;
0 ignored issues
show
Bug introduced by
The type OCP\Files\Folder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use \OCP\IL10N;
0 ignored issues
show
Bug introduced by
The type OCP\IL10N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use \OCP\IRequest;
0 ignored issues
show
Bug introduced by
The type OCP\IRequest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use \OCP\IURLGenerator;
0 ignored issues
show
Bug introduced by
The type OCP\IURLGenerator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
27
use \OCA\Music\AppFramework\Core\Logger;
28
use \OCA\Music\BusinessLayer\AlbumBusinessLayer;
29
use \OCA\Music\BusinessLayer\ArtistBusinessLayer;
30
use \OCA\Music\BusinessLayer\TrackBusinessLayer;
31
use \OCA\Music\Db\Album;
32
use \OCA\Music\Db\Artist;
33
use \OCA\Music\Db\Maintenance;
34
use \OCA\Music\Db\Track;
35
use \OCA\Music\Http\ErrorResponse;
36
use \OCA\Music\Http\FileResponse;
37
use \OCA\Music\Utility\CollectionHelper;
38
use \OCA\Music\Utility\CoverHelper;
39
use \OCA\Music\Utility\DetailsHelper;
40
use \OCA\Music\Utility\Scanner;
41
use OCA\Music\BusinessLayer\GenreBusinessLayer;
42
43
class ApiController extends Controller {
44
45
	/** @var IL10N */
46
	private $l10n;
47
	/** @var TrackBusinessLayer */
48
	private $trackBusinessLayer;
49
	/** @var ArtistBusinessLayer */
50
	private $artistBusinessLayer;
51
	/** @var AlbumBusinessLayer */
52
	private $albumBusinessLayer;
53
	/** @var GenreBusinessLayer */
54
	private $genreBusinessLayer;
55
	/** @var Scanner */
56
	private $scanner;
57
	/** @var CollectionHelper */
58
	private $collectionHelper;
59
	/** @var CoverHelper */
60
	private $coverHelper;
61
	/** @var DetailsHelper */
62
	private $detailsHelper;
63
	/** @var Maintenance */
64
	private $maintenance;
65
	/** @var string */
66
	private $userId;
67
	/** @var IURLGenerator */
68
	private $urlGenerator;
69
	/** @var Folder */
70
	private $userFolder;
71
	/** @var Logger */
72
	private $logger;
73
74 16
	public function __construct($appname,
75
								IRequest $request,
76
								IURLGenerator $urlGenerator,
77
								TrackBusinessLayer $trackbusinesslayer,
78
								ArtistBusinessLayer $artistbusinesslayer,
79
								AlbumBusinessLayer $albumbusinesslayer,
80
								GenreBusinessLayer $genreBusinessLayer,
81
								Scanner $scanner,
82
								CollectionHelper $collectionHelper,
83
								CoverHelper $coverHelper,
84
								DetailsHelper $detailsHelper,
85
								Maintenance $maintenance,
86
								$userId,
87
								IL10N $l10n,
88
								/*Folder*/ $userFolder, // no type-hint as this may sometimes be null
89
								Logger $logger) {
90 16
		parent::__construct($appname, $request);
91 16
		$this->l10n = $l10n;
92 16
		$this->trackBusinessLayer = $trackbusinesslayer;
93 16
		$this->artistBusinessLayer = $artistbusinesslayer;
94 16
		$this->albumBusinessLayer = $albumbusinesslayer;
95 16
		$this->genreBusinessLayer = $genreBusinessLayer;
96 16
		$this->scanner = $scanner;
97 16
		$this->collectionHelper = $collectionHelper;
98 16
		$this->coverHelper = $coverHelper;
99 16
		$this->detailsHelper = $detailsHelper;
100 16
		$this->maintenance = $maintenance;
101 16
		$this->userId = $userId;
102 16
		$this->urlGenerator = $urlGenerator;
103 16
		$this->userFolder = $userFolder;
104 16
		$this->logger = $logger;
105 16
	}
106
107
	/**
108
	 * Extracts the id from an unique slug (id-slug)
109
	 * @param string $slug the slug
110
	 * @return integer the id
111
	 */
112 2
	protected static function getIdFromSlug($slug) {
113 2
		$split = \explode('-', $slug, 2);
114
115 2
		return (int)$split[0];
116
	}
117
118
	/**
119
	 * @NoAdminRequired
120
	 * @NoCSRFRequired
121
	 */
122
	public function prepareCollection() {
123
		$hash = $this->collectionHelper->getCachedJsonHash();
124
		if ($hash === null) {
125
			// build the collection but ignore the data for now
126
			$this->collectionHelper->getJson();
127
			$hash = $this->collectionHelper->getCachedJsonHash();
128
		}
129
		return new JSONResponse(['hash' => $hash]);
130
	}
131
132
	/**
133
	 * @NoAdminRequired
134
	 * @NoCSRFRequired
135
	 */
136
	public function collection() {
137
138
		$collectionJson = $this->collectionHelper->getJson();
139
		$response = new DataDisplayResponse($collectionJson);
140
		$response->addHeader('Content-Type', 'application/json; charset=utf-8');
141
142
		// Instruct the client to cache the result in case it requested the collection with
143
		// the correct hash. The hash could be incorrect if the collection would have changed
144
		// between calls to prepareCollection() and colletion().
145
		$requestHash = $this->request->getParam('hash');
146
		$actualHash = $this->collectionHelper->getCachedJsonHash();
147
		if (!empty($actualHash) && $requestHash === $actualHash) {
148
			self::setClientCaching($response, 90); // cache for 3 months
149
		}
150
151
		return $response;
152
	}
153
154
	/**
155
	 * @NoAdminRequired
156
	 * @NoCSRFRequired
157
	 */
158
	public function folders() {
159
		$folders = $this->trackBusinessLayer->findAllFolders($this->userId, $this->userFolder);
160
		return new JSONResponse($folders);
161
	}
162
163
	/**
164
	 * @NoAdminRequired
165
	 * @NoCSRFRequired
166
	 */
167
	public function genres() {
168
		$genres = $this->genreBusinessLayer->findAllWithTrackIds($this->userId);
169
		$unscanned =  $this->trackBusinessLayer->findFilesWithoutScannedGenre($this->userId);
170
		return new JSONResponse([
171
			'genres' => \array_map(function($g) {return $g->toApi();}, $genres),
172
			'unscanned' => $unscanned
173
		]);
174
	}
175
176
	/**
177
	 * @NoAdminRequired
178
	 * @NoCSRFRequired
179
	 */
180
	public function artists($fulltree, $albums) {
181
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
182
		$includeAlbums = \filter_var($albums, FILTER_VALIDATE_BOOLEAN);
183
		/** @var Artist[] $artists */
184
		$artists = $this->artistBusinessLayer->findAll($this->userId);
185
186
		$artists = \array_map(function($a) use ($fulltree, $includeAlbums) {
187
			return $this->artistToApi($a, $includeAlbums || $fulltree, $fulltree);
188
		}, $artists);
189
190
		return new JSONResponse($artists);
191
	}
192
193
	/**
194
	 * @NoAdminRequired
195
	 * @NoCSRFRequired
196
	 */
197
	public function artist($artistIdOrSlug, $fulltree) {
198
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
199
		$artistId = $this->getIdFromSlug($artistIdOrSlug);
200
		/** @var Artist $artist */
201
		$artist = $this->artistBusinessLayer->find($artistId, $this->userId);
202
		$artist = $this->artistToApi($artist, $fulltree, $fulltree);
203
		return new JSONResponse($artist);
204
	}
205
206
	/**
207
	 * Return given artist in Shia API format
208
	 * @param Artist $artist
209
	 * @param boolean $includeAlbums
210
	 * @param boolean $includeTracks (ignored if $includeAlbums==false)
211
	 * @return array
212
	 */
213
	private function artistToApi($artist, $includeAlbums, $includeTracks) {
214
		$artistInApi = $artist->toAPI($this->urlGenerator, $this->l10n);
215
		if ($includeAlbums) {
216
			$artistId = $artist->getId();
217
			$albums = $this->albumBusinessLayer->findAllByArtist($artistId, $this->userId);
218
219
			$artistInApi['albums'] = \array_map(function($a) use ($includeTracks) {
220
				return $this->albumToApi($a, $includeTracks, false);
221
			}, $albums);
222
		}
223
		return $artistInApi;
224
	}
225
226
	/**
227
	 * @NoAdminRequired
228
	 * @NoCSRFRequired
229
	 */
230 1
	public function albums($artist, $fulltree) {
231 1
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
232 1
		if ($artist) {
233
			$albums = $this->albumBusinessLayer->findAllByArtist($artist, $this->userId);
234
		} else {
235 1
			$albums = $this->albumBusinessLayer->findAll($this->userId);
236
		}
237
238
		$albums = \array_map(function($a) use ($fulltree) {
239 1
			return $this->albumToApi($a, $fulltree, $fulltree);
240 1
		}, $albums);
241
242 1
		return new JSONResponse($albums);
243
	}
244
245
	/**
246
	 * @NoAdminRequired
247
	 * @NoCSRFRequired
248
	 */
249 1
	public function album($albumIdOrSlug, $fulltree) {
250 1
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
251 1
		$albumId = $this->getIdFromSlug($albumIdOrSlug);
252 1
		$album = $this->albumBusinessLayer->find($albumId, $this->userId);
253 1
		$album = $this->albumToApi($album, $fulltree, $fulltree);
254 1
		return new JSONResponse($album);
255
	}
256
257
	/**
258
	 * Return given album in the Shiva API format
259
	 * @param Album $album
260
	 * @param boolean $includeTracks
261
	 * @param boolean $includeAlbums
262
	 * @return array
263
	 */
264 2
	private function albumToApi($album, $includeTracks, $includeArtists) {
265 2
		$albumInApi = $album->toAPI($this->urlGenerator, $this->l10n);
266
267 2
		if ($includeTracks) {
268
			$albumId = $album->getId();
269
			$tracks = $this->trackBusinessLayer->findAllByAlbum($albumId, $this->userId);
270
			$albumInApi['tracks'] = \array_map(function($t) {
271
				return $t->toAPI($this->urlGenerator);
272
			}, $tracks);
273
		}
274
275 2
		if ($includeArtists) {
276
			$artistIds = $album->getArtistIds();
277
			$artists = $this->artistBusinessLayer->findById($artistIds, $this->userId);
278
			$albumInApi['artists'] = \array_map(function($a) {
279
				return $a->toAPI($this->urlGenerator, $this->l10n);
280
			}, $artists);
281
		}
282
283 2
		return $albumInApi;
284
	}
285
286
	/**
287
	 * @NoAdminRequired
288
	 * @NoCSRFRequired
289
	 */
290 3
	public function tracks($artist, $album, $fulltree) {
291 3
		$fulltree = \filter_var($fulltree, FILTER_VALIDATE_BOOLEAN);
292 3
		if ($artist) {
293 1
			$tracks = $this->trackBusinessLayer->findAllByArtist($artist, $this->userId);
294 2
		} elseif ($album) {
295 1
			$tracks = $this->trackBusinessLayer->findAllByAlbum($album, $this->userId);
296
		} else {
297 1
			$tracks = $this->trackBusinessLayer->findAll($this->userId);
298
		}
299 3
		foreach ($tracks as &$track) {
300 3
			$artistId = $track->getArtistId();
301 3
			$albumId = $track->getAlbumId();
302 3
			$track = $track->toAPI($this->urlGenerator);
303 3
			if ($fulltree) {
304
				/** @var Artist $artist */
305
				$artist = $this->artistBusinessLayer->find($artistId, $this->userId);
306
				$track['artist'] = $artist->toAPI($this->urlGenerator, $this->l10n);
307
				$album = $this->albumBusinessLayer->find($albumId, $this->userId);
308 3
				$track['album'] = $album->toAPI($this->urlGenerator, $this->l10n);
309
			}
310
		}
311 3
		return new JSONResponse($tracks);
312
	}
313
314
	/**
315
	 * @NoAdminRequired
316
	 * @NoCSRFRequired
317
	 */
318 1
	public function track($trackIdOrSlug) {
319 1
		$trackId = $this->getIdFromSlug($trackIdOrSlug);
320
		/** @var Track $track */
321 1
		$track = $this->trackBusinessLayer->find($trackId, $this->userId);
322 1
		return new JSONResponse($track->toAPI($this->urlGenerator));
323
	}
324
325
	/**
326
	 * @NoAdminRequired
327
	 * @NoCSRFRequired
328
	 */
329 1
	public function trackByFileId($fileId) {
330 1
		$track = $this->trackBusinessLayer->findByFileId($fileId, $this->userId);
331 1
		if ($track !== null) {
332 1
			$track->setAlbum($this->albumBusinessLayer->find($track->getAlbumId(), $this->userId));
333 1
			$track->setArtist($this->artistBusinessLayer->find($track->getArtistId(), $this->userId));
334 1
			return new JSONResponse($track->toCollection($this->l10n));
335
		} else {
336
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
337
		}
338
	}
339
340
	/**
341
	 * @NoAdminRequired
342
	 * @NoCSRFRequired
343
	 */
344
	public function getScanState() {
345
		return new JSONResponse([
346
			'unscannedFiles' => $this->scanner->getUnscannedMusicFileIds($this->userId),
347
			'scannedCount' => $this->trackBusinessLayer->count($this->userId)
348
		]);
349
	}
350
351
	/**
352
	 * @NoAdminRequired
353
	 * @UseSession to keep the session reserved while execution in progress
354
	 */
355
	public function scan($files, $finalize) {
356
		// extract the parameters
357
		$fileIds = \array_map('intval', \explode(',', $files));
358
		$finalize = \filter_var($finalize, FILTER_VALIDATE_BOOLEAN);
359
360
		$filesScanned = $this->scanner->scanFiles($this->userId, $this->userFolder, $fileIds);
361
362
		$coversUpdated = false;
363
		if ($finalize) {
364
			$coversUpdated = $this->scanner->findAlbumCovers($this->userId)
365
							|| $this->scanner->findArtistCovers($this->userId);
366
			$totalCount = $this->trackBusinessLayer->count($this->userId);
367
			$this->logger->log("Scanning finished, user $this->userId has $totalCount scanned tracks in total", 'info');
368
		}
369
370
		return new JSONResponse([
371
			'filesScanned' => $filesScanned,
372
			'coversUpdated' => $coversUpdated
373
		]);
374
	}
375
376
	/**
377
	 * @NoAdminRequired
378
	 * @UseSession to keep the session reserved while execution in progress
379
	 */
380
	public function resetScanned() {
381
		$this->maintenance->resetDb($this->userId);
382
		return new JSONResponse(['success' => true]);
383
	}
384
385
	/**
386
	 * @NoAdminRequired
387
	 * @NoCSRFRequired
388
	 */
389
	public function download($fileId) {
390
		$track = $this->trackBusinessLayer->findByFileId($fileId, $this->userId);
391
		if ($track === null) {
392
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'track not found');
393
		}
394
395
		$nodes = $this->userFolder->getById($track->getFileId());
396
		if (\count($nodes) > 0) {
397
			// get the first valid node
398
			$node = $nodes[0];
399
400
			$mime = $node->getMimeType();
401
			$content = $node->getContent();
402
			return new FileResponse(['mimetype' => $mime, 'content' => $content]);
403
		}
404
405
		return new ErrorResponse(Http::STATUS_NOT_FOUND, 'file not found');
406
	}
407
408
	/**
409
	 * @NoAdminRequired
410
	 * @NoCSRFRequired
411
	 */
412
	public function filePath($fileId) {
413
		$nodes = $this->userFolder->getById($fileId);
414
		if (\count($nodes) == 0) {
415
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
416
		} else {
417
			$node = $nodes[0];
418
			$path = $this->userFolder->getRelativePath($node->getPath());
419
			// URL encode each part of the file path
420
			$path = \join('/', \array_map('rawurlencode', \explode('/', $path)));
421
			return new JSONResponse(['path' => $path]);
422
		}
423
	}
424
425
	/**
426
	 * @NoAdminRequired
427
	 * @NoCSRFRequired
428
	 */
429
	public function fileInfo($fileId) {
430
		$info = $this->scanner->getFileInfo($fileId, $this->userId, $this->userFolder);
431
		if ($info) {
432
			return new JSONResponse($info);
433
		} else {
434
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
435
		}
436
	}
437
438
	/**
439
	 * @NoAdminRequired
440
	 * @NoCSRFRequired
441
	 */
442
	public function fileDetails($fileId) {
443
		$details = $this->detailsHelper->getDetails($fileId, $this->userFolder);
444
		if ($details) {
445
			return new JSONResponse($details);
446
		} else {
447
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
448
		}
449
	}
450
451
	/**
452
	 * @NoAdminRequired
453
	 * @NoCSRFRequired
454
	 */
455
	public function albumCover($albumIdOrSlug) {
456
		$albumId = $this->getIdFromSlug($albumIdOrSlug);
457
		$album = $this->albumBusinessLayer->find($albumId, $this->userId);
458
		return $this->cover($album);
459
	}
460
461
	/**
462
	 * @NoAdminRequired
463
	 * @NoCSRFRequired
464
	 */
465
	public function artistCover($artistIdOrSlug) {
466
		$artistId = $this->getIdFromSlug($artistIdOrSlug);
467
		$artist = $this->artistBusinessLayer->find($artistId, $this->userId);
468
		return $this->cover($artist);
469
	}
470
471
	private function cover($entity) {
472
		$coverAndHash = $this->coverHelper->getCoverAndHash($entity, $this->userId, $this->userFolder);
473
474
		if ($coverAndHash['hash'] !== null) {
475
			// Cover is in cache. Return a redirection response so that the client
476
			// will fetch the content through a cacheable route.
477
			$link = $this->urlGenerator->linkToRoute('music.api.cachedCover', ['hash' => $coverAndHash['hash']]);
478
			return new RedirectResponse($link);
479
		} else if ($coverAndHash['data'] !== null) {
480
			return new FileResponse($coverAndHash['data']);
481
		} else {
482
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
483
		}
484
	}
485
486
	/**
487
	 * @NoAdminRequired
488
	 * @NoCSRFRequired
489
	 */
490
	public function cachedCover($hash) {
491
		$coverData = $this->coverHelper->getCoverFromCache($hash, $this->userId);
492
493
		if ($coverData !== null) {
494
			$response =  new FileResponse($coverData);
495
			// instruct also the client-side to cache the result, this is safe
496
			// as the resource URI contains the image hash
497
			self::setClientCaching($response);
498
			return $response;
499
		} else {
500
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
501
		}
502
	}
503
504
	private static function setClientCaching(&$httpResponse, $days=365) {
505
		$httpResponse->cacheFor($days * 24 * 60 * 60);
506
		$httpResponse->addHeader('Pragma', 'cache');
507
	}
508
}
509