mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-08-26 20:22:18 +00:00
Refactored search and slug repo components
This commit is contained in:
parent
7f9de2c8ab
commit
241278226f
@ -4,6 +4,8 @@
|
|||||||
class Entity extends Ownable
|
class Entity extends Ownable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
protected $fieldsToSearch = ['name', 'description'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares this entity to another given entity.
|
* Compares this entity to another given entity.
|
||||||
* Matches by comparing class and id.
|
* Matches by comparing class and id.
|
||||||
@ -157,7 +159,7 @@ class Entity extends Ownable
|
|||||||
* @param string[] array $wheres
|
* @param string[] array $wheres
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function fullTextSearchQuery($fieldsToSearch, $terms, $wheres = [])
|
public function fullTextSearchQuery($terms, $wheres = [])
|
||||||
{
|
{
|
||||||
$exactTerms = [];
|
$exactTerms = [];
|
||||||
$fuzzyTerms = [];
|
$fuzzyTerms = [];
|
||||||
@ -181,16 +183,16 @@ class Entity extends Ownable
|
|||||||
// Perform fulltext search if relevant terms exist.
|
// Perform fulltext search if relevant terms exist.
|
||||||
if ($isFuzzy) {
|
if ($isFuzzy) {
|
||||||
$termString = implode(' ', $fuzzyTerms);
|
$termString = implode(' ', $fuzzyTerms);
|
||||||
$fields = implode(',', $fieldsToSearch);
|
$fields = implode(',', $this->fieldsToSearch);
|
||||||
$search = $search->selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]);
|
$search = $search->selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]);
|
||||||
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
|
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure at least one exact term matches if in search
|
// Ensure at least one exact term matches if in search
|
||||||
if (count($exactTerms) > 0) {
|
if (count($exactTerms) > 0) {
|
||||||
$search = $search->where(function ($query) use ($exactTerms, $fieldsToSearch) {
|
$search = $search->where(function ($query) use ($exactTerms) {
|
||||||
foreach ($exactTerms as $exactTerm) {
|
foreach ($exactTerms as $exactTerm) {
|
||||||
foreach ($fieldsToSearch as $field) {
|
foreach ($this->fieldsToSearch as $field) {
|
||||||
$query->orWhere($field, 'like', $exactTerm);
|
$query->orWhere($field, 'like', $exactTerm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ class ChapterController extends Controller
|
|||||||
$chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
|
$chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
|
||||||
$this->checkOwnablePermission('chapter-update', $chapter);
|
$this->checkOwnablePermission('chapter-update', $chapter);
|
||||||
if ($chapter->name !== $request->get('name')) {
|
if ($chapter->name !== $request->get('name')) {
|
||||||
$chapter->slug = $this->chapterRepo->findSuitableSlug($request->get('name'), $chapter->book->id, $chapter->id);
|
$chapter->slug = $this->entityRepo->findSuitableSlug('chapter', $request->get('name'), $chapter->id, $chapter->book->id);
|
||||||
}
|
}
|
||||||
$chapter->fill($request->all());
|
$chapter->fill($request->all());
|
||||||
$chapter->updated_by = user()->id;
|
$chapter->updated_by = user()->id;
|
||||||
|
@ -26,6 +26,7 @@ class PageController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* PageController constructor.
|
* PageController constructor.
|
||||||
|
* @param EntityRepo $entityRepo
|
||||||
* @param PageRepo $pageRepo
|
* @param PageRepo $pageRepo
|
||||||
* @param BookRepo $bookRepo
|
* @param BookRepo $bookRepo
|
||||||
* @param ChapterRepo $chapterRepo
|
* @param ChapterRepo $chapterRepo
|
||||||
|
@ -1,30 +1,22 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Repos\EntityRepo;
|
||||||
use BookStack\Services\ViewService;
|
use BookStack\Services\ViewService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use BookStack\Repos\BookRepo;
|
|
||||||
use BookStack\Repos\ChapterRepo;
|
|
||||||
use BookStack\Repos\PageRepo;
|
|
||||||
|
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
{
|
{
|
||||||
protected $pageRepo;
|
protected $entityRepo;
|
||||||
protected $bookRepo;
|
|
||||||
protected $chapterRepo;
|
|
||||||
protected $viewService;
|
protected $viewService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SearchController constructor.
|
* SearchController constructor.
|
||||||
* @param PageRepo $pageRepo
|
* @param EntityRepo $entityRepo
|
||||||
* @param BookRepo $bookRepo
|
|
||||||
* @param ChapterRepo $chapterRepo
|
|
||||||
* @param ViewService $viewService
|
* @param ViewService $viewService
|
||||||
*/
|
*/
|
||||||
public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ViewService $viewService)
|
public function __construct(EntityRepo $entityRepo, ViewService $viewService)
|
||||||
{
|
{
|
||||||
$this->pageRepo = $pageRepo;
|
$this->entityRepo = $entityRepo;
|
||||||
$this->bookRepo = $bookRepo;
|
|
||||||
$this->chapterRepo = $chapterRepo;
|
|
||||||
$this->viewService = $viewService;
|
$this->viewService = $viewService;
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
@ -42,9 +34,9 @@ class SearchController extends Controller
|
|||||||
}
|
}
|
||||||
$searchTerm = $request->get('term');
|
$searchTerm = $request->get('term');
|
||||||
$paginationAppends = $request->only('term');
|
$paginationAppends = $request->only('term');
|
||||||
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
$pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
|
||||||
$books = $this->bookRepo->getBySearch($searchTerm, 10, $paginationAppends);
|
$books = $this->entityRepo->getBySearch('book', $searchTerm, [], 10, $paginationAppends);
|
||||||
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 10, $paginationAppends);
|
$chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 10, $paginationAppends);
|
||||||
$this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
|
$this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
|
||||||
return view('search/all', [
|
return view('search/all', [
|
||||||
'pages' => $pages,
|
'pages' => $pages,
|
||||||
@ -65,7 +57,7 @@ class SearchController extends Controller
|
|||||||
|
|
||||||
$searchTerm = $request->get('term');
|
$searchTerm = $request->get('term');
|
||||||
$paginationAppends = $request->only('term');
|
$paginationAppends = $request->only('term');
|
||||||
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
$pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
|
||||||
$this->setPageTitle(trans('entities.search_page_for_term', ['term' => $searchTerm]));
|
$this->setPageTitle(trans('entities.search_page_for_term', ['term' => $searchTerm]));
|
||||||
return view('search/entity-search-list', [
|
return view('search/entity-search-list', [
|
||||||
'entities' => $pages,
|
'entities' => $pages,
|
||||||
@ -85,7 +77,7 @@ class SearchController extends Controller
|
|||||||
|
|
||||||
$searchTerm = $request->get('term');
|
$searchTerm = $request->get('term');
|
||||||
$paginationAppends = $request->only('term');
|
$paginationAppends = $request->only('term');
|
||||||
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
$chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 20, $paginationAppends);
|
||||||
$this->setPageTitle(trans('entities.search_chapter_for_term', ['term' => $searchTerm]));
|
$this->setPageTitle(trans('entities.search_chapter_for_term', ['term' => $searchTerm]));
|
||||||
return view('search/entity-search-list', [
|
return view('search/entity-search-list', [
|
||||||
'entities' => $chapters,
|
'entities' => $chapters,
|
||||||
@ -105,7 +97,7 @@ class SearchController extends Controller
|
|||||||
|
|
||||||
$searchTerm = $request->get('term');
|
$searchTerm = $request->get('term');
|
||||||
$paginationAppends = $request->only('term');
|
$paginationAppends = $request->only('term');
|
||||||
$books = $this->bookRepo->getBySearch($searchTerm, 20, $paginationAppends);
|
$books = $this->entityRepo->getBySearch('book', $searchTerm, [], 20, $paginationAppends);
|
||||||
$this->setPageTitle(trans('entities.search_book_for_term', ['term' => $searchTerm]));
|
$this->setPageTitle(trans('entities.search_book_for_term', ['term' => $searchTerm]));
|
||||||
return view('search/entity-search-list', [
|
return view('search/entity-search-list', [
|
||||||
'entities' => $books,
|
'entities' => $books,
|
||||||
@ -128,8 +120,8 @@ class SearchController extends Controller
|
|||||||
}
|
}
|
||||||
$searchTerm = $request->get('term');
|
$searchTerm = $request->get('term');
|
||||||
$searchWhereTerms = [['book_id', '=', $bookId]];
|
$searchWhereTerms = [['book_id', '=', $bookId]];
|
||||||
$pages = $this->pageRepo->getBySearch($searchTerm, $searchWhereTerms);
|
$pages = $this->entityRepo->getBySearch('page', $searchTerm, $searchWhereTerms);
|
||||||
$chapters = $this->chapterRepo->getBySearch($searchTerm, $searchWhereTerms);
|
$chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, $searchWhereTerms);
|
||||||
return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
|
return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,9 +140,11 @@ class SearchController extends Controller
|
|||||||
|
|
||||||
// Search for entities otherwise show most popular
|
// Search for entities otherwise show most popular
|
||||||
if ($searchTerm !== false) {
|
if ($searchTerm !== false) {
|
||||||
if ($entityTypes->contains('page')) $entities = $entities->merge($this->pageRepo->getBySearch($searchTerm)->items());
|
foreach (['page', 'chapter', 'book'] as $entityType) {
|
||||||
if ($entityTypes->contains('chapter')) $entities = $entities->merge($this->chapterRepo->getBySearch($searchTerm)->items());
|
if ($entityTypes->contains($entityType)) {
|
||||||
if ($entityTypes->contains('book')) $entities = $entities->merge($this->bookRepo->getBySearch($searchTerm)->items());
|
$entities = $entities->merge($this->entityRepo->getBySearch($entityType, $searchTerm)->items());
|
||||||
|
}
|
||||||
|
}
|
||||||
$entities = $entities->sortByDesc('title_relevance');
|
$entities = $entities->sortByDesc('title_relevance');
|
||||||
} else {
|
} else {
|
||||||
$entityNames = $entityTypes->map(function ($type) {
|
$entityNames = $entityTypes->map(function ($type) {
|
||||||
|
@ -9,6 +9,8 @@ class Page extends Entity
|
|||||||
|
|
||||||
protected $with = ['book'];
|
protected $with = ['book'];
|
||||||
|
|
||||||
|
protected $fieldsToSearch = ['name', 'text'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this page into a simplified array.
|
* Converts this page into a simplified array.
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
@ -27,7 +27,7 @@ class BookRepo extends EntityRepo
|
|||||||
public function createFromInput($input)
|
public function createFromInput($input)
|
||||||
{
|
{
|
||||||
$book = $this->book->newInstance($input);
|
$book = $this->book->newInstance($input);
|
||||||
$book->slug = $this->findSuitableSlug($book->name);
|
$book->slug = $this->findSuitableSlug('book', $book->name);
|
||||||
$book->created_by = user()->id;
|
$book->created_by = user()->id;
|
||||||
$book->updated_by = user()->id;
|
$book->updated_by = user()->id;
|
||||||
$book->save();
|
$book->save();
|
||||||
@ -44,7 +44,7 @@ class BookRepo extends EntityRepo
|
|||||||
public function updateFromInput(Book $book, $input)
|
public function updateFromInput(Book $book, $input)
|
||||||
{
|
{
|
||||||
if ($book->name !== $input['name']) {
|
if ($book->name !== $input['name']) {
|
||||||
$book->slug = $this->findSuitableSlug($input['name'], $book->id);
|
$book->slug = $this->findSuitableSlug('book', $input['name'], $book->id);
|
||||||
}
|
}
|
||||||
$book->fill($input);
|
$book->fill($input);
|
||||||
$book->updated_by = user()->id;
|
$book->updated_by = user()->id;
|
||||||
@ -83,36 +83,6 @@ class BookRepo extends EntityRepo
|
|||||||
return $lastElem ? $lastElem->priority + 1 : 0;
|
return $lastElem ? $lastElem->priority + 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $slug
|
|
||||||
* @param bool|false $currentId
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function doesSlugExist($slug, $currentId = false)
|
|
||||||
{
|
|
||||||
$query = $this->book->where('slug', '=', $slug);
|
|
||||||
if ($currentId) {
|
|
||||||
$query = $query->where('id', '!=', $currentId);
|
|
||||||
}
|
|
||||||
return $query->count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a suitable slug for the given book name.
|
|
||||||
* Ensures the returned slug is unique in the system.
|
|
||||||
* @param string $name
|
|
||||||
* @param bool|false $currentId
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function findSuitableSlug($name, $currentId = false)
|
|
||||||
{
|
|
||||||
$slug = $this->nameToSlug($name);
|
|
||||||
while ($this->doesSlugExist($slug, $currentId)) {
|
|
||||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
|
||||||
}
|
|
||||||
return $slug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all child objects of a book.
|
* Get all child objects of a book.
|
||||||
* Returns a sorted collection of Pages and Chapters.
|
* Returns a sorted collection of Pages and Chapters.
|
||||||
@ -166,26 +136,4 @@ class BookRepo extends EntityRepo
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get books by search term.
|
|
||||||
* @param $term
|
|
||||||
* @param int $count
|
|
||||||
* @param array $paginationAppends
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getBySearch($term, $count = 20, $paginationAppends = [])
|
|
||||||
{
|
|
||||||
$terms = $this->prepareSearchTerms($term);
|
|
||||||
$bookQuery = $this->permissionService->enforceBookRestrictions($this->book->fullTextSearchQuery(['name', 'description'], $terms));
|
|
||||||
$bookQuery = $this->addAdvancedSearchQueries($bookQuery, $term);
|
|
||||||
$books = $bookQuery->paginate($count)->appends($paginationAppends);
|
|
||||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
|
||||||
foreach ($books as $book) {
|
|
||||||
//highlight
|
|
||||||
$result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $book->getExcerpt(100));
|
|
||||||
$book->searchSnippet = $result;
|
|
||||||
}
|
|
||||||
return $books;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -45,7 +45,7 @@ class ChapterRepo extends EntityRepo
|
|||||||
public function createFromInput($input, Book $book)
|
public function createFromInput($input, Book $book)
|
||||||
{
|
{
|
||||||
$chapter = $this->chapter->newInstance($input);
|
$chapter = $this->chapter->newInstance($input);
|
||||||
$chapter->slug = $this->findSuitableSlug($chapter->name, $book->id);
|
$chapter->slug = $this->findSuitableSlug('chapter', $chapter->name, false, $book->id);
|
||||||
$chapter->created_by = user()->id;
|
$chapter->created_by = user()->id;
|
||||||
$chapter->updated_by = user()->id;
|
$chapter->updated_by = user()->id;
|
||||||
$chapter = $book->chapters()->save($chapter);
|
$chapter = $book->chapters()->save($chapter);
|
||||||
@ -72,38 +72,6 @@ class ChapterRepo extends EntityRepo
|
|||||||
$chapter->delete();
|
$chapter->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a chapter's slug exists.
|
|
||||||
* @param $slug
|
|
||||||
* @param $bookId
|
|
||||||
* @param bool|false $currentId
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function doesSlugExist($slug, $bookId, $currentId = false)
|
|
||||||
{
|
|
||||||
$query = $this->chapter->where('slug', '=', $slug)->where('book_id', '=', $bookId);
|
|
||||||
if ($currentId) {
|
|
||||||
$query = $query->where('id', '!=', $currentId);
|
|
||||||
}
|
|
||||||
return $query->count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a suitable slug for the provided name.
|
|
||||||
* Checks database to prevent duplicate slugs.
|
|
||||||
* @param $name
|
|
||||||
* @param $bookId
|
|
||||||
* @param bool|false $currentId
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function findSuitableSlug($name, $bookId, $currentId = false)
|
|
||||||
{
|
|
||||||
$slug = $this->nameToSlug($name);
|
|
||||||
while ($this->doesSlugExist($slug, $bookId, $currentId)) {
|
|
||||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
|
||||||
}
|
|
||||||
return $slug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a new priority value for a new page to be added
|
* Get a new priority value for a new page to be added
|
||||||
@ -117,29 +85,6 @@ class ChapterRepo extends EntityRepo
|
|||||||
return $lastPage !== null ? $lastPage->priority + 1 : 0;
|
return $lastPage !== null ? $lastPage->priority + 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get chapters by the given search term.
|
|
||||||
* @param string $term
|
|
||||||
* @param array $whereTerms
|
|
||||||
* @param int $count
|
|
||||||
* @param array $paginationAppends
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
|
|
||||||
{
|
|
||||||
$terms = $this->prepareSearchTerms($term);
|
|
||||||
$chapterQuery = $this->permissionService->enforceChapterRestrictions($this->chapter->fullTextSearchQuery(['name', 'description'], $terms, $whereTerms));
|
|
||||||
$chapterQuery = $this->addAdvancedSearchQueries($chapterQuery, $term);
|
|
||||||
$chapters = $chapterQuery->paginate($count)->appends($paginationAppends);
|
|
||||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
|
||||||
foreach ($chapters as $chapter) {
|
|
||||||
//highlight
|
|
||||||
$result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $chapter->getExcerpt(100));
|
|
||||||
$chapter->searchSnippet = $result;
|
|
||||||
}
|
|
||||||
return $chapters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the book relation of this chapter.
|
* Changes the book relation of this chapter.
|
||||||
* @param $bookId
|
* @param $bookId
|
||||||
@ -155,7 +100,7 @@ class ChapterRepo extends EntityRepo
|
|||||||
$activity->book_id = $bookId;
|
$activity->book_id = $bookId;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
$chapter->slug = $this->findSuitableSlug($chapter->name, $bookId, $chapter->id);
|
$chapter->slug = $this->findSuitableSlug('chapter', $chapter->name, $chapter->id, $bookId);
|
||||||
$chapter->save();
|
$chapter->save();
|
||||||
// Update all child pages
|
// Update all child pages
|
||||||
foreach ($chapter->pages as $page) {
|
foreach ($chapter->pages as $page) {
|
||||||
|
@ -238,6 +238,83 @@ class EntityRepo
|
|||||||
->skip($count * $page)->take($count)->get();
|
->skip($count * $page)->take($count)->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getBySearch($type, $term, $whereTerms = [], $count = 20, $paginationAppends = [])
|
||||||
|
{
|
||||||
|
$terms = $this->prepareSearchTerms($term);
|
||||||
|
$q = $this->permissionService->enforceChapterRestrictions($this->getEntity($type)->fullTextSearchQuery($terms, $whereTerms));
|
||||||
|
$q = $this->addAdvancedSearchQueries($q, $term);
|
||||||
|
$entities = $q->paginate($count)->appends($paginationAppends);
|
||||||
|
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
||||||
|
|
||||||
|
// Highlight page content
|
||||||
|
if ($type === 'page') {
|
||||||
|
//lookahead/behind assertions ensures cut between words
|
||||||
|
$s = '\s\x00-/:-@\[-`{-~'; //character set for start/end of words
|
||||||
|
|
||||||
|
foreach ($entities as $page) {
|
||||||
|
preg_match_all('#(?<=[' . $s . ']).{1,30}((' . $words . ').{1,30})+(?=[' . $s . '])#uis', $page->text, $matches, PREG_SET_ORDER);
|
||||||
|
//delimiter between occurrences
|
||||||
|
$results = [];
|
||||||
|
foreach ($matches as $line) {
|
||||||
|
$results[] = htmlspecialchars($line[0], 0, 'UTF-8');
|
||||||
|
}
|
||||||
|
$matchLimit = 6;
|
||||||
|
if (count($results) > $matchLimit) $results = array_slice($results, 0, $matchLimit);
|
||||||
|
$result = join('... ', $results);
|
||||||
|
|
||||||
|
//highlight
|
||||||
|
$result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $result);
|
||||||
|
if (strlen($result) < 5) $result = $page->getExcerpt(80);
|
||||||
|
|
||||||
|
$page->searchSnippet = $result;
|
||||||
|
}
|
||||||
|
return $entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight chapter/book content
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
//highlight
|
||||||
|
$result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $entity->getExcerpt(100));
|
||||||
|
$entity->searchSnippet = $result;
|
||||||
|
}
|
||||||
|
return $entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a suitable slug for an entity.
|
||||||
|
* @param string $type
|
||||||
|
* @param string $name
|
||||||
|
* @param bool|integer $currentId
|
||||||
|
* @param bool|integer $bookId Only pass if type is not a book
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function findSuitableSlug($type, $name, $currentId = false, $bookId = false)
|
||||||
|
{
|
||||||
|
$slug = $this->nameToSlug($name);
|
||||||
|
while ($this->slugExists($type, $slug, $currentId, $bookId)) {
|
||||||
|
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
||||||
|
}
|
||||||
|
return $slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a slug already exists in the database.
|
||||||
|
* @param string $type
|
||||||
|
* @param string $slug
|
||||||
|
* @param bool|integer $currentId
|
||||||
|
* @param bool|integer $bookId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function slugExists($type, $slug, $currentId = false, $bookId = false)
|
||||||
|
{
|
||||||
|
$query = $this->getEntity($type)->where('slug', '=', $slug);
|
||||||
|
if (strtolower($type) === 'page' || strtolower($type) === 'chapter') {
|
||||||
|
$query = $query->where('book_id', '=', $bookId);
|
||||||
|
}
|
||||||
|
if ($currentId) $query = $query->where('id', '!=', $currentId);
|
||||||
|
return $query->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates entity restrictions from a request
|
* Updates entity restrictions from a request
|
||||||
* @param $request
|
* @param $request
|
||||||
|
@ -65,17 +65,6 @@ class PageRepo extends EntityRepo
|
|||||||
return $revision !== null ? $revision->page : null;
|
return $revision !== null ? $revision->page : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a new Page instance from the given input.
|
|
||||||
* @param $input
|
|
||||||
* @return Page
|
|
||||||
*/
|
|
||||||
public function newFromInput($input)
|
|
||||||
{
|
|
||||||
$page = $this->page->fill($input);
|
|
||||||
return $page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the pages with a particular slug within a book.
|
* Count the pages with a particular slug within a book.
|
||||||
* @param $slug
|
* @param $slug
|
||||||
@ -103,7 +92,7 @@ class PageRepo extends EntityRepo
|
|||||||
$this->tagRepo->saveTagsToEntity($draftPage, $input['tags']);
|
$this->tagRepo->saveTagsToEntity($draftPage, $input['tags']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$draftPage->slug = $this->findSuitableSlug($draftPage->name, $draftPage->book->id);
|
$draftPage->slug = $this->findSuitableSlug('page', $draftPage->name, false, $draftPage->book->id);
|
||||||
$draftPage->html = $this->formatHtml($input['html']);
|
$draftPage->html = $this->formatHtml($input['html']);
|
||||||
$draftPage->text = strip_tags($draftPage->html);
|
$draftPage->text = strip_tags($draftPage->html);
|
||||||
$draftPage->draft = false;
|
$draftPage->draft = false;
|
||||||
@ -222,50 +211,6 @@ class PageRepo extends EntityRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets pages by a search term.
|
|
||||||
* Highlights page content for showing in results.
|
|
||||||
* @param string $term
|
|
||||||
* @param array $whereTerms
|
|
||||||
* @param int $count
|
|
||||||
* @param array $paginationAppends
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
|
|
||||||
{
|
|
||||||
$terms = $this->prepareSearchTerms($term);
|
|
||||||
$pageQuery = $this->permissionService->enforcePageRestrictions($this->page->fullTextSearchQuery(['name', 'text'], $terms, $whereTerms));
|
|
||||||
$pageQuery = $this->addAdvancedSearchQueries($pageQuery, $term);
|
|
||||||
$pages = $pageQuery->paginate($count)->appends($paginationAppends);
|
|
||||||
|
|
||||||
// Add highlights to page text.
|
|
||||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
|
||||||
//lookahead/behind assertions ensures cut between words
|
|
||||||
$s = '\s\x00-/:-@\[-`{-~'; //character set for start/end of words
|
|
||||||
|
|
||||||
foreach ($pages as $page) {
|
|
||||||
preg_match_all('#(?<=[' . $s . ']).{1,30}((' . $words . ').{1,30})+(?=[' . $s . '])#uis', $page->text, $matches, PREG_SET_ORDER);
|
|
||||||
//delimiter between occurrences
|
|
||||||
$results = [];
|
|
||||||
foreach ($matches as $line) {
|
|
||||||
$results[] = htmlspecialchars($line[0], 0, 'UTF-8');
|
|
||||||
}
|
|
||||||
$matchLimit = 6;
|
|
||||||
if (count($results) > $matchLimit) {
|
|
||||||
$results = array_slice($results, 0, $matchLimit);
|
|
||||||
}
|
|
||||||
$result = join('... ', $results);
|
|
||||||
|
|
||||||
//highlight
|
|
||||||
$result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $result);
|
|
||||||
if (strlen($result) < 5) {
|
|
||||||
$result = $page->getExcerpt(80);
|
|
||||||
}
|
|
||||||
$page->searchSnippet = $result;
|
|
||||||
}
|
|
||||||
return $pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for image usage.
|
* Search for image usage.
|
||||||
* @param $imageString
|
* @param $imageString
|
||||||
@ -297,7 +242,7 @@ class PageRepo extends EntityRepo
|
|||||||
|
|
||||||
// Prevent slug being updated if no name change
|
// Prevent slug being updated if no name change
|
||||||
if ($page->name !== $input['name']) {
|
if ($page->name !== $input['name']) {
|
||||||
$page->slug = $this->findSuitableSlug($input['name'], $book_id, $page->id);
|
$page->slug = $this->findSuitableSlug('page', $input['name'], $page->id, $book_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save page tags if present
|
// Save page tags if present
|
||||||
@ -337,7 +282,7 @@ class PageRepo extends EntityRepo
|
|||||||
$this->saveRevision($page);
|
$this->saveRevision($page);
|
||||||
$revision = $this->getRevisionById($revisionId);
|
$revision = $this->getRevisionById($revisionId);
|
||||||
$page->fill($revision->toArray());
|
$page->fill($revision->toArray());
|
||||||
$page->slug = $this->findSuitableSlug($page->name, $book->id, $page->id);
|
$page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $book->id);
|
||||||
$page->text = strip_tags($page->html);
|
$page->text = strip_tags($page->html);
|
||||||
$page->updated_by = user()->id;
|
$page->updated_by = user()->id;
|
||||||
$page->save();
|
$page->save();
|
||||||
@ -529,20 +474,6 @@ class PageRepo extends EntityRepo
|
|||||||
return $this->pageRevision->findOrFail($id);
|
return $this->pageRevision->findOrFail($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a slug exists within a book already.
|
|
||||||
* @param $slug
|
|
||||||
* @param $bookId
|
|
||||||
* @param bool|false $currentId
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function doesSlugExist($slug, $bookId, $currentId = false)
|
|
||||||
{
|
|
||||||
$query = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId);
|
|
||||||
if ($currentId) $query = $query->where('id', '!=', $currentId);
|
|
||||||
return $query->count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the related book for the specified page.
|
* Changes the related book for the specified page.
|
||||||
* Changes the book id of any relations to the page that store the book id.
|
* Changes the book id of any relations to the page that store the book id.
|
||||||
@ -557,7 +488,7 @@ class PageRepo extends EntityRepo
|
|||||||
$activity->book_id = $bookId;
|
$activity->book_id = $bookId;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
$page->slug = $this->findSuitableSlug($page->name, $bookId, $page->id);
|
$page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $bookId);
|
||||||
$page->save();
|
$page->save();
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
@ -578,22 +509,6 @@ class PageRepo extends EntityRepo
|
|||||||
$this->permissionService->buildJointPermissionsForEntity($book);
|
$this->permissionService->buildJointPermissionsForEntity($book);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a suitable slug for the resource
|
|
||||||
* @param string $name
|
|
||||||
* @param int $bookId
|
|
||||||
* @param bool|false $currentId
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function findSuitableSlug($name, $bookId, $currentId = false)
|
|
||||||
{
|
|
||||||
$slug = $this->nameToSlug($name);
|
|
||||||
while ($this->doesSlugExist($slug, $bookId, $currentId)) {
|
|
||||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
|
||||||
}
|
|
||||||
return $slug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a given page along with its dependencies.
|
* Destroy a given page along with its dependencies.
|
||||||
* @param $page
|
* @param $page
|
||||||
|
Loading…
Reference in New Issue
Block a user