Passed
Pull Request — master (#99)
by Daniel
26:12 queued 03:19
created

Theme::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 3
c 1
b 0
f 1
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 2
crap 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\Model;
26
27
use OCA\CMSPico\Exceptions\ThemeNotCompatibleException;
28
use OCA\CMSPico\Files\FolderInterface;
29
use OCA\CMSPico\Pico;
30
use OCA\CMSPico\Service\MiscService;
31
use OCP\Files\InvalidPathException;
32
use OCP\Files\NotFoundException;
33
use OCP\Files\NotPermittedException;
34
use Symfony\Component\Yaml\Exception\ParseException as YamlParseException;
35
use Symfony\Component\Yaml\Parser as YamlParser;
36
37
class Theme implements \JsonSerializable
38
{
39
	/** @var int */
40
	public const TYPE_SYSTEM = 1;
41
42
	/** @var int */
43
	public const TYPE_CUSTOM = 2;
44
45
	/** @var int[] */
46
	public const THEME_API_VERSIONS = [
47
		Pico::API_VERSION_0,
48
		Pico::API_VERSION_1,
49
		Pico::API_VERSION_2,
50
		Pico::API_VERSION_3,
51
	];
52
53
	/** @var MiscService */
54
	private $miscService;
55
56
	/** @var FolderInterface */
57
	private $folder;
58
59
	/** @var int */
60
	private $type;
61
62
	/** @var bool|null */
63
	private $compat;
64
65
	/** @var ThemeNotCompatibleException|null */
66
	private $compatException;
67
68
	/**
69
	 * Theme constructor.
70
	 *
71
	 * @param FolderInterface $folder
72
	 * @param int             $type
73
	 */
74 1
	public function __construct(FolderInterface $folder, int $type = self::TYPE_SYSTEM)
75
	{
76 1
		$this->miscService = \OC::$server->query(MiscService::class);
77
78 1
		$this->folder = $folder;
79 1
		$this->type = $type;
80 1
	}
81
82
	/**
83
	 * @return string
84
	 */
85 1
	public function getName(): string
86
	{
87 1
		return $this->folder->getName();
88
	}
89
90
	/**
91
	 * @return FolderInterface
92
	 */
93 1
	public function getFolder(): FolderInterface
94
	{
95 1
		return $this->folder;
96
	}
97
98
	/**
99
	 * @return int
100
	 */
101 1
	public function getType(): int
102
	{
103 1
		return $this->type;
104
	}
105
106
	/**
107
	 * @return bool
108
	 */
109 1
	public function isCompatible(): bool
110
	{
111 1
		if ($this->compat !== null) {
112 1
			return $this->compat;
113
		}
114
115
		try {
116 1
			$this->checkCompatibility();
117 1
			return true;
118
		} catch (ThemeNotCompatibleException $e) {
119
			return false;
120
		}
121
	}
122
123
	/**
124
	 * @throws ThemeNotCompatibleException
125
	 */
126 1
	public function checkCompatibility(): void
127
	{
128 1
		if ($this->compat === false) {
129
			throw $this->compatException;
130 1
		} elseif ($this->compat) {
131
			return;
132
		}
133
134
		try {
135
			try {
136 1
				$this->getFolder()->getFile('index.twig');
137
			} catch (InvalidPathException | NotFoundException $e) {
138
				throw new ThemeNotCompatibleException(
139
					$this->getName(),
140
					'Incompatible theme: Twig template "{file}" not found.',
141
					[ 'file' => $this->getName() . '/index.twig' ]
142
				);
143
			}
144
145
			try {
146 1
				$themeConfigFile = $this->getFolder()->getFile('pico-theme.yml');
147
				$themeConfigYaml = $themeConfigFile->getContent();
148
149
				$themeConfig = (new YamlParser())->parse($themeConfigYaml);
150
				$themeConfig = is_array($themeConfig) ? $themeConfig : [];
151 1
			} catch (InvalidPathException | NotFoundException | NotPermittedException | YamlParseException $e) {
152 1
				$themeConfig = [];
153
			}
154
155 1
			$apiVersion = Pico::API_VERSION_0;
156 1
			if (isset($themeConfig['api_version'])) {
157
				if (is_int($themeConfig['api_version']) || preg_match('/^[0-9]+$/', $themeConfig['api_version'])) {
158
					$apiVersion = (int)$themeConfig['api_version'];
159
				}
160
			}
161
162 1
			if (!in_array($apiVersion, static::THEME_API_VERSIONS, true)) {
163
				throw new ThemeNotCompatibleException(
164
					$this->getName(),
165
					'Incompatible theme: Themes for Pico CMS for Nextcloud must use one of the API versions '
166
					. '{compatApiVersions}, but this theme uses API version {apiVersion}.',
167
					[ 'compatApiVersions' => implode(', ', static::THEME_API_VERSIONS), 'apiVersion' => $apiVersion ]
168
				);
169
			}
170
171 1
			$this->compat = true;
172 1
			$this->compatException = null;
173
		} catch (ThemeNotCompatibleException $e) {
174
			$this->compat = false;
175
			$this->compatException = $e;
176
177
			throw $e;
178
		}
179 1
	}
180
181
	/**
182
	 * @return array
183
	 */
184 1
	public function toArray(): array
185
	{
186
		$data = [
187 1
			'name' => $this->getName(),
188 1
			'type' => $this->getType(),
189 1
			'compat' => $this->isCompatible(),
190
		];
191
192 1
		if (!$this->isCompatible() && ($this->compatException !== null)) {
193
			$data['compatReason'] = $this->compatException->getRawReason();
194
			$data['compatReasonData'] = $this->compatException->getRawReasonData();
195
		}
196
197 1
		return $data;
198
	}
199
200
	/**
201
	 * @return array
202
	 */
203 1
	public function jsonSerialize(): array
204
	{
205 1
		return $this->toArray();
206
	}
207
}
208