mirror of
https://github.com/flarum/framework.git
synced 2025-02-21 13:08:13 +08:00
feat: NoJs Admin View (#3059)
Adds a nojs blade template to be able to enable/disable extensions when one of them misbehaves.
This commit is contained in:
parent
7d59bbad88
commit
c3ab5b96bb
@ -10,4 +10,5 @@
|
||||
@import "admin/ExtensionWidget";
|
||||
@import "admin/AppearancePage";
|
||||
@import "admin/MailPage";
|
||||
@import "admin/NoJs";
|
||||
@import "admin/UsersListPage.less";
|
||||
|
17
framework/core/less/admin/NoJs.less
Normal file
17
framework/core/less/admin/NoJs.less
Normal file
@ -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;
|
||||
}
|
||||
}
|
69
framework/core/less/common/Table.less
Normal file
69
framework/core/less/common/Table.less
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
@import "Placeholder";
|
||||
@import "Search";
|
||||
@import "Select";
|
||||
@import "Table";
|
||||
@import "TextEditor";
|
||||
@import "Tooltip";
|
||||
@import "ValidationError";
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 <a>FontAwesome</a> icon class, <em>including</em> the <code>fas fa-</code> prefix."
|
||||
load_more: Load More
|
||||
|
60
framework/core/src/Admin/Content/Index.php
Normal file
60
framework/core/src/Admin/Content/Index.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Admin\Content;
|
||||
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class Index
|
||||
{
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var ExtensionManager
|
||||
*/
|
||||
protected $extensions;
|
||||
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
public function __construct(Factory $view, ExtensionManager $extensions, SettingsRepositoryInterface $settings)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Admin\Controller;
|
||||
|
||||
use Flarum\Bus\Dispatcher;
|
||||
use Flarum\Extension\Command\ToggleExtension;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Laminas\Diactoros\Response\RedirectResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class UpdateExtensionController implements RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var UrlGenerator
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
public function __construct(UrlGenerator $url, Dispatcher $bus)
|
||||
{
|
||||
$this->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());
|
||||
}
|
||||
}
|
@ -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)
|
||||
);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
37
framework/core/src/Extension/Command/ToggleExtension.php
Normal file
37
framework/core/src/Extension/Command/ToggleExtension.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Extension\Command;
|
||||
|
||||
use Flarum\User\User;
|
||||
|
||||
class ToggleExtension
|
||||
{
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $enabled;
|
||||
|
||||
public function __construct(User $actor, string $name, bool $enabled)
|
||||
{
|
||||
$this->actor = $actor;
|
||||
$this->name = $name;
|
||||
$this->enabled = $enabled;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Extension\Command;
|
||||
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
|
||||
class ToggleExtensionHandler
|
||||
{
|
||||
/**
|
||||
* @var ExtensionManager
|
||||
*/
|
||||
protected $extensions;
|
||||
|
||||
public function __construct(ExtensionManager $extensions)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
*/
|
||||
|
@ -28,9 +28,9 @@
|
||||
<div id="admin-navigation" class="App-nav sideNav"></div>
|
||||
</div>
|
||||
|
||||
<div id="content" class="sideNavOffset"></div>
|
||||
|
||||
{!! $content !!}
|
||||
<div id="content" class="sideNavOffset">
|
||||
{!! $content !!}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
70
framework/core/views/frontend/content/admin.blade.php
Normal file
70
framework/core/views/frontend/content/admin.blade.php
Normal file
@ -0,0 +1,70 @@
|
||||
@inject('url', 'Flarum\Http\UrlGenerator')
|
||||
|
||||
<div class="container">
|
||||
<h2>{{ $translator->trans('core.views.admin.title') }}</h2>
|
||||
|
||||
<table class="NoJs-InfoTable Table">
|
||||
<caption><h3>{{ $translator->trans('core.views.admin.info.caption') }}</h3></caption>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Flarum</td>
|
||||
<td>{{ $flarumVersion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PHP</td>
|
||||
<td>{{ $phpVersion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MySQL</td>
|
||||
<td>{{ $mysqlVersion }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="NoJs-ExtensionsTable Table">
|
||||
<caption><h3>{{ $translator->trans('core.views.admin.extensions.caption') }}</h3></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{{ $translator->trans('core.views.admin.extensions.name') }}</th>
|
||||
<th>{{ $translator->trans('core.views.admin.extensions.package_name') }}</th>
|
||||
<th>{{ $translator->trans('core.views.admin.extensions.version') }}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($extensions as $extension)
|
||||
@php $isEnabled = in_array($extension->getId(), $extensionsEnabled); @endphp
|
||||
|
||||
<tr>
|
||||
<td class="NoJs-ExtensionsTable-icon">
|
||||
<div class="ExtensionIcon" style="{{ $extension->getIconStyles() }}">
|
||||
<span class="icon {{ $extension->getIcon()['name'] ?? '' }}"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="NoJs-ExtensionsTable-title">{{ $extension->getTitle() }}</td>
|
||||
<td class="NoJs-ExtensionsTable-name">{{ $extension->name }}</td>
|
||||
<td class="NoJs-ExtensionsTable-version">{{ $extension->getVersion() }}</td>
|
||||
<td class="NoJs-ExtensionsTable-state">
|
||||
<span class="ExtensionListItem-Dot {{ $isEnabled ? 'enabled' : 'disabled' }}" aria-hidden="true"></span>
|
||||
</td>
|
||||
<td class="NoJs-ExtensionsTable-toggle Table-controls">
|
||||
<form action="{{ $url->to('admin')->route('extensions.update', ['name' => $extension->getId()]) }}" method="POST">
|
||||
<input type="hidden" name="csrfToken" value="{{ $csrfToken }}">
|
||||
<input type="hidden" name="enabled" value="{{ $isEnabled ? 0 : 1 }}">
|
||||
|
||||
@if($isEnabled)
|
||||
<button type="submit" class="Button Table-controls-item">{{ $translator->trans('core.views.admin.extensions.disable') }}</button>
|
||||
@else
|
||||
<button type="submit" class="Button Table-controls-item">{{ $translator->trans('core.views.admin.extensions.enable') }}</button>
|
||||
@endif
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="NoJs-ExtensionsTable-empty">{{ $translator->trans('core.views.admin.extensions.empty') }}</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
Loading…
x
Reference in New Issue
Block a user