chore(phpstan): enable phpstan in bundled extensions (#3667)

* feat(phpstan): pick up extended model relations typings
* feat(phpstan): pick up extended model date attributes
* feat(core): introduce `castAttribute` extender
Stops using `dates` as it's deprecated in laravel 8
* feat(phpstan): pick up extended model attributes through casts
* fix: extenders not resolved when declared namespace
* fix(phpstan): new model attributes are always nullable
* chore(phpstan): add helpful cache clearing command
* Apply fixes from StyleCI
* chore: improve extend files provider logic
* chore: rename `castAttribute` to just `cast`
* chore: update phpstan package to detect `cast` method
* chore: enable phpstan in bundled extensions
* chore: rebasing conflicts
* chore: rebasing conflicts
* chore: typings for latest 1.7 changes

Signed-off-by: Sami Mazouz <sychocouldy@gmail.com>
This commit is contained in:
Sami Mazouz 2023-01-19 21:49:38 +01:00 committed by GitHub
parent ccf9442d79
commit da1bf8da21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 215 additions and 138 deletions

View File

@ -13,6 +13,7 @@ use Flarum\Approval\Event\PostWasApproved;
use Flarum\Extend;
use Flarum\Post\Event\Hidden;
use Flarum\Post\Event\Saving;
use Flarum\Post\Post;
return [
(new Extend\Frontend('forum'))
@ -30,4 +31,7 @@ return [
(new Extend\ServiceProvider())
->register(AkismetProvider::class),
(new Extend\Model(Post::class))
->cast('is_spam', 'bool'),
];

View File

@ -50,7 +50,7 @@ class ValidatePost
->withContent($post->content)
->withAuthorName($post->user->username)
->withAuthorEmail($post->user->email)
->withType($post->number == 1 ? 'forum-post' : 'reply')
->withType($post->number === 1 ? 'forum-post' : 'reply')
->withIp($post->ip_address)
->withUserAgent($_SERVER['HTTP_USER_AGENT'])
->checkSpam();

View File

@ -28,15 +28,17 @@ return [
// Discussions should be approved by default
(new Extend\Model(Discussion::class))
->default('is_approved', true),
->default('is_approved', true)
->cast('is_approved', 'bool'),
// Posts should be approved by default
(new Extend\Model(Post::class))
->default('is_approved', true),
->default('is_approved', true)
->cast('is_approved', 'bool'),
(new Extend\ApiSerializer(BasicDiscussionSerializer::class))
->attribute('isApproved', function ($serializer, Discussion $discussion) {
return (bool) $discussion->is_approved;
return $discussion->is_approved;
}),
(new Extend\ApiSerializer(PostSerializer::class))

View File

@ -9,6 +9,7 @@
namespace Flarum\Approval\Listener;
use Carbon\Carbon;
use Flarum\Discussion\Discussion;
use Flarum\Flags\Flag;
use Flarum\Post\CommentPost;
@ -55,7 +56,7 @@ class UnapproveNewContent
$flag->post_id = $post->id;
$flag->type = 'approval';
$flag->created_at = time();
$flag->created_at = Carbon::now();
$flag->save();
});

View File

@ -22,7 +22,7 @@ class UpdateDiscussionAfterPostApproval
$discussion->refreshCommentCount();
$discussion->refreshLastPost();
if ($post->number == 1) {
if ($post->number === 1) {
$discussion->is_approved = true;
$discussion->afterSave(function () use ($user) {

View File

@ -46,7 +46,7 @@ return [
->delete('/posts/{id}/flags', 'flags.delete', DeleteFlagsController::class),
(new Extend\Model(User::class))
->dateAttribute('read_flags_at'),
->cast('read_flags_at', 'datetime'),
(new Extend\Model(Post::class))
->hasMany('flags', Flag::class, 'post_id'),

View File

@ -9,6 +9,7 @@
namespace Flarum\Flags\Api\Controller;
use Carbon\Carbon;
use Flarum\Api\Controller\AbstractListController;
use Flarum\Flags\Api\Serializer\FlagSerializer;
use Flarum\Flags\Flag;
@ -43,7 +44,7 @@ class ListFlagsController extends AbstractListController
$actor->assertRegistered();
$actor->read_flags_at = time();
$actor->read_flags_at = Carbon::now();
$actor->save();
$flags = Flag::whereVisibleTo($actor)

View File

@ -12,6 +12,8 @@ namespace Flarum\Flags\Api\Serializer;
use Flarum\Api\Serializer\AbstractSerializer;
use Flarum\Api\Serializer\BasicUserSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Flags\Flag;
use InvalidArgumentException;
class FlagSerializer extends AbstractSerializer
{
@ -20,11 +22,14 @@ class FlagSerializer extends AbstractSerializer
*/
protected $type = 'flags';
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($flag)
{
if (! ($flag instanceof Flag)) {
throw new InvalidArgumentException(
get_class($this).' can only serialize instances of '.Flag::class
);
}
return [
'type' => $flag->type,
'reason' => $flag->reason,

View File

@ -9,6 +9,7 @@
namespace Flarum\Flags\Command;
use Carbon\Carbon;
use Flarum\Flags\Event\Created;
use Flarum\Flags\Flag;
use Flarum\Foundation\ValidationException;
@ -99,7 +100,7 @@ class CreateFlagHandler
$flag->type = 'user';
$flag->reason = Arr::get($data, 'attributes.reason');
$flag->reason_detail = Arr::get($data, 'attributes.reasonDetail');
$flag->created_at = time();
$flag->created_at = Carbon::now();
$flag->save();

View File

@ -11,7 +11,7 @@ namespace Flarum\Flags\Command;
use Flarum\Flags\Event\Deleting;
use Flarum\Flags\Event\FlagsWillBeDeleted;
use Flarum\Flags\Flag;
use Flarum\Post\Post;
use Flarum\Post\PostRepository;
use Illuminate\Events\Dispatcher;
@ -39,7 +39,7 @@ class DeleteFlagsHandler
/**
* @param DeleteFlags $command
* @return Flag
* @return Post
*/
public function handle(DeleteFlags $command)
{

View File

@ -9,11 +9,23 @@
namespace Flarum\Flags;
use Carbon\Carbon;
use Flarum\Database\AbstractModel;
use Flarum\Database\ScopeVisibilityTrait;
use Flarum\Post\Post;
use Flarum\User\User;
/**
* @property int $post_id
* @property int $user_id
* @property string $type
* @property string $reason
* @property string $reason_detail
* @property Carbon $created_at
*
* @property-read Post $post
* @property-read User $user
*/
class Flag extends AbstractModel
{
use ScopeVisibilityTrait;

View File

@ -35,12 +35,15 @@ return [
(new Extend\Notification())
->type(DiscussionLockedBlueprint::class, BasicDiscussionSerializer::class, ['alert']),
(new Extend\Model(Discussion::class))
->cast('is_locked', 'bool'),
(new Extend\ApiSerializer(DiscussionSerializer::class))
->attribute('isLocked', function (DiscussionSerializer $serializer, Discussion $discussion) {
return (bool) $discussion->is_locked;
return $discussion->is_locked;
})
->attribute('canLock', function (DiscussionSerializer $serializer, Discussion $discussion) {
return (bool) $serializer->getActor()->can('lock', $discussion);
return $serializer->getActor()->can('lock', $discussion);
}),
(new Extend\Post())

View File

@ -18,7 +18,7 @@ class DiscussionPolicy extends AbstractPolicy
/**
* @param User $actor
* @param Discussion $discussion
* @return bool
* @return string|void
*/
public function reply(User $actor, Discussion $discussion)
{

View File

@ -9,6 +9,7 @@
namespace Flarum\Lock\Post;
use Carbon\Carbon;
use Flarum\Post\AbstractEventPost;
use Flarum\Post\MergeableInterface;
use Flarum\Post\Post;
@ -59,7 +60,7 @@ class DiscussionLockedPost extends AbstractEventPost implements MergeableInterfa
$post = new static;
$post->content = static::buildContent($isLocked);
$post->created_at = time();
$post->created_at = Carbon::now();
$post->discussion_id = $discussionId;
$post->user_id = $userId;

View File

@ -16,6 +16,7 @@ use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\User;
use Illuminate\Support\Str;
use s9e\TextFormatter\Configurator;
use s9e\TextFormatter\Parser\Tag;
class ConfigureMentions
{
@ -39,7 +40,7 @@ class ConfigureMentions
$this->configureGroupMentions($config);
}
private function configureUserMentions(Configurator $config)
private function configureUserMentions(Configurator $config): void
{
$config->rendering->parameters['PROFILE_URL'] = $this->url->to('forum')->route('user', ['username' => '']);
@ -66,9 +67,8 @@ class ConfigureMentions
}
/**
* @param $tag
*
* @return bool
* @param Tag $tag
* @return bool|void
*/
public static function addUserId($tag)
{
@ -81,7 +81,7 @@ class ConfigureMentions
}
if (isset($user)) {
$tag->setAttribute('id', $user->id);
$tag->setAttribute('id', (string) $user->id);
$tag->setAttribute('displayname', $user->display_name);
return true;
@ -90,7 +90,7 @@ class ConfigureMentions
$tag->invalidate();
}
private function configurePostMentions(Configurator $config)
private function configurePostMentions(Configurator $config): void
{
$config->rendering->parameters['DISCUSSION_URL'] = $this->url->to('forum')->route('discussion', ['id' => '']);
@ -122,8 +122,8 @@ class ConfigureMentions
}
/**
* @param $tag
* @return bool
* @param Tag $tag
* @return bool|void
*/
public static function addPostId($tag, User $actor)
{
@ -132,8 +132,8 @@ class ConfigureMentions
->find($tag->getAttribute('id'));
if ($post) {
$tag->setAttribute('discussionid', (int) $post->discussion_id);
$tag->setAttribute('number', (int) $post->number);
$tag->setAttribute('discussionid', (string) $post->discussion_id);
$tag->setAttribute('number', (string) $post->number);
if ($post->user) {
$tag->setAttribute('displayname', $post->user->display_name);
@ -171,7 +171,7 @@ class ConfigureMentions
/**
* @param $tag
* @return bool
* @return bool|void
*/
public static function addGroupId($tag)
{
@ -208,7 +208,7 @@ class ConfigureMentions
$hexNumbers = Str::replace('#', '', $hexColor);
if (Str::length($hexNumbers) === 3) {
$hexNumbers += $hexNumbers;
$hexNumbers .= $hexNumbers;
}
$r = hexdec(Str::substr($hexNumbers, 0, 2));

View File

@ -29,11 +29,11 @@ class FormatPostMentions
/**
* Configure rendering for post mentions.
*
* @param s9e\TextFormatter\Renderer $renderer
* @param \s9e\TextFormatter\Renderer $renderer
* @param mixed $context
* @param string|null $xml
* @param Psr\Http\Message\ServerRequestInterface $request
* @return void
* @param \Psr\Http\Message\ServerRequestInterface $request
* @return string
*/
public function __invoke(Renderer $renderer, $context, $xml, Request $request = null)
{

View File

@ -37,9 +37,9 @@ class FormatUserMentions
/**
* Configure rendering for user mentions.
*
* @param s9e\TextFormatter\Renderer $renderer
* @param \s9e\TextFormatter\Renderer $renderer
* @param mixed $context
* @param string|null $xml
* @param string $xml
* @return string $xml to be rendered
*/
public function __invoke(Renderer $renderer, $context, string $xml)

View File

@ -40,7 +40,7 @@ class UpdateMentionsMetadataWhenVisible
*/
public function handle($event)
{
$content = $event->post->parsedContent;
$content = $event->post->parsed_content;
$this->syncUserMentions(
$event->post,

View File

@ -26,6 +26,9 @@ return [
new Extend\Locales(__DIR__.'/locale'),
(new Extend\Model(User::class))
->cast('nickname', 'string'),
(new Extend\User())
->displayNameDriver('nickname', NicknameDriver::class),

View File

@ -58,5 +58,7 @@ class NicknameFullTextGambit implements GambitInterface
'id',
$this->getUserSearchSubQuery($searchValue)
);
return true;
}
}

View File

@ -58,7 +58,7 @@ class ListTasksController extends AbstractListController
$total = $this->repository->query()->count();
$document->addMeta('total', $total);
$document->addMeta('total', (string) $total);
$document->addPaginationLinks(
$this->url->to('api')->route('package-manager.tasks.index'),

View File

@ -9,7 +9,14 @@
namespace Flarum\PackageManager\Command;
interface BusinessCommandInterface
use Flarum\PackageManager\Task\Task;
abstract class AbstractActionCommand
{
public function getOperationName(): string;
/**
* @var Task|null
*/
public $task = null;
abstract public function getOperationName(): string;
}

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class CheckForUpdates implements BusinessCommandInterface
class CheckForUpdates extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var \Flarum\User\User
*/

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class GlobalUpdate implements BusinessCommandInterface
class GlobalUpdate extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var \Flarum\User\User
*/

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class MajorUpdate implements BusinessCommandInterface
class MajorUpdate extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var \Flarum\User\User
*/

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class MinorUpdate implements BusinessCommandInterface
class MinorUpdate extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var \Flarum\User\User
*/

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class RemoveExtension implements BusinessCommandInterface
class RemoveExtension extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var User
*/

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class RequireExtension implements BusinessCommandInterface
class RequireExtension extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var User
*/

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class UpdateExtension implements BusinessCommandInterface
class UpdateExtension extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var User
*/

View File

@ -12,13 +12,8 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Task\Task;
use Flarum\User\User;
class WhyNot implements BusinessCommandInterface
class WhyNot extends AbstractActionCommand
{
/**
* @var Task
*/
public $task = null;
/**
* @var User
*/

View File

@ -60,6 +60,7 @@ class ComposerAdapter
$exitCode = $this->application->run($input, $this->output);
chdir($currDir);
// @phpstan-ignore-next-line
$command = $input->__toString();
$output = $this->output->fetch();

View File

@ -10,7 +10,7 @@
namespace Flarum\PackageManager\Job;
use Flarum\Bus\Dispatcher;
use Flarum\PackageManager\Command\BusinessCommandInterface;
use Flarum\PackageManager\Command\AbstractActionCommand;
use Flarum\PackageManager\Composer\ComposerAdapter;
use Flarum\Queue\AbstractJob;
use Illuminate\Queue\Middleware\WithoutOverlapping;
@ -19,7 +19,7 @@ use Throwable;
class ComposerCommandJob extends AbstractJob
{
/**
* @var BusinessCommandInterface
* @var AbstractActionCommand
*/
protected $command;
@ -28,7 +28,7 @@ class ComposerCommandJob extends AbstractJob
*/
protected $phpVersion;
public function __construct(BusinessCommandInterface $command, string $phpVersion)
public function __construct(AbstractActionCommand $command, string $phpVersion)
{
$this->command = $command;
$this->phpVersion = $phpVersion;

View File

@ -10,7 +10,7 @@
namespace Flarum\PackageManager\Job;
use Flarum\Bus\Dispatcher as Bus;
use Flarum\PackageManager\Command\BusinessCommandInterface;
use Flarum\PackageManager\Command\AbstractActionCommand;
use Flarum\PackageManager\Task\Task;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Queue\Queue;
@ -63,7 +63,7 @@ class Dispatcher
return $this;
}
public function dispatch(BusinessCommandInterface $command): DispatcherResponse
public function dispatch(AbstractActionCommand $command): DispatcherResponse
{
$queueJobs = ($this->runSyncOverride === false) || ($this->runSyncOverride !== true && $this->settings->get('flarum-package-manager.queue_jobs'));

View File

@ -26,7 +26,7 @@ class LastUpdateRun implements JsonSetting
protected $data;
/**
* @var {'major'|'minor'|'global'}
* @var string|null
*/
protected $activeUpdate;

View File

@ -13,16 +13,16 @@ use Carbon\Carbon;
use Flarum\Database\AbstractModel;
/**
* @property int id
* @property int status
* @property string operation
* @property string command
* @property string package
* @property string output
* @property Carbon created_at
* @property Carbon started_at
* @property Carbon finished_at
* @property int peak_memory_used
* @property int $id
* @property int $status
* @property string $operation
* @property string $command
* @property string $package
* @property string $output
* @property Carbon $created_at
* @property Carbon $started_at
* @property Carbon $finished_at
* @property float $peak_memory_used
*/
class Task extends AbstractModel
{

View File

@ -48,6 +48,7 @@ class AuthController implements RequestHandlerInterface
$this->settings->get('flarum-pusher.app_key'),
$this->settings->get('flarum-pusher.app_secret'),
$this->settings->get('flarum-pusher.app_id'),
// @phpstan-ignore-next-line
['cluster' => $this->settings->get('flarum-pusher.app_cluster')]
);

View File

@ -9,6 +9,7 @@
namespace Flarum\Pusher\Listener;
use Flarum\Extension\ExtensionManager;
use Flarum\Post\Event\Posted;
use Flarum\User\Guest;
use Flarum\User\User;
@ -22,9 +23,15 @@ class PushNewPost
*/
protected $pusher;
public function __construct(Pusher $pusher)
/**
* @var ExtensionManager
*/
protected $extensions;
public function __construct(Pusher $pusher, ExtensionManager $extensions)
{
$this->pusher = $pusher;
$this->extensions = $extensions;
}
public function handle(Posted $event)
@ -43,6 +50,7 @@ class PushNewPost
return;
}
// @phpstan-ignore-next-line
foreach ($response->channels as $name => $channel) {
$userId = Str::after($name, 'private-user');
@ -53,7 +61,7 @@ class PushNewPost
}
if (count($channels)) {
$tags = $event->post->discussion->tags;
$tags = $this->extensions->isEnabled('flarum-tags') ? $event->post->discussion->tags : null;
$this->pusher->trigger($channels, 'newPost', [
'postId' => $event->post->id,

View File

@ -29,6 +29,7 @@ class PusherProvider extends AbstractServiceProvider
$settings->get('flarum-pusher.app_key'),
$settings->get('flarum-pusher.app_secret'),
$settings->get('flarum-pusher.app_id'),
// @phpstan-ignore-next-line
$options
);
});

View File

@ -9,6 +9,7 @@
use Flarum\Api\Controller\ListDiscussionsController;
use Flarum\Api\Serializer\DiscussionSerializer;
use Flarum\Discussion\Discussion;
use Flarum\Discussion\Event\Saving;
use Flarum\Discussion\Filter\DiscussionFilterer;
use Flarum\Discussion\Search\DiscussionSearcher;
@ -26,6 +27,9 @@ return [
->js(__DIR__.'/js/dist/forum.js')
->css(__DIR__.'/less/forum.less'),
(new Extend\Model(Discussion::class))
->cast('is_sticky', 'bool'),
(new Extend\Post())
->type(DiscussionStickiedPost::class),

View File

@ -43,12 +43,12 @@ class PinStickiedDiscussionsToTop
// reorder the unread ones up to the top.
$sticky = clone $query;
$sticky->where('is_sticky', true);
$sticky->orders = null;
unset($sticky->orders);
$query->union($sticky);
$read = $query->newQuery()
->selectRaw(1)
->selectRaw('1')
->from('discussion_user as sticky')
->whereColumn('sticky.discussion_id', 'id')
->where('sticky.user_id', '=', $filterState->getActor()->id)
@ -58,14 +58,14 @@ class PinStickiedDiscussionsToTop
// argument in orderByRaw) for now due to a bug in Laravel which
// would add the bindings in the wrong order.
$query->orderByRaw('is_sticky and not exists ('.$read->toSql().') and last_posted_at > ? desc')
->addBinding(array_merge($read->getBindings(), [$filterState->getActor()->read_time ?: 0]), 'union');
->addBinding(array_merge($read->getBindings(), [$filterState->getActor()->marked_all_as_read_at ?: 0]), 'union');
$query->unionOrders = array_merge($query->unionOrders, $query->orders);
$query->unionLimit = $query->limit;
$query->unionOffset = $query->offset;
$query->limit = $sticky->limit = $query->offset + $query->limit;
$query->offset = $sticky->offset = null;
unset($query->offset, $sticky->offset);
}
}
}

View File

@ -9,6 +9,7 @@
namespace Flarum\Sticky\Post;
use Carbon\Carbon;
use Flarum\Post\AbstractEventPost;
use Flarum\Post\MergeableInterface;
use Flarum\Post\Post;
@ -59,7 +60,7 @@ class DiscussionStickiedPost extends AbstractEventPost implements MergeableInter
$post = new static;
$post->content = static::buildContent($isSticky);
$post->created_at = time();
$post->created_at = Carbon::now();
$post->discussion_id = $discussionId;
$post->user_id = $userId;

View File

@ -14,6 +14,7 @@ use Flarum\Discussion\Discussion;
use Flarum\Discussion\Event\Saving;
use Flarum\Discussion\Filter\DiscussionFilterer;
use Flarum\Discussion\Search\DiscussionSearcher;
use Flarum\Discussion\UserState;
use Flarum\Extend;
use Flarum\Post\Event\Deleted;
use Flarum\Post\Event\Hidden;
@ -24,6 +25,7 @@ use Flarum\Subscriptions\Listener;
use Flarum\Subscriptions\Notification\FilterVisiblePostsBeforeSending;
use Flarum\Subscriptions\Notification\NewPostBlueprint;
use Flarum\Subscriptions\Query\SubscriptionFilterGambit;
use Flarum\User\User;
return [
(new Extend\Frontend('forum'))
@ -33,6 +35,12 @@ return [
new Extend\Locales(__DIR__.'/locale'),
(new Extend\Model(User::class))
->cast('last_read_post_number', 'integer'),
(new Extend\Model(UserState::class))
->cast('subscription', 'string'),
(new Extend\View)
->namespace('flarum-subscriptions', __DIR__.'/views'),

View File

@ -34,7 +34,9 @@ return [
->css(__DIR__.'/less/admin.less'),
(new Extend\Model(User::class))
->dateAttribute('suspended_until'),
->cast('suspended_until', 'datetime')
->cast('suspend_reason', 'string')
->cast('suspend_message', 'string'),
(new Extend\ApiSerializer(UserSerializer::class))
->attributes(AddUserSuspendAttributes::class),

View File

@ -9,6 +9,7 @@
namespace Flarum\Suspend\Listener;
use Carbon\Carbon;
use DateTime;
use Flarum\Suspend\Event\Suspended;
use Flarum\Suspend\Event\Unsuspended;
@ -54,7 +55,7 @@ class SaveSuspensionToDatabase
$actor->assertCan('suspend', $user);
if ($attributes['suspendedUntil']) {
$user->suspended_until = new DateTime($attributes['suspendedUntil']);
$user->suspended_until = Carbon::createFromTimestamp((new DateTime($attributes['suspendedUntil']))->getTimestamp());
$user->suspend_reason = empty($attributes['suspendReason']) ? null : $attributes['suspendReason'];
$user->suspend_message = empty($attributes['suspendMessage']) ? null : $attributes['suspendMessage'];
} else {

View File

@ -34,7 +34,7 @@ class DiscussionPolicy extends AbstractPolicy
* @param User $actor
* @param string $ability
* @param Discussion $discussion
* @return bool
* @return string|void
*/
public function can(User $actor, $ability, Discussion $discussion)
{
@ -68,7 +68,7 @@ class DiscussionPolicy extends AbstractPolicy
*
* @param User $actor
* @param Discussion $discussion
* @return bool
* @return string|void
*/
public function tag(User $actor, Discussion $discussion)
{

View File

@ -27,9 +27,9 @@ class GlobalPolicy extends AbstractPolicy
}
/**
* @param Flarum\User\User $actor
* @param User $actor
* @param string $ability
* @return bool|void
* @return string|void
*/
public function can(User $actor, string $ability)
{

View File

@ -13,6 +13,7 @@ use Flarum\Api\Serializer\AbstractSerializer;
use Flarum\Api\Serializer\DiscussionSerializer;
use Flarum\Http\SlugManager;
use Flarum\Tags\Tag;
use InvalidArgumentException;
class TagSerializer extends AbstractSerializer
{
@ -39,6 +40,12 @@ class TagSerializer extends AbstractSerializer
*/
protected function getDefaultAttributes($tag)
{
if (! ($tag instanceof Tag)) {
throw new InvalidArgumentException(
get_class($this).' can only serialize instances of '.Tag::class
);
}
$attributes = [
'name' => $tag->name,
'description' => $tag->description,

View File

@ -18,9 +18,11 @@ use Flarum\Post\Event\Deleted as PostDeleted;
use Flarum\Post\Event\Hidden as PostHidden;
use Flarum\Post\Event\Posted;
use Flarum\Post\Event\Restored as PostRestored;
use Flarum\Post\Post;
use Flarum\Tags\Event\DiscussionWasTagged;
use Flarum\Tags\Tag;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Arr;
class UpdateTagMetadata
@ -124,9 +126,9 @@ class UpdateTagMetadata
}
/**
* @param \Flarum\Discussion\Discussion $discussion
* @param Discussion $discussion
* @param int $delta
* @param Tag[]|null $tags
* @param Collection<Tag>|null $tags
* @param Post $post: This is only used when a post has been hidden
*/
protected function updateTags(Discussion $discussion, $delta = 0, $tags = null, $post = null)

View File

@ -9,6 +9,7 @@
namespace Flarum\Tags\Post;
use Carbon\Carbon;
use Flarum\Post\AbstractEventPost;
use Flarum\Post\MergeableInterface;
use Flarum\Post\Post;
@ -61,7 +62,7 @@ class DiscussionTaggedPost extends AbstractEventPost implements MergeableInterfa
$post = new static;
$post->content = static::buildContent($oldTagIds, $newTagIds);
$post->created_at = time();
$post->created_at = Carbon::now();
$post->discussion_id = $discussionId;
$post->user_id = $userId;

View File

@ -38,7 +38,7 @@ class TagFilterGambit extends AbstractRegexGambit implements FilterInterface
protected function conditions(SearchState $search, array $matches, $negate)
{
$this->constrain($search->getQuery(), $matches[1], $negate);
$this->constrain($search->getQuery(), $matches[1], $negate, $search->getActor());
}
public function getFilterKey(): string

View File

@ -15,6 +15,7 @@ use Flarum\Discussion\Discussion;
use Flarum\Group\Permission;
use Flarum\User\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
/**
* @property int $id
@ -34,7 +35,13 @@ use Illuminate\Database\Eloquent\Builder;
* @property int $last_posted_discussion_id
* @property int $last_posted_user_id
* @property string $icon
* @property TagState
*
* @property TagState $state
* @property Tag|null $parent
* @property-read Collection<Tag> $children
* @property-read Collection<Discussion> $discussions
* @property Discussion|null $lastPostedDiscussion
* @property User|null $lastPostedUser
*/
class Tag extends AbstractModel
{

View File

@ -29,7 +29,7 @@ class TagRepository
/**
* @param array|string $relations
* @param User $actor
* @return Builder
* @return Builder<Tag>
*/
public function with($relations, User $actor): Builder
{
@ -64,9 +64,8 @@ class TagRepository
* user, or throw an exception.
*
* @param int $id
* @param User $actor
* @param User|null $actor
* @return Tag
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function findOrFail($id, User $actor = null)
{
@ -80,7 +79,7 @@ class TagRepository
* certain user.
*
* @param User|null $user
* @return \Illuminate\Database\Eloquent\Collection
* @return \Illuminate\Database\Eloquent\Collection<Tag>
*/
public function all(User $user = null)
{
@ -106,9 +105,9 @@ class TagRepository
/**
* Scope a query to only include records that are visible to a user.
*
* @param Builder $query
* @param User $user
* @return Builder
* @param Builder<Tag> $query
* @param User|null $user
* @return Builder<Tag>
*/
protected function scopeVisibleTo(Builder $query, User $user = null)
{

View File

@ -44,10 +44,13 @@ class Utf8SlugDriver implements SlugDriverInterface
*/
public function fromSlug(string $slug, User $actor): AbstractModel
{
return $this->repository
/** @var Tag $tag */
$tag = $this->repository
->query()
->where('slug', urldecode($slug))
->whereVisibleTo($actor)
->firstOrFail();
return $tag;
}
}

View File

@ -31,7 +31,7 @@ class Validator implements ExtenderInterface
* Configure the validator. This is often used to adjust validation rules, but can be
* used to make other changes to the validator as well.
*
* @param callable $callback
* @param callable|class-string $callback
*
* The callback can be a closure or invokable class, and should accept:
* - \Flarum\Foundation\AbstractValidator $flarumValidator: The Flarum validator wrapper

View File

@ -236,7 +236,7 @@ class Extension implements Arrayable
}
/**
* @return string
* @return string|null
*/
public function getVersion()
{

View File

@ -36,7 +36,7 @@ interface BlueprintInterface
/**
* Get the data to be stored in the notification.
*
* @return array|null
* @return mixed
*/
public function getData();

View File

@ -16,7 +16,7 @@ interface MailableInterface
/**
* Get the name of the view to construct a notification email with.
*
* @return string
* @return string|array
*/
public function getEmailView();

View File

@ -9,25 +9,26 @@
namespace Flarum\Post\Event;
use Flarum\Post\Post;
use Flarum\Post\CommentPost;
use Flarum\User\User;
class Posted
{
/**
* @var \Flarum\Post\Post
* @var CommentPost
*/
public $post;
/**
* @var User
* @var User|null
*/
public $actor;
/**
* @param \Flarum\Post\Post $post
* @param CommentPost $post
* @param User|null $actor
*/
public function __construct(Post $post, User $actor = null)
public function __construct(CommentPost $post, User $actor = null)
{
$this->post = $post;
$this->actor = $actor;

View File

@ -5,6 +5,34 @@ parameters:
level: 5
paths:
- framework/core/src
- extensions/akismet/src
- extensions/akismet/extend.php
- extensions/approval/src
- extensions/approval/extend.php
- extensions/flags/src
- extensions/flags/extend.php
- extensions/likes/src
- extensions/likes/extend.php
- extensions/lock/src
- extensions/lock/extend.php
- extensions/mentions/src
- extensions/mentions/extend.php
- extensions/nicknames/src
- extensions/nicknames/extend.php
- extensions/package-manager/src
- extensions/package-manager/extend.php
- extensions/pusher/src
- extensions/pusher/extend.php
- extensions/statistics/src
- extensions/statistics/extend.php
- extensions/sticky/src
- extensions/sticky/extend.php
- extensions/subscriptions/src
- extensions/subscriptions/extend.php
- extensions/suspend/src
- extensions/suspend/extend.php
- extensions/tags/src
- extensions/tags/extend.php
excludePaths:
- *.blade.php
checkMissingIterableValueType: false