diff --git a/framework/core/less/admin.less b/framework/core/less/admin.less
index 772207002..07f528fc0 100644
--- a/framework/core/less/admin.less
+++ b/framework/core/less/admin.less
@@ -10,4 +10,5 @@
@import "admin/ExtensionWidget";
@import "admin/AppearancePage";
@import "admin/MailPage";
+@import "admin/NoJs";
@import "admin/UsersListPage.less";
diff --git a/framework/core/less/admin/NoJs.less b/framework/core/less/admin/NoJs.less
new file mode 100644
index 000000000..b89082bd4
--- /dev/null
+++ b/framework/core/less/admin/NoJs.less
@@ -0,0 +1,17 @@
+// Minimal NoJs specific styles
+.NoJs-ExtensionsTable {
+ td&-icon {
+ padding-top: 0;
+ padding-bottom: 0;
+ }
+
+ .ExtensionListItem-Dot {
+ position: relative;
+ right: 0;
+ margin: 0;
+ }
+
+ .ExtensionIcon {
+ --size: 25px;
+ }
+}
diff --git a/framework/core/less/common/Table.less b/framework/core/less/common/Table.less
new file mode 100644
index 000000000..dbcb52c8a
--- /dev/null
+++ b/framework/core/less/common/Table.less
@@ -0,0 +1,69 @@
+.Table {
+ background: @control-bg;
+ border-radius: @border-radius;
+ border-collapse: collapse;
+ border-spacing: 0;
+
+ caption {
+ text-align: start;
+ }
+
+ td, th {
+ border-bottom: 1px solid @body-bg;
+ color: @control-color;
+ }
+
+ td, th, .Checkbox, &-controls-item {
+ padding: 10px 15px;
+ }
+
+ & &-checkbox, & &-controls {
+ padding: 0;
+ }
+
+ thead {
+ th {
+ text-align: center;
+ padding: 15px 25px;
+ }
+
+ .icon {
+ display: block;
+ font-size: 14px;
+ width: auto;
+ margin-bottom: 5px;
+ }
+ }
+
+ &-groupToggle {
+ cursor: pointer;
+
+ .icon {
+ font-size: 14px;
+ margin-right: 2px;
+ .fa-fw();
+ }
+ }
+
+ &-checkbox {
+ .Checkbox {
+ display: block;
+ }
+
+ .Checkbox-display {
+ text-align: center;
+ cursor: pointer;
+ }
+
+ &.highlighted .Checkbox, .Checkbox:hover {
+ &:not(.disabled) {
+ background: darken(@control-bg, 4%);
+ }
+ }
+ }
+
+ &-controls-item {
+ width: 100%;
+ border-radius: 0;
+ }
+}
diff --git a/framework/core/less/common/common.less b/framework/core/less/common/common.less
index 39b6bbb79..b141aaec1 100644
--- a/framework/core/less/common/common.less
+++ b/framework/core/less/common/common.less
@@ -27,6 +27,7 @@
@import "Placeholder";
@import "Search";
@import "Select";
+@import "Table";
@import "TextEditor";
@import "Tooltip";
@import "ValidationError";
diff --git a/framework/core/less/forum/NotificationGrid.less b/framework/core/less/forum/NotificationGrid.less
index 4691be78c..bfea1992c 100644
--- a/framework/core/less/forum/NotificationGrid.less
+++ b/framework/core/less/forum/NotificationGrid.less
@@ -1,52 +1,3 @@
.NotificationGrid {
- background: @control-bg;
- border-radius: @border-radius;
- border-collapse: collapse;
- border-spacing: 0;
-
- td, th {
- border-bottom: 1px solid @body-bg;
- color: @control-color;
- }
- td, th, .Checkbox {
- padding: 10px 15px;
- }
- .NotificationGrid-checkbox {
- padding: 0;
- }
- thead {
- th {
- text-align: center;
- padding: 15px 25px;
- }
- .icon {
- display: block;
- font-size: 14px;
- width: auto;
- margin-bottom: 5px;
- }
- }
-}
-.NotificationGrid-groupToggle {
- cursor: pointer;
-
- .icon {
- font-size: 14px;
- margin-right: 2px;
- .fa-fw();
- }
-}
-.NotificationGrid-checkbox {
- .Checkbox {
- display: block;
- }
- .Checkbox-display {
- text-align: center;
- cursor: pointer;
- }
- &.highlighted .Checkbox, .Checkbox:hover {
- &:not(.disabled) {
- background: darken(@control-bg, 4%);
- }
- }
+ .Table();
}
diff --git a/framework/core/locale/core.yml b/framework/core/locale/core.yml
index 7f4f0cf81..79660abf5 100644
--- a/framework/core/locale/core.yml
+++ b/framework/core/locale/core.yml
@@ -545,6 +545,20 @@ core:
# Translations in this namespace are used in views other than Flarum's normal JS client.
views:
+ # Translations in this namespace are displayed by the basic HTML admin index.
+ admin:
+ extensions:
+ caption: => core.ref.extensions
+ disable: Disable
+ empty: No installed extensions
+ enable: Enable
+ name: Extension Name
+ package_name: Package Name
+ version: Version
+ info:
+ caption: Application Info
+ title: Administration
+
# Translations in this namespace are displayed by the Confirm Email interface.
confirm_email:
submit_button: => core.ref.confirm_email
@@ -673,6 +687,7 @@ core:
edit: Edit
edit_user: Edit User
email: Email
+ extensions: Extensions
icon: Icon
icon_text: "Enter the name of any FontAwesome icon class, including the fas fa-
prefix."
load_more: Load More
diff --git a/framework/core/src/Admin/Content/Index.php b/framework/core/src/Admin/Content/Index.php
new file mode 100644
index 000000000..2060955cb
--- /dev/null
+++ b/framework/core/src/Admin/Content/Index.php
@@ -0,0 +1,60 @@
+view = $view;
+ $this->extensions = $extensions;
+ $this->settings = $settings;
+ }
+
+ public function __invoke(Document $document, Request $request): Document
+ {
+ $extensions = $this->extensions->getExtensions();
+ $extensionsEnabled = json_decode($this->settings->get('extensions_enabled', '{}'), true);
+ $csrfToken = $request->getAttribute('session')->token();
+
+ $mysqlVersion = $document->payload['mysqlVersion'];
+ $phpVersion = $document->payload['phpVersion'];
+ $flarumVersion = Application::VERSION;
+
+ $document->content = $this->view->make(
+ 'flarum.admin::frontend.content.admin',
+ compact('extensions', 'extensionsEnabled', 'csrfToken', 'flarumVersion', 'phpVersion', 'mysqlVersion')
+ );
+
+ return $document;
+ }
+}
diff --git a/framework/core/src/Admin/Controller/UpdateExtensionController.php b/framework/core/src/Admin/Controller/UpdateExtensionController.php
new file mode 100644
index 000000000..61db18a59
--- /dev/null
+++ b/framework/core/src/Admin/Controller/UpdateExtensionController.php
@@ -0,0 +1,55 @@
+url = $url;
+ $this->bus = $bus;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handle(Request $request): ResponseInterface
+ {
+ $actor = RequestUtil::getActor($request);
+ $enabled = (bool) (int) Arr::get($request->getParsedBody(), 'enabled');
+ $name = Arr::get($request->getQueryParams(), 'name');
+
+ $this->bus->dispatch(
+ new ToggleExtension($actor, $name, $enabled)
+ );
+
+ return new RedirectResponse($this->url->to('admin')->base());
+ }
+}
diff --git a/framework/core/src/Admin/routes.php b/framework/core/src/Admin/routes.php
index 66ae2e85b..d81517dfb 100644
--- a/framework/core/src/Admin/routes.php
+++ b/framework/core/src/Admin/routes.php
@@ -7,6 +7,8 @@
* LICENSE file that was distributed with this source code.
*/
+use Flarum\Admin\Content\Index;
+use Flarum\Admin\Controller\UpdateExtensionController;
use Flarum\Http\RouteCollection;
use Flarum\Http\RouteHandlerFactory;
@@ -14,6 +16,12 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
$map->get(
'/',
'index',
- $route->toAdmin()
+ $route->toAdmin(Index::class)
+ );
+
+ $map->post(
+ '/extensions/{name}',
+ 'extensions.update',
+ $route->toController(UpdateExtensionController::class)
);
};
diff --git a/framework/core/src/Api/Controller/UpdateExtensionController.php b/framework/core/src/Api/Controller/UpdateExtensionController.php
index 35cf3939a..0a9e4e59e 100644
--- a/framework/core/src/Api/Controller/UpdateExtensionController.php
+++ b/framework/core/src/Api/Controller/UpdateExtensionController.php
@@ -9,7 +9,8 @@
namespace Flarum\Api\Controller;
-use Flarum\Extension\ExtensionManager;
+use Flarum\Bus\Dispatcher;
+use Flarum\Extension\Command\ToggleExtension;
use Flarum\Http\RequestUtil;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\EmptyResponse;
@@ -20,16 +21,13 @@ use Psr\Http\Server\RequestHandlerInterface;
class UpdateExtensionController implements RequestHandlerInterface
{
/**
- * @var ExtensionManager
+ * @var Dispatcher
*/
- protected $extensions;
+ protected $bus;
- /**
- * @param ExtensionManager $extensions
- */
- public function __construct(ExtensionManager $extensions)
+ public function __construct(Dispatcher $bus)
{
- $this->extensions = $extensions;
+ $this->bus = $bus;
}
/**
@@ -37,16 +35,13 @@ class UpdateExtensionController implements RequestHandlerInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
- RequestUtil::getActor($request)->assertAdmin();
-
- $enabled = Arr::get($request->getParsedBody(), 'enabled');
+ $actor = RequestUtil::getActor($request);
+ $enabled = (bool) (int) Arr::get($request->getParsedBody(), 'enabled');
$name = Arr::get($request->getQueryParams(), 'name');
- if ($enabled === true) {
- $this->extensions->enable($name);
- } elseif ($enabled === false) {
- $this->extensions->disable($name);
- }
+ $this->bus->dispatch(
+ new ToggleExtension($actor, $name, $enabled)
+ );
return new EmptyResponse;
}
diff --git a/framework/core/src/Extension/Command/ToggleExtension.php b/framework/core/src/Extension/Command/ToggleExtension.php
new file mode 100644
index 000000000..69d3060a4
--- /dev/null
+++ b/framework/core/src/Extension/Command/ToggleExtension.php
@@ -0,0 +1,37 @@
+actor = $actor;
+ $this->name = $name;
+ $this->enabled = $enabled;
+ }
+}
diff --git a/framework/core/src/Extension/Command/ToggleExtensionHandler.php b/framework/core/src/Extension/Command/ToggleExtensionHandler.php
new file mode 100644
index 000000000..564d543df
--- /dev/null
+++ b/framework/core/src/Extension/Command/ToggleExtensionHandler.php
@@ -0,0 +1,41 @@
+extensions = $extensions;
+ }
+
+ /**
+ * @throws \Flarum\User\Exception\PermissionDeniedException
+ * @throws \Flarum\Extension\Exception\MissingDependenciesException
+ * @throws \Flarum\Extension\Exception\DependentExtensionsException
+ */
+ public function handle(ToggleExtension $command)
+ {
+ $command->actor->assertAdmin();
+
+ if ($command->enabled) {
+ $this->extensions->enable($command->name);
+ } else {
+ $this->extensions->disable($command->name);
+ }
+ }
+}
diff --git a/framework/core/src/Extension/Extension.php b/framework/core/src/Extension/Extension.php
index a705578fb..41e73482a 100644
--- a/framework/core/src/Extension/Extension.php
+++ b/framework/core/src/Extension/Extension.php
@@ -279,6 +279,19 @@ class Extension implements Arrayable
return $icon;
}
+ public function getIconStyles(): string
+ {
+ $properties = $this->getIcon();
+
+ unset($properties['name']);
+
+ return implode(';', array_map(function (string $property, string $value) {
+ $property = Str::kebab($property);
+
+ return "$property: $value";
+ }, array_keys($properties), $properties));
+ }
+
/**
* @internal
*/
diff --git a/framework/core/views/frontend/admin.blade.php b/framework/core/views/frontend/admin.blade.php
index 56a6aa2b9..351acd8cf 100644
--- a/framework/core/views/frontend/admin.blade.php
+++ b/framework/core/views/frontend/admin.blade.php
@@ -28,9 +28,9 @@
Flarum | +{{ $flarumVersion }} | +
PHP | +{{ $phpVersion }} | +
MySQL | +{{ $mysqlVersion }} | +
+ | {{ $translator->trans('core.views.admin.extensions.name') }} | +{{ $translator->trans('core.views.admin.extensions.package_name') }} | +{{ $translator->trans('core.views.admin.extensions.version') }} | ++ | + |
---|---|---|---|---|---|
+
+
+
+ |
+ {{ $extension->getTitle() }} | +{{ $extension->name }} | +{{ $extension->getVersion() }} | ++ + | ++ + | +
{{ $translator->trans('core.views.admin.extensions.empty') }} |