mirror of
https://github.com/flarum/core.git
synced 2025-08-13 20:04:24 +02:00
Compare commits
8 Commits
as/depreca
...
as/search-
Author | SHA1 | Date | |
---|---|---|---|
|
f2aa804753 | ||
|
baa5fdad95 | ||
|
0b2d01b75f | ||
|
84c4e8dd6e | ||
|
c4daeeb1f2 | ||
|
87d2f3d246 | ||
|
0172008693 | ||
|
76680d197d |
@@ -83,7 +83,7 @@ export default class Store {
|
||||
*/
|
||||
find(type, id, query = {}, options = {}) {
|
||||
let params = query;
|
||||
let url = app.forum.attribute('apiUrl') + '/' + type;
|
||||
let url = app.forum.attribute('apiUrl') + (query.search ? '/search/' : '/') + type;
|
||||
|
||||
if (id instanceof Array) {
|
||||
url += '?filter[id]=' + id.join(',');
|
||||
|
@@ -24,7 +24,7 @@ export default class DiscussionsSearchSource {
|
||||
include: 'mostRelevantPost',
|
||||
};
|
||||
|
||||
return app.store.find('discussions', params).then((results) => (this.results[query] = results));
|
||||
return app.store.find('discussions', params, { search: query }).then((results) => (this.results[query] = results));
|
||||
}
|
||||
|
||||
view(query) {
|
||||
|
@@ -16,10 +16,14 @@ export default class UsersSearchResults {
|
||||
|
||||
search(query) {
|
||||
return app.store
|
||||
.find('users', {
|
||||
filter: { q: query },
|
||||
page: { limit: 5 },
|
||||
})
|
||||
.find(
|
||||
'users',
|
||||
{
|
||||
filter: { q: query },
|
||||
page: { limit: 5 },
|
||||
},
|
||||
{ search: query }
|
||||
)
|
||||
.then((results) => {
|
||||
this.results[query] = results;
|
||||
m.redraw();
|
||||
|
@@ -119,7 +119,7 @@ export default class DiscussionListState {
|
||||
params.page = { offset };
|
||||
params.include = params.include.join(',');
|
||||
|
||||
return this.app.store.find('discussions', params);
|
||||
return this.app.store.find('discussions', params, { search: params.filter.q });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -11,10 +11,10 @@ namespace Flarum\Api\Controller;
|
||||
|
||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Discussion\DiscussionRepository;
|
||||
use Flarum\Discussion\Search\DiscussionSearcher;
|
||||
use Flarum\Filter\Filterer;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Search\SearchCriteria;
|
||||
use Illuminate\Support\Arr;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
@@ -49,9 +49,14 @@ class ListDiscussionsController extends AbstractListController
|
||||
public $sortFields = ['lastPostedAt', 'commentCount', 'createdAt'];
|
||||
|
||||
/**
|
||||
* @var DiscussionSearcher
|
||||
* @var DiscussionRepository
|
||||
*/
|
||||
protected $searcher;
|
||||
protected $discussions;
|
||||
|
||||
/**
|
||||
* @var Filterer
|
||||
*/
|
||||
protected $filterer;
|
||||
|
||||
/**
|
||||
* @var UrlGenerator
|
||||
@@ -62,9 +67,10 @@ class ListDiscussionsController extends AbstractListController
|
||||
* @param DiscussionSearcher $searcher
|
||||
* @param UrlGenerator $url
|
||||
*/
|
||||
public function __construct(DiscussionSearcher $searcher, UrlGenerator $url)
|
||||
public function __construct(DiscussionRepository $discussions, Filterer $filterer, UrlGenerator $url)
|
||||
{
|
||||
$this->searcher = $searcher;
|
||||
$this->discussions = $discussions;
|
||||
$this->filterer = $filterer;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
@@ -74,16 +80,16 @@ class ListDiscussionsController extends AbstractListController
|
||||
protected function data(ServerRequestInterface $request, Document $document)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
$query = Arr::get($this->extractFilter($request), 'q');
|
||||
$sort = $this->extractSort($request);
|
||||
|
||||
$criteria = new SearchCriteria($actor, $query, $sort);
|
||||
$filters = $this->extractFilter($request);
|
||||
$sort = $this->extractSort($request);
|
||||
$query = $this->discussions->query();
|
||||
|
||||
$limit = $this->extractLimit($request);
|
||||
$offset = $this->extractOffset($request);
|
||||
$load = array_merge($this->extractInclude($request), ['state']);
|
||||
|
||||
$results = $this->searcher->search($criteria, $limit, $offset);
|
||||
$results = $this->filterer->filter($actor, $query, $filters, $sort, $limit, $offset, $load);
|
||||
|
||||
$document->addPaginationLinks(
|
||||
$this->url->to('api')->route('discussions.index'),
|
||||
|
@@ -10,10 +10,9 @@
|
||||
namespace Flarum\Api\Controller;
|
||||
|
||||
use Flarum\Api\Serializer\UserSerializer;
|
||||
use Flarum\Filter\Filterer;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Search\SearchCriteria;
|
||||
use Flarum\User\Search\UserSearcher;
|
||||
use Illuminate\Support\Arr;
|
||||
use Flarum\User\UserRepository;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
@@ -41,9 +40,9 @@ class ListUsersController extends AbstractListController
|
||||
];
|
||||
|
||||
/**
|
||||
* @var UserSearcher
|
||||
* @var Filterer
|
||||
*/
|
||||
protected $searcher;
|
||||
protected $filterer;
|
||||
|
||||
/**
|
||||
* @var UrlGenerator
|
||||
@@ -51,13 +50,20 @@ class ListUsersController extends AbstractListController
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @param UserSearcher $searcher
|
||||
* @param UrlGenerator $url
|
||||
* @var UserRepository
|
||||
*/
|
||||
public function __construct(UserSearcher $searcher, UrlGenerator $url)
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* @param Filterer $filterer
|
||||
* @param UrlGenerator $url
|
||||
* @param UserRepository $users
|
||||
*/
|
||||
public function __construct(Filterer $filterer, UrlGenerator $url, UserRepository $users)
|
||||
{
|
||||
$this->searcher = $searcher;
|
||||
$this->filterer = $filterer;
|
||||
$this->url = $url;
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,16 +75,16 @@ class ListUsersController extends AbstractListController
|
||||
|
||||
$actor->assertCan('viewUserList');
|
||||
|
||||
$query = Arr::get($this->extractFilter($request), 'q');
|
||||
$sort = $this->extractSort($request);
|
||||
$query = $this->users->query();
|
||||
|
||||
$criteria = new SearchCriteria($actor, $query, $sort);
|
||||
$filters = $this->extractFilter($request);
|
||||
$sort = $this->extractSort($request);
|
||||
|
||||
$limit = $this->extractLimit($request);
|
||||
$offset = $this->extractOffset($request);
|
||||
$load = $this->extractInclude($request);
|
||||
|
||||
$results = $this->searcher->search($criteria, $limit, $offset, $load);
|
||||
$results = $this->filterer->filter($actor, $query, $filters, $sort, $limit, $offset, $load);
|
||||
|
||||
$document->addPaginationLinks(
|
||||
$this->url->to('api')->route('users.index'),
|
||||
|
112
src/Api/Controller/SearchDiscussionsController.php
Normal file
112
src/Api/Controller/SearchDiscussionsController.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?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\Discussion;
|
||||
use Flarum\Discussion\Search\DiscussionSearcher;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Search\SearchCriteria;
|
||||
use Illuminate\Support\Arr;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
class SearchDiscussionsController extends AbstractListController
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $serializer = DiscussionSerializer::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $include = [
|
||||
'user',
|
||||
'lastPostedUser',
|
||||
'mostRelevantPost',
|
||||
'mostRelevantPost.user'
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $optionalInclude = [
|
||||
'firstPost',
|
||||
'lastPost'
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $sortFields = ['lastPostedAt', 'commentCount', 'createdAt'];
|
||||
|
||||
/**
|
||||
* @var DiscussionSearcher
|
||||
*/
|
||||
protected $searcher;
|
||||
|
||||
/**
|
||||
* @var UrlGenerator
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @param DiscussionSearcher $searcher
|
||||
* @param UrlGenerator $url
|
||||
*/
|
||||
public function __construct(DiscussionSearcher $searcher, UrlGenerator $url)
|
||||
{
|
||||
$this->searcher = $searcher;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function data(ServerRequestInterface $request, Document $document)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
$query = Arr::get($this->extractFilter($request), 'q');
|
||||
$sort = $this->extractSort($request);
|
||||
|
||||
$criteria = new SearchCriteria($actor, $query, $sort);
|
||||
|
||||
$limit = $this->extractLimit($request);
|
||||
$offset = $this->extractOffset($request);
|
||||
$load = array_merge($this->extractInclude($request), ['state']);
|
||||
|
||||
$results = $this->searcher->search($criteria, $limit, $offset);
|
||||
|
||||
$document->addPaginationLinks(
|
||||
$this->url->to('api')->route('discussions.index'),
|
||||
$request->getQueryParams(),
|
||||
$offset,
|
||||
$limit,
|
||||
$results->areMoreResults() ? null : 0
|
||||
);
|
||||
|
||||
Discussion::setStateUser($actor);
|
||||
|
||||
$results = $results->getResults()->load($load);
|
||||
|
||||
if ($relations = array_intersect($load, ['firstPost', 'lastPost'])) {
|
||||
foreach ($results as $discussion) {
|
||||
foreach ($relations as $relation) {
|
||||
if ($discussion->$relation) {
|
||||
$discussion->$relation->discussion = $discussion;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
93
src/Api/Controller/SearchUsersController.php
Normal file
93
src/Api/Controller/SearchUsersController.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Controller;
|
||||
|
||||
use Flarum\Api\Serializer\UserSerializer;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Search\SearchCriteria;
|
||||
use Flarum\User\Search\UserSearcher;
|
||||
use Illuminate\Support\Arr;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
class SearchUsersController extends AbstractListController
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $serializer = UserSerializer::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $include = ['groups'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $sortFields = [
|
||||
'username',
|
||||
'commentCount',
|
||||
'discussionCount',
|
||||
'lastSeenAt',
|
||||
'joinedAt'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var UserSearcher
|
||||
*/
|
||||
protected $searcher;
|
||||
|
||||
/**
|
||||
* @var UrlGenerator
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @param UserSearcher $searcher
|
||||
* @param UrlGenerator $url
|
||||
*/
|
||||
public function __construct(UserSearcher $searcher, UrlGenerator $url)
|
||||
{
|
||||
$this->searcher = $searcher;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function data(ServerRequestInterface $request, Document $document)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
|
||||
$actor->assertCan('viewUserList');
|
||||
|
||||
$query = Arr::get($this->extractFilter($request), 'q');
|
||||
$sort = $this->extractSort($request);
|
||||
|
||||
$criteria = new SearchCriteria($actor, $query, $sort);
|
||||
|
||||
$limit = $this->extractLimit($request);
|
||||
$offset = $this->extractOffset($request);
|
||||
$load = $this->extractInclude($request);
|
||||
|
||||
$results = $this->searcher->search($criteria, $limit, $offset, $load);
|
||||
|
||||
$document->addPaginationLinks(
|
||||
$this->url->to('api')->route('users.index'),
|
||||
$request->getQueryParams(),
|
||||
$offset,
|
||||
$limit,
|
||||
$results->areMoreResults() ? null : 0
|
||||
);
|
||||
|
||||
return $results->getResults();
|
||||
}
|
||||
}
|
@@ -95,6 +95,13 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
||||
$route->toController(Controller\SendConfirmationEmailController::class)
|
||||
);
|
||||
|
||||
// List users
|
||||
$map->get(
|
||||
'/search/users',
|
||||
'users.search',
|
||||
$route->toController(Controller\SearchUsersController::class)
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Notifications
|
||||
@@ -163,6 +170,13 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
||||
$route->toController(Controller\DeleteDiscussionController::class)
|
||||
);
|
||||
|
||||
// Search discussions
|
||||
$map->get(
|
||||
'/search/discussions',
|
||||
'discussions.search',
|
||||
$route->toController(Controller\SearchDiscussionsController::class)
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Posts
|
||||
|
63
src/Extend/Filter.php
Normal file
63
src/Extend/Filter.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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\Extend;
|
||||
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Filter\Filterer;
|
||||
use Flarum\Foundation\ContainerUtil;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class Filter implements ExtenderInterface
|
||||
{
|
||||
private $resource;
|
||||
private $filters = [];
|
||||
private $filterMutators = [];
|
||||
|
||||
/**
|
||||
* @param string $resource: The ::class attribute of the resource this applies to, which is typically an Eloquent model.
|
||||
*/
|
||||
public function __construct($resource)
|
||||
{
|
||||
$this->resource = $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter to run when the resource is filtered.
|
||||
*
|
||||
* @param string $filterClass: The ::class attribute of the filter you are adding.
|
||||
*/
|
||||
public function addFilter(string $filterClass)
|
||||
{
|
||||
$this->filters[] = $filterClass;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback through which to run all filter queries after filters have been applied.
|
||||
*/
|
||||
public function addFilterMutator($callback)
|
||||
{
|
||||
$this->filterMutators[] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
foreach ($this->filters as $filter) {
|
||||
Filterer::addFilter($this->resource, $container->make($filter));
|
||||
}
|
||||
|
||||
foreach ($this->filterMutators as $mutator) {
|
||||
Filterer::addFilterMutator($this->resource, ContainerUtil::wrapCallback($mutator, $container));
|
||||
}
|
||||
}
|
||||
}
|
26
src/Filter/FilterInterface.php
Normal file
26
src/Filter/FilterInterface.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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\Filter;
|
||||
|
||||
interface FilterInterface
|
||||
{
|
||||
/**
|
||||
* This filter will only be run when a query contains a filter param with this key.
|
||||
*/
|
||||
public function getKey(): string;
|
||||
|
||||
/**
|
||||
* Filters a query.
|
||||
*
|
||||
* @param WrappedFilter $filter
|
||||
* @param string $value The value of the requested filter
|
||||
*/
|
||||
public function apply(WrappedFilter $wrappedFilter, $filterValue);
|
||||
}
|
88
src/Filter/Filterer.php
Normal file
88
src/Filter/Filterer.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?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\Filter;
|
||||
|
||||
use Flarum\Search\ApplySearchParametersTrait;
|
||||
use Flarum\Search\SearchResults;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Filterer
|
||||
{
|
||||
use ApplySearchParametersTrait;
|
||||
|
||||
protected static $filters = [];
|
||||
|
||||
protected static $filterMutators = [];
|
||||
|
||||
public static function addFilter($resource, FilterInterface $filter)
|
||||
{
|
||||
if (! array_key_exists($resource, static::$filters)) {
|
||||
static::$filters[$resource] = [];
|
||||
}
|
||||
|
||||
if (! array_key_exists($filter->getKey(), static::$filters[$resource])) {
|
||||
static::$filters[$resource][$filter->getKey()] = [];
|
||||
}
|
||||
|
||||
static::$filters[$resource][$filter->getKey()][] = $filter;
|
||||
}
|
||||
|
||||
public static function addFilterMutator($resource, $mutator)
|
||||
{
|
||||
if (! array_key_exists($resource, static::$filterMutators)) {
|
||||
static::$filterMutators[$resource] = [];
|
||||
}
|
||||
|
||||
static::$filterMutators[$resource][] = $mutator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterCriteria $criteria
|
||||
* @param int|null $limit
|
||||
* @param int $offset
|
||||
*
|
||||
* @return FilterResults
|
||||
*/
|
||||
public function filter($actor, $query, $filters, $sort = null, $limit = null, $offset = 0, array $load = [])
|
||||
{
|
||||
$resource = get_class($query->getModel());
|
||||
|
||||
$query->whereVisibleTo($actor);
|
||||
|
||||
$wrappedFilter = new WrappedFilter($query->getQuery(), $actor);
|
||||
|
||||
foreach ($filters as $filterKey => $filterValue) {
|
||||
foreach (Arr::get(static::$filters, "$resource.$filterKey", []) as $filter) {
|
||||
$filter->apply($wrappedFilter, $filterValue);
|
||||
}
|
||||
}
|
||||
|
||||
$this->applySort($wrappedFilter, $sort);
|
||||
$this->applyOffset($wrappedFilter, $offset);
|
||||
$this->applyLimit($wrappedFilter, $limit + 1);
|
||||
|
||||
foreach (Arr::get(static::$filterMutators, $resource, []) as $mutator) {
|
||||
$mutator($query, $actor, $filters, $sort);
|
||||
}
|
||||
|
||||
// Execute the filter query and retrieve the results. We get one more
|
||||
// results than the user asked for, so that we can say if there are more
|
||||
// results. If there are, we will get rid of that extra result.
|
||||
$results = $query->get();
|
||||
|
||||
if ($areMoreResults = $limit > 0 && $results->count() > $limit) {
|
||||
$results->pop();
|
||||
}
|
||||
|
||||
$results->load($load);
|
||||
|
||||
return new SearchResults($results, $areMoreResults);
|
||||
}
|
||||
}
|
16
src/Filter/WrappedFilter.php
Normal file
16
src/Filter/WrappedFilter.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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\Filter;
|
||||
|
||||
use Flarum\Search\AbstractSearch;
|
||||
|
||||
class WrappedFilter extends AbstractSearch
|
||||
{
|
||||
}
|
@@ -11,6 +11,7 @@ namespace Flarum\Forum\Content;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Controller\ListDiscussionsController;
|
||||
use Flarum\Api\Controller\SearchDiscussionsController;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
@@ -114,6 +115,6 @@ class Index
|
||||
*/
|
||||
private function getApiDocument(User $actor, array $params)
|
||||
{
|
||||
return json_decode($this->api->send(ListDiscussionsController::class, $actor, $params)->getBody());
|
||||
return json_decode($this->api->send(($params['filter']['q'] ? SearchDiscussionsController::class : ListDiscussionsController::class), $actor, $params)->getBody());
|
||||
}
|
||||
}
|
||||
|
57
src/Http/RequestUtil.php
Normal file
57
src/Http/RequestUtil.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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\Http;
|
||||
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Session\Session;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class RequestUtil
|
||||
{
|
||||
public static function getActor(Request $request): User
|
||||
{
|
||||
return $request->getAttribute('actor');
|
||||
}
|
||||
|
||||
public function withActor(Request $request, User $actor): Request
|
||||
{
|
||||
return $request->withAttribute('actor', $actor);
|
||||
}
|
||||
|
||||
public function getSession(Request $request): Session
|
||||
{
|
||||
return $request->getAttribute('session');
|
||||
}
|
||||
|
||||
public function withSession(Request $request, Session $session): Request
|
||||
{
|
||||
return $request->withAttribute('session', $session);
|
||||
}
|
||||
|
||||
public function getLocale(Request $request): string
|
||||
{
|
||||
return $request->getAttribute('bypassCsrfToken');
|
||||
}
|
||||
|
||||
public function withLocale(Request $request, string $locale): Request
|
||||
{
|
||||
return $request->withAttribute('locale', $locale);
|
||||
}
|
||||
|
||||
public function getRouteName(Request $request): string
|
||||
{
|
||||
return $request->getAttribute('routeName');
|
||||
}
|
||||
|
||||
public function withRouteName(Request $request, string $routeName): Request
|
||||
{
|
||||
return $request->withAttribute('routeName', $routeName);
|
||||
}
|
||||
}
|
@@ -56,111 +56,19 @@ class ListTest extends TestCase
|
||||
$this->assertEquals(1, count($data['data']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function can_search_for_author()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => 'author:normal foo'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
// /**
|
||||
// * @test
|
||||
// */
|
||||
// public function can_search_for_author()
|
||||
// {
|
||||
// $response = $this->send(
|
||||
// $this->request('GET', '/api/search/discussions')
|
||||
// ->withQueryParams([
|
||||
// 'filter' => ['q' => 'author:normal foo'],
|
||||
// 'include' => 'mostRelevantPost',
|
||||
// ])
|
||||
// );
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function can_search_for_word_in_post()
|
||||
{
|
||||
$this->database()->table('discussions')->insert([
|
||||
['id' => 2, 'title' => 'lightsail in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
['id' => 3, 'title' => 'not in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
]);
|
||||
|
||||
$this->database()->table('posts')->insert([
|
||||
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>not in text</p></t>'],
|
||||
['id' => 3, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>lightsail in text</p></t>'],
|
||||
]);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => 'lightsail'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$ids = array_map(function ($row) {
|
||||
return $row['id'];
|
||||
}, $data['data']);
|
||||
|
||||
// Order-independent comparison
|
||||
$this->assertEquals(['3'], $ids, 'IDs do not match', 0.0, 10, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function ignores_non_word_characters_when_searching()
|
||||
{
|
||||
$this->database()->table('discussions')->insert([
|
||||
['id' => 2, 'title' => 'lightsail in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
['id' => 3, 'title' => 'not in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
]);
|
||||
|
||||
$this->database()->table('posts')->insert([
|
||||
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>not in text</p></t>'],
|
||||
['id' => 3, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>lightsail in text</p></t>'],
|
||||
]);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => 'lightsail+'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$ids = array_map(function ($row) {
|
||||
return $row['id'];
|
||||
}, $data['data']);
|
||||
|
||||
// Order-independent comparison
|
||||
$this->assertEquals(['3'], $ids, 'IDs do not match', 0.0, 10, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function search_for_special_characters_gives_empty_result()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => '*'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$this->assertEquals([], $data['data']);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => '@'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$this->assertEquals([], $data['data']);
|
||||
}
|
||||
// $this->assertEquals(200, $response->getStatusCode());
|
||||
// }
|
||||
}
|
||||
|
166
tests/integration/api/discussions/SearchTest.php
Normal file
166
tests/integration/api/discussions/SearchTest.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Tests\integration\api\discussions;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
|
||||
class SearchTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->prepareDatabase([
|
||||
'discussions' => [
|
||||
['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 1, 'comment_count' => 1],
|
||||
],
|
||||
'posts' => [
|
||||
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>foo bar</p></t>'],
|
||||
],
|
||||
'users' => [
|
||||
$this->normalUser(),
|
||||
],
|
||||
'groups' => [
|
||||
$this->memberGroup(),
|
||||
$this->guestGroup(),
|
||||
],
|
||||
'group_permission' => [
|
||||
['permission' => 'viewDiscussions', 'group_id' => 2],
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shows_index_for_guest()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/discussions')
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertEquals(1, count($data['data']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function can_search_for_author()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => 'author:normal foo'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function can_search_for_word_in_post()
|
||||
{
|
||||
$this->database()->table('discussions')->insert([
|
||||
['id' => 2, 'title' => 'lightsail in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
['id' => 3, 'title' => 'not in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
]);
|
||||
|
||||
$this->database()->table('posts')->insert([
|
||||
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>not in text</p></t>'],
|
||||
['id' => 3, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>lightsail in text</p></t>'],
|
||||
]);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => 'lightsail'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$ids = array_map(function ($row) {
|
||||
return $row['id'];
|
||||
}, $data['data']);
|
||||
|
||||
// Order-independent comparison
|
||||
$this->assertEquals(['3'], $ids, 'IDs do not match', 0.0, 10, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function ignores_non_word_characters_when_searching()
|
||||
{
|
||||
$this->database()->table('discussions')->insert([
|
||||
['id' => 2, 'title' => 'lightsail in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
['id' => 3, 'title' => 'not in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'comment_count' => 1],
|
||||
]);
|
||||
|
||||
$this->database()->table('posts')->insert([
|
||||
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>not in text</p></t>'],
|
||||
['id' => 3, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>lightsail in text</p></t>'],
|
||||
]);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => 'lightsail+'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$ids = array_map(function ($row) {
|
||||
return $row['id'];
|
||||
}, $data['data']);
|
||||
|
||||
// Order-independent comparison
|
||||
$this->assertEquals(['3'], $ids, 'IDs do not match', 0.0, 10, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function search_for_special_characters_gives_empty_result()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => '*'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$this->assertEquals([], $data['data']);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/discussions')
|
||||
->withQueryParams([
|
||||
'filter' => ['q' => '@'],
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
$this->assertEquals([], $data['data']);
|
||||
}
|
||||
}
|
83
tests/integration/api/users/SearchTest.php
Normal file
83
tests/integration/api/users/SearchTest.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Tests\integration\api\users;
|
||||
|
||||
use Flarum\Group\Permission;
|
||||
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
|
||||
class SearchTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->prepareDatabase([
|
||||
'users' => [
|
||||
$this->adminUser(),
|
||||
],
|
||||
'groups' => [
|
||||
$this->adminGroup(),
|
||||
$this->guestGroup(),
|
||||
],
|
||||
'group_permission' => [],
|
||||
'group_user' => [
|
||||
['user_id' => 1, 'group_id' => 1],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function disallows_index_for_guest()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/users')
|
||||
);
|
||||
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shows_index_for_guest_when_they_have_permission()
|
||||
{
|
||||
Permission::unguarded(function () {
|
||||
Permission::create([
|
||||
'permission' => 'viewUserList',
|
||||
'group_id' => 2,
|
||||
]);
|
||||
});
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/users')
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shows_index_for_admin()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/search/users', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
135
tests/integration/extenders/FilterTest.php
Normal file
135
tests/integration/extenders/FilterTest.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Tests\integration\extenders;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Extend;
|
||||
use Flarum\Filter\FilterInterface;
|
||||
use Flarum\Filter\WrappedFilter;
|
||||
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
|
||||
class FilterTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
public function prepDb()
|
||||
{
|
||||
$this->prepareDatabase([
|
||||
'discussions' => [
|
||||
['id' => 1, 'title' => 'DISCUSSION 1', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 1, 'comment_count' => 1],
|
||||
['id' => 2, 'title' => 'DISCUSSION 2', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 2, 'comment_count' => 1],
|
||||
],
|
||||
'posts' => [
|
||||
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>foo bar</p></t>'],
|
||||
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>foo bar not the same</p></t>'],
|
||||
],
|
||||
'users' => [
|
||||
$this->adminUser(),
|
||||
$this->normalUser(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function filterDiscussions($filters, $limit = null)
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'filter' => $filters,
|
||||
'include' => 'mostRelevantPost',
|
||||
])
|
||||
);
|
||||
|
||||
return json_decode($response->getBody()->getContents(), true)['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function works_as_expected_with_no_modifications()
|
||||
{
|
||||
$this->prepDb();
|
||||
|
||||
$searchForAll = json_encode($this->filterDiscussions([], 5));
|
||||
$this->assertContains('DISCUSSION 1', $searchForAll);
|
||||
$this->assertContains('DISCUSSION 2', $searchForAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_filter_gambit_has_effect_if_added()
|
||||
{
|
||||
$this->extend((new Extend\Filter(Discussion::class))->addFilter(NoResultFilter::class));
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$withResultSearch = json_encode($this->filterDiscussions(['noResult' => 0], 5));
|
||||
$this->assertContains('DISCUSSION 1', $withResultSearch);
|
||||
$this->assertContains('DISCUSSION 2', $withResultSearch);
|
||||
$this->assertEquals([], $this->filterDiscussions(['noResult' => 1], 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function filter_mutator_has_effect_if_added()
|
||||
{
|
||||
$this->extend((new Extend\Filter(Discussion::class))->addFilterMutator(function ($query, $actor, $filters, $sort) {
|
||||
$query->getQuery()->whereRaw('1=0');
|
||||
}));
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$this->assertEquals([], $this->filterDiscussions([], 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function filter_mutator_has_effect_if_added_with_invokable_class()
|
||||
{
|
||||
$this->extend((new Extend\Filter(Discussion::class))->addFilterMutator(CustomFilterMutator::class));
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$this->assertEquals([], $this->filterDiscussions([], 5));
|
||||
}
|
||||
}
|
||||
|
||||
class NoResultFilter implements FilterInterface
|
||||
{
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'noResult';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply(WrappedFilter $wrappedFilter, $filterValue)
|
||||
{
|
||||
if ($filterValue) {
|
||||
$wrappedFilter->getQuery()
|
||||
->whereRaw('0=1');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CustomFilterMutator
|
||||
{
|
||||
public function __invoke($query, $actor, $filters, $sort)
|
||||
{
|
||||
$query->getQuery()->whereRaw('1=0');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user