mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-01-13 05:42:30 +03:00
Sets some reasonable limits, which are higher when logged in since that infers a little extra trust. Helps prevent against large resource consuption attacks via super heavy search queries. Thanks to Gabriel Rodrigues AKA TEXUGO for reporting.
147 lines
5.1 KiB
PHP
147 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace BookStack\Search;
|
|
|
|
use BookStack\Entities\Queries\PageQueries;
|
|
use BookStack\Entities\Queries\QueryPopular;
|
|
use BookStack\Entities\Tools\SiblingFetcher;
|
|
use BookStack\Http\Controller;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
|
|
class SearchController extends Controller
|
|
{
|
|
public function __construct(
|
|
protected SearchRunner $searchRunner,
|
|
protected PageQueries $pageQueries,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Searches all entities.
|
|
*/
|
|
public function search(Request $request, SearchResultsFormatter $formatter)
|
|
{
|
|
$searchOpts = SearchOptions::fromRequest($request);
|
|
$fullSearchString = $searchOpts->toString();
|
|
$page = intval($request->get('page', '0')) ?: 1;
|
|
$count = setting()->getInteger('lists-page-count-search', 18, 1, 1000);
|
|
|
|
$results = $this->searchRunner->searchEntities($searchOpts, 'all', $page, $count);
|
|
$formatter->format($results['results']->all(), $searchOpts);
|
|
$paginator = new LengthAwarePaginator($results['results'], $results['total'], $count, $page);
|
|
$paginator->setPath(url('/search'));
|
|
$paginator->appends($request->except('page'));
|
|
|
|
$this->setPageTitle(trans('entities.search_for_term', ['term' => $fullSearchString]));
|
|
|
|
return view('search.all', [
|
|
'entities' => $results['results'],
|
|
'totalResults' => $results['total'],
|
|
'paginator' => $paginator,
|
|
'searchTerm' => $fullSearchString,
|
|
'options' => $searchOpts,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Searches all entities within a book.
|
|
*/
|
|
public function searchBook(Request $request, int $bookId)
|
|
{
|
|
$term = $request->get('term', '');
|
|
$results = $this->searchRunner->searchBook($bookId, $term);
|
|
|
|
return view('entities.list', ['entities' => $results]);
|
|
}
|
|
|
|
/**
|
|
* Searches all entities within a chapter.
|
|
*/
|
|
public function searchChapter(Request $request, int $chapterId)
|
|
{
|
|
$term = $request->get('term', '');
|
|
$results = $this->searchRunner->searchChapter($chapterId, $term);
|
|
|
|
return view('entities.list', ['entities' => $results]);
|
|
}
|
|
|
|
/**
|
|
* Search for a list of entities and return a partial HTML response of matching entities.
|
|
* Returns the most popular entities if no search is provided.
|
|
*/
|
|
public function searchForSelector(Request $request, QueryPopular $queryPopular)
|
|
{
|
|
$entityTypes = $request->filled('types') ? explode(',', $request->get('types')) : ['page', 'chapter', 'book'];
|
|
$searchTerm = $request->get('term', false);
|
|
$permission = $request->get('permission', 'view');
|
|
|
|
// Search for entities otherwise show most popular
|
|
if ($searchTerm !== false) {
|
|
$options = SearchOptions::fromString($searchTerm);
|
|
$options->setFilter('type', implode('|', $entityTypes));
|
|
$entities = $this->searchRunner->searchEntities($options, 'all', 1, 20)['results'];
|
|
} else {
|
|
$entities = $queryPopular->run(20, 0, $entityTypes);
|
|
}
|
|
|
|
return view('search.parts.entity-selector-list', ['entities' => $entities, 'permission' => $permission]);
|
|
}
|
|
|
|
/**
|
|
* Search for a list of templates to choose from.
|
|
*/
|
|
public function templatesForSelector(Request $request)
|
|
{
|
|
$searchTerm = $request->get('term', false);
|
|
|
|
if ($searchTerm !== false) {
|
|
$searchOptions = SearchOptions::fromString($searchTerm);
|
|
$searchOptions->setFilter('is_template');
|
|
$entities = $this->searchRunner->searchEntities($searchOptions, 'page', 1, 20)['results'];
|
|
} else {
|
|
$entities = $this->pageQueries->visibleTemplates()
|
|
->where('draft', '=', false)
|
|
->orderBy('updated_at', 'desc')
|
|
->take(20)
|
|
->get();
|
|
}
|
|
|
|
return view('search.parts.entity-selector-list', [
|
|
'entities' => $entities,
|
|
'permission' => 'view'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Search for a list of entities and return a partial HTML response of matching entities
|
|
* to be used as a result preview suggestion list for global system searches.
|
|
*/
|
|
public function searchSuggestions(Request $request)
|
|
{
|
|
$searchTerm = $request->get('term', '');
|
|
$entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 5)['results'];
|
|
|
|
foreach ($entities as $entity) {
|
|
$entity->setAttribute('preview_content', '');
|
|
}
|
|
|
|
return view('search.parts.entity-suggestion-list', [
|
|
'entities' => $entities->slice(0, 5)
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Search sibling items in the system.
|
|
*/
|
|
public function searchSiblings(Request $request, SiblingFetcher $siblingFetcher)
|
|
{
|
|
$type = $request->get('entity_type', null);
|
|
$id = $request->get('entity_id', null);
|
|
|
|
$entities = $siblingFetcher->fetch($type, $id);
|
|
|
|
return view('entities.list-basic', ['entities' => $entities, 'style' => 'compact']);
|
|
}
|
|
}
|