Browse Source

Settings control panel saves to admin-override.toml in the app/config directory, resolves #7

Timothy J. Warren 1 month ago
parent
commit
88b68b847c
9 changed files with 142 additions and 37 deletions
  1. 6
    0
      CHANGELOG.md
  2. 2
    2
      app/appConf/routes.php
  3. 2
    3
      app/views/settings.php
  4. 7
    1
      index.php
  5. 16
    0
      src/AnimeClient.php
  6. 21
    23
      src/Controller/Index.php
  7. 1
    1
      src/FormGenerator.php
  8. 81
    7
      src/Model/Settings.php
  9. 6
    0
      sw.js

+ 6
- 0
CHANGELOG.md View File

@@ -1,5 +1,11 @@
1 1
 # Changelog
2 2
 
3
+## Version 4.1
4
+* Removed MAL integration, added Anilist Integration
5
+* Now uses WebP cache images when the browser supports it
6
+* Replaces JS minifier with pre-minified scripts (Removes the need for one caching folder, too)
7
+* Updated console command to sync Kitsu and Anilist data
8
+
3 9
 ## Version 4
4 10
 * Updated to use Kitsu API after discontinuation of Hummingbird
5 11
 * Added streaming links to list entries from the Kitsu API

+ 2
- 2
app/appConf/routes.php View File

@@ -216,8 +216,8 @@ return [
216 216
 		'verb' => 'get',
217 217
 	],
218 218
 	'settings-post' => [
219
-		'path' => '/settings',
220
-		'action' => 'settings',
219
+		'path' => '/settings-save',
220
+		'action' => 'settings_post',
221 221
 		'controller' => DEFAULT_CONTROLLER,
222 222
 		'verb' => 'post',
223 223
 	],

+ 2
- 3
app/views/settings.php View File

@@ -6,6 +6,7 @@ if ( ! $auth->isAuthenticated())
6 6
 }
7 7
 
8 8
 $sectionMapping = [
9
+	'anilist' => 'Anilist API Integration',
9 10
 	'config' => 'General Settings',
10 11
 	'cache' => 'Caching',
11 12
 	'database' => 'Collection Database Settings',
@@ -15,9 +16,7 @@ $hiddenFields = [];
15 16
 $nestedPrefix = 'config';
16 17
 ?>
17 18
 
18
-<pre><?= print_r($_POST, TRUE) ?></pre>
19
-
20
-<form action="<?= $_SERVER['REQUEST_URI'] ?>" method="POST">
19
+<form action="<?= $url->generate('settings-post') ?>" method="POST">
21 20
 	<main class='form'>
22 21
 		<button type="submit">Save Changes</button>
23 22
 		<br />

+ 7
- 1
index.php View File

@@ -49,7 +49,13 @@ $baseConfig = require $APPCONF_DIR . '/base_config.php';
49 49
 $di = require $APP_DIR . '/bootstrap.php';
50 50
 
51 51
 $config = loadToml($CONF_DIR);
52
-$configArray = array_merge($baseConfig, $config);
52
+
53
+$overrideFile = $CONF_DIR . '/admin-override.toml';
54
+$overrideConfig = file_exists($overrideFile)
55
+	? loadTomlFile($overrideFile)
56
+	: [];
57
+
58
+$configArray = array_merge($baseConfig, $config, $overrideConfig);
53 59
 
54 60
 $checkedConfig = (new ConfigType($configArray))->toArray();
55 61
 $container = $di($checkedConfig);

+ 16
- 0
src/AnimeClient.php View File

@@ -33,6 +33,11 @@ function loadToml(string $path): array
33 33
 	foreach ($files as $file)
34 34
 	{
35 35
 		$key = str_replace('.toml', '', basename($file));
36
+		if ($key === 'admin-override')
37
+		{
38
+			continue;
39
+		}
40
+
36 41
 		$config = Toml::parseFile($file);
37 42
 
38 43
 		if ($key === 'config')
@@ -71,6 +76,17 @@ function loadTomlByFile(string $path): array
71 76
 	return $output;
72 77
 }
73 78
 
79
+/**
80
+ * Load config from one specific TOML file
81
+ *
82
+ * @param string $filename
83
+ * @return array
84
+ */
85
+function loadTomlFile(string $filename): array
86
+{
87
+	return Toml::parseFile($filename);
88
+}
89
+
74 90
 /**
75 91
  * Is the array sequential, not associative?
76 92
  *

+ 21
- 23
src/Controller/Index.php View File

@@ -39,9 +39,6 @@ final class Index extends BaseController {
39 39
 	/**
40 40
 	 * Purges the API cache
41 41
 	 *
42
-	 * @throws \Aviat\Ion\Di\ContainerException
43
-	 * @throws \Aviat\Ion\Di\NotFoundException
44
-	 * @throws \InvalidArgumentException
45 42
 	 * @return void
46 43
 	 */
47 44
 	public function clearCache()
@@ -56,9 +53,6 @@ final class Index extends BaseController {
56 53
 	 * Show the login form
57 54
 	 *
58 55
 	 * @param string $status
59
-	 * @throws \Aviat\Ion\Di\ContainerException
60
-	 * @throws \Aviat\Ion\Di\NotFoundException
61
-	 * @throws \InvalidArgumentException
62 56
 	 * @return void
63 57
 	 */
64 58
 	public function login(string $status = '')
@@ -109,10 +103,6 @@ final class Index extends BaseController {
109 103
 	/**
110 104
 	 * Attempt login authentication
111 105
 	 *
112
-	 * @throws \Aviat\Ion\Di\ContainerException
113
-	 * @throws \Aviat\Ion\Di\NotFoundException
114
-	 * @throws \Aura\Router\Exception\RouteNotFound
115
-	 * @throws \InvalidArgumentException
116 106
 	 * @return void
117 107
 	 */
118 108
 	public function loginAction()
@@ -133,9 +123,6 @@ final class Index extends BaseController {
133 123
 	/**
134 124
 	 * Deauthorize the current user
135 125
 	 *
136
-	 * @throws \Aviat\Ion\Di\ContainerException
137
-	 * @throws \Aviat\Ion\Di\NotFoundException
138
-	 * @throws \InvalidArgumentException
139 126
 	 * @return void
140 127
 	 */
141 128
 	public function logout()
@@ -149,9 +136,6 @@ final class Index extends BaseController {
149 136
 	/**
150 137
 	 * Show the user profile page
151 138
 	 *
152
-	 * @throws \Aviat\Ion\Di\ContainerException
153
-	 * @throws \Aviat\Ion\Di\NotFoundException
154
-	 * @throws \InvalidArgumentException
155 139
 	 * @return void
156 140
 	 */
157 141
 	public function me()
@@ -188,14 +172,28 @@ final class Index extends BaseController {
188 172
 		]);
189 173
 	}
190 174
 
175
+	/**
176
+	 * Attempt to save the user's settings
177
+	 *
178
+	 * @throws \Aura\Router\Exception\RouteNotFound
179
+	 */
191 180
 	public function settings_post()
192 181
 	{
193
-		$auth = $this->container->get('auth');
194
-		$this->outputHTML('settings', [
195
-			'auth' => $auth,
196
-			'config' => $this->config,
197
-			'title' => $this->config->get('whose_list') . "'s Settings",
198
-		]);
182
+		$post = $this->request->getParsedBody();
183
+
184
+		// dump($post);
185
+		$saved = $this->settingsModel->saveSettingsFile($post);
186
+
187
+		if ($saved)
188
+		{
189
+			$this->setFlashMessage('Saved config settings.', 'success');
190
+		}
191
+		else
192
+		{
193
+			$this->setFlashMessage('Failed to save config file.', 'error');
194
+		}
195
+
196
+		$this->redirect($this->url->generate('settings'), 303);
199 197
 	}
200 198
 
201 199
 	/**
@@ -250,7 +248,7 @@ final class Index extends BaseController {
250 248
 		$baseSavePath = $this->config->get('img_cache_path');
251 249
 		$filePrefix = "{$baseSavePath}/{$type}/{$id}";
252 250
 
253
-		[$origWidth, $origHeight] = getimagesizefromstring($data);
251
+		[$origWidth] = getimagesizefromstring($data);
254 252
 		$gdImg = imagecreatefromstring($data);
255 253
 		$resizedImg = imagescale($gdImg, $width ?? $origWidth);
256 254
 

+ 1
- 1
src/FormGenerator.php View File

@@ -83,7 +83,7 @@ final class FormGenerator {
83 83
 				$params['attribs']['label'] = $form['description'];
84 84
 				$params['attribs']['value'] = TRUE;
85 85
 				$params['attribs']['value_unchecked'] = '0'; */
86
-				
86
+
87 87
 				$params['type'] = 'radio';
88 88
 				$params['options'] = [
89 89
 					'1' => 'Yes',

+ 81
- 7
src/Model/Settings.php View File

@@ -16,6 +16,9 @@
16 16
 
17 17
 namespace Aviat\AnimeClient\Model;
18 18
 
19
+use function Aviat\AnimeClient\arrayToToml;
20
+use function Aviat\Ion\_dir;
21
+
19 22
 use Aviat\AnimeClient\Types\{Config, UndefinedPropertyException};
20 23
 
21 24
 use Aviat\Ion\ConfigInterface;
@@ -36,7 +39,12 @@ final class Settings {
36 39
 	 */
37 40
 	private const SETTINGS_MAP = [
38 41
 		'anilist' => [
39
-
42
+			'enabled' => [
43
+				'type' => 'boolean',
44
+				'title' => 'Enable Anilist Integration',
45
+				'default' => FALSE,
46
+				'description' => 'Enable syncing data between Kitsu and Anilist. Requires appropriate API keys to be set in config',
47
+			],
40 48
 		],
41 49
 		'config' => [
42 50
 			'kitsu_username' => [
@@ -265,22 +273,88 @@ final class Settings {
265 273
 		return $output;
266 274
 	}
267 275
 
268
-	public function validateSettings(array $settings): bool
276
+	public function validateSettings(array $settings)
277
+	{
278
+		$config = (new Config($settings))->toArray();
279
+
280
+		$looseConfig = [];
281
+		$keyedConfig = [];
282
+
283
+		// Convert 'boolean' values to true and false
284
+		// Also order keys so they can be saved properly
285
+		foreach ($config as $key => $val)
286
+		{
287
+			if (is_scalar($val))
288
+			{
289
+				if ($val === '1')
290
+				{
291
+					$looseConfig[$key] = TRUE;
292
+				}
293
+				elseif ($val === '0')
294
+				{
295
+					$looseConfig[$key] = FALSE;
296
+				}
297
+				else
298
+				{
299
+					$looseConfig[$key] = $val;
300
+				}
301
+			}
302
+			elseif (is_array($val))
303
+			{
304
+				foreach($val as $k => $v)
305
+				{
306
+					if ($v === '1')
307
+					{
308
+						$keyedConfig[$key][$k] = TRUE;
309
+					}
310
+					elseif($v === '0')
311
+					{
312
+						$keyedConfig[$key][$k] = FALSE;
313
+					}
314
+					else
315
+					{
316
+						$keyedConfig[$key][$k] = $v;
317
+					}
318
+				}
319
+			}
320
+		}
321
+
322
+		ksort($looseConfig);
323
+		ksort($keyedConfig);
324
+
325
+		$output = [];
326
+
327
+		foreach($looseConfig as $k => $v)
328
+		{
329
+			$output[$k] = $v;
330
+		}
331
+
332
+		foreach($keyedConfig as $k => $v)
333
+		{
334
+			$output[$k] = $v;
335
+		}
336
+
337
+		return $output;
338
+	}
339
+
340
+	public function saveSettingsFile(array $settings): bool
269 341
 	{
342
+		$settings = $settings['config'];
343
+
270 344
 		try
271 345
 		{
272
-			new Config($settings);
346
+			$settings = $this->validateSettings($settings);
273 347
 		}
274 348
 		catch (UndefinedPropertyException $e)
275 349
 		{
276 350
 			return FALSE;
277 351
 		}
278 352
 
279
-		return TRUE;
280
-	}
353
+		$savePath = realpath(_dir(__DIR__, '..', '..', 'app', 'config'));
354
+		$saveFile = _dir($savePath, 'admin-override.toml');
281 355
 
282
-	public function saveSettingsFile()
283
-	{
356
+		$saved = file_put_contents($saveFile, arrayToToml($settings));
284 357
 
358
+		return $saved !== FALSE;
285 359
 	}
286 360
 }

+ 6
- 0
sw.js View File

@@ -61,6 +61,12 @@ self.addEventListener('activate', event => {
61 61
 
62 62
 // Pull css, images, and javascript from cache
63 63
 self.addEventListener('fetch', event => {
64
+	// Only cache things with a file extension,
65
+	// Ignore other requests
66
+	if ( ! event.request.url.includes('/public/')) {
67
+		return;
68
+	}
69
+
64 70
 	fromCache(event.request).then(cached => {
65 71
 		if (cached !== undefined) {
66 72
 			event.respondWith(cached);