refactored
This commit is contained in:
parent
4fa4ccce99
commit
0574307439
|
|
@ -2,33 +2,39 @@
|
||||||
|
|
||||||
namespace Blomstra\Search\Api\Controllers;
|
namespace Blomstra\Search\Api\Controllers;
|
||||||
|
|
||||||
|
use Blomstra\Search\Save\Document as ElasticDocument;
|
||||||
use Blomstra\Search\Elasticsearch\TermsQuery;
|
use Blomstra\Search\Elasticsearch\TermsQuery;
|
||||||
use Blomstra\Search\Schemas\Schema;
|
|
||||||
use Elasticsearch\Client;
|
use Elasticsearch\Client;
|
||||||
use Flarum\Api\Controller\AbstractListController;
|
|
||||||
use Flarum\Api\Controller\ListDiscussionsController;
|
use Flarum\Api\Controller\ListDiscussionsController;
|
||||||
use Flarum\Discussion\Filter\DiscussionFilterer;
|
use Flarum\Api\Serializer\DiscussionSerializer;
|
||||||
use Flarum\Discussion\Search\DiscussionSearcher;
|
use Flarum\Discussion\Discussion;
|
||||||
use Flarum\Extension\ExtensionManager;
|
use Flarum\Extension\ExtensionManager;
|
||||||
use Flarum\Group\Group;
|
use Flarum\Group\Group;
|
||||||
use Flarum\Http\RequestUtil;
|
use Flarum\Http\RequestUtil;
|
||||||
use Flarum\Http\UrlGenerator;
|
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Str;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Spatie\ElasticsearchQueryBuilder\Aggregations\TermsAggregation;
|
use Spatie\ElasticsearchQueryBuilder\Aggregations\TermsAggregation;
|
||||||
use Spatie\ElasticsearchQueryBuilder\Aggregations\TopHitsAggregation;
|
use Spatie\ElasticsearchQueryBuilder\Aggregations\TopHitsAggregation;
|
||||||
use Spatie\ElasticsearchQueryBuilder\Builder;
|
use Spatie\ElasticsearchQueryBuilder\Builder;
|
||||||
use Spatie\ElasticsearchQueryBuilder\Queries\BoolQuery;
|
use Spatie\ElasticsearchQueryBuilder\Queries\BoolQuery;
|
||||||
use Spatie\ElasticsearchQueryBuilder\Queries\MultiMatchQuery;
|
use Spatie\ElasticsearchQueryBuilder\Queries\MatchQuery;
|
||||||
use Spatie\ElasticsearchQueryBuilder\Queries\TermQuery;
|
use Spatie\ElasticsearchQueryBuilder\Queries\TermQuery;
|
||||||
use Spatie\ElasticsearchQueryBuilder\Sorts\Sort;
|
use Spatie\ElasticsearchQueryBuilder\Sorts\Sort;
|
||||||
use Tobscure\JsonApi\Document;
|
use Tobscure\JsonApi\Document;
|
||||||
|
|
||||||
class SearchController extends ListDiscussionsController
|
class SearchController extends ListDiscussionsController
|
||||||
{
|
{
|
||||||
|
public $serializer = DiscussionSerializer::class;
|
||||||
|
|
||||||
|
protected array $translateSort = [
|
||||||
|
'lastPostedAt' => 'updated_at',
|
||||||
|
'createdAt' => 'created_at'
|
||||||
|
];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -44,17 +50,13 @@ class SearchController extends ListDiscussionsController
|
||||||
|
|
||||||
$filters = $this->extractFilter($request);
|
$filters = $this->extractFilter($request);
|
||||||
|
|
||||||
$schema = $this->getSchema($type);
|
$include = array_merge($this->extractInclude($request), ['state']);
|
||||||
|
|
||||||
$this->serializer = $schema::serializer();
|
|
||||||
|
|
||||||
$filterQuery = (BoolQuery::create())
|
$filterQuery = (BoolQuery::create())
|
||||||
->add(
|
->add(
|
||||||
BoolQuery::create()
|
BoolQuery::create()
|
||||||
->add(MultiMatchQuery::create($filters['q'], array_keys($schema->fulltext(new ($schema::model())))))
|
->add(MatchQuery::create('content', $filters['q']))
|
||||||
->add(TermQuery::create('type', $type))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$builder = (new Builder($client))
|
$builder = (new Builder($client))
|
||||||
->index(resolve('blomstra.search.elastic_index'))
|
->index(resolve('blomstra.search.elastic_index'))
|
||||||
->size($this->extractLimit($request))
|
->size($this->extractLimit($request))
|
||||||
|
|
@ -62,26 +64,78 @@ class SearchController extends ListDiscussionsController
|
||||||
->addQuery(
|
->addQuery(
|
||||||
$this->addFilters($filterQuery, $actor)
|
$this->addFilters($filterQuery, $actor)
|
||||||
)
|
)
|
||||||
->addAggregation(
|
// ->addAggregation(
|
||||||
TermsAggregation::create('discussions', 'discussion_id')
|
// TermsAggregation::create('discussions', 'discussion_id')
|
||||||
->aggregation(TopHitsAggregation::create('hits', 1))
|
// ->aggregation(TopHitsAggregation::create('hits', 1))
|
||||||
);
|
// )
|
||||||
|
;
|
||||||
|
|
||||||
foreach ($this->extractSort($request) as $field => $direction) {
|
foreach ($this->extractSort($request) as $field => $direction) {
|
||||||
|
$field = $this->translateSort[$field] ?? $field;
|
||||||
$builder->addSort(new Sort($field, $direction));
|
$builder->addSort(new Sort($field, $direction));
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $builder->search();
|
$result = $builder->search();
|
||||||
|
|
||||||
return $schema::results(Arr::get($result, 'hits.hits'));
|
Discussion::setStateUser($actor);
|
||||||
|
|
||||||
|
// Eager load groups for use in the policies (isAdmin check)
|
||||||
|
if (in_array('mostRelevantPost.user', $include)) {
|
||||||
|
$include[] = 'mostRelevantPost.user.groups';
|
||||||
|
|
||||||
|
// If the first level of the relationship wasn't explicitly included,
|
||||||
|
// add it so the code below can look for it
|
||||||
|
if (! in_array('mostRelevantPost', $include)) {
|
||||||
|
$include[] = 'mostRelevantPost';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getSchema(string $type): ?Schema
|
$results = Collection::make(Arr::get($result, 'hits.hits'))
|
||||||
{
|
->map(function ($hit) {
|
||||||
$mapping = resolve(Container::class)->tagged('blomstra.search.schemas');
|
$id = Str::after($hit['_source']['id'], ':');
|
||||||
|
$type = $hit['_source']['type'];
|
||||||
|
|
||||||
return collect($mapping)->first(function (Schema $schema) use ($type) {
|
if ($type === 'posts') {
|
||||||
return $schema::type() === $type;
|
/** @var Discussion $discussion */
|
||||||
|
$discussion = Discussion::whereHas('posts', function ($query) use ($id) {
|
||||||
|
$query->where('id', $id);
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
$discussion->most_relevant_post_id = $id;
|
||||||
|
}
|
||||||
|
if ($type === 'discussions') {
|
||||||
|
/** @var Discussion $discussion */
|
||||||
|
$discussion = Discussion::find($id);
|
||||||
|
|
||||||
|
$discussion->most_relevant_post_id = $discussion->first_post_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $discussion;
|
||||||
|
})
|
||||||
|
->keyBy('id')
|
||||||
|
->unique();
|
||||||
|
|
||||||
|
$this->loadRelations($results, $include);
|
||||||
|
|
||||||
|
if ($relations = array_intersect($include, ['firstPost', 'lastPost', 'mostRelevantPost'])) {
|
||||||
|
foreach ($results as $discussion) {
|
||||||
|
foreach ($relations as $relation) {
|
||||||
|
if ($discussion->$relation) {
|
||||||
|
$discussion->$relation->discussion = $discussion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDocument(string $type): ?ElasticDocument
|
||||||
|
{
|
||||||
|
$documents = resolve(Container::class)->tagged('blomstra.search.documents');
|
||||||
|
|
||||||
|
return collect($documents)->first(function (ElasticDocument $document) use ($type) {
|
||||||
|
return $document->type() === $type;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,12 +157,12 @@ class SearchController extends ListDiscussionsController
|
||||||
if ($actor->is_email_confirmed) $groups->add(Group::MEMBER_ID);
|
if ($actor->is_email_confirmed) $groups->add(Group::MEMBER_ID);
|
||||||
|
|
||||||
$subQuery = BoolQuery::create()
|
$subQuery = BoolQuery::create()
|
||||||
->add(TermQuery::create('private', 'false'))
|
->add(TermQuery::create('is_private', 'false'))
|
||||||
->add(TermsQuery::create('groups', $groups->toArray()));
|
->add(TermsQuery::create('groups', $groups->toArray()));
|
||||||
|
|
||||||
if ($this->extensionEnabled('fof-byobu') && $actor->exists) {
|
if ($this->extensionEnabled('fof-byobu') && $actor->exists) {
|
||||||
$byobuQuery = BoolQuery::create()
|
$byobuQuery = BoolQuery::create()
|
||||||
->add(TermQuery::create('private', 'true'), 'should')
|
->add(TermQuery::create('is_private', 'true'), 'should')
|
||||||
->add(
|
->add(
|
||||||
BoolQuery::create()
|
BoolQuery::create()
|
||||||
->add(TermsQuery::create('recipient-groups', $groups->toArray()))
|
->add(TermsQuery::create('recipient-groups', $groups->toArray()))
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@ use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
class RebuildDocumentsCommand extends Command
|
class RebuildDocumentsCommand extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'blomstra:search:documents:rebuild {--flush : Flushes ALL the documents inside the index}';
|
protected $signature = 'blomstra:search:documents:rebuild
|
||||||
|
{--flush : Flushes ALL the documents inside the index}
|
||||||
|
{--mapping : Create property mappings}
|
||||||
|
{--max-id= : Limits for each object the number of items to seed}';
|
||||||
protected $description = 'Rebuilds the complete search server with its documents.';
|
protected $description = 'Rebuilds the complete search server with its documents.';
|
||||||
|
|
||||||
public function handle(Container $container)
|
public function handle(Container $container)
|
||||||
|
|
@ -27,17 +30,44 @@ class RebuildDocumentsCommand extends Command
|
||||||
$client = $container->make('blomstra.search.elastic');
|
$client = $container->make('blomstra.search.elastic');
|
||||||
|
|
||||||
// Flush the index.
|
// Flush the index.
|
||||||
if ($this->option('flush')) $client->indices()->delete([
|
if ($this->option('flush')) {
|
||||||
|
$client->indices()->delete([
|
||||||
|
'index' => $container->make('blomstra.search.elastic_index'),
|
||||||
|
'ignore_unavailable' => true
|
||||||
|
]);
|
||||||
|
$client->indices()->create([
|
||||||
'index' => $container->make('blomstra.search.elastic_index')
|
'index' => $container->make('blomstra.search.elastic_index')
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->option('mapping')) {
|
||||||
|
$client->indices()->putMapping([
|
||||||
|
'index' => $container->make('blomstra.search.elastic_index'),
|
||||||
|
'body' => [
|
||||||
|
'properties' => [
|
||||||
|
'content' => ['type' => 'text'],
|
||||||
|
'created_at' => ['type' => 'date'],
|
||||||
|
'updated_at' => ['type' => 'date'],
|
||||||
|
'is_private' => ['type' => 'boolean'],
|
||||||
|
'is_sticky' => ['type' => 'boolean'],
|
||||||
|
'groups' => ['type' => 'integer'],
|
||||||
|
'recipient_groups' => ['type' => 'integer'],
|
||||||
|
'recipient_users' => ['type' => 'integer'],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/** @var Seeder $seeder */
|
/** @var Seeder $seeder */
|
||||||
foreach ($seeders as $seeder) {
|
foreach ($seeders as $seeder) {
|
||||||
$seeder->query()->chunk(50, function (Collection $collection) use ($queue, &$total) {
|
$seeder->query()
|
||||||
|
->when($this->option('max-id'), function ($query, $id) {
|
||||||
|
$query->where('id', '<=', $id);
|
||||||
|
})
|
||||||
|
->chunk(50, function (Collection $collection) use ($queue, &$total, $seeder) {
|
||||||
|
$queue->push(new SavingJob($collection, $seeder));
|
||||||
|
|
||||||
$queue->push(new SavingJob($collection));
|
$this->info("Pushed into the index, type: {$seeder->type()}, amount: {$collection->count()}.");
|
||||||
|
|
||||||
$this->info("Pushed {$collection->count()} into the index.");
|
|
||||||
|
|
||||||
$total += $collection->count();
|
$total += $collection->count();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Blomstra\Search\Documents;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\PostSerializer;
|
|
||||||
use Flarum\Post\CommentPost;
|
|
||||||
|
|
||||||
class CommentDocument extends Document
|
|
||||||
{
|
|
||||||
public function __construct(protected CommentPost $model)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public function fulltext(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'content' => $this->model->content,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
$attributes = [
|
|
||||||
'author' => $this->model->user_id,
|
|
||||||
'createdAt' => $this->model->created_at->toAtomString(),
|
|
||||||
'is_private' => $this->model->is_private,
|
|
||||||
'discussion_id' => $this->model->discussion?->id
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-flags')) {
|
|
||||||
$attributes['flags_count'] = $this->model->flags->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-approval')) {
|
|
||||||
$attributes['approved'] = $this->model->is_approved;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function serializer(): string
|
|
||||||
{
|
|
||||||
return PostSerializer::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function model(): string
|
|
||||||
{
|
|
||||||
return CommentPost::class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Blomstra\Search\Documents;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
|
||||||
use Flarum\Discussion\Discussion;
|
|
||||||
|
|
||||||
class DiscussionDocument extends Document
|
|
||||||
{
|
|
||||||
public function __construct(protected Discussion $model)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public function fulltext(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'content' => $this->model->title
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
$attributes = [
|
|
||||||
'author' => $this->model->user_id,
|
|
||||||
'createdAt' => $this->model->created_at->toAtomString(),
|
|
||||||
'lastPostedAt' => $this->model->last_posted_at?->toAtomString(),
|
|
||||||
'is_private' => $this->model->is_private,
|
|
||||||
'first_post_id' => $this->model->first_post_id,
|
|
||||||
'last_post_id' => $this->model->last_post_id,
|
|
||||||
'commentCount' => $this->model?->comment_count,
|
|
||||||
'groups' => $this->groupsForDiscussion($this->model),
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('fof-byobu')) {
|
|
||||||
$attributes['recipient-users'] = $this->model->recipientUsers->pluck('id')->toArray();
|
|
||||||
$attributes['recipient-groups'] = $this->model->recipientGroups->pluck('id')->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-sticky')) {
|
|
||||||
$attributes['is_sticky'] = $this->model->is_sticky;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function serializer(): string
|
|
||||||
{
|
|
||||||
return DiscussionSerializer::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function model(): string
|
|
||||||
{
|
|
||||||
return Discussion::class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Blomstra\Search\Documents;
|
|
||||||
|
|
||||||
use Flarum\Discussion\Discussion;
|
|
||||||
use Flarum\Extension\ExtensionManager;
|
|
||||||
use Flarum\Group\Group;
|
|
||||||
use Flarum\Group\Permission;
|
|
||||||
use Flarum\Tags\Tag;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
|
|
||||||
abstract class Document
|
|
||||||
{
|
|
||||||
abstract public function fulltext(): array;
|
|
||||||
abstract public function attributes(): array;
|
|
||||||
|
|
||||||
public function id(): string
|
|
||||||
{
|
|
||||||
return $this->type() . ':' . $this->model->getKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index(): string
|
|
||||||
{
|
|
||||||
return resolve('blomstra.search.elastic_index');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function type(): string
|
|
||||||
{
|
|
||||||
return resolve($this->serializer())->getType($this->model);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract public function serializer(): string;
|
|
||||||
|
|
||||||
abstract public function model(): string;
|
|
||||||
|
|
||||||
protected function groupsForDiscussion(Discussion $discussion): array
|
|
||||||
{
|
|
||||||
$permissions = collect();
|
|
||||||
|
|
||||||
$globalPermission = Permission::query()
|
|
||||||
->where('permission', 'viewForum')
|
|
||||||
->pluck('group_id');
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-tags')) {
|
|
||||||
/** @var Collection $tags */
|
|
||||||
$tags = $discussion->tags;
|
|
||||||
|
|
||||||
$filters['tags'] = $tags->pluck('id')->toArray();
|
|
||||||
$tagPermissions = Permission::query()
|
|
||||||
->whereIn(
|
|
||||||
'permission',
|
|
||||||
$tags->pluck('id')->map(function (int $id) {
|
|
||||||
return "tag$id.viewForum";
|
|
||||||
})
|
|
||||||
)->get();
|
|
||||||
|
|
||||||
$permissions = $tags->map(function (Tag $tag) use ($tagPermissions) {
|
|
||||||
$permissions = $tagPermissions->where('permission', "tag$tag->id.viewForum");
|
|
||||||
|
|
||||||
if ($tag->is_restricted) {
|
|
||||||
$permissions = $permissions->add(['group_id' => Group::ADMINISTRATOR_ID]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $permissions->pluck('group_id');
|
|
||||||
})->flatten();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $discussion->is_private && $permissions->isEmpty()) {
|
|
||||||
$permissions = $globalPermission;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $permissions->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function extensionEnabled(string $extension): bool
|
|
||||||
{
|
|
||||||
/** @var ExtensionManager $manager */
|
|
||||||
$manager = resolve(ExtensionManager::class);
|
|
||||||
|
|
||||||
return $manager->isEnabled($extension);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,26 +2,16 @@
|
||||||
|
|
||||||
namespace Blomstra\Search\Observe;
|
namespace Blomstra\Search\Observe;
|
||||||
|
|
||||||
use Blomstra\Search\Documents\Document;
|
use Blomstra\Search\Seeders\Seeder;
|
||||||
use Flarum\Queue\AbstractJob;
|
use Flarum\Queue\AbstractJob;
|
||||||
use Illuminate\Contracts\Container\Container;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
abstract class Job extends AbstractJob
|
abstract class Job extends AbstractJob
|
||||||
{
|
{
|
||||||
public function __construct(protected Collection $models)
|
protected string $index;
|
||||||
{}
|
|
||||||
|
|
||||||
protected function getDocument(): ?Document
|
public function __construct(protected Collection $models, protected Seeder $seeder)
|
||||||
{
|
{
|
||||||
$documents = resolve(Container::class)->tagged('blomstra.search.documents');
|
$this->index = resolve('blomstra.search.elastic_index');
|
||||||
|
|
||||||
/** @var Model $model */
|
|
||||||
$model = $this->models->first();
|
|
||||||
|
|
||||||
return collect($documents)->first(function (Document $document) use ($model) {
|
|
||||||
return $document->model() === get_class($model);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace Blomstra\Search\Observe;
|
namespace Blomstra\Search\Observe;
|
||||||
|
|
||||||
use Blomstra\Search\Documents\Document;
|
|
||||||
use Elasticsearch\Client;
|
use Elasticsearch\Client;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -16,62 +15,19 @@ class SavingJob extends Job
|
||||||
/** @var Client $client */
|
/** @var Client $client */
|
||||||
$client = $container->make('blomstra.search.elastic');
|
$client = $container->make('blomstra.search.elastic');
|
||||||
|
|
||||||
$document = $this->getDocument();
|
|
||||||
|
|
||||||
if (! $document) return;
|
|
||||||
|
|
||||||
if($first = $this->models->first()) {
|
|
||||||
// $properties = [];
|
|
||||||
//
|
|
||||||
// foreach (array_keys($schema->fulltext($first)) as $key) {
|
|
||||||
// $properties[$key] = ['type' => 'text'];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Set up the index
|
|
||||||
// if (! $client->indices()->exists([
|
|
||||||
// 'index' => $schema::index(),
|
|
||||||
// 'expand_wildcards' => 'none'
|
|
||||||
// ])) {
|
|
||||||
// $client->indices()->create([
|
|
||||||
// 'index' => $schema::index(),
|
|
||||||
// 'body' => [
|
|
||||||
// 'mappings' => [
|
|
||||||
// 'properties' => $properties
|
|
||||||
// ],
|
|
||||||
// 'settings' => [
|
|
||||||
// 'index' => [
|
|
||||||
// 'query' => [
|
|
||||||
// 'default_field' => array_keys($schema->fulltext($first))
|
|
||||||
// ]
|
|
||||||
// ]
|
|
||||||
// ]
|
|
||||||
// ]
|
|
||||||
// ]);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preparing body for storing.
|
// Preparing body for storing.
|
||||||
$body = $this->models->map(function (Model $model) use ($document) {
|
$body = $this->models->map(function (Model $model) {
|
||||||
/** @var Document $markup */
|
$document = $this->seeder->toDocument($model);
|
||||||
$markup = new $document($model);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
['index' => ['_index' => $this->index, '_id' => $document->id]],
|
||||||
'index' => [
|
$document->toArray()
|
||||||
'_id' => $markup->id(),
|
|
||||||
'_index' => resolve('blomstra.search.elastic_index')
|
|
||||||
]
|
|
||||||
],
|
|
||||||
array_merge(
|
|
||||||
['type' => $markup->type()],
|
|
||||||
$markup->fulltext(),
|
|
||||||
$markup->attributes()
|
|
||||||
)
|
|
||||||
];
|
];
|
||||||
})->flatten(1);
|
})
|
||||||
|
->flatten(1);
|
||||||
|
|
||||||
$response = $client->bulk([
|
$response = $client->bulk([
|
||||||
'index' => resolve('blomstra.search.elastic_index'),
|
'index' => $this->index,
|
||||||
'body' => $body->toArray(),
|
'body' => $body->toArray(),
|
||||||
'refresh' => true
|
'refresh' => true
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@
|
||||||
|
|
||||||
namespace Blomstra\Search;
|
namespace Blomstra\Search;
|
||||||
|
|
||||||
use Blomstra\Search\Documents;
|
|
||||||
use Blomstra\Search\Observe\DeletingJob;
|
use Blomstra\Search\Observe\DeletingJob;
|
||||||
use Blomstra\Search\Observe\SavingJob;
|
use Blomstra\Search\Observe\SavingJob;
|
||||||
use Blomstra\Search\Schemas\Schema;
|
|
||||||
use Blomstra\Search\Seeders;
|
use Blomstra\Search\Seeders;
|
||||||
use Elasticsearch\ClientBuilder;
|
use Elasticsearch\ClientBuilder;
|
||||||
use Flarum\Foundation\AbstractServiceProvider;
|
use Flarum\Foundation\AbstractServiceProvider;
|
||||||
|
|
@ -21,13 +19,8 @@ class Provider extends AbstractServiceProvider
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
$this->container->tag([
|
$this->container->tag([
|
||||||
Documents\CommentDocument::class,
|
Seeders\DiscussionSeeder::class,
|
||||||
Documents\DiscussionDocument::class
|
|
||||||
], 'blomstra.search.documents');
|
|
||||||
|
|
||||||
$this->container->tag([
|
|
||||||
Seeders\CommentSeeder::class,
|
Seeders\CommentSeeder::class,
|
||||||
Seeders\DiscussionSeeder::class
|
|
||||||
], 'blomstra.search.seeders');
|
], 'blomstra.search.seeders');
|
||||||
|
|
||||||
$config = $this->container->make('flarum.config') ?? [];
|
$config = $this->container->make('flarum.config') ?? [];
|
||||||
|
|
@ -64,12 +57,12 @@ class Provider extends AbstractServiceProvider
|
||||||
|
|
||||||
/** @var string|Seeders\Seeder $seeder */
|
/** @var string|Seeders\Seeder $seeder */
|
||||||
foreach ($seeders as $seeder) {
|
foreach ($seeders as $seeder) {
|
||||||
$seeder::savingOn($events, function ($model) use ($queue) {
|
$seeder::savingOn($events, function ($model) use ($queue, $seeder) {
|
||||||
$queue->push(new SavingJob(Collection::make([$model])));
|
$queue->push(new SavingJob(Collection::make([$model]), $seeder));
|
||||||
});
|
});
|
||||||
|
|
||||||
$seeder::deletingOn($events, function ($model) use ($queue) {
|
$seeder::deletingOn($events, function ($model) use ($queue, $seeder) {
|
||||||
$queue->push(new DeletingJob(Collection::make([$model])));
|
$queue->push(new DeletingJob(Collection::make([$model]), $seeder));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Blomstra\Search\Save;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Fluent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property string $type
|
||||||
|
* @property string $id
|
||||||
|
* @property string $content
|
||||||
|
* @property Carbon $created_at
|
||||||
|
* @property Carbon $updated_at
|
||||||
|
* @property int $user_id
|
||||||
|
* @property bool $is_private
|
||||||
|
* @property bool $is_sticky
|
||||||
|
* @property array|int[] $groups
|
||||||
|
* @property array|int[] $recipient_groups
|
||||||
|
* @property array|int[] $recipient_users
|
||||||
|
*/
|
||||||
|
class Document extends Fluent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Blomstra\Search\Schemas;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
|
||||||
use Flarum\Discussion\Discussion;
|
|
||||||
use Flarum\Post\CommentPost;
|
|
||||||
use Flarum\Post\Event\Deleted;
|
|
||||||
use Flarum\Post\Event\Posted;
|
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
class CommentPostSchema extends Schema
|
|
||||||
{
|
|
||||||
public function filters(CommentPost $post): array
|
|
||||||
{
|
|
||||||
$filters = [
|
|
||||||
'type' => CommentPost::$type,
|
|
||||||
'author' => $post->user_id,
|
|
||||||
'createdAt' => $post->created_at->toAtomString(),
|
|
||||||
'private' => $post->is_private,
|
|
||||||
'discussion_id' => $post->discussion?->id
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-flags')) {
|
|
||||||
$filters['flags_count'] = $post->flags->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-approval')) {
|
|
||||||
$filters['approved'] = $post->is_approved;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fulltext(CommentPost $post): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'content' => $post->content,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function model(): string
|
|
||||||
{
|
|
||||||
return CommentPost::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function query(): Builder
|
|
||||||
{
|
|
||||||
return CommentPost::query()
|
|
||||||
->where('type', CommentPost::$type)
|
|
||||||
->with('discussion');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function results(array $hits): \Illuminate\Database\Eloquent\Collection
|
|
||||||
{
|
|
||||||
$postIds = Collection::make($hits)->keyBy('_source.discussion_id')->pluck('_id');
|
|
||||||
$discussionIds = Collection::make($hits)->pluck('_source.discussion_id');
|
|
||||||
|
|
||||||
return Discussion::query()->findMany($discussionIds)->map(function (Discussion $discussion) use ($postIds) {
|
|
||||||
$discussion->most_relevant_post_id = $postIds->get($discussion->id);
|
|
||||||
|
|
||||||
return $discussion;
|
|
||||||
})->load('mostRelevantPost');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function serializer(): string
|
|
||||||
{
|
|
||||||
return DiscussionSerializer::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function savingOn(Dispatcher $events, callable $callable)
|
|
||||||
{
|
|
||||||
$events->listen(Posted::class, function (Posted $event) use ($callable) {
|
|
||||||
$callable($event->post);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function deletingOn(Dispatcher $events, callable $callable)
|
|
||||||
{
|
|
||||||
$events->listen(Deleted::class, function (Deleted $event) use ($callable) {
|
|
||||||
$callable($event->post);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function type(): string
|
|
||||||
{
|
|
||||||
return CommentPost::$type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Blomstra\Search\Schemas;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
|
||||||
use Flarum\Discussion\Discussion;
|
|
||||||
use Flarum\Discussion\Event\Deleted;
|
|
||||||
use Flarum\Discussion\Event\Hidden;
|
|
||||||
use Flarum\Discussion\Event\Restored;
|
|
||||||
use Flarum\Discussion\Event\Started;
|
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
|
|
||||||
class DiscussionSchema extends Schema
|
|
||||||
{
|
|
||||||
public function filters(Discussion $discussion): array
|
|
||||||
{
|
|
||||||
$filters = [
|
|
||||||
'type' => 'discussions',
|
|
||||||
'author' => $discussion->user_id,
|
|
||||||
'createdAt' => $discussion->created_at->toAtomString(),
|
|
||||||
'lastPostedAt' => $discussion->last_posted_at->toAtomString(),
|
|
||||||
'private' => $discussion->is_private,
|
|
||||||
'first_post_id' => $discussion->first_post_id,
|
|
||||||
'last_post_id' => $discussion->last_post_id,
|
|
||||||
'commentCount' => $discussion?->comment_count,
|
|
||||||
'groups' => $this->groupsForDiscussion($discussion),
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('fof-byobu')) {
|
|
||||||
$filters['recipient-users'] = $discussion->recipientUsers->pluck('id')->toArray();
|
|
||||||
$filters['recipient-groups'] = $discussion->recipientGroups->pluck('id')->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-sticky')) {
|
|
||||||
$filters['is_sticky'] = $discussion->is_sticky;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function relations()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'discussion_id' => [
|
|
||||||
'type' => 'discussion'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fulltext(Discussion $discussion): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'title' => $discussion->title
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function model(): string
|
|
||||||
{
|
|
||||||
return Discussion::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function serializer(): string
|
|
||||||
{
|
|
||||||
return DiscussionSerializer::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function savingOn(Dispatcher $events, callable $callable)
|
|
||||||
{
|
|
||||||
$events->listen([Started::class, Restored::class], function ($event) use ($callable) {
|
|
||||||
return $callable($event->discussion);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function deletingOn(Dispatcher $events, callable $callable)
|
|
||||||
{
|
|
||||||
$events->listen([Deleted::class, Hidden::class], function ($event) use ($callable) {
|
|
||||||
return $callable($event->discussion);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function query(): Builder
|
|
||||||
{
|
|
||||||
return Discussion::query();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function results(array $hits): Collection
|
|
||||||
{
|
|
||||||
$postIds = \Illuminate\Support\Collection::make($hits)->keyBy('_source.discussion_id')->pluck('_id');
|
|
||||||
$discussionIds = Collection::make($hits)->pluck('_source.discussion_id');
|
|
||||||
|
|
||||||
return Discussion::query()->findMany($discussionIds)->map(function (Discussion $discussion) use ($postIds) {
|
|
||||||
$discussion->most_relevant_post_id = $postIds->get($discussion->id);
|
|
||||||
|
|
||||||
return $discussion;
|
|
||||||
})->load('mostRelevantPost');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function type(): string
|
|
||||||
{
|
|
||||||
return 'discussion';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Blomstra\Search\Schemas;
|
|
||||||
|
|
||||||
use Flarum\Discussion\Discussion;
|
|
||||||
use Flarum\Extension\ExtensionManager;
|
|
||||||
use Flarum\Group\Group;
|
|
||||||
use Flarum\Group\Permission;
|
|
||||||
use Flarum\Tags\Tag;
|
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
|
|
||||||
abstract class Schema
|
|
||||||
{
|
|
||||||
protected function extensionEnabled(string $extension): bool
|
|
||||||
{
|
|
||||||
/** @var ExtensionManager $manager */
|
|
||||||
$manager = resolve(ExtensionManager::class);
|
|
||||||
|
|
||||||
return $manager->isEnabled($extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract public static function type(): string;
|
|
||||||
|
|
||||||
abstract public static function model(): string;
|
|
||||||
abstract public static function query(): Builder;
|
|
||||||
abstract public static function results(array $hits): Collection;
|
|
||||||
|
|
||||||
abstract public static function serializer(): string;
|
|
||||||
|
|
||||||
abstract public static function savingOn(Dispatcher $events, callable $callable);
|
|
||||||
abstract public static function deletingOn(Dispatcher $events, callable $callable);
|
|
||||||
|
|
||||||
protected function groupsForDiscussion(Discussion $discussion): array
|
|
||||||
{
|
|
||||||
$permissions = collect();
|
|
||||||
|
|
||||||
$globalPermission = Permission::query()
|
|
||||||
->where('permission', 'viewForum')
|
|
||||||
->pluck('group_id');
|
|
||||||
|
|
||||||
if ($this->extensionEnabled('flarum-tags')) {
|
|
||||||
/** @var Collection $tags */
|
|
||||||
$tags = $discussion->tags;
|
|
||||||
|
|
||||||
$filters['tags'] = $tags->pluck('id')->toArray();
|
|
||||||
$tagPermissions = Permission::query()
|
|
||||||
->whereIn(
|
|
||||||
'permission',
|
|
||||||
$tags->pluck('id')->map(function (int $id) {
|
|
||||||
return "tag$id.viewForum";
|
|
||||||
})
|
|
||||||
)->get();
|
|
||||||
|
|
||||||
$permissions = $tags->map(function (Tag $tag) use ($tagPermissions) {
|
|
||||||
$permissions = $tagPermissions->where('permission', "tag$tag->id.viewForum");
|
|
||||||
|
|
||||||
if ($tag->is_restricted) {
|
|
||||||
$permissions = $permissions->add(['group_id' => Group::ADMINISTRATOR_ID]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $permissions->pluck('group_id');
|
|
||||||
})->flatten();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $discussion->is_private && $permissions->isEmpty()) {
|
|
||||||
$permissions = $globalPermission;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $permissions->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,18 +2,20 @@
|
||||||
|
|
||||||
namespace Blomstra\Search\Seeders;
|
namespace Blomstra\Search\Seeders;
|
||||||
|
|
||||||
|
use Blomstra\Search\Save\Document;
|
||||||
use Flarum\Api\Serializer\PostSerializer;
|
use Flarum\Api\Serializer\PostSerializer;
|
||||||
use Flarum\Post\CommentPost;
|
use Flarum\Post\CommentPost;
|
||||||
use Flarum\Post\Event\Deleted;
|
use Flarum\Post\Event\Deleted;
|
||||||
use Flarum\Post\Event\Posted;
|
use Flarum\Post\Event\Posted;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class CommentSeeder extends Seeder
|
class CommentSeeder extends Seeder
|
||||||
{
|
{
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return resolve(PostSerializer::class)->type;
|
return resolve(PostSerializer::class)->getType(new CommentPost);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function query(): Builder
|
public function query(): Builder
|
||||||
|
|
@ -36,4 +38,29 @@ class CommentSeeder extends Seeder
|
||||||
$callable($event->post);
|
$callable($event->post);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CommentPost $model
|
||||||
|
* @return Document
|
||||||
|
*/
|
||||||
|
public function toDocument(Model $model): Document
|
||||||
|
{
|
||||||
|
$document = new Document([
|
||||||
|
'type' => $this->type(),
|
||||||
|
'id' => $this->type() . ':' . $model->id,
|
||||||
|
'content' => $model->content,
|
||||||
|
'created_at' => $model->created_at?->toAtomString(),
|
||||||
|
'updated_at' => $model->edited_at?->toAtomString(),
|
||||||
|
'is_private' => $model->is_private,
|
||||||
|
'user_id' => $model->user_id,
|
||||||
|
'groups' => $this->groupsForDiscussion($model->discussion)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($this->extensionEnabled('fof-byobu')) {
|
||||||
|
$document['recipient_users'] = $model->discussion->recipientUsers->pluck('id')->toArray();
|
||||||
|
$document['recipient_groups'] = $model->discussion->recipientGroups->pluck('id')->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $document;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Blomstra\Search\Seeders;
|
namespace Blomstra\Search\Seeders;
|
||||||
|
|
||||||
|
use Blomstra\Search\Save\Document;
|
||||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
use Flarum\Api\Serializer\DiscussionSerializer;
|
||||||
use Flarum\Discussion\Discussion;
|
use Flarum\Discussion\Discussion;
|
||||||
use Flarum\Discussion\Event\Deleted;
|
use Flarum\Discussion\Event\Deleted;
|
||||||
|
|
@ -10,12 +11,13 @@ use Flarum\Discussion\Event\Restored;
|
||||||
use Flarum\Discussion\Event\Started;
|
use Flarum\Discussion\Event\Started;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class DiscussionSeeder extends Seeder
|
class DiscussionSeeder extends Seeder
|
||||||
{
|
{
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return resolve(DiscussionSerializer::class)->type;
|
return resolve(DiscussionSerializer::class)->getType(new Discussion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function query(): Builder
|
public function query(): Builder
|
||||||
|
|
@ -36,4 +38,33 @@ class DiscussionSeeder extends Seeder
|
||||||
return $callable($event->discussion);
|
return $callable($event->discussion);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Discussion $model
|
||||||
|
* @return Document
|
||||||
|
*/
|
||||||
|
public function toDocument(Model $model): Document
|
||||||
|
{
|
||||||
|
$document = new Document([
|
||||||
|
'type' => $this->type(),
|
||||||
|
'id' => $this->type() . ':' . $model->id,
|
||||||
|
'content' => $model->title,
|
||||||
|
'created_at' => $model->created_at?->toAtomString(),
|
||||||
|
'updated_at' => $model->last_posted_at?->toAtomString(),
|
||||||
|
'is_private' => $model->is_private,
|
||||||
|
'user_id' => $model->user_id,
|
||||||
|
'groups' => $this->groupsForDiscussion($model)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($this->extensionEnabled('fof-byobu')) {
|
||||||
|
$document['recipient_users'] = $model->recipientUsers->pluck('id')->toArray();
|
||||||
|
$document['recipient_groups'] = $model->recipientGroups->pluck('id')->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->extensionEnabled('flarum-sticky')) {
|
||||||
|
$document['is_sticky'] = $model->is_sticky;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $document;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,16 @@
|
||||||
|
|
||||||
namespace Blomstra\Search\Seeders;
|
namespace Blomstra\Search\Seeders;
|
||||||
|
|
||||||
|
use Blomstra\Search\Save\Document;
|
||||||
|
use Flarum\Discussion\Discussion;
|
||||||
|
use Flarum\Extension\ExtensionManager;
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Group\Permission;
|
||||||
|
use Flarum\Tags\Tag;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
abstract class Seeder
|
abstract class Seeder
|
||||||
{
|
{
|
||||||
|
|
@ -13,4 +21,53 @@ abstract class Seeder
|
||||||
|
|
||||||
abstract public static function savingOn(Dispatcher $events, callable $callable);
|
abstract public static function savingOn(Dispatcher $events, callable $callable);
|
||||||
abstract public static function deletingOn(Dispatcher $events, callable $callable);
|
abstract public static function deletingOn(Dispatcher $events, callable $callable);
|
||||||
|
|
||||||
|
abstract public function toDocument(Model $model): Document;
|
||||||
|
|
||||||
|
protected function groupsForDiscussion(Discussion $discussion): array
|
||||||
|
{
|
||||||
|
$permissions = collect();
|
||||||
|
|
||||||
|
$globalPermission = Permission::query()
|
||||||
|
->where('permission', 'viewForum')
|
||||||
|
->pluck('group_id');
|
||||||
|
|
||||||
|
if ($this->extensionEnabled('flarum-tags')) {
|
||||||
|
/** @var Collection $tags */
|
||||||
|
$tags = $discussion->tags;
|
||||||
|
|
||||||
|
$filters['tags'] = $tags->pluck('id')->toArray();
|
||||||
|
$tagPermissions = Permission::query()
|
||||||
|
->whereIn(
|
||||||
|
'permission',
|
||||||
|
$tags->pluck('id')->map(function (int $id) {
|
||||||
|
return "tag$id.viewForum";
|
||||||
|
})
|
||||||
|
)->get();
|
||||||
|
|
||||||
|
$permissions = $tags->map(function (Tag $tag) use ($tagPermissions) {
|
||||||
|
$permissions = $tagPermissions->where('permission', "tag$tag->id.viewForum");
|
||||||
|
|
||||||
|
if ($tag->is_restricted) {
|
||||||
|
$permissions = $permissions->add(['group_id' => Group::ADMINISTRATOR_ID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $permissions->pluck('group_id');
|
||||||
|
})->flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $discussion->is_private && $permissions->isEmpty()) {
|
||||||
|
$permissions = $globalPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $permissions->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extensionEnabled(string $extension): bool
|
||||||
|
{
|
||||||
|
/** @var ExtensionManager $manager */
|
||||||
|
$manager = resolve(ExtensionManager::class);
|
||||||
|
|
||||||
|
return $manager->isEnabled($extension);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue