Passed
Pull Request — master (#77)
by Daniel
51:29
created

PicoAssetResponse   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 11
eloc 51
c 0
b 0
f 0
dl 0
loc 113
ccs 0
cts 51
cp 0
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 4
A noCache() 0 7 1
A setContentSecurityPolicy() 0 9 2
A cacheFor() 0 20 3
A render() 0 3 1
1
<?php
2
/**
3
 * CMS Pico - Create websites using Pico CMS for Nextcloud.
4
 *
5
 * @copyright Copyright (c) 2019, Daniel Rudolf (<[email protected]>)
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
declare(strict_types=1);
24
25
namespace OCA\CMSPico\Http;
26
27
use OCA\CMSPico\Model\PicoAsset;
28
use OCP\AppFramework\Http\DownloadResponse;
29
use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
30
31
class PicoAssetResponse extends DownloadResponse
32
{
33
	/** @var PicoAsset */
34
	private $asset;
35
36
	/** @var array<string,int> */
37
	private $cacheFor = [
38
		'application/font-sfnt' => 2592000,
39
		'application/font-woff' => 2592000,
40
		'application/javascript' => 604800,
41
		'application/json' => 604800,
42
		'application/vnd.ms-fontobject' => 2592000,
43
		'image/bmp' => 2592000,
44
		'image/gif' => 2592000,
45
		'image/jpeg' => 2592000,
46
		'image/png' => 2592000,
47
		'image/svg+xml' => 2592000,
48
		'image/tiff' => 2592000,
49
		'image/vnd.microsoft.icon' => 2592000,
50
		'image/webp' => 2592000,
51
		'image/x-icon' => 2592000,
52
		'text/css' => 604800,
53
	];
54
55
	/**
56
	 * PicoAssetResponse constructor.
57
	 *
58
	 * @param PicoAsset $asset
59
	 * @param bool      $enableCache
60
	 * @param bool      $enableSecureMimeType
61
	 */
62
	public function __construct(PicoAsset $asset, bool $enableCache = true, bool $enableSecureMimeType = true)
63
	{
64
		$this->asset = $asset;
65
66
		parent::setContentSecurityPolicy(new PicoContentSecurityPolicy());
67
68
		$mimeType = $asset->getMimeType();
69
		$contentType = $enableSecureMimeType ? $asset->getSecureMimeType() : $mimeType;
70
71
		parent::__construct($asset->getName(), $contentType);
72
73
		$this->setETag($asset->getEtag());
74
		$this->setLastModified($asset->getLastModified());
75
76
		if ($enableCache && isset($this->cacheFor[$mimeType])) {
77
			$this->cacheFor($this->cacheFor[$mimeType]);
78
		} else {
79
			$this->cacheFor(0);
80
		}
81
	}
82
83
	/**
84
	 * @param EmptyContentSecurityPolicy $csp
85
	 *
86
	 * @return $this
87
	 */
88
	public function setContentSecurityPolicy(EmptyContentSecurityPolicy $csp): self
89
	{
90
		if (!($csp instanceof PicoContentSecurityPolicy)) {
91
			// Pico really needs its own CSP...
92
			return $this;
93
		}
94
95
		parent::setContentSecurityPolicy($csp);
96
		return $this;
97
	}
98
99
	/**
100
	 * @param int $cacheSeconds
101
	 *
102
	 * @return $this
103
	 */
104
	public function cacheFor(int $cacheSeconds): self
105
	{
106
		if ($cacheSeconds > 0) {
107
			$this->addHeader('Cache-Control', 'max-age=' . $cacheSeconds . ', public');
108
			$this->addHeader('Pragma', 'public');
109
110
			try {
111
				$expires = new \DateTime();
112
				$expires->add(new \DateInterval('PT' . $cacheSeconds . 'S'));
113
				$this->addHeader('Expires', $expires->format(\DateTime::RFC2822));
114
			} catch (\Exception $e) {
115
				// ignore DateTime and DateInterval exceptions
116
			}
117
		} else {
118
			$this->addHeader('Cache-Control', 'no-cache, must-revalidate');
119
			$this->addHeader('Pragma', null);
120
			$this->addHeader('Expires', null);
121
		}
122
123
		return $this;
124
	}
125
126
	/**
127
	 * @return $this
128
	 */
129
	public function noCache(): self
130
	{
131
		$this->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
132
		$this->addHeader('Pragma', null);
133
		$this->addHeader('Expires', null);
134
135
		return $this;
136
	}
137
138
	/**
139
	 * @return string
140
	 */
141
	public function render(): string
142
	{
143
		return $this->asset->getContent();
144
	}
145
}
146