diff --git a/framework/core/js/src/admin/components/DashboardPage.tsx b/framework/core/js/src/admin/components/DashboardPage.tsx index 59ecd5cf2..5a53d38f6 100644 --- a/framework/core/js/src/admin/components/DashboardPage.tsx +++ b/framework/core/js/src/admin/components/DashboardPage.tsx @@ -4,6 +4,7 @@ import ExtensionsWidget from './ExtensionsWidget'; import ItemList from '../../common/utils/ItemList'; import AdminPage from './AdminPage'; import type { Children } from 'mithril'; +import DebugWarningWidget from './DebugWarningWidget'; export default class DashboardPage extends AdminPage { headerInfo() { @@ -22,6 +23,10 @@ export default class DashboardPage extends AdminPage { availableWidgets(): ItemList { const items = new ItemList(); + if (app.data.debugEnabled) { + items.add('debug-warning', , 100); + } + items.add('status', , 30); items.add('extensions', , 10); diff --git a/framework/core/js/src/admin/components/DebugWarningWidget.tsx b/framework/core/js/src/admin/components/DebugWarningWidget.tsx new file mode 100644 index 000000000..eaa7882d0 --- /dev/null +++ b/framework/core/js/src/admin/components/DebugWarningWidget.tsx @@ -0,0 +1,20 @@ +import app from '../../admin/app'; +import Alert from '../../common/components/Alert'; +import Link from '../../common/components/Link'; +import DashboardWidget from './DashboardWidget'; + +export default class DebugWarningWidget extends DashboardWidget { + className() { + return 'DebugWarningWidget'; + } + + content() { + return ( + + {app.translator.trans('core.admin.debug-warning.detail', { + link: , + })} + + ); + } +} diff --git a/framework/core/js/src/common/components/Alert.tsx b/framework/core/js/src/common/components/Alert.tsx index d9601b04e..5efac4731 100644 --- a/framework/core/js/src/common/components/Alert.tsx +++ b/framework/core/js/src/common/components/Alert.tsx @@ -5,10 +5,15 @@ import extract from '../utils/extract'; import type Mithril from 'mithril'; import classList from '../utils/classList'; import app from '../app'; +import iconHelper from '../helpers/icon'; export interface AlertAttrs extends ComponentAttrs { /** The type of alert this is. Will be used to give the alert a class name of `Alert--{type}`. */ type?: string; + /** Title of the alert. Optional. */ + title?: Mithril.Children; + /** Icon used next to the title. Optional. */ + icon?: string; /** An array of controls to show in the alert. */ controls?: Mithril.Children; /** Whether or not the alert can be dismissed. */ @@ -28,6 +33,8 @@ export default class Alert extends Component< const type = extract(attrs, 'type'); attrs.className = classList('Alert', `Alert--${type}`, attrs.className); + const title = extract(attrs, 'title'); + const icon = extract(attrs, 'icon'); const content = extract(attrs, 'content') || vnode.children; const controls = (extract(attrs, 'controls') || []) as Mithril.Vnode[]; @@ -51,6 +58,12 @@ export default class Alert extends Component< return (
+ {!!title && ( +
+ {!!icon && {iconHelper(icon)}} + {title} +
+ )} {content}
    {listItems(controls.concat(dismissControl))}
diff --git a/framework/core/less/admin.less b/framework/core/less/admin.less index 49a3ab12f..2b1c658d9 100644 --- a/framework/core/less/admin.less +++ b/framework/core/less/admin.less @@ -3,6 +3,7 @@ @import "admin/AdminHeader"; @import "admin/AdminNav"; @import "admin/DashboardPage"; +@import "admin/DebugWarningWidget"; @import "admin/BasicsPage"; @import "admin/PermissionsPage"; @import "admin/EditGroupModal"; diff --git a/framework/core/less/admin/DebugWarningWidget.less b/framework/core/less/admin/DebugWarningWidget.less new file mode 100644 index 000000000..20f444e0b --- /dev/null +++ b/framework/core/less/admin/DebugWarningWidget.less @@ -0,0 +1,3 @@ +.DebugWarningWidget { + padding: 0; +} diff --git a/framework/core/less/common/Alert.less b/framework/core/less/common/Alert.less index 8af4ad80a..337a4b848 100644 --- a/framework/core/less/common/Alert.less +++ b/framework/core/less/common/Alert.less @@ -61,4 +61,15 @@ vertical-align: 0; } } + + &:empty { + display: none; + } +} +.Alert-title { + margin-bottom: 8px; + font-weight: bold; + display: flex; + align-items: center; + gap: 8px; } diff --git a/framework/core/locale/core.yml b/framework/core/locale/core.yml index dc0c1f660..1f7d9dba3 100644 --- a/framework/core/locale/core.yml +++ b/framework/core/locale/core.yml @@ -59,6 +59,14 @@ core: title: Dashboard tools_button: Tools + # These translations are usin in the debug warning widget. + debug-warning: + detail: | + When debug mode is active, Flarum will rebuild its JavaScript and CSS assets on every request, and could also potentially leak other information, such as database secrets, environment variables, etc. + + It is highly recommended to disable debug in your config.php file in production. See Flarum docs for more information. + label: Debug mode active + # These translations are used in the Edit Custom CSS modal dialog. edit_css: customize_text: "Customize your forum's appearance by adding your own Less/CSS code to be applied on top of Flarum's default styles." diff --git a/framework/core/src/Admin/Content/AdminPayload.php b/framework/core/src/Admin/Content/AdminPayload.php index 8c4237c8e..c82b3d2fb 100644 --- a/framework/core/src/Admin/Content/AdminPayload.php +++ b/framework/core/src/Admin/Content/AdminPayload.php @@ -10,6 +10,7 @@ namespace Flarum\Admin\Content; use Flarum\Extension\ExtensionManager; +use Flarum\Foundation\Config; use Flarum\Frontend\Document; use Flarum\Group\Permission; use Flarum\Settings\Event\Deserializing; @@ -18,6 +19,7 @@ use Flarum\User\User; use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Database\ConnectionInterface; +use Illuminate\Support\Arr; use Psr\Http\Message\ServerRequestInterface as Request; class AdminPayload @@ -42,25 +44,33 @@ class AdminPayload */ protected $db; + /** + * @var Config + */ + protected $config; + /** * @param Container $container * @param SettingsRepositoryInterface $settings * @param ExtensionManager $extensions * @param ConnectionInterface $db * @param Dispatcher $events + * @param Config $config */ public function __construct( Container $container, SettingsRepositoryInterface $settings, ExtensionManager $extensions, ConnectionInterface $db, - Dispatcher $events + Dispatcher $events, + Config $config ) { $this->container = $container; $this->settings = $settings; $this->extensions = $extensions; $this->db = $db; $this->events = $events; + $this->config = $config; } public function __invoke(Document $document, Request $request) @@ -82,6 +92,7 @@ class AdminPayload $document->payload['phpVersion'] = PHP_VERSION; $document->payload['mysqlVersion'] = $this->db->selectOne('select version() as version')->version; + $document->payload['debugEnabled'] = Arr::get($this->config, 'debug'); /** * Used in the admin user list. Implemented as this as it matches the API in flarum/statistics.