diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2312db..7ee14b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## [unreleased]
+### Added
+
+- Add a graphical interface to download themes in the admin dashboard.
+
### Fixed
- Fix admin pannel's highlight preview overflow on mobile screens.
diff --git a/assets/Makefile b/assets/Makefile
index fe922b6..2d103a7 100644
--- a/assets/Makefile
+++ b/assets/Makefile
@@ -3,7 +3,7 @@ JSREV = 9-18-stable
REPO = https://github.com/highlightjs/cdn-release
THEMES = github github-dark base16/monokai
-all: highlight.min.js $(THEMES:=.min.css)
+all: highlight.min.js styles.min.js $(THEMES:=.min.css)
%.min.js: .FORCE
wget -q $(REPO)/raw/$(JSREV)/build/$(@F) -O $@
@@ -11,5 +11,11 @@ all: highlight.min.js $(THEMES:=.min.css)
%.min.css: .FORCE
wget -q $(REPO)/raw/$(CSSREV)/build/styles/$*.min.css -O $@
+styles.min.js: .FORCE
+ printf 'let hljsStyles = ' > $@
+ curl -s https://api.github.com/repos/highlightjs/cdn-release/git/trees/main?recursive=1 \
+ | jq -c '[.tree[].path|select(.|startswith("build/styles/") and endswith(".min.css"))|ltrimstr("build/styles/")|rtrimstr(".min.css")]' \
+ >> $@
+
.FORCE:;
.PHONY: .FORCE
diff --git a/assets/styles.min.js b/assets/styles.min.js
new file mode 100644
index 0000000..6f482c2
--- /dev/null
+++ b/assets/styles.min.js
@@ -0,0 +1 @@
+let hljsStyles = ["a11y-dark","a11y-light","agate","an-old-hope","androidstudio","arduino-light","arta","ascetic","atom-one-dark-reasonable","atom-one-dark","atom-one-light","base16/3024","base16/apathy","base16/apprentice","base16/ashes","base16/atelier-cave-light","base16/atelier-cave","base16/atelier-dune-light","base16/atelier-dune","base16/atelier-estuary-light","base16/atelier-estuary","base16/atelier-forest-light","base16/atelier-forest","base16/atelier-heath-light","base16/atelier-heath","base16/atelier-lakeside-light","base16/atelier-lakeside","base16/atelier-plateau-light","base16/atelier-plateau","base16/atelier-savanna-light","base16/atelier-savanna","base16/atelier-seaside-light","base16/atelier-seaside","base16/atelier-sulphurpool-light","base16/atelier-sulphurpool","base16/atlas","base16/bespin","base16/black-metal-bathory","base16/black-metal-burzum","base16/black-metal-dark-funeral","base16/black-metal-gorgoroth","base16/black-metal-immortal","base16/black-metal-khold","base16/black-metal-marduk","base16/black-metal-mayhem","base16/black-metal-nile","base16/black-metal-venom","base16/black-metal","base16/brewer","base16/bright","base16/brogrammer","base16/brush-trees-dark","base16/brush-trees","base16/chalk","base16/circus","base16/classic-dark","base16/classic-light","base16/codeschool","base16/colors","base16/cupcake","base16/cupertino","base16/danqing","base16/darcula","base16/dark-violet","base16/darkmoss","base16/darktooth","base16/decaf","base16/default-dark","base16/default-light","base16/dirtysea","base16/dracula","base16/edge-dark","base16/edge-light","base16/eighties","base16/embers","base16/equilibrium-dark","base16/equilibrium-gray-dark","base16/equilibrium-gray-light","base16/equilibrium-light","base16/espresso","base16/eva-dim","base16/eva","base16/flat","base16/framer","base16/fruit-soda","base16/gigavolt","base16/github","base16/google-dark","base16/google-light","base16/grayscale-dark","base16/grayscale-light","base16/green-screen","base16/gruvbox-dark-hard","base16/gruvbox-dark-medium","base16/gruvbox-dark-pale","base16/gruvbox-dark-soft","base16/gruvbox-light-hard","base16/gruvbox-light-medium","base16/gruvbox-light-soft","base16/hardcore","base16/harmonic16-dark","base16/harmonic16-light","base16/heetch-dark","base16/heetch-light","base16/helios","base16/hopscotch","base16/horizon-dark","base16/horizon-light","base16/humanoid-dark","base16/humanoid-light","base16/ia-dark","base16/ia-light","base16/icy-dark","base16/ir-black","base16/isotope","base16/kimber","base16/london-tube","base16/macintosh","base16/marrakesh","base16/materia","base16/material-darker","base16/material-lighter","base16/material-palenight","base16/material-vivid","base16/material","base16/mellow-purple","base16/mexico-light","base16/mocha","base16/monokai","base16/nebula","base16/nord","base16/nova","base16/ocean","base16/oceanicnext","base16/one-light","base16/onedark","base16/outrun-dark","base16/papercolor-dark","base16/papercolor-light","base16/paraiso","base16/pasque","base16/phd","base16/pico","base16/pop","base16/porple","base16/qualia","base16/railscasts","base16/rebecca","base16/ros-pine-dawn","base16/ros-pine-moon","base16/ros-pine","base16/sagelight","base16/sandcastle","base16/seti-ui","base16/shapeshifter","base16/silk-dark","base16/silk-light","base16/snazzy","base16/solar-flare-light","base16/solar-flare","base16/solarized-dark","base16/solarized-light","base16/spacemacs","base16/summercamp","base16/summerfruit-dark","base16/summerfruit-light","base16/synth-midnight-terminal-dark","base16/synth-midnight-terminal-light","base16/tango","base16/tender","base16/tomorrow-night","base16/tomorrow","base16/twilight","base16/unikitty-dark","base16/unikitty-light","base16/vulcan","base16/windows-10-light","base16/windows-10","base16/windows-95-light","base16/windows-95","base16/windows-high-contrast-light","base16/windows-high-contrast","base16/windows-nt-light","base16/windows-nt","base16/woodland","base16/xcode-dusk","base16/zenburn","brown-paper","codepen-embed","color-brewer","dark","default","devibeans","docco","far","foundation","github-gist","github","gml","googlecode","gradient-dark","gradient-light","grayscale","hybrid","idea","ir-black","isbl-editor-dark","isbl-editor-light","kimbie-dark","kimbie-light","lightfair","lioshi","magula","mono-blue","monokai-sublime","monokai","night-owl","nnfx-dark","nnfx-light","nord","obsidian","paraiso-dark","paraiso-light","pojoaque","purebasic","qtcreator-dark","qtcreator-light","rainbow","routeros","school-book","shades-of-purple","srcery","stackoverflow-dark","stackoverflow-light","sunburst","tomorrow-night-blue","tomorrow-night-bright","vs","vs2015","xcode","xt256"]
diff --git a/extend.php b/extend.php
index 356fe00..434a810 100644
--- a/extend.php
+++ b/extend.php
@@ -22,10 +22,15 @@
*/
use Club1\ServerSideHighlight\Console\DownloadCommand;
+use Club1\ServerSideHighlight\Controller\DownloadController;
use Club1\ServerSideHighlight\Controller\PreviewController;
+use Club1\ServerSideHighlight\Exception\InvalidArgumentException;
+use Club1\ServerSideHighlight\Exception\IOException;
+use Club1\ServerSideHighlight\Exception\KnownExceptionHandler;
use Club1\ServerSideHighlight\Formatter\Configurator;
use Club1\ServerSideHighlight\Formatter\Renderer;
-use Club1\ServerSideHighlight\Frontend\Content;
+use Club1\ServerSideHighlight\Frontend\Admin;
+use Club1\ServerSideHighlight\Frontend\Forum;
use Club1\ServerSideHighlight\Serializer\HighlightThemeSerializer;
use Flarum\Extend;
@@ -35,17 +40,23 @@
->render(Renderer::class),
(new Extend\Frontend('forum'))
- ->content(Content::class)
+ ->content(Forum::class)
->js(__DIR__.'/js/forum.js')
->css(__DIR__.'/css/forum.css'),
(new Extend\Frontend('admin'))
+ ->content(Admin::class)
->js(__DIR__.'/js/admin.js')
->css(__DIR__.'/css/admin.css'),
(new Extend\Routes('api'))
+ ->get('/highlight-download', 'club-1-server-side-highlight.download', DownloadController::class)
->get('/highlight-preview', 'club-1-server-side-highlight.preview', PreviewController::class),
+ (new Extend\ErrorHandling)
+ ->handler(InvalidArgumentException::class, KnownExceptionHandler::class)
+ ->handler(IOException::class, KnownExceptionHandler::class),
+
(new Extend\Settings())
->default('club-1-server-side-highlight.light_theme_bg_color', '#f7f7f7')
->default('club-1-server-side-highlight.light_theme_text_color', '#000000')
diff --git a/js/admin.js b/js/admin.js
index b963c4f..3bf16e6 100644
--- a/js/admin.js
+++ b/js/admin.js
@@ -1,7 +1,32 @@
+let hljsStyle;
+let themes;
+
app.initializers.add('server-side-highlight', function(app) {
- const themes = JSON.parse(app.data.settings['club-1-server-side-highlight.available_themes']);
+ themes = JSON.parse(app.data.settings['club-1-server-side-highlight.available_themes']);
app.extensionData
.for('club-1-server-side-highlight')
+ .registerSetting(function () {
+ return [
+ m('div.Form-group', [
+ m('label', app.translator.trans('club-1-server-side-highlight.admin.download_theme')),
+ m('span.Select', {
+ onchange: e => hljsStyle = e.target.value,
+ value: hljsStyle,
+ }, [
+ m('select.Select-input.FormControl', [
+ m('option', {disabled: true, selected: !hljsStyle}, app.translator.trans('club-1-server-side-highlight.admin.choose_theme')),
+ hljsStyles.map(x => m('option', x)),
+ ]),
+ m('i.icon.fas.fa-sort.Select-caret'),
+ ]),
+ ]),
+ m('button.Button.Button--primary', {
+ onclick: () => download.bind(this)(hljsStyle),
+ disabled: !hljsStyle,
+ }, app.translator.trans('club-1-server-side-highlight.admin.download')),
+ m('hr')
+ ];
+ })
.registerSetting({
setting: 'club-1-server-side-highlight.light_theme_bg_color',
label: app.translator.trans('club-1-server-side-highlight.admin.light_theme_bg_color'),
@@ -46,10 +71,36 @@ function preview(mode) {
let bg = encodeURIComponent(this.setting(`club-1-server-side-highlight.${mode}_theme_bg_color`)());
let text = encodeURIComponent(this.setting(`club-1-server-side-highlight.${mode}_theme_text_color`)());
let theme = encodeURIComponent(this.setting(`club-1-server-side-highlight.${mode}_theme_highlight_theme`)());
- return m('div.Form-group', m('iframe', {
+ return m('div.Form-group', m('iframe.highlight-preview', {
src: app.forum.attribute('baseUrl') + `/api/highlight-preview?bg=${bg}&text=${text}&theme=${theme}`,
- class: 'highlight-preview',
}));
}
+function download(style) {
+ hljsStyle = null;
+ app.request({
+ method: 'GET',
+ url: '/api/highlight-download',
+ params: {name: style},
+ errorHandler,
+ })
+ .then(res => {
+ for (const [key, value] of Object.entries(res)) {
+ themes[key] = value;
+ }
+ app.alerts.show({type: 'success'}, app.translator.trans('club-1-server-side-highlight.admin.download_success'));
+ m.redraw();
+ })
+}
+
+function errorHandler(err) {
+ let msgs = err.response.errors.filter(e => e.message).map(e => e.message);
+ if (msgs.length == 0) {
+ app.alerts.show({type: err.alert.type}, err.alert.content);
+ } else {
+ msgs.forEach(m => app.alerts.show({type: 'error'}, m));
+ }
+}
+
+
module.exports = {}
diff --git a/locale/en.yml b/locale/en.yml
index 39ceba4..a37b8d7 100644
--- a/locale/en.yml
+++ b/locale/en.yml
@@ -1,5 +1,9 @@
club-1-server-side-highlight:
admin:
+ download_theme: 'Download a theme'
+ choose_theme: 'Choose a theme'
+ download: 'Download'
+ download_success: 'Theme downloaded successfully'
light_theme_bg_color: 'Light theme background color'
light_theme_text_color: 'Light theme text color'
light_theme_highlight_theme: 'Light theme highlight theme'
diff --git a/locale/fr.yml b/locale/fr.yml
index 39b0908..f5298b4 100644
--- a/locale/fr.yml
+++ b/locale/fr.yml
@@ -1,5 +1,9 @@
club-1-server-side-highlight:
admin:
+ download_theme: 'Télécharger un thème'
+ choose_theme: 'Selectionner un thème'
+ download: 'Télécharger'
+ download_success: 'Thème téléchargé avec succes'
light_theme_bg_color: 'Couleur de fond pour le thème clair'
light_theme_text_color: 'Couleur du texte pour le thème clair'
light_theme_highlight_theme: 'Thème de couleur pour le thème clair'
diff --git a/src/Console/DownloadCommand.php b/src/Console/DownloadCommand.php
index 7f6603a..b2a85df 100644
--- a/src/Console/DownloadCommand.php
+++ b/src/Console/DownloadCommand.php
@@ -24,6 +24,8 @@
namespace Club1\ServerSideHighlight\Console;
use Club1\ServerSideHighlight\Consts;
+use Club1\ServerSideHighlight\Exception\InvalidArgumentException;
+use Club1\ServerSideHighlight\Exception\IOException;
use ErrorException;
use Flarum\Console\AbstractCommand;
use Flarum\Settings\SettingsRepositoryInterface;
@@ -69,10 +71,14 @@ protected function fire(): void
try {
$remote = fopen($remoteFile, 'r');
assert(is_resource($remote));
+ } catch (\Throwable $t) {
+ throw new InvalidArgumentException("Could not download theme $name", 1, $t);
+ }
+ try {
$this->assetsDisk->put($localFile, $remote);
} catch (\Throwable $t) {
$this->assetsDisk->delete($localFile);
- throw $t;
+ throw new IOException("Could not write theme to assets", 2, $t);
}
$this->info("Updating available highlight themes...");
diff --git a/src/Controller/DownloadController.php b/src/Controller/DownloadController.php
new file mode 100644
index 0000000..0fb69f1
--- /dev/null
+++ b/src/Controller/DownloadController.php
@@ -0,0 +1,62 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Controller;
+
+use Club1\ServerSideHighlight\Console\DownloadCommand;
+use Flarum\Http\RequestUtil;
+use Flarum\Settings\SettingsRepositoryInterface;
+use Laminas\Diactoros\Response\TextResponse;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Server\RequestHandlerInterface;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Output\NullOutput;
+
+class DownloadController implements RequestHandlerInterface
+{
+ /** @var SettingsRepositoryInterface */
+ protected $settings;
+
+ /** @var DownloadCommand */
+ protected $command;
+
+ public function __construct(SettingsRepositoryInterface $settings, DownloadCommand $command)
+ {
+ $this->settings = $settings;
+ $this->command = $command;
+ }
+
+ public function handle(Request $request): Response
+ {
+ $actor = RequestUtil::getActor($request);
+ $actor->assertAdmin();
+ $name = $request->getQueryParams()['name'];
+ $this->command->run(new ArrayInput(['name' => $name]), new NullOutput);
+ return new TextResponse(
+ $this->settings->get('club-1-server-side-highlight.available_themes'),
+ 200,
+ ['Content-Type' => ['application/json']]
+ );
+ }
+}
diff --git a/src/Exception/IOException.php b/src/Exception/IOException.php
new file mode 100644
index 0000000..e655665
--- /dev/null
+++ b/src/Exception/IOException.php
@@ -0,0 +1,39 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Exception;
+
+use RuntimeException;
+
+class IOException extends RuntimeException implements KnownException
+{
+ public function getType(): string
+ {
+ return 'io_error';
+ }
+
+ public function getStatusCode(): int
+ {
+ return 409;
+ }
+}
diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php
new file mode 100644
index 0000000..839bf81
--- /dev/null
+++ b/src/Exception/InvalidArgumentException.php
@@ -0,0 +1,37 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Exception;
+
+class InvalidArgumentException extends \InvalidArgumentException implements KnownException
+{
+ public function getType(): string
+ {
+ return 'invalid_parameter';
+ }
+
+ public function getStatusCode(): int
+ {
+ return 400;
+ }
+}
diff --git a/src/Exception/KnownException.php b/src/Exception/KnownException.php
new file mode 100644
index 0000000..6994b07
--- /dev/null
+++ b/src/Exception/KnownException.php
@@ -0,0 +1,52 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Exception;
+
+use Throwable;
+
+interface KnownException extends Throwable
+{
+ /**
+ * Determine the exception's type.
+ *
+ * This should be a short, precise identifier for the error that can be
+ * exposed to users as an error code. Furthermore, it can be used to find
+ * appropriate error messages in translations or views to render pretty
+ * error pages.
+ *
+ * Different exception classes are allowed to return the same status code,
+ * e.g. when they have similar semantic meaning to the end user, but are
+ * thrown by different subsystems.
+ *
+ * @return string
+ */
+ public function getType(): string;
+
+ /**
+ * Determine the HTTP status code to use with this exception.
+ *
+ * @return int The HTTP status code to use.
+ */
+ public function getStatusCode(): int;
+}
diff --git a/src/Exception/KnownExceptionHandler.php b/src/Exception/KnownExceptionHandler.php
new file mode 100644
index 0000000..846fe44
--- /dev/null
+++ b/src/Exception/KnownExceptionHandler.php
@@ -0,0 +1,45 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Exception;
+
+use Flarum\Foundation\ErrorHandling\HandledError;
+
+class KnownExceptionHandler
+{
+ public function handle(KnownException $e): HandledError
+ {
+ $prev = $e->getPrevious();
+ $message = $e->getMessage();
+ if (!is_null($prev)) {
+ $message .= ': ' . $prev->getMessage();
+ }
+ return (new HandledError(
+ $e,
+ $e->getType(),
+ $e->getStatusCode()
+ ))->withDetails([
+ ['message' => $message]
+ ]);
+ }
+}
diff --git a/src/Frontend/Admin.php b/src/Frontend/Admin.php
new file mode 100644
index 0000000..402b38c
--- /dev/null
+++ b/src/Frontend/Admin.php
@@ -0,0 +1,49 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Frontend;
+
+use Club1\ServerSideHighlight\Consts;
+use Flarum\Frontend\Document;
+use Illuminate\Contracts\Filesystem\Cloud;
+use Illuminate\Contracts\Filesystem\Factory;
+
+class Admin
+{
+ /** @var Cloud */
+ protected $assetsDisk;
+
+ public function __construct(Factory $filesystemFactory)
+ {
+ $this->assetsDisk = $filesystemFactory->disk('flarum-assets');
+ }
+
+ public function getAssetUrl(string $assetPath): string
+ {
+ return $this->assetsDisk->url(Consts::ASSETS_PATH . $assetPath);
+ }
+
+ public function __invoke(Document $document): void {
+ $document->js[] = $this->getAssetUrl('styles.min.js');
+ }
+}
diff --git a/src/Frontend/Content.php b/src/Frontend/Forum.php
similarity index 99%
rename from src/Frontend/Content.php
rename to src/Frontend/Forum.php
index 1c907e3..72d0de5 100644
--- a/src/Frontend/Content.php
+++ b/src/Frontend/Forum.php
@@ -29,7 +29,7 @@
use Illuminate\Contracts\Filesystem\Cloud;
use Illuminate\Contracts\Filesystem\Factory;
-class Content
+class Forum
{
/** @var SettingsRepositoryInterface */
protected $settings;
diff --git a/tests/integration/DownloadCommandTest.php b/tests/integration/DownloadCommandTest.php
index 6b6c964..32c8a82 100644
--- a/tests/integration/DownloadCommandTest.php
+++ b/tests/integration/DownloadCommandTest.php
@@ -21,6 +21,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+use Club1\ServerSideHighlight\Exception\InvalidArgumentException;
+use Club1\ServerSideHighlight\Exception\IOException;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Testing\integration\ConsoleTestCase;
@@ -95,8 +97,8 @@ public function testExceptions(array $input, string $class, string $regex, int $
public function exceptionsProvider(): array
{
return [
- "remote not exists" => [['name' => 'artaz'], ErrorException::class, '/404/'],
- "dir not writable" => [['name' => 'agate'], ErrorException::class, '/permission denied/i', 0544],
+ "remote not exists" => [['name' => 'artaz'], InvalidArgumentException::class, '/^Could not download theme artaz\.min\.css$/'],
+ "dir not writable" => [['name' => 'agate'], IOException::class, '/^Could not write theme to assets$/', 0544],
];
}
}
diff --git a/tests/integration/DownloadControllerTest.php b/tests/integration/DownloadControllerTest.php
new file mode 100644
index 0000000..dca15b8
--- /dev/null
+++ b/tests/integration/DownloadControllerTest.php
@@ -0,0 +1,72 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+use Flarum\Testing\integration\RetrievesAuthorizedUsers;
+use Flarum\Testing\integration\TestCase;
+use Mockery as m;
+use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
+use Mockery\MockInterface;
+
+class DownloadControllerTest extends TestCase
+{
+ use RetrievesAuthorizedUsers;
+ use MockeryPHPUnitIntegration;
+
+ /** @var \Club1\ServerSideHighlight\Console\DownloadCommand&MockInterface */
+ protected $command;
+
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->extension('club-1-server-side-highlight');
+ $this->command = m::mock('overload:Club1\ServerSideHighlight\Console\DownloadCommand');
+
+ $this->prepareDatabase([
+ 'users' => [
+ $this->normalUser(),
+ ],
+ ]);
+ }
+
+ /**
+ * @dataProvider responseCodeProvider
+ */
+ public function testResponseCode(int $user, int $commandCalls, int $code): void
+ {
+ $this->command->shouldReceive('run')->withAnyArgs()->times($commandCalls);
+ $response = $this->send(
+ $this->request('GET', '/api/highlight-download', ['authenticatedAs' => $user])
+ ->withQueryParams(['name' => 'arta'])
+ );
+
+ $this->assertEquals($code, $response->getStatusCode());
+ }
+
+ public function responseCodeProvider(): array
+ {
+ return [
+ 'admin user' => [1, 1, 200],
+ 'normal user' => [2, 0, 403],
+ ];
+ }
+}
diff --git a/tests/unit/AdminTest.php b/tests/unit/AdminTest.php
new file mode 100644
index 0000000..a983dfa
--- /dev/null
+++ b/tests/unit/AdminTest.php
@@ -0,0 +1,66 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Tests\unit;
+
+use Club1\ServerSideHighlight\Consts;
+use Club1\ServerSideHighlight\Frontend\Admin;
+use Club1\ServerSideHighlight\Frontend\Forum;
+use Flarum\Frontend\Document;
+use Flarum\Testing\unit\TestCase;
+use Illuminate\Contracts\Filesystem\Cloud;
+use Illuminate\Contracts\Filesystem\Factory;
+use Mockery as m;
+use Mockery\MockInterface;
+
+class AdminTest extends TestCase
+{
+ /** @var Cloud&MockInterface */
+ protected $disk;
+
+ /** @var Factory&MockInterface */
+ protected $factory;
+
+ /** @var Document&MockInterface */
+ protected $doc;
+
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->disk = m::mock(Cloud::class);
+ $this->factory = m::mock(Factory::class);
+ $this->factory->shouldReceive('disk')->andReturn($this->disk);
+ $this->doc = m::mock(Document::class);
+ }
+
+ public function testBasic(): void
+ {
+ $jsPath = Consts::ASSETS_PATH . 'styles.min.js';
+ $this->disk->shouldReceive('url')->once()->with($jsPath)->andReturn($jsPath);
+
+ $content = new Admin($this->factory);
+ $content($this->doc);
+ $this->assertCount(1, $this->doc->js);
+ $this->assertEquals($jsPath, $this->doc->js[0]);
+ }
+}
diff --git a/tests/unit/ContentTest.php b/tests/unit/ForumTest.php
similarity index 95%
rename from tests/unit/ContentTest.php
rename to tests/unit/ForumTest.php
index 7b2fbae..24c0624 100644
--- a/tests/unit/ContentTest.php
+++ b/tests/unit/ForumTest.php
@@ -24,7 +24,7 @@
namespace Club1\ServerSideHighlight\Tests\unit;
use Club1\ServerSideHighlight\Consts;
-use Club1\ServerSideHighlight\Frontend\Content;
+use Club1\ServerSideHighlight\Frontend\Forum;
use Flarum\Frontend\Document;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Testing\unit\TestCase;
@@ -33,7 +33,7 @@
use Mockery as m;
use Mockery\MockInterface;
-class ContentTest extends TestCase
+class ForumTest extends TestCase
{
/** @var SettingsRepositoryInterface&MockInterface */
protected $settings;
@@ -72,7 +72,7 @@ public function testBasic(bool $dark, string $css): void
$this->settings->shouldReceive('get')->withSomeOfArgs("club-1-server-side-highlight.{$theme}_theme_bg_color")->andReturn('#000');
$this->settings->shouldReceive('get')->withSomeOfArgs("club-1-server-side-highlight.{$theme}_theme_text_color")->andReturn('#fff');
- $content = new Content($this->settings, $this->factory);
+ $content = new Forum($this->settings, $this->factory);
$content($this->doc);
$this->assertCount(1, $this->doc->js);
$this->assertEquals($jsPath, $this->doc->js[0]);
diff --git a/tests/unit/KnownExceptionHandlerTest.php b/tests/unit/KnownExceptionHandlerTest.php
new file mode 100644
index 0000000..7c74f4d
--- /dev/null
+++ b/tests/unit/KnownExceptionHandlerTest.php
@@ -0,0 +1,54 @@
+.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Club1\ServerSideHighlight\Tests\unit;
+
+use Club1\ServerSideHighlight\Exception\InvalidArgumentException;
+use Club1\ServerSideHighlight\Exception\IOException;
+use Club1\ServerSideHighlight\Exception\KnownException;
+use Club1\ServerSideHighlight\Exception\KnownExceptionHandler;
+use Flarum\Testing\unit\TestCase;
+
+class KnownExceptionHandlerTest extends TestCase
+{
+ /**
+ * @dataProvider basicProvider
+ */
+ public function testBasic(KnownException $e, string $type, int $statusCode, array $details): void
+ {
+ $handler = new KnownExceptionHandler();
+ $error = $handler->handle($e);
+ $this->assertEquals($type, $error->getType());
+ $this->assertEquals($statusCode, $error->getStatusCode());
+ $this->assertEquals($details, $error->getDetails());
+ }
+
+ public function basicProvider(): array
+ {
+ return [
+ 'invalid' => [new InvalidArgumentException('msg'), 'invalid_parameter', 400, [['message' => 'msg']]],
+ 'IO' => [new IOException('msg'), 'io_error', 409, [['message' => 'msg']]],
+ 'prev message' => [new IOException('msg', 1, new \Exception('another')), 'io_error', 409, [['message' => 'msg: another']]],
+ ];
+ }
+}