mirror of
https://github.com/flarum/core.git
synced 2025-08-05 16:07:34 +02:00
feat: allow dependency injection in resources
This commit is contained in:
@@ -23,6 +23,7 @@ use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Laminas\Stratigility\MiddlewarePipe;
|
||||
use ReflectionClass;
|
||||
|
||||
class ApiServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
@@ -48,11 +49,12 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
$resources = $this->container->make('flarum.api.resources');
|
||||
|
||||
$api = new JsonApi('/');
|
||||
$api->container($container);
|
||||
|
||||
foreach ($resources as $resourceClass) {
|
||||
/** @var \Flarum\Api\Resource\AbstractResource|\Flarum\Api\Resource\AbstractDatabaseResource $resource */
|
||||
$resource = new $resourceClass;
|
||||
$resource->boot($container);
|
||||
$resource = $container->make($resourceClass);
|
||||
$resource->boot($api);
|
||||
$api->resource($resource);
|
||||
}
|
||||
|
||||
@@ -61,9 +63,9 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
|
||||
$this->container->alias('flarum.api.resource_handler', JsonApi::class);
|
||||
|
||||
$this->container->singleton('flarum.api.routes', function () {
|
||||
$this->container->singleton('flarum.api.routes', function (Container $container) {
|
||||
$routes = new RouteCollection;
|
||||
$this->populateRoutes($routes);
|
||||
$this->populateRoutes($routes, $container);
|
||||
|
||||
return $routes;
|
||||
});
|
||||
@@ -158,10 +160,10 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
AbstractSerializer::setContainer($container);
|
||||
}
|
||||
|
||||
protected function populateRoutes(RouteCollection $routes): void
|
||||
protected function populateRoutes(RouteCollection $routes, Container $container): void
|
||||
{
|
||||
/** @var RouteHandlerFactory $factory */
|
||||
$factory = $this->container->make(RouteHandlerFactory::class);
|
||||
$factory = $container->make(RouteHandlerFactory::class);
|
||||
|
||||
$callback = include __DIR__.'/routes.php';
|
||||
$callback($routes, $factory);
|
||||
@@ -169,16 +171,30 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
$resources = $this->container->make('flarum.api.resources');
|
||||
|
||||
foreach ($resources as $resourceClass) {
|
||||
/** @var \Flarum\Api\Resource\AbstractResource|\Flarum\Api\Resource\AbstractDatabaseResource $resource */
|
||||
$resource = new $resourceClass;
|
||||
/** @var \Flarum\Api\Endpoint\Endpoint[] $endpoints */
|
||||
$endpoints = $resource->endpoints();
|
||||
/**
|
||||
* This is an empty shell instance,
|
||||
* we only need it to get the endpoint routes and types.
|
||||
*
|
||||
* We avoid dependency injection here to avoid early resolution.
|
||||
*
|
||||
* @var \Flarum\Api\Resource\AbstractResource|\Flarum\Api\Resource\AbstractDatabaseResource $resource
|
||||
*/
|
||||
$resource = (new ReflectionClass($resourceClass))->newInstanceWithoutConstructor();
|
||||
|
||||
$type = $resource->type();
|
||||
|
||||
/**
|
||||
* None of the injected dependencies should be directly used within
|
||||
* the `endpoints` method. Encourage using callbacks.
|
||||
*
|
||||
* @var \Flarum\Api\Endpoint\Endpoint[] $endpoints
|
||||
*/
|
||||
$endpoints = $resource->endpoints();
|
||||
|
||||
foreach ($endpoints as $endpoint) {
|
||||
$route = $endpoint->route();
|
||||
|
||||
$routes->addRoute($route->method, rtrim("/$type$route->path", '/'), "$type.$route->name", $factory->toApiResource($resourceClass, $endpoint::class));
|
||||
$routes->addRoute($route->method, rtrim("/$type$route->path", '/'), "$type.$route->name", $factory->toApiResource($resource::class, $endpoint::class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ namespace Flarum\Api;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Flarum\Search\SearchResults;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Tobyz\JsonApiServer\Context as BaseContext;
|
||||
|
||||
class Context extends BaseContext
|
||||
|
@@ -1,74 +0,0 @@
|
||||
<?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\Api\Controller;
|
||||
|
||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
||||
use Flarum\Discussion\Command\EditDiscussion;
|
||||
use Flarum\Discussion\Command\ReadDiscussion;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Flarum\Post\Post;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Arr;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
class UpdateDiscussionController extends AbstractShowController
|
||||
{
|
||||
public ?string $serializer = DiscussionSerializer::class;
|
||||
|
||||
public function __construct(
|
||||
protected Dispatcher $bus
|
||||
) {
|
||||
}
|
||||
|
||||
protected function data(ServerRequestInterface $request, Document $document): Discussion
|
||||
{
|
||||
$actor = RequestUtil::getActor($request);
|
||||
$discussionId = (int) Arr::get($request->getQueryParams(), 'id');
|
||||
$data = Arr::get($request->getParsedBody(), 'data', []);
|
||||
|
||||
/** @var Discussion $discussion */
|
||||
$discussion = $this->bus->dispatch(
|
||||
new EditDiscussion($discussionId, $actor, $data)
|
||||
);
|
||||
|
||||
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
|
||||
// That's what extensions will do anyway.
|
||||
if ($readNumber = Arr::get($data, 'attributes.lastReadPostNumber')) {
|
||||
$state = $this->bus->dispatch(
|
||||
new ReadDiscussion($discussionId, $actor, $readNumber)
|
||||
);
|
||||
|
||||
$discussion = $state->discussion;
|
||||
}
|
||||
|
||||
if ($posts = $discussion->getModifiedPosts()) {
|
||||
/** @var Collection<int, Post> $posts */
|
||||
$posts = (new Collection($posts))->load('discussion', 'user');
|
||||
$discussionPosts = $discussion->posts()->whereVisibleTo($actor)->oldest()->pluck('id')->all();
|
||||
|
||||
foreach ($discussionPosts as &$id) {
|
||||
foreach ($posts as $post) {
|
||||
if ($id == $post->id) {
|
||||
$id = $post;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$discussion->setRelation('posts', $discussionPosts);
|
||||
|
||||
$this->include = array_merge($this->include, ['posts', 'posts.discussion', 'posts.user']);
|
||||
}
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -71,7 +71,7 @@ class Index extends BaseIndex implements Endpoint
|
||||
|
||||
// This model has a searcher API, so we'll use that instead of the default.
|
||||
// The searcher API allows swapping the default search engine for a custom one.
|
||||
$search = resolve(SearchManager::class);
|
||||
$search = $context->api->getContainer()->make(SearchManager::class);
|
||||
$modelClass = $query->getModel()::class;
|
||||
|
||||
if ($query instanceof Builder && $search->searchable($modelClass)) {
|
||||
@@ -86,8 +86,7 @@ class Index extends BaseIndex implements Endpoint
|
||||
|
||||
$sortIsDefault = ! $context->queryParam('sort');
|
||||
|
||||
// @todo: resources and endpoints have no room for dependency injection
|
||||
$results = resolve(SearchManager::class)->query(
|
||||
$results = $search->query(
|
||||
$modelClass,
|
||||
new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault),
|
||||
);
|
||||
|
@@ -6,20 +6,22 @@ use Flarum\Api\Endpoint\Endpoint;
|
||||
use Flarum\Api\Endpoint\EndpointRoute;
|
||||
use Flarum\Api\Resource\AbstractDatabaseResource;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use Laminas\Diactoros\Uri;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Tobyz\JsonApiServer\Exception\BadRequestException;
|
||||
use Tobyz\JsonApiServer\JsonApi as BaseJsonApi;
|
||||
use Tobyz\JsonApiServer\Resource\Collection;
|
||||
use Tobyz\JsonApiServer\Resource\Resource;
|
||||
|
||||
class JsonApi extends BaseJsonApi
|
||||
{
|
||||
protected string $resourceClass;
|
||||
protected string $endpoint;
|
||||
protected ?Request $baseRequest = null;
|
||||
protected ?Container $container = null;
|
||||
|
||||
public function forResource(string $resourceClass): self
|
||||
{
|
||||
@@ -41,7 +43,7 @@ class JsonApi extends BaseJsonApi
|
||||
throw new BadRequestException('No resource or endpoint specified');
|
||||
}
|
||||
|
||||
$collection = $this->getCollection((new $this->resourceClass)->type());
|
||||
$collection = $this->getCollection($this->resourceClass);
|
||||
|
||||
return (new Context($this, $request))
|
||||
->withCollection($collection)
|
||||
@@ -85,6 +87,8 @@ class JsonApi extends BaseJsonApi
|
||||
$request = RequestUtil::withActor($request, $options['actor']);
|
||||
}
|
||||
|
||||
$resource = $this->getCollection($this->resourceClass);
|
||||
|
||||
$request = $request
|
||||
->withMethod($route->method)
|
||||
->withUri(new Uri($route->path))
|
||||
@@ -93,7 +97,9 @@ class JsonApi extends BaseJsonApi
|
||||
'data' => [
|
||||
...($request->getParsedBody()['data'] ?? []),
|
||||
...($body['data'] ?? []),
|
||||
'type' => (new $this->resourceClass)->type(),
|
||||
'type' => $resource instanceof Resource
|
||||
? $resource->type()
|
||||
: $resource->name(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -136,4 +142,16 @@ class JsonApi extends BaseJsonApi
|
||||
{
|
||||
return array_values(array_unique(array_map(fn ($modelClass) => $this->typeForModel($modelClass), $modelClasses)));
|
||||
}
|
||||
|
||||
public function container(Container $container): static
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContainer(): ?Container
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,6 @@ abstract class AbstractDatabaseResource extends BaseResource implements
|
||||
use DispatchEventsTrait {
|
||||
dispatchEventsFor as traitDispatchEventsFor;
|
||||
}
|
||||
use ResolvesValidationFactory;
|
||||
|
||||
abstract public function model(): string;
|
||||
|
||||
@@ -147,7 +146,7 @@ abstract class AbstractDatabaseResource extends BaseResource implements
|
||||
$savingEvent = $this->newSavingEvent($context, Arr::get($context->body(), 'data', []));
|
||||
|
||||
if ($savingEvent) {
|
||||
$this->container->make(Dispatcher::class)->dispatch($savingEvent);
|
||||
$this->events->dispatch($savingEvent);
|
||||
|
||||
$dirtyAfterEvent = $context->model->getDirty();
|
||||
|
||||
|
@@ -9,5 +9,4 @@ use Tobyz\JsonApiServer\Resource\AbstractResource as BaseResource;
|
||||
abstract class AbstractResource extends BaseResource
|
||||
{
|
||||
use Bootable;
|
||||
use ResolvesValidationFactory;
|
||||
}
|
||||
|
@@ -19,6 +19,11 @@ use Jenssegers\Agent\Agent;
|
||||
|
||||
class AccessTokenResource extends AbstractDatabaseResource
|
||||
{
|
||||
public function __construct(
|
||||
protected TranslatorInterface $translator
|
||||
) {
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'access-tokens';
|
||||
@@ -84,12 +89,10 @@ class AccessTokenResource extends AbstractDatabaseResource
|
||||
Schema\Str::make('lastIpAddress'),
|
||||
Schema\Str::make('device')
|
||||
->get(function (AccessToken $token) {
|
||||
$translator = resolve(TranslatorInterface::class);
|
||||
|
||||
$agent = new Agent();
|
||||
$agent->setUserAgent($token->last_user_agent);
|
||||
|
||||
return $translator->trans('core.forum.security.browser_on_operating_system', [
|
||||
return $this->translator->trans('core.forum.security.browser_on_operating_system', [
|
||||
'browser' => $agent->browser(),
|
||||
'os' => $agent->platform(),
|
||||
]);
|
||||
|
@@ -2,17 +2,32 @@
|
||||
|
||||
namespace Flarum\Api\Resource\Concerns;
|
||||
|
||||
use Flarum\Api\JsonApi;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
|
||||
trait Bootable
|
||||
{
|
||||
protected readonly Container $container;
|
||||
protected readonly JsonApi $api;
|
||||
protected readonly Dispatcher $events;
|
||||
protected readonly Factory $validation;
|
||||
|
||||
public function boot(Container $container): void
|
||||
/**
|
||||
* Avoids polluting the constructor of the resource with dependencies.
|
||||
*/
|
||||
public function boot(JsonApi $api): void
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->events = $container->make(Dispatcher::class);
|
||||
$this->api = $api;
|
||||
$this->events = $api->getContainer()->make(Dispatcher::class);
|
||||
$this->validation = $api->getContainer()->make(Factory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the JSON:API server package to resolve the validation factory.
|
||||
*/
|
||||
public function validationFactory(): Factory
|
||||
{
|
||||
return $this->validation;
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Flarum\Api\Resource\Concerns;
|
||||
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
|
||||
trait ResolvesValidationFactory
|
||||
{
|
||||
/**
|
||||
* Called by the JSON:API server package to resolve the validation factory.
|
||||
*/
|
||||
public function validationFactory(): Factory
|
||||
{
|
||||
return resolve(Factory::class);
|
||||
}
|
||||
}
|
@@ -24,6 +24,13 @@ use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
||||
|
||||
class DiscussionResource extends AbstractDatabaseResource
|
||||
{
|
||||
public function __construct(
|
||||
protected Dispatcher $bus,
|
||||
protected SlugManager $slugManager,
|
||||
protected PostRepository $posts
|
||||
) {
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'discussions';
|
||||
@@ -41,11 +48,10 @@ class DiscussionResource extends AbstractDatabaseResource
|
||||
|
||||
public function find(string $id, \Tobyz\JsonApiServer\Context $context): ?object
|
||||
{
|
||||
$slugManager = resolve(SlugManager::class);
|
||||
$actor = $context->getActor();
|
||||
|
||||
if (Arr::get($context->request->getQueryParams(), 'bySlug', false)) {
|
||||
$discussion = $slugManager->forResource(Discussion::class)->fromSlug($id, $actor);
|
||||
$discussion = $this->slugManager->forResource(Discussion::class)->fromSlug($id, $actor);
|
||||
} else {
|
||||
$discussion = $this->query($context)->findOrFail($id);
|
||||
}
|
||||
@@ -113,7 +119,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
||||
->set(fn () => null),
|
||||
Schema\Str::make('slug')
|
||||
->get(function (Discussion $discussion) {
|
||||
return resolve(SlugManager::class)->forResource(Discussion::class)->toSlug($discussion);
|
||||
return $this->slugManager->forResource(Discussion::class)->toSlug($discussion);
|
||||
}),
|
||||
Schema\Integer::make('commentCount'),
|
||||
Schema\Integer::make('participantCount'),
|
||||
@@ -167,7 +173,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
||||
->set(function (Discussion $discussion, int $value, Context $context) {
|
||||
if ($readNumber = Arr::get($context->body(), 'data.attributes.lastReadPostNumber')) {
|
||||
$discussion->afterSave(function (Discussion $discussion) use ($readNumber, $context) {
|
||||
resolve(Dispatcher::class)->dispatch(
|
||||
$this->bus->dispatch(
|
||||
new ReadDiscussion($discussion->id, $context->getActor(), $readNumber)
|
||||
);
|
||||
});
|
||||
@@ -196,7 +202,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
||||
$limit = $context->endpoint->extractLimitValue($context, $context->endpoint->defaultExtracts($context));
|
||||
|
||||
if (($near = Arr::get($context->request->getQueryParams(), 'page.near')) > 1) {
|
||||
$offset = resolve(PostRepository::class)->getIndexForNumber($discussion->id, $near, $actor);
|
||||
$offset = $this->posts->getIndexForNumber($discussion->id, $near, $actor);
|
||||
$offset = max(0, $offset - $limit / 2);
|
||||
} else {
|
||||
$offset = $context->endpoint->extractOffsetValue($context, $context->endpoint->defaultExtracts($context));
|
||||
@@ -263,7 +269,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
||||
$actor = $context->getActor();
|
||||
|
||||
if ($actor->exists) {
|
||||
resolve(Dispatcher::class)->dispatch(
|
||||
$this->bus->dispatch(
|
||||
new ReadDiscussion($model->id, $actor, 1)
|
||||
);
|
||||
}
|
||||
|
@@ -12,10 +12,22 @@ use Flarum\Group\Group;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Filesystem\Factory;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use stdClass;
|
||||
|
||||
class ForumResource extends AbstractResource implements Findable
|
||||
{
|
||||
protected Filesystem $assetsFilesystem;
|
||||
|
||||
public function __construct(
|
||||
protected UrlGenerator $url,
|
||||
protected SettingsRepositoryInterface $settings,
|
||||
protected Config $config,
|
||||
Factory $filesystemFactory
|
||||
) {
|
||||
$this->assetsFilesystem = $filesystemFactory->disk('flarum-assets');
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'forums';
|
||||
@@ -42,21 +54,16 @@ class ForumResource extends AbstractResource implements Findable
|
||||
|
||||
public function fields(): array
|
||||
{
|
||||
$url = resolve(UrlGenerator::class);
|
||||
$settings = resolve(SettingsRepositoryInterface::class);
|
||||
$config = resolve(Config::class);
|
||||
$assetsFilesystem = resolve(Factory::class)->disk('flarum-assets');
|
||||
|
||||
$forumUrl = $url->to('forum')->base();
|
||||
$forumUrl = $this->url->to('forum')->base();
|
||||
$path = parse_url($forumUrl, PHP_URL_PATH) ?: '';
|
||||
|
||||
return [
|
||||
Schema\Str::make('title')
|
||||
->get(fn () => $settings->get('forum_title')),
|
||||
->get(fn () => $this->settings->get('forum_title')),
|
||||
Schema\Str::make('description')
|
||||
->get(fn () => $settings->get('forum_description')),
|
||||
->get(fn () => $this->settings->get('forum_description')),
|
||||
Schema\Boolean::make('showLanguageSelector')
|
||||
->get(fn () => $settings->get('show_language_selector', true)),
|
||||
->get(fn () => $this->settings->get('show_language_selector', true)),
|
||||
Schema\Str::make('baseUrl')
|
||||
->get(fn () => $forumUrl),
|
||||
Schema\Str::make('basePath')
|
||||
@@ -64,29 +71,29 @@ class ForumResource extends AbstractResource implements Findable
|
||||
Schema\Str::make('baseOrigin')
|
||||
->get(fn () => substr($forumUrl, 0, strlen($forumUrl) - strlen($path))),
|
||||
Schema\Str::make('debug')
|
||||
->get(fn () => $config->inDebugMode()),
|
||||
->get(fn () => $this->config->inDebugMode()),
|
||||
Schema\Str::make('apiUrl')
|
||||
->get(fn () => $url->to('api')->base()),
|
||||
->get(fn () => $this->url->to('api')->base()),
|
||||
Schema\Str::make('welcomeTitle')
|
||||
->get(fn () => $settings->get('welcome_title')),
|
||||
->get(fn () => $this->settings->get('welcome_title')),
|
||||
Schema\Str::make('welcomeMessage')
|
||||
->get(fn () => $settings->get('welcome_message')),
|
||||
->get(fn () => $this->settings->get('welcome_message')),
|
||||
Schema\Str::make('themePrimaryColor')
|
||||
->get(fn () => $settings->get('theme_primary_color')),
|
||||
->get(fn () => $this->settings->get('theme_primary_color')),
|
||||
Schema\Str::make('themeSecondaryColor')
|
||||
->get(fn () => $settings->get('theme_secondary_color')),
|
||||
->get(fn () => $this->settings->get('theme_secondary_color')),
|
||||
Schema\Str::make('logoUrl')
|
||||
->get(fn () => $this->getLogoUrl()),
|
||||
Schema\Str::make('faviconUrl')
|
||||
->get(fn () => $this->getFaviconUrl()),
|
||||
Schema\Str::make('headerHtml')
|
||||
->get(fn () => $settings->get('custom_header')),
|
||||
->get(fn () => $this->settings->get('custom_header')),
|
||||
Schema\Str::make('footerHtml')
|
||||
->get(fn () => $settings->get('custom_footer')),
|
||||
->get(fn () => $this->settings->get('custom_footer')),
|
||||
Schema\Boolean::make('allowSignUp')
|
||||
->get(fn () => $settings->get('allow_sign_up')),
|
||||
->get(fn () => $this->settings->get('allow_sign_up')),
|
||||
Schema\Str::make('defaultRoute')
|
||||
->get(fn () => $settings->get('default_route')),
|
||||
->get(fn () => $this->settings->get('default_route')),
|
||||
Schema\Boolean::make('canViewForum')
|
||||
->get(fn ($model, Context $context) => $context->getActor()->can('viewForum')),
|
||||
Schema\Boolean::make('canStartDiscussion')
|
||||
@@ -100,13 +107,13 @@ class ForumResource extends AbstractResource implements Findable
|
||||
Schema\Boolean::make('canEditUserCredentials')
|
||||
->get(fn ($model, Context $context) => $context->getActor()->hasPermission('user.editCredentials')),
|
||||
Schema\Str::make('assetsBaseUrl')
|
||||
->get(fn () => rtrim($assetsFilesystem->url(''), '/')),
|
||||
->get(fn () => rtrim($this->assetsFilesystem->url(''), '/')),
|
||||
Schema\Str::make('jsChunksBaseUrl')
|
||||
->get(fn () => $assetsFilesystem->url('js')),
|
||||
->get(fn () => $this->assetsFilesystem->url('js')),
|
||||
|
||||
Schema\Str::make('adminUrl')
|
||||
->visible(fn ($model, Context $context) => $context->getActor()->can('administrate'))
|
||||
->get(fn () => $url->to('admin')->base()),
|
||||
->get(fn () => $this->url->to('admin')->base()),
|
||||
Schema\Str::make('version')
|
||||
->visible(fn ($model, Context $context) => $context->getActor()->can('administrate'))
|
||||
->get(fn () => Application::VERSION),
|
||||
@@ -123,20 +130,20 @@ class ForumResource extends AbstractResource implements Findable
|
||||
|
||||
protected function getLogoUrl(): ?string
|
||||
{
|
||||
$logoPath = resolve(SettingsRepositoryInterface::class)->get('logo_path');
|
||||
$logoPath = $this->settings->get('logo_path');
|
||||
|
||||
return $logoPath ? $this->getAssetUrl($logoPath) : null;
|
||||
}
|
||||
|
||||
protected function getFaviconUrl(): ?string
|
||||
{
|
||||
$faviconPath = resolve(SettingsRepositoryInterface::class)->get('favicon_path');
|
||||
$faviconPath = $this->settings->get('favicon_path');
|
||||
|
||||
return $faviconPath ? $this->getAssetUrl($faviconPath) : null;
|
||||
}
|
||||
|
||||
public function getAssetUrl(string $assetPath): string
|
||||
{
|
||||
return resolve(Factory::class)->disk('flarum-assets')->url($assetPath);
|
||||
return $this->assetsFilesystem->url($assetPath);
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,11 @@ use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
||||
|
||||
class GroupResource extends AbstractDatabaseResource
|
||||
{
|
||||
public function __construct(
|
||||
protected TranslatorInterface $translator
|
||||
) {
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'groups';
|
||||
@@ -92,7 +97,7 @@ class GroupResource extends AbstractDatabaseResource
|
||||
|
||||
private function translateGroupName(string $name): string
|
||||
{
|
||||
$translation = resolve(TranslatorInterface::class)->trans($key = 'core.group.'.strtolower($name));
|
||||
$translation = $this->translator->trans($key = 'core.group.'.strtolower($name));
|
||||
|
||||
if ($translation !== $key) {
|
||||
return $translation;
|
||||
|
@@ -14,6 +14,12 @@ use Tobyz\JsonApiServer\Pagination\Pagination;
|
||||
|
||||
class NotificationResource extends AbstractDatabaseResource
|
||||
{
|
||||
public function __construct(
|
||||
protected Dispatcher $bus,
|
||||
protected NotificationRepository $notifications,
|
||||
) {
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'notifications';
|
||||
@@ -30,7 +36,7 @@ class NotificationResource extends AbstractDatabaseResource
|
||||
/** @var Pagination $pagination */
|
||||
$pagination = ($context->endpoint->paginationResolver)($context);
|
||||
|
||||
return resolve(NotificationRepository::class)->query($context->getActor(), $pagination->limit, $pagination->offset);
|
||||
return $this->notifications->query($context->getActor(), $pagination->limit, $pagination->offset);
|
||||
}
|
||||
|
||||
return parent::query($context);
|
||||
@@ -57,7 +63,7 @@ class NotificationResource extends AbstractDatabaseResource
|
||||
|
||||
public function fields(): array
|
||||
{
|
||||
$subjectTypes = resolve(JsonApi::class)->typesForModels(
|
||||
$subjectTypes = $this->api->typesForModels(
|
||||
(new Notification())->getSubjectModels()
|
||||
);
|
||||
|
||||
@@ -71,7 +77,7 @@ class NotificationResource extends AbstractDatabaseResource
|
||||
->writable()
|
||||
->get(fn (Notification $notification) => (bool) $notification->read_at)
|
||||
->set(function (Notification $notification, Context $context) {
|
||||
resolve(Dispatcher::class)->dispatch(
|
||||
$this->bus->dispatch(
|
||||
new ReadNotification($notification->id, $context->getActor())
|
||||
);
|
||||
}),
|
||||
|
@@ -23,6 +23,14 @@ use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
||||
|
||||
class PostResource extends AbstractDatabaseResource
|
||||
{
|
||||
public function __construct(
|
||||
protected PostRepository $posts,
|
||||
protected TranslatorInterface $translator,
|
||||
protected LogReporter $log,
|
||||
protected Dispatcher $bus
|
||||
) {
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'posts';
|
||||
@@ -115,7 +123,7 @@ class PostResource extends AbstractDatabaseResource
|
||||
}
|
||||
|
||||
$limit = $defaultExtracts['limit'];
|
||||
$offset = resolve(PostRepository::class)->getIndexForNumber((int) $filter['discussion'], $near, $context->getActor());
|
||||
$offset = $this->posts->getIndexForNumber((int) $filter['discussion'], $near, $context->getActor());
|
||||
|
||||
return max(0, $offset - $limit / 2);
|
||||
}
|
||||
@@ -184,8 +192,8 @@ class PostResource extends AbstractDatabaseResource
|
||||
$rendered = $post->formatContent($context->request);
|
||||
$post->setAttribute('renderFailed', false);
|
||||
} catch (\Exception $e) {
|
||||
$rendered = resolve(TranslatorInterface::class)->trans('core.lib.error.render_failed_message');
|
||||
resolve(LogReporter::class)->report($e);
|
||||
$rendered = $this->translator->trans('core.lib.error.render_failed_message');
|
||||
$this->log->report($e);
|
||||
$post->setAttribute('renderFailed', true);
|
||||
}
|
||||
|
||||
@@ -258,7 +266,7 @@ class PostResource extends AbstractDatabaseResource
|
||||
// in the discussion; thus, we will mark the discussion as read if
|
||||
// they are logged in.
|
||||
if ($actor->exists) {
|
||||
resolve(Dispatcher::class)->dispatch(
|
||||
$this->bus->dispatch(
|
||||
new ReadDiscussion($model->discussion_id, $actor, $model->number)
|
||||
);
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ use Flarum\Foundation\ValidationException;
|
||||
use Flarum\Http\SlugManager;
|
||||
use Flarum\Locale\TranslatorInterface;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\AvatarUploader;
|
||||
use Flarum\User\Event\Deleting;
|
||||
use Flarum\User\Event\GroupsChanged;
|
||||
use Flarum\User\Event\RegisteringFromProvider;
|
||||
@@ -19,11 +20,21 @@ use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\ImageManager;
|
||||
use InvalidArgumentException;
|
||||
use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
||||
|
||||
class UserResource extends AbstractDatabaseResource
|
||||
{
|
||||
public function __construct(
|
||||
protected TranslatorInterface $translator,
|
||||
protected SlugManager $slugManager,
|
||||
protected SettingsRepositoryInterface $settings,
|
||||
protected ImageManager $imageManager,
|
||||
protected AvatarUploader $avatarUploader
|
||||
) {
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'users';
|
||||
@@ -41,11 +52,10 @@ class UserResource extends AbstractDatabaseResource
|
||||
|
||||
public function find(string $id, \Tobyz\JsonApiServer\Context $context): ?object
|
||||
{
|
||||
$slugManager = resolve(SlugManager::class);
|
||||
$actor = $context->getActor();
|
||||
|
||||
if (Arr::get($context->request->getQueryParams(), 'bySlug', false)) {
|
||||
$user = $slugManager->forResource(User::class)->fromSlug($id, $actor);
|
||||
$user = $this->slugManager->forResource(User::class)->fromSlug($id, $actor);
|
||||
} else {
|
||||
$user = $this->query($context)->findOrFail($id);
|
||||
}
|
||||
@@ -58,9 +68,7 @@ class UserResource extends AbstractDatabaseResource
|
||||
return [
|
||||
Endpoint\Create::make()
|
||||
->visible(function (Context $context) {
|
||||
$settings = resolve(SettingsRepositoryInterface::class);
|
||||
|
||||
if (! $settings->get('allow_sign_up')) {
|
||||
if (! $this->settings->get('allow_sign_up')) {
|
||||
return $context->getActor()->isAdmin();
|
||||
}
|
||||
|
||||
@@ -101,7 +109,7 @@ class UserResource extends AbstractDatabaseResource
|
||||
|
||||
public function fields(): array
|
||||
{
|
||||
$translator = resolve(TranslatorInterface::class);
|
||||
$translator = $this->translator;
|
||||
|
||||
return [
|
||||
Schema\Str::make('username')
|
||||
@@ -203,7 +211,7 @@ class UserResource extends AbstractDatabaseResource
|
||||
Schema\Str::make('avatarUrl'),
|
||||
Schema\Str::make('slug')
|
||||
->get(function (User $user) {
|
||||
return resolve(SlugManager::class)->forResource(User::class)->toSlug($user);
|
||||
return $this->slugManager->forResource(User::class)->toSlug($user);
|
||||
}),
|
||||
Schema\DateTime::make('joinTime')
|
||||
->property('joined_at'),
|
||||
@@ -363,12 +371,7 @@ class UserResource extends AbstractDatabaseResource
|
||||
*/
|
||||
private function uploadAvatarFromUrl(User $user, string $url): void
|
||||
{
|
||||
// @todo: constructor dependency injection
|
||||
$this->validator = resolve(\Illuminate\Contracts\Validation\Factory::class);
|
||||
$this->imageManager = resolve(\Intervention\Image\ImageManager::class);
|
||||
$this->avatarUploader = resolve(\Flarum\User\AvatarUploader::class);
|
||||
|
||||
$urlValidator = $this->validator->make(compact('url'), [
|
||||
$urlValidator = $this->validation->make(compact('url'), [
|
||||
'url' => 'required|active_url',
|
||||
]);
|
||||
|
||||
|
@@ -10,6 +10,7 @@
|
||||
namespace Flarum\Http;
|
||||
|
||||
use Closure;
|
||||
use Flarum\Api\JsonApi;
|
||||
use Flarum\Frontend\Controller as FrontendController;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
@@ -45,8 +46,8 @@ class RouteHandlerFactory
|
||||
public function toApiResource(string $resourceClass, string $endpointClass): Closure
|
||||
{
|
||||
return function (Request $request, array $routeParams) use ($resourceClass, $endpointClass) {
|
||||
/** @var \Flarum\Api\JsonApi $api */
|
||||
$api = $this->container->make(\Flarum\Api\JsonApi::class);
|
||||
/** @var JsonApi $api */
|
||||
$api = $this->container->make(JsonApi::class);
|
||||
|
||||
$api->validateQueryParameters($request);
|
||||
|
||||
|
Reference in New Issue
Block a user