themes   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 275
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 275
rs 8.3673
c 0
b 0
f 0
ccs 0
cts 126
cp 0
wmc 45

10 Methods

Rating   Name   Duplication   Size   Complexity  
A get_themes_list() 0 22 3
B admin_themes_extract() 0 16 5
C get_update_dependencies_for_theme() 0 29 7
B set_current_theme() 0 26 5
D admin_themes_update() 0 44 10
A admin_themes_get() 0 16 3
A get_current_theme() 0 2 1
A check_theme_feature_availability() 0 9 3
B admin_themes_delete() 0 13 5
A admin_themes_put() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like themes often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use themes, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package    CleverStyle Framework
4
 * @subpackage System module
5
 * @category   modules
6
 * @author     Nazar Mokrynskyi <[email protected]>
7
 * @license    0BSD
8
 */
9
namespace cs\modules\System\api\Controller\admin;
10
use
11
	cs\Config,
12
	cs\Event,
13
	cs\ExitException,
14
	cs\Language,
15
	cs\Session,
16
	cs\modules\System\Packages_manipulation;
17
18
trait themes {
19
	/**
20
	 * @param \cs\Request $Request
21
	 *
22
	 * @return mixed
23
	 *
24
	 * @throws ExitException
25
	 */
26
	public static function admin_themes_get ($Request) {
27
		if ($Request->route_path(3) == 'update_dependencies') {
28
			/**
29
			 * Get dependencies for theme during update
30
			 */
31
			return static::get_update_dependencies_for_theme($Request->route_path[2]);
32
		} elseif ($Request->route_path(2) == 'current') {
33
			/**
34
			 * Get current theme
35
			 */
36
			return static::get_current_theme();
37
		} else {
38
			/**
39
			 * Get array of themes in extended form
40
			 */
41
			return static::get_themes_list();
42
		}
43
	}
44
	/**
45
	 * @param string $theme
46
	 *
47
	 * @return array
48
	 *
49
	 * @throws ExitException
50
	 */
51
	protected static function get_update_dependencies_for_theme ($theme) {
52
		$themes = get_files_list(THEMES, false, 'd');
53
		if (!in_array($theme, $themes, true)) {
54
			throw new ExitException(404);
55
		}
56
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
0 ignored issues
show
Bug introduced by
Are you sure cs\Session::instance()->get_id() of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

56
		$tmp_location = TEMP.'/System/admin/'./** @scrutinizer ignore-type */ Session::instance()->get_id().'.phar';
Loading history...
57
		$tmp_dir      = "phar://$tmp_location";
58
		if (
59
			!file_exists(THEMES."/$theme/meta.json") ||
60
			!file_exists("$tmp_dir/meta.json")
61
		) {
62
			throw new ExitException(400);
63
		}
64
		$existing_meta = file_get_json(THEMES."/$theme/meta.json");
65
		$new_meta      = file_get_json("$tmp_dir/meta.json");
66
		if (
67
			$existing_meta['package'] !== $new_meta['package'] ||
68
			$existing_meta['category'] !== $new_meta['category']
69
		) {
70
			throw new ExitException(Language::instance()->this_is_not_theme_installer_file, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property this_is_not_theme_installer_file does not exist on cs\Language. Since you implemented __get, consider adding a @property annotation.
Loading history...
71
		}
72
		$dependencies = [];
73
		if (version_compare($new_meta['version'], $existing_meta['version'], '<')) {
74
			$dependencies['update_older'] = [
75
				'from' => $existing_meta['version'],
76
				'to'   => $new_meta['version']
77
			];
78
		}
79
		return $dependencies;
80
	}
81
	/**
82
	 * @return string
83
	 */
84
	protected static function get_current_theme () {
85
		return Config::instance()->core['theme'];
86
	}
87
	/**
88
	 * @return array
89
	 */
90
	protected static function get_themes_list () {
91
		$themes = get_files_list(THEMES, false, 'd');
92
		asort($themes);
93
		$themes_list = [];
94
		foreach ($themes as $theme_name) {
95
			$theme = [
96
				'name' => $theme_name
97
			];
98
			/**
99
			 * Check if readme available
100
			 */
101
			static::check_theme_feature_availability($theme, 'readme');
102
			/**
103
			 * Check if license available
104
			 */
105
			static::check_theme_feature_availability($theme, 'license');
106
			if (file_exists(THEMES."/$theme_name/meta.json")) {
107
				$theme['meta'] = file_get_json(THEMES."/$theme_name/meta.json");
108
			}
109
			$themes_list[] = $theme;
110
		}
111
		return $themes_list;
112
	}
113
	/**
114
	 * @param array  $theme
115
	 * @param string $feature
116
	 */
117
	protected static function check_theme_feature_availability (&$theme, $feature) {
118
		/**
119
		 * Check if feature available
120
		 */
121
		$file = file_exists_with_extension(THEMES."/$theme[name]/$feature", ['txt', 'html']);
122
		if ($file) {
123
			$theme[$feature] = [
124
				'type'    => substr($file, -3) == 'txt' ? 'txt' : 'html',
125
				'content' => file_get_contents($file)
126
			];
127
		}
128
	}
129
	/**
130
	 * @param \cs\Request $Request
131
	 *
132
	 * @throws ExitException
133
	 */
134
	public static function admin_themes_put ($Request) {
135
		if ($Request->route_path(2) == 'current') {
136
			$theme = $Request->data('theme');
0 ignored issues
show
Bug introduced by
'theme' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::data(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

136
			$theme = $Request->data(/** @scrutinizer ignore-type */ 'theme');
Loading history...
137
			if (!$theme) {
138
				throw new ExitException(400);
139
			}
140
			/**
141
			 * Set current theme
142
			 */
143
			static::set_current_theme($theme);
0 ignored issues
show
Bug introduced by
It seems like $theme can also be of type array and array; however, parameter $theme of cs\modules\System\api\Co...es::set_current_theme() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
			static::set_current_theme(/** @scrutinizer ignore-type */ $theme);
Loading history...
144
		} else {
145
			throw new ExitException(400);
146
		}
147
	}
148
	/**
149
	 * Provides next events:
150
	 *  admin/System/themes/current/before
151
	 *  ['name' => theme_name]
152
	 *
153
	 *  admin/System/themes/current/after
154
	 *  ['name' => theme_name]
155
	 *
156
	 * @param string $theme
157
	 *
158
	 * @throws ExitException
159
	 */
160
	protected static function set_current_theme ($theme) {
161
		$Config = Config::instance();
162
		$themes = get_files_list(THEMES, false, 'd');
163
		if (!in_array($theme, $themes, true)) {
164
			throw new ExitException(404);
165
		}
166
		if ($theme == $Config->core['theme']) {
167
			throw new ExitException(400);
168
		}
169
		if (!Event::instance()->fire(
170
			'admin/System/themes/current/before',
171
			[
172
				'name' => $theme
173
			]
174
		)
175
		) {
176
			throw new ExitException(500);
177
		}
178
		$Config->core['theme'] = $theme;
179
		if (!$Config->save()) {
180
			throw new ExitException(500);
181
		}
182
		Event::instance()->fire(
183
			'admin/System/themes/current/after',
184
			[
185
				'name' => $theme
186
			]
187
		);
188
	}
189
	/**
190
	 * Extract uploaded theme
191
	 *
192
	 * @throws ExitException
193
	 */
194
	public static function admin_themes_extract () {
195
		$L            = Language::instance();
196
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
0 ignored issues
show
Bug introduced by
Are you sure cs\Session::instance()->get_id() of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

196
		$tmp_location = TEMP.'/System/admin/'./** @scrutinizer ignore-type */ Session::instance()->get_id().'.phar';
Loading history...
197
		$tmp_dir      = "phar://$tmp_location";
198
		if (
199
			!file_exists($tmp_location) ||
200
			!file_exists("$tmp_dir/meta.json")
201
		) {
202
			throw new ExitException(400);
203
		}
204
		$new_meta = file_get_json("$tmp_dir/meta.json");
205
		if ($new_meta['category'] !== 'themes') {
206
			throw new ExitException($L->this_is_not_theme_installer_file, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property this_is_not_theme_installer_file does not exist on cs\Language. Since you implemented __get, consider adding a @property annotation.
Loading history...
207
		}
208
		if (!Packages_manipulation::install_extract(THEMES."/$new_meta[package]", $tmp_location)) {
209
			throw new ExitException($L->theme_files_unpacking_error, 500);
0 ignored issues
show
Bug Best Practice introduced by
The property theme_files_unpacking_error does not exist on cs\Language. Since you implemented __get, consider adding a @property annotation.
Loading history...
210
		}
211
	}
212
	/**
213
	 * Update theme
214
	 *
215
	 * Provides next events:
216
	 *  admin/System/themes/update/before
217
	 *  ['name' => theme_name]
218
	 *
219
	 *  admin/System/themes/update/after
220
	 *  ['name' => theme_name]
221
	 *
222
	 * @param \cs\Request $Request
223
	 *
224
	 * @throws ExitException
225
	 */
226
	public static function admin_themes_update ($Request) {
227
		$L      = Language::instance();
228
		$theme  = $Request->route_path(2);
229
		$themes = get_files_list(THEMES, false, 'd');
230
		if (!in_array($theme, $themes, true)) {
231
			throw new ExitException(404);
232
		}
233
		$tmp_location = TEMP.'/System/admin/'.Session::instance()->get_id().'.phar';
0 ignored issues
show
Bug introduced by
Are you sure cs\Session::instance()->get_id() of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

233
		$tmp_location = TEMP.'/System/admin/'./** @scrutinizer ignore-type */ Session::instance()->get_id().'.phar';
Loading history...
234
		$tmp_dir      = "phar://$tmp_location";
235
		$theme_dir    = THEMES."/$theme";
236
		if (
237
			!file_exists($tmp_location) ||
238
			!file_exists("$theme_dir/meta.json") ||
239
			!file_exists("$tmp_dir/meta.json")
240
		) {
241
			throw new ExitException(400);
242
		}
243
		$new_meta = file_get_json("$tmp_dir/meta.json");
244
		if (
245
			$new_meta['package'] !== $theme ||
246
			$new_meta['category'] !== 'themes'
247
		) {
248
			throw new ExitException($L->this_is_not_theme_installer_file, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property this_is_not_theme_installer_file does not exist on cs\Language. Since you implemented __get, consider adding a @property annotation.
Loading history...
249
		}
250
		if (!Event::instance()->fire(
251
			'admin/System/themes/update/before',
252
			[
253
				'name' => $theme
254
			]
255
		)
256
		) {
257
			throw new ExitException(500);
258
		}
259
		if (!is_writable($theme_dir)) {
260
			throw new ExitException($L->cant_unpack_theme_no_write_permissions, 500);
0 ignored issues
show
Bug Best Practice introduced by
The property cant_unpack_theme_no_write_permissions does not exist on cs\Language. Since you implemented __get, consider adding a @property annotation.
Loading history...
261
		}
262
		if (!Packages_manipulation::update_extract(THEMES."/$theme", $tmp_location)) {
263
			throw new ExitException($L->theme_files_unpacking_error, 500);
0 ignored issues
show
Bug Best Practice introduced by
The property theme_files_unpacking_error does not exist on cs\Language. Since you implemented __get, consider adding a @property annotation.
Loading history...
264
		}
265
		clean_public_cache();
266
		Event::instance()->fire(
267
			'admin/System/themes/update/after',
268
			[
269
				'name' => $theme
270
			]
271
		);
272
	}
273
	/**
274
	 * Delete theme completely
275
	 *
276
	 * @param \cs\Request $Request
277
	 *
278
	 * @throws ExitException
279
	 */
280
	public static function admin_themes_delete ($Request) {
281
		$Config = Config::instance();
282
		$theme  = $Request->route_path(2);
283
		$themes = get_files_list(THEMES, false, 'd');
284
		if (
285
			$theme == Config::SYSTEM_THEME ||
286
			$Config->core['theme'] == $theme ||
287
			!in_array($theme, $themes, true)
288
		) {
289
			throw new ExitException(400);
290
		}
291
		if (!rmdir_recursive(THEMES."/$theme")) {
292
			throw new ExitException(500);
293
		}
294
	}
295
}
296