mirror of
https://github.com/flarum/core.git
synced 2025-09-07 06:31:19 +02:00
Compare commits
15 Commits
as/safe-mo
...
v1.1.1
Author | SHA1 | Date | |
---|---|---|---|
|
eb3a60338f | ||
|
9c8dceff33 | ||
|
99112429f9 | ||
|
b4772e5399 | ||
|
2b47e90827 | ||
|
1c2465b2da | ||
|
a6717ee981 | ||
|
450ab61620 | ||
|
e2f01c040b | ||
|
1d15cff9ca | ||
|
88724bb4cb | ||
|
1637b90531 | ||
|
245d0d2550 | ||
|
5dd48e1b86 | ||
|
c1a8c6c190 |
59
CHANGELOG.md
59
CHANGELOG.md
@@ -1,5 +1,64 @@
|
||||
# Changelog
|
||||
|
||||
## [1.1.1](https://github.com/flarum/core/compare/v1.1.0...v1.1.1)
|
||||
|
||||
### Fixed
|
||||
- Performance issue with very large communities.
|
||||
|
||||
## [1.1.0](https://github.com/flarum/core/compare/v1.0.4...v1.1.0)
|
||||
|
||||
### Added
|
||||
- Info command now displays MySQL version, queue driver, mail driver (https://github.com/flarum/core/pull/2991)
|
||||
- Use organization Prettier config (https://github.com/flarum/core/pull/2967)
|
||||
- Support for global typings in extensions (https://github.com/flarum/core/pull/2992)
|
||||
- Typings for class component state attribute (https://github.com/flarum/core/pull/2995)
|
||||
- Custom colorising with CSS custom properties (https://github.com/flarum/core/pull/3001)
|
||||
- Theme Extender to allow overriding LESS files (https://github.com/flarum/core/pull/3008)
|
||||
- Update lastSeenAt when authenticating via API (https://github.com/flarum/core/pull/3058)
|
||||
- NoJs Admin View (https://github.com/flarum/core/pull/3059)
|
||||
- Preload FontAwesome, JS and CSS, and add `preload` extender (https://github.com/flarum/core/pull/3057)
|
||||
|
||||
### Changed
|
||||
- Move Day.js plugin types import to global typings (https://github.com/flarum/core/pull/2954)
|
||||
- Avoid resolving excluded middleware on each middleware items
|
||||
- Allow extra attrs provided to `<Select>` to be passed through to the DOM element (https://github.com/flarum/core/pull/2959)
|
||||
- Limit height of code blocks (https://github.com/flarum/core/pull/3012)
|
||||
- Update normalize.css from v3.0.2 to v8.0.1 (https://github.com/flarum/core/pull/3015)
|
||||
- Permission Grid: stick the headers to handle a lot of tags (https://github.com/flarum/core/pull/2887)
|
||||
- Use `ItemList` for `DiscussionPage` content (https://github.com/flarum/core/pull/3004)
|
||||
- Move email confirmation to POST request (https://github.com/flarum/core/pull/3038)
|
||||
- Minor CSS code cleanup (https://github.com/flarum/core/pull/3026)
|
||||
- Replace username with display name in more places (https://github.com/flarum/core/pull/3040)
|
||||
- Rewrite Button to Typescript (https://github.com/flarum/core/pull/2984)
|
||||
- Rewrite AdminPage abstract component into Typescript (https://github.com/flarum/core/pull/2996)
|
||||
- Allow adding page parameters to PaginatedListState (https://github.com/flarum/core/pull/2935)
|
||||
- Pass filter params to getApiDocument (https://github.com/flarum/core/pull/3037)
|
||||
- Use author filter instead of gambit to get a user's discussions (https://github.com/flarum/core/pull/3068)
|
||||
- [A11Y] Accessibility improvements for the Search component (https://github.com/flarum/core/pull/3017)
|
||||
- Add determinsm to extension order resolution (https://github.com/flarum/core/pull/3076)
|
||||
- Add cache control headers to the admin area (https://github.com/flarum/core/pull/3097)
|
||||
|
||||
### Fixed
|
||||
- HLJS 11 new styles resulting in double padding (https://github.com/flarum/core/pull/2909)
|
||||
- Internal API client attempting to load an uninstantiated session
|
||||
- Empty post footer taking visual space (https://github.com/flarum/core/pull/2926)
|
||||
- Unrecognized component class custom attribute typings (https://github.com/flarum/core/pull/2962)
|
||||
- User edit groups permission not visually depending on view hidden groups permission (https://github.com/flarum/core/pull/2880)
|
||||
- Event post excerpt preview triggers error (https://github.com/flarum/core/pull/2964)
|
||||
- Missing settings defaults for display name driver and User slug driver (https://github.com/flarum/core/pull/2971)
|
||||
- [A11Y] Icons not hidden from screenreaders (https://github.com/flarum/core/pull/3027)
|
||||
- [A11Y] Checkboxes not focusable (https://github.com/flarum/core/pull/3014)
|
||||
- Uploading ICO favicons resulting in server errors (https://github.com/flarum/core/pull/2949)
|
||||
- Missing proper validation for large avatar upload payload (https://github.com/flarum/core/pull/3042)
|
||||
- [A11Y] Missing focus rings in control elements (https://github.com/flarum/core/pull/3016)
|
||||
- Unsanitised integer query parameters (https://github.com/flarum/core/pull/3064)
|
||||
|
||||
###### Code Contributors
|
||||
@lhsazevedo, @Ornanovitch, @pierres, @the-turk, @iPurpl3x
|
||||
|
||||
###### Issue Reporters
|
||||
@uamv, @dannyuk1982, @BurnNoticeSpy, @haarp, @peopleinside, @matteocontrini
|
||||
|
||||
## [1.0.4](https://github.com/flarum/core/compare/v1.0.3...v1.0.4)
|
||||
|
||||
### Fixed
|
||||
|
4
js/dist/admin.js
generated
vendored
4
js/dist/admin.js
generated
vendored
File diff suppressed because one or more lines are too long
2
js/dist/admin.js.map
generated
vendored
2
js/dist/admin.js.map
generated
vendored
File diff suppressed because one or more lines are too long
4
js/dist/forum.js
generated
vendored
4
js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
js/dist/forum.js.map
generated
vendored
2
js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
1478
js/package-lock.json
generated
1478
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@
|
||||
"prettier": "^2.3.0",
|
||||
"typescript": "^4.2.4",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-cli": "^4.9.0",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
@@ -23,7 +23,7 @@ export default class HeaderSecondary extends Component {
|
||||
|
||||
items.add(
|
||||
'help',
|
||||
<LinkButton href="https://docs.flarum.org/troubleshoot.html" icon="fas fa-question-circle" external={true} target="_blank">
|
||||
<LinkButton href="https://docs.flarum.org/troubleshoot/" icon="fas fa-question-circle" external={true} target="_blank">
|
||||
{app.translator.trans('core.admin.header.get_help')}
|
||||
</LinkButton>
|
||||
);
|
||||
|
@@ -27,6 +27,7 @@ export default class LinkButton extends Button {
|
||||
|
||||
vdom.tag = Link;
|
||||
vdom.attrs.active = String(vdom.attrs.active);
|
||||
delete vdom.attrs.type;
|
||||
|
||||
return vdom;
|
||||
}
|
||||
|
@@ -103,14 +103,18 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
||||
|
||||
const searchLabel = extractText(app.translator.trans('core.forum.header.search_placeholder'));
|
||||
|
||||
const isActive = !!currentSearch;
|
||||
const shouldShowResults = !!(!this.loadingSources && this.state.getValue() && this.hasFocus);
|
||||
const shouldShowClearButton = !!(!this.loadingSources && this.state.getValue());
|
||||
|
||||
return (
|
||||
<div
|
||||
role="search"
|
||||
className={classList({
|
||||
Search: true,
|
||||
aria-label={app.translator.trans('core.forum.header.search_role_label')}
|
||||
className={classList('Search', {
|
||||
open: this.state.getValue() && this.hasFocus,
|
||||
focused: this.hasFocus,
|
||||
active: !!currentSearch,
|
||||
active: isActive,
|
||||
loading: !!this.loadingSources,
|
||||
})}
|
||||
>
|
||||
@@ -125,18 +129,23 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
||||
onfocus={() => (this.hasFocus = true)}
|
||||
onblur={() => (this.hasFocus = false)}
|
||||
/>
|
||||
{this.loadingSources ? (
|
||||
<LoadingIndicator size="small" display="inline" containerClassName="Button Button--icon Button--link" />
|
||||
) : currentSearch ? (
|
||||
<button className="Search-clear Button Button--icon Button--link" onclick={this.clear.bind(this)}>
|
||||
{!!this.loadingSources && <LoadingIndicator size="small" display="inline" containerClassName="Button Button--icon Button--link" />}
|
||||
{shouldShowClearButton && (
|
||||
<button
|
||||
className="Search-clear Button Button--icon Button--link"
|
||||
onclick={this.clear.bind(this)}
|
||||
aria-label={app.translator.trans('core.forum.header.search_clear_button_accessible_label')}
|
||||
>
|
||||
{icon('fas fa-times-circle')}
|
||||
</button>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
<ul className="Dropdown-menu Search-results">
|
||||
{this.state.getValue() && this.hasFocus ? this.sources.map((source) => source.view(this.state.getValue())) : ''}
|
||||
<ul
|
||||
className="Dropdown-menu Search-results"
|
||||
aria-hidden={!shouldShowResults || undefined}
|
||||
aria-live={shouldShowResults ? 'polite' : undefined}
|
||||
>
|
||||
{shouldShowResults && this.sources.map((source) => source.view(this.state.getValue()))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
@@ -174,7 +183,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
||||
|
||||
this.$('.Search-results')
|
||||
.on('mousedown', (e) => e.preventDefault())
|
||||
.on('click', () => this.$('input').blur())
|
||||
.on('click', () => this.$('input').trigger('blur'))
|
||||
|
||||
// Whenever the mouse is hovered over a search result, highlight it.
|
||||
.on('mouseenter', '> li:not(.Dropdown-header)', function () {
|
||||
@@ -223,7 +232,7 @@ export default class Search<T extends SearchAttrs = SearchAttrs> extends Compone
|
||||
.on('focus', function () {
|
||||
$(this)
|
||||
.one('mouseup', (e) => e.preventDefault())
|
||||
.select();
|
||||
.trigger('select');
|
||||
});
|
||||
|
||||
this.updateMaxHeightHandler = this.updateMaxHeight.bind(this);
|
||||
|
@@ -14,7 +14,7 @@ export default class DiscussionListState extends PaginatedListState<Discussion>
|
||||
}
|
||||
|
||||
requestParams() {
|
||||
const params: any = { include: ['user', 'lastPostedUser'], filter: {} };
|
||||
const params: any = { include: ['user', 'lastPostedUser'], filter: this.params.filter || {} };
|
||||
|
||||
params.sort = this.sortMap()[this.params.sort];
|
||||
|
||||
|
@@ -1,5 +1,16 @@
|
||||
.Search {
|
||||
position: relative;
|
||||
|
||||
&-clear {
|
||||
// It looks very weird due to the padding given to the button..
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// ...so we display the ring around the icon inside the button, with an offset
|
||||
.add-keyboard-focus-ring-nearby("> *");
|
||||
.add-keyboard-focus-ring-nearby-offset("> *", 4px);
|
||||
}
|
||||
}
|
||||
@media @tablet-up {
|
||||
.Search {
|
||||
|
@@ -130,3 +130,39 @@
|
||||
.offset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This mixin allows support for a custom element nearby the focused one
|
||||
* to have a focus style applied to it
|
||||
*
|
||||
* For example...
|
||||
*
|
||||
*? button { .add-keyboard-focus-ring-nearby("+ .myOtherElement") }
|
||||
* becomes
|
||||
*? button:-moz-focusring + .myOtherElement { <styles> }
|
||||
*? button:focus-within + .myOtherElement { <styles> }
|
||||
*/
|
||||
.add-keyboard-focus-ring-nearby-offset(@nearbySelector, @offset) {
|
||||
@realNearbySelector: ~"@{nearbySelector}";
|
||||
|
||||
.offset() {
|
||||
outline-offset: @offset;
|
||||
}
|
||||
|
||||
// We need to declare these separately, otherwise
|
||||
// browsers will ignore `:focus-visible` as they
|
||||
// don't understand `:-moz-focusring`
|
||||
|
||||
// These are the keyboard-only versions of :focus
|
||||
&:-moz-focusring {
|
||||
@{realNearbySelector} {
|
||||
.offset();
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@{realNearbySelector} {
|
||||
.offset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -348,7 +348,9 @@ core:
|
||||
log_in_link: => core.ref.log_in
|
||||
log_out_button: => core.ref.log_out
|
||||
profile_button: Profile
|
||||
search_clear_button_accessible_label: Clear search query
|
||||
search_placeholder: Search Forum
|
||||
search_role_label: Search Forum
|
||||
session_dropdown_accessible_label: Toggle session options dropdown menu
|
||||
settings_button: => core.ref.settings
|
||||
sign_up_link: => core.ref.sign_up
|
||||
|
@@ -61,7 +61,8 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
HttpMiddleware\CheckCsrfToken::class,
|
||||
Middleware\RequireAdministrateAbility::class,
|
||||
HttpMiddleware\ReferrerPolicyHeader::class,
|
||||
HttpMiddleware\ContentTypeOptionsHeader::class
|
||||
HttpMiddleware\ContentTypeOptionsHeader::class,
|
||||
Middleware\DisableBrowserCache::class,
|
||||
];
|
||||
});
|
||||
|
||||
|
25
src/Admin/Middleware/DisableBrowserCache.php
Normal file
25
src/Admin/Middleware/DisableBrowserCache.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
|
||||
class DisableBrowserCache implements Middleware
|
||||
{
|
||||
public function process(Request $request, Handler $handler): Response
|
||||
{
|
||||
$response = $handler->handle($request);
|
||||
|
||||
return $response->withHeader('Cache-Control', 'max-age=0, no-store');
|
||||
}
|
||||
}
|
@@ -236,7 +236,7 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
|
||||
*/
|
||||
protected function extractOffset(ServerRequestInterface $request)
|
||||
{
|
||||
return $this->buildParameters($request)->getOffset($this->extractLimit($request)) ?: 0;
|
||||
return (int) $this->buildParameters($request)->getOffset($this->extractLimit($request)) ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +245,7 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
|
||||
*/
|
||||
protected function extractLimit(ServerRequestInterface $request)
|
||||
{
|
||||
return $this->buildParameters($request)->getLimit($this->maxLimit) ?: $this->limit;
|
||||
return (int) $this->buildParameters($request)->getLimit($this->maxLimit) ?: $this->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -125,6 +125,17 @@ class ListDiscussionsController extends AbstractListController
|
||||
|
||||
$results = $results->getResults();
|
||||
|
||||
/*
|
||||
* @TODO replace in 1.2 with proper implementation!!!
|
||||
*/
|
||||
if (in_array('tags.state', $include, true)) {
|
||||
$results->load([
|
||||
'tags.state' => function ($query) use ($actor) {
|
||||
$query->where('user_id', $actor->id);
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
$this->loadRelations($results, $include);
|
||||
|
||||
if ($relations = array_intersect($include, ['firstPost', 'lastPost', 'mostRelevantPost'])) {
|
||||
|
@@ -16,6 +16,7 @@ use Flarum\Foundation\ContainerUtil;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Frontend\Assets;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Frontend\Frontend as ActualFrontend;
|
||||
use Flarum\Frontend\RecompileFrontendAssets;
|
||||
use Flarum\Http\RouteCollection;
|
||||
@@ -33,6 +34,7 @@ class Frontend implements ExtenderInterface
|
||||
private $routes = [];
|
||||
private $removedRoutes = [];
|
||||
private $content = [];
|
||||
private $preloadArrs = [];
|
||||
|
||||
/**
|
||||
* @param string $frontend: The name of the frontend.
|
||||
@@ -124,11 +126,45 @@ class Frontend implements ExtenderInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple asset preloads.
|
||||
*
|
||||
* The parameter should be an array of preload arrays, or a callable that returns this.
|
||||
*
|
||||
* A preload array must contain keys that pertain to the `<link rel="preload">` tag.
|
||||
*
|
||||
* For example, the following will add preload tags for a script and font file:
|
||||
* ```
|
||||
* $frontend->preloads([
|
||||
* [
|
||||
* 'href' => '/assets/my-script.js',
|
||||
* 'as' => 'script',
|
||||
* ],
|
||||
* [
|
||||
* 'href' => '/assets/fonts/my-font.woff2',
|
||||
* 'as' => 'font',
|
||||
* 'type' => 'font/woff2',
|
||||
* 'crossorigin' => ''
|
||||
* ]
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @param callable|array $preloads
|
||||
* @return self
|
||||
*/
|
||||
public function preloads($preloads): self
|
||||
{
|
||||
$this->preloadArrs[] = $preloads;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
$this->registerAssets($container, $this->getModuleName($extension));
|
||||
$this->registerRoutes($container);
|
||||
$this->registerContent($container);
|
||||
$this->registerPreloads($container);
|
||||
}
|
||||
|
||||
private function registerAssets(Container $container, string $moduleName): void
|
||||
@@ -236,6 +272,25 @@ class Frontend implements ExtenderInterface
|
||||
);
|
||||
}
|
||||
|
||||
private function registerPreloads(Container $container): void
|
||||
{
|
||||
if (empty($this->preloadArrs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$container->resolving(
|
||||
"flarum.frontend.$this->frontend",
|
||||
function (ActualFrontend $frontend, Container $container) {
|
||||
$frontend->content(function (Document $document) use ($container) {
|
||||
foreach ($this->preloadArrs as $preloadArr) {
|
||||
$preloads = is_callable($preloadArr) ? ContainerUtil::wrapCallback($preloadArr, $container)($document) : $preloadArr;
|
||||
$document->preloads = array_merge($document->preloads, $preloads);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function getModuleName(?Extension $extension): string
|
||||
{
|
||||
return $extension ? $extension->getId() : 'site-custom';
|
||||
|
@@ -21,7 +21,7 @@ class ServiceProvider implements ExtenderInterface
|
||||
*
|
||||
* Service providers are an advanced feature and might give access to Flarum internals that do not come with backward compatibility.
|
||||
* Please read our documentation about service providers for recommendations.
|
||||
* @see https://docs.flarum.org/extend/service-provider.html
|
||||
* @see https://docs.flarum.org/extend/service-provider/
|
||||
*
|
||||
* @param string $serviceProviderClass The ::class attribute of the service provider class.
|
||||
* @return self
|
||||
|
@@ -283,6 +283,14 @@ class Extension implements Arrayable
|
||||
{
|
||||
$properties = $this->getIcon();
|
||||
|
||||
if (empty($properties)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$properties = array_filter($properties, function ($item) {
|
||||
return is_string($item);
|
||||
});
|
||||
|
||||
unset($properties['name']);
|
||||
|
||||
return implode(';', array_map(function (string $property, string $value) {
|
||||
|
@@ -421,7 +421,7 @@ class ExtensionManager
|
||||
* Sort a list of extensions so that they are properly resolved in respect to order.
|
||||
* Effectively just topological sorting.
|
||||
*
|
||||
* @param Extension[] $extensionList: an array of \Flarum\Extension\Extension objects
|
||||
* @param Extension[] $extensionList
|
||||
*
|
||||
* @return array with 2 keys: 'valid' points to an ordered array of \Flarum\Extension\Extension
|
||||
* 'missingDependencies' points to an associative array of extensions that could not be resolved due
|
||||
@@ -443,6 +443,12 @@ class ExtensionManager
|
||||
$pendingQueue = [];
|
||||
$inDegreeCount = []; // How many extensions are dependent on a given extension?
|
||||
|
||||
// Sort alphabetically by ID. This guarantees that any set of extensions will always be sorted the same way.
|
||||
// This makes boot order deterministic, and independent of enabled order.
|
||||
$extensionList = Arr::sort($extensionList, function ($ext) {
|
||||
return $ext->getId();
|
||||
});
|
||||
|
||||
foreach ($extensionList as $extension) {
|
||||
$extensionIdMapping[$extension->getId()] = $extension;
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ class Application
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.0.5-dev';
|
||||
const VERSION = '1.1.1';
|
||||
|
||||
/**
|
||||
* The IoC container for the Flarum application.
|
||||
|
@@ -122,6 +122,28 @@ class Document implements Renderable
|
||||
*/
|
||||
public $css = [];
|
||||
|
||||
/**
|
||||
* An array of preloaded assets.
|
||||
*
|
||||
* Each array item should be an array containing keys that pertain to the
|
||||
* `<link rel="preload">` tag.
|
||||
*
|
||||
* For example, the following will add a preload tag for a FontAwesome font file:
|
||||
* ```
|
||||
* $this->preloads[] = [
|
||||
* 'href' => '/assets/fonts/fa-solid-900.woff2',
|
||||
* 'as' => 'font',
|
||||
* 'type' => 'font/woff2',
|
||||
* 'crossorigin' => ''
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $preloads = [];
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
@@ -203,6 +225,19 @@ class Document implements Renderable
|
||||
return $this->view->make($this->contentView)->with('content', $this->content);
|
||||
}
|
||||
|
||||
protected function makePreloads(): array
|
||||
{
|
||||
return array_map(function ($preload) {
|
||||
$attributes = '';
|
||||
|
||||
foreach ($preload as $key => $value) {
|
||||
$attributes .= " $key=\"".e($value).'"';
|
||||
}
|
||||
|
||||
return "<link rel=\"preload\"$attributes>";
|
||||
}, $this->preloads);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -216,6 +251,8 @@ class Document implements Renderable
|
||||
$head[] = '<link rel="canonical" href="'.e($this->canonicalUrl).'">';
|
||||
}
|
||||
|
||||
$head = array_merge($head, $this->makePreloads());
|
||||
|
||||
$head = array_merge($head, array_map(function ($content, $name) {
|
||||
return '<meta name="'.e($name).'" content="'.e($content).'">';
|
||||
}, $this->meta, array_keys($this->meta)));
|
||||
|
@@ -54,9 +54,58 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
||||
$frontend->content($container->make(Content\CorePayload::class));
|
||||
$frontend->content($container->make(Content\Meta::class));
|
||||
|
||||
$frontend->content(function (Document $document) use ($container) {
|
||||
$default_preloads = $container->make('flarum.frontend.default_preloads');
|
||||
|
||||
// Add preloads for base CSS and JS assets. Extensions should add their own via the extender.
|
||||
$js_preloads = [];
|
||||
$css_preloads = [];
|
||||
|
||||
foreach ($document->css as $url) {
|
||||
$css_preloads[] = [
|
||||
'href' => $url,
|
||||
'as' => 'style'
|
||||
];
|
||||
}
|
||||
foreach ($document->js as $url) {
|
||||
$css_preloads[] = [
|
||||
'href' => $url,
|
||||
'as' => 'script'
|
||||
];
|
||||
}
|
||||
|
||||
$document->preloads = array_merge(
|
||||
$css_preloads,
|
||||
$js_preloads,
|
||||
$default_preloads,
|
||||
$document->preloads,
|
||||
);
|
||||
});
|
||||
|
||||
return $frontend;
|
||||
};
|
||||
});
|
||||
|
||||
$this->container->singleton(
|
||||
'flarum.frontend.default_preloads',
|
||||
function (Container $container) {
|
||||
$filesystem = $container->make('filesystem')->disk('flarum-assets');
|
||||
|
||||
return [
|
||||
[
|
||||
'href' => $filesystem->url('fonts/fa-solid-900.woff2'),
|
||||
'as' => 'font',
|
||||
'type' => 'font/woff2',
|
||||
'crossorigin' => ''
|
||||
], [
|
||||
'href' => $filesystem->url('fonts/fa-regular-400.woff2'),
|
||||
'as' => 'font',
|
||||
'type' => 'font/woff2',
|
||||
'crossorigin' => ''
|
||||
]
|
||||
];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -53,7 +53,7 @@ class WritablePaths implements PrerequisiteInterface
|
||||
})->map(function ($path, $index) {
|
||||
return [
|
||||
'message' => 'The '.$this->getAbsolutePath($path).' directory is not writable.',
|
||||
'detail' => 'Please make sure your web server/PHP user has write access to this directory'.(in_array($index, $this->wildcards) ? ' and its contents' : '').'. Read the <a href="https://docs.flarum.org/install.html#folder-ownership">installation documentation</a> for a detailed explanation and steps to resolve this error.'
|
||||
'detail' => 'Please make sure your web server/PHP user has write access to this directory'.(in_array($index, $this->wildcards) ? ' and its contents' : '').'. Read the <a href="https://docs.flarum.org/install/#folder-ownership">installation documentation</a> for a detailed explanation and steps to resolve this error.'
|
||||
];
|
||||
});
|
||||
}
|
||||
|
93
tests/integration/extenders/FrontendPreloadTest.php
Normal file
93
tests/integration/extenders/FrontendPreloadTest.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?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\Tests\integration\extenders;
|
||||
|
||||
use Flarum\Extend;
|
||||
use Flarum\Testing\integration\TestCase;
|
||||
|
||||
class FrontendPreloadTest extends TestCase
|
||||
{
|
||||
private $customPreloadUrls = ['/my-preload', '/my-preload2'];
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function default_preloads_are_present()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/')
|
||||
);
|
||||
|
||||
$filesystem = $this->app()->getContainer()->make('filesystem')->disk('flarum-assets');
|
||||
|
||||
$urls = [
|
||||
$filesystem->url('fonts/fa-solid-900.woff2'),
|
||||
$filesystem->url('fonts/fa-regular-400.woff2'),
|
||||
];
|
||||
|
||||
$body = $response->getBody()->getContents();
|
||||
|
||||
foreach ($urls as $url) {
|
||||
$this->assertStringContainsString("<link rel=\"preload\" href=\"$url\" as=\"font\" type=\"font/woff2\" crossorigin=\"\">", $body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function preloads_can_be_added()
|
||||
{
|
||||
$urls = $this->customPreloadUrls;
|
||||
|
||||
$this->extend(
|
||||
(new Extend\Frontend('forum'))
|
||||
->preloads(
|
||||
array_map(function ($url) {
|
||||
return ['href' => $url];
|
||||
}, $urls)
|
||||
)
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/')
|
||||
);
|
||||
$body = $response->getBody()->getContents();
|
||||
|
||||
foreach ($urls as $url) {
|
||||
$this->assertStringContainsString("<link rel=\"preload\" href=\"$url\">", $body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function preloads_can_be_added_via_callable()
|
||||
{
|
||||
$urls = $this->customPreloadUrls;
|
||||
|
||||
$this->extend(
|
||||
(new Extend\Frontend('forum'))
|
||||
->preloads(function () use ($urls) {
|
||||
return array_map(function ($url) {
|
||||
return ['href' => $url];
|
||||
}, $urls);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/')
|
||||
);
|
||||
$body = $response->getBody()->getContents();
|
||||
|
||||
foreach ($urls as $url) {
|
||||
$this->assertStringContainsString("<link rel=\"preload\" href=\"$url\">", $body);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user