diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index b65bca51e..bf8165afe 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -61,16 +61,24 @@ class SearchController extends Controller */ public function searchBook(Request $request, $bookId) { - if (!$request->has('term')) { - return redirect()->back(); - } - $searchTerm = $request->get('term'); - $searchWhereTerms = [['book_id', '=', $bookId]]; - $pages = $this->entityRepo->getBySearch('page', $searchTerm, $searchWhereTerms); - $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, $searchWhereTerms); - return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]); + $term = $request->get('term', ''); + $results = $this->searchService->searchBook($bookId, $term); + return view('partials/entity-list', ['entities' => $results]); } + /** + * Searches all entities within a chapter. + * @param Request $request + * @param integer $chapterId + * @return \Illuminate\View\View + * @internal param string $searchTerm + */ + public function searchChapter(Request $request, $chapterId) + { + $term = $request->get('term', ''); + $results = $this->searchService->searchChapter($chapterId, $term); + return view('partials/entity-list', ['entities' => $results]); + } /** * Search for a list of entities and return a partial HTML response of matching entities. @@ -80,19 +88,13 @@ class SearchController extends Controller */ public function searchEntitiesAjax(Request $request) { - $entities = collect(); $entityTypes = $request->has('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']); $searchTerm = ($request->has('term') && trim($request->get('term')) !== '') ? $request->get('term') : false; // Search for entities otherwise show most popular if ($searchTerm !== false) { - foreach (['page', 'chapter', 'book'] as $entityType) { - if ($entityTypes->contains($entityType)) { - // TODO - Update to new system - $entities = $entities->merge($this->entityRepo->getBySearch($entityType, $searchTerm)->items()); - } - } - $entities = $entities->sortByDesc('title_relevance'); + $searchTerm .= ' {type:'. implode('|', $entityTypes->toArray()) .'}'; + $entities = $this->searchService->searchEntities($searchTerm)['results']; } else { $entityNames = $entityTypes->map(function ($type) { return 'BookStack\\' . ucfirst($type); diff --git a/app/Repos/EntityRepo.php b/app/Repos/EntityRepo.php index b1b69814d..975929639 100644 --- a/app/Repos/EntityRepo.php +++ b/app/Repos/EntityRepo.php @@ -569,7 +569,7 @@ class EntityRepo $draftPage->save(); $this->savePageRevision($draftPage, trans('entities.pages_initial_revision')); - + $this->searchService->indexEntity($draftPage); return $draftPage; } diff --git a/app/Services/SearchService.php b/app/Services/SearchService.php index 7ecfb95c7..ec4889e50 100644 --- a/app/Services/SearchService.php +++ b/app/Services/SearchService.php @@ -8,6 +8,7 @@ use BookStack\SearchTerm; use Illuminate\Database\Connection; use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\JoinClause; +use Illuminate\Support\Collection; class SearchService { @@ -86,6 +87,35 @@ class SearchService ]; } + + /** + * Search a book for entities + * @param integer $bookId + * @param string $searchString + * @return Collection + */ + public function searchBook($bookId, $searchString) + { + $terms = $this->parseSearchString($searchString); + $results = collect(); + $pages = $this->buildEntitySearchQuery($terms, 'page')->where('book_id', '=', $bookId)->take(20)->get(); + $chapters = $this->buildEntitySearchQuery($terms, 'chapter')->where('book_id', '=', $bookId)->take(20)->get(); + return $results->merge($pages)->merge($chapters)->sortByDesc('score')->take(20); + } + + /** + * Search a book for entities + * @param integer $chapterId + * @param string $searchString + * @return Collection + */ + public function searchChapter($chapterId, $searchString) + { + $terms = $this->parseSearchString($searchString); + $pages = $this->buildEntitySearchQuery($terms, 'page')->where('chapter_id', '=', $chapterId)->take(20)->get(); + return $pages->sortByDesc('score'); + } + /** * Search across a particular entity type. * @param array $terms @@ -96,6 +126,21 @@ class SearchService * @return \Illuminate\Database\Eloquent\Collection|int|static[] */ public function searchEntityTable($terms, $entityType = 'page', $page = 1, $count = 20, $getCount = false) + { + $query = $this->buildEntitySearchQuery($terms, $entityType); + if ($getCount) return $query->count(); + + $query = $query->skip(($page-1) * $count)->take($count); + return $query->get(); + } + + /** + * Create a search query for an entity + * @param array $terms + * @param string $entityType + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function buildEntitySearchQuery($terms, $entityType = 'page') { $entity = $this->getEntity($entityType); $entitySelect = $entity->newQuery(); @@ -137,11 +182,7 @@ class SearchService if (method_exists($this, $functionName)) $this->$functionName($entitySelect, $entity, $filterValue); } - $query = $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, 'view'); - if ($getCount) return $query->count(); - - $query = $query->skip(($page-1) * $count)->take($count); - return $query->get(); + return $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, 'view'); } diff --git a/resources/assets/js/controllers.js b/resources/assets/js/controllers.js index c5baecf16..6a88aa811 100644 --- a/resources/assets/js/controllers.js +++ b/resources/assets/js/controllers.js @@ -259,39 +259,6 @@ module.exports = function (ngApp, events) { }]); - - ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', '$sce', function ($scope, $http, $attrs, $sce) { - $scope.searching = false; - $scope.searchTerm = ''; - $scope.searchResults = ''; - - $scope.searchBook = function (e) { - e.preventDefault(); - let term = $scope.searchTerm; - if (term.length == 0) return; - $scope.searching = true; - $scope.searchResults = ''; - let searchUrl = window.baseUrl('/search/book/' + $attrs.bookId); - searchUrl += '?term=' + encodeURIComponent(term); - $http.get(searchUrl).then((response) => { - $scope.searchResults = $sce.trustAsHtml(response.data); - }); - }; - - $scope.checkSearchForm = function () { - if ($scope.searchTerm.length < 1) { - $scope.searching = false; - } - }; - - $scope.clearSearch = function () { - $scope.searching = false; - $scope.searchTerm = ''; - }; - - }]); - - ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce', function ($scope, $http, $attrs, $interval, $timeout, $sce) { diff --git a/resources/assets/js/vues/entity-search.js b/resources/assets/js/vues/entity-search.js new file mode 100644 index 000000000..7266bf33d --- /dev/null +++ b/resources/assets/js/vues/entity-search.js @@ -0,0 +1,44 @@ +let data = { + id: null, + type: '', + searching: false, + searchTerm: '', + searchResults: '', +}; + +let computed = { + +}; + +let methods = { + + searchBook() { + if (this.searchTerm.trim().length === 0) return; + this.searching = true; + this.searchResults = ''; + let url = window.baseUrl(`/search/${this.type}/${this.id}`); + url += `?term=${encodeURIComponent(this.searchTerm)}`; + this.$http.get(url).then(resp => { + this.searchResults = resp.data; + }); + }, + + checkSearchForm() { + this.searching = this.searchTerm > 0; + }, + + clearSearch() { + this.searching = false; + this.searchTerm = ''; + } + +}; + +function mounted() { + this.id = Number(this.$el.getAttribute('entity-id')); + this.type = this.$el.getAttribute('entity-type'); +} + +module.exports = { + data, computed, methods, mounted +}; \ No newline at end of file diff --git a/resources/assets/js/vues/vues.js b/resources/assets/js/vues/vues.js index 832a5415d..8cc1dd656 100644 --- a/resources/assets/js/vues/vues.js +++ b/resources/assets/js/vues/vues.js @@ -5,7 +5,8 @@ function exists(id) { } let vueMapping = { - 'search-system': require('./search') + 'search-system': require('./search'), + 'entity-dashboard': require('./entity-search'), }; Object.keys(vueMapping).forEach(id => { diff --git a/resources/assets/sass/_lists.scss b/resources/assets/sass/_lists.scss index 6acc47468..051268926 100644 --- a/resources/assets/sass/_lists.scss +++ b/resources/assets/sass/_lists.scss @@ -109,6 +109,7 @@ transition-property: right, border; border-left: 0px solid #FFF; background-color: #FFF; + max-width: 320px; &.fixed { background-color: #FFF; z-index: 5; diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php index 66c2e8042..8644f7a4a 100644 --- a/resources/lang/en/entities.php +++ b/resources/lang/en/entities.php @@ -120,6 +120,7 @@ return [ 'chapters_empty' => 'No pages are currently in this chapter.', 'chapters_permissions_active' => 'Chapter Permissions Active', 'chapters_permissions_success' => 'Chapter Permissions Updated', + 'chapters_search_this' => 'Search this chapter', /** * Pages diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php index f5e08b2f6..adfec4525 100644 --- a/resources/views/books/show.blade.php +++ b/resources/views/books/show.blade.php @@ -50,15 +50,15 @@ -
+

{{$book->name}}

-
-

{{$book->description}}

+
+

{{$book->description}}

-
+

@if(count($bookChildren) > 0) @foreach($bookChildren as $childElement) @@ -81,12 +81,12 @@ @include('partials.entity-meta', ['entity' => $book])
-
-

{{ trans('entities.search_results') }} {{ trans('entities.search_clear') }}

-
+
+

{{ trans('entities.search_results') }} {{ trans('entities.search_clear') }}

+
@include('partials/loading-icon')
-
+
@@ -94,6 +94,7 @@
+ @if($book->restricted)

@if(userCan('restrictions-manage', $book)) @@ -103,14 +104,16 @@ @endif

@endif + -
+ +

{{ trans('entities.recent_activity') }}

@include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)])
diff --git a/resources/views/chapters/show.blade.php b/resources/views/chapters/show.blade.php index 28c34eef2..d4126cbcc 100644 --- a/resources/views/chapters/show.blade.php +++ b/resources/views/chapters/show.blade.php @@ -47,40 +47,50 @@
-
+
-
+

{{ $chapter->name }}

-

{{ $chapter->description }}

+
+

{{ $chapter->description }}

- @if(count($pages) > 0) -
-
- @foreach($pages as $page) - @include('pages/list-item', ['page' => $page]) + @if(count($pages) > 0) +

- @endforeach -
- @else -
-

{{ trans('entities.chapters_empty') }}

-

- @if(userCan('page-create', $chapter)) - {{ trans('entities.books_empty_create_page') }} - @endif - @if(userCan('page-create', $chapter) && userCan('book-update', $book)) -   -{{ trans('entities.books_empty_or') }}-    - @endif - @if(userCan('book-update', $book)) - {{ trans('entities.books_empty_sort_current_book') }} - @endif -

-
- @endif + @foreach($pages as $page) + @include('pages/list-item', ['page' => $page]) +
+ @endforeach +
+ @else +
+

{{ trans('entities.chapters_empty') }}

+

+ @if(userCan('page-create', $chapter)) + {{ trans('entities.books_empty_create_page') }} + @endif + @if(userCan('page-create', $chapter) && userCan('book-update', $book)) +   -{{ trans('entities.books_empty_or') }}-    + @endif + @if(userCan('book-update', $book)) + {{ trans('entities.books_empty_sort_current_book') }} + @endif +

+
+ @endif - @include('partials.entity-meta', ['entity' => $chapter]) + @include('partials.entity-meta', ['entity' => $chapter]) +
+ +
+

{{ trans('entities.search_results') }} {{ trans('entities.search_clear') }}

+
+ @include('partials/loading-icon') +
+
+
-
+
@if($book->restricted || $chapter->restricted)
@@ -105,7 +115,16 @@
@endif + + @include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree]) +
diff --git a/resources/views/pages/sidebar-tree-list.blade.php b/resources/views/pages/sidebar-tree-list.blade.php index f366e9e9b..faae6420a 100644 --- a/resources/views/pages/sidebar-tree-list.blade.php +++ b/resources/views/pages/sidebar-tree-list.blade.php @@ -3,13 +3,13 @@ @if(isset($page) && $page->tags->count() > 0)
-
Page Tags
+
{{ trans('entities.page_tags') }}
@foreach($page->tags as $tag) - - @if($tag->value) @endif + + @if($tag->value) @endif @endforeach diff --git a/routes/web.php b/routes/web.php index dad7a55e5..8ecfd9465 100644 --- a/routes/web.php +++ b/routes/web.php @@ -125,6 +125,7 @@ Route::group(['middleware' => 'auth'], function () { // Search Route::get('/search', 'SearchController@search'); Route::get('/search/book/{bookId}', 'SearchController@searchBook'); + Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter'); // Other Pages Route::get('/', 'HomeController@index');
value) colspan="2" @endif>{{ $tag->name }}{{$tag->value}}value) colspan="2" @endif>{{ $tag->name }}{{$tag->value}}