From 0e0945ef8465adaf1a195d729c4bdc9e8c37de60 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sat, 15 Apr 2017 15:04:30 +0100 Subject: [PATCH] Finished off UI for search system --- app/Http/Controllers/SearchController.php | 14 +- app/Services/SearchService.php | 30 ++-- resources/assets/js/vues/search.js | 35 ++++- resources/assets/sass/_animations.scss | 2 +- resources/assets/sass/_forms.scss | 19 ++- resources/assets/sass/styles.scss | 13 +- resources/lang/de/entities.php | 9 -- resources/lang/en/common.php | 1 + resources/lang/en/entities.php | 26 ++-- resources/lang/es/entities.php | 9 -- resources/lang/fr/entities.php | 9 -- resources/lang/nl/entities.php | 9 -- resources/lang/pt_BR/entities.php | 9 -- resources/views/search/all.blade.php | 160 ++++++++++++++++------ 14 files changed, 225 insertions(+), 120 deletions(-) diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 6d29ab17b..b65bca51e 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -34,14 +34,20 @@ class SearchController extends Controller public function search(Request $request) { $searchTerm = $request->get('term'); -// $paginationAppends = $request->only('term'); TODO - Check pagination $this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm])); - $entities = $this->searchService->searchEntities($searchTerm); + $page = $request->has('page') && is_int(intval($request->get('page'))) ? intval($request->get('page')) : 1; + $nextPageLink = baseUrl('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1)); + + $results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20); + $hasNextPage = $this->searchService->searchEntities($searchTerm, 'all', $page+1, 20)['count'] > 0; return view('search/all', [ - 'entities' => $entities, - 'searchTerm' => $searchTerm + 'entities' => $results['results'], + 'totalResults' => $results['total'], + 'searchTerm' => $searchTerm, + 'hasNextPage' => $hasNextPage, + 'nextPageLink' => $nextPageLink ]); } diff --git a/app/Services/SearchService.php b/app/Services/SearchService.php index a2844c593..7ecfb95c7 100644 --- a/app/Services/SearchService.php +++ b/app/Services/SearchService.php @@ -8,7 +8,6 @@ use BookStack\SearchTerm; use Illuminate\Database\Connection; use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\JoinClause; -use Illuminate\Support\Collection; class SearchService { @@ -56,9 +55,9 @@ class SearchService * @param string $entityType * @param int $page * @param int $count - * @return Collection + * @return array[int, Collection]; */ - public function searchEntities($searchString, $entityType = 'all', $page = 0, $count = 20) + public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20) { $terms = $this->parseSearchString($searchString); $entityTypes = array_keys($this->entities); @@ -71,14 +70,20 @@ class SearchService $entityTypesToSearch = explode('|', $terms['filters']['type']); } - // TODO - Check drafts don't show up in results + $total = 0; + foreach ($entityTypesToSearch as $entityType) { if (!in_array($entityType, $entityTypes)) continue; $search = $this->searchEntityTable($terms, $entityType, $page, $count); + $total += $this->searchEntityTable($terms, $entityType, $page, $count, true); $results = $results->merge($search); } - return $results->sortByDesc('score'); + return [ + 'total' => $total, + 'count' => count($results), + 'results' => $results->sortByDesc('score') + ]; } /** @@ -87,9 +92,10 @@ class SearchService * @param string $entityType * @param int $page * @param int $count - * @return \Illuminate\Database\Eloquent\Collection|static[] + * @param bool $getCount Return the total count of the search + * @return \Illuminate\Database\Eloquent\Collection|int|static[] */ - public function searchEntityTable($terms, $entityType = 'page', $page = 0, $count = 20) + public function searchEntityTable($terms, $entityType = 'page', $page = 1, $count = 20, $getCount = false) { $entity = $this->getEntity($entityType); $entitySelect = $entity->newQuery(); @@ -131,8 +137,10 @@ class SearchService if (method_exists($this, $functionName)) $this->$functionName($entitySelect, $entity, $filterValue); } - $entitySelect->skip($page * $count)->take($count); $query = $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, 'view'); + if ($getCount) return $query->count(); + + $query = $query->skip(($page-1) * $count)->take($count); return $query->get(); } @@ -371,13 +379,15 @@ class SearchService protected function filterCreatedBy(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) { - if (!is_numeric($input)) return; + if (!is_numeric($input) && $input !== 'me') return; + if ($input === 'me') $input = user()->id; $query->where('created_by', '=', $input); } protected function filterUpdatedBy(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input) { - if (!is_numeric($input)) return; + if (!is_numeric($input) && $input !== 'me') return; + if ($input === 'me') $input = user()->id; $query->where('updated_by', '=', $input); } diff --git a/resources/assets/js/vues/search.js b/resources/assets/js/vues/search.js index 710f4b214..515ca3bc9 100644 --- a/resources/assets/js/vues/search.js +++ b/resources/assets/js/vues/search.js @@ -12,7 +12,12 @@ let data = { exactTerms: [], tagTerms: [], option: {}, - dates: {} + dates: { + updated_after: false, + updated_before: false, + created_after: false, + created_before: false, + } } }; @@ -126,7 +131,7 @@ let methods = { }, optionParse(searchString) { - let optionFilter = /{([a-z_-]+?)}/gi; + let optionFilter = /{([a-z_\-:]+?)}/gi; let matches; while ((matches = optionFilter.exec(searchString)) !== null) { this.search.option[matches[1].toLowerCase()] = true; @@ -148,7 +153,30 @@ let methods = { }, enableDate(optionName) { - this.search.dates[optionName] = moment().format('YYYY-MM-DD'); + this.search.dates[optionName.toLowerCase()] = moment().format('YYYY-MM-DD'); + this.dateChange(optionName); + }, + + dateParse(searchString) { + let dateFilter = /{([a-z_\-]+?):([a-z_\-0-9]+?)}/gi; + let dateTags = Object.keys(this.search.dates); + let matches; + while ((matches = dateFilter.exec(searchString)) !== null) { + if (dateTags.indexOf(matches[1]) === -1) continue; + this.search.dates[matches[1].toLowerCase()] = matches[2]; + } + }, + + dateChange(optionName) { + let dateFilter = new RegExp('{\\s?'+optionName+'\\s?:([a-z_\\-0-9]+?)}', 'gi'); + this.termString = this.termString.replace(dateFilter, ''); + if (!this.search.dates[optionName]) return; + this.appendTerm(`{${optionName}:${this.search.dates[optionName]}}`); + }, + + dateRemove(optionName) { + this.search.dates[optionName] = false; + this.dateChange(optionName); } }; @@ -159,6 +187,7 @@ function created() { this.exactParse(this.termString); this.tagParse(this.termString); this.optionParse(this.termString); + this.dateParse(this.termString); } module.exports = { diff --git a/resources/assets/sass/_animations.scss b/resources/assets/sass/_animations.scss index 582d718c8..afcf01cff 100644 --- a/resources/assets/sass/_animations.scss +++ b/resources/assets/sass/_animations.scss @@ -2,7 +2,7 @@ .anim.fadeIn { opacity: 0; animation-name: fadeIn; - animation-duration: 160ms; + animation-duration: 180ms; animation-timing-function: ease-in-out; animation-fill-mode: forwards; } diff --git a/resources/assets/sass/_forms.scss b/resources/assets/sass/_forms.scss index 7e6b800d2..1fc812896 100644 --- a/resources/assets/sass/_forms.scss +++ b/resources/assets/sass/_forms.scss @@ -98,19 +98,36 @@ label { label.radio, label.checkbox { font-weight: 400; + user-select: none; input[type="radio"], input[type="checkbox"] { margin-right: $-xs; } } +label.inline.checkbox { + margin-right: $-m; +} + label + p.small { margin-bottom: 0.8em; } -input[type="text"], input[type="number"], input[type="email"], input[type="search"], input[type="url"], input[type="password"], select, textarea { +table.form-table { + max-width: 100%; + td { + overflow: hidden; + padding: $-xxs/2 0; + } +} + +input[type="text"], input[type="number"], input[type="email"], input[type="date"], input[type="search"], input[type="url"], input[type="password"], select, textarea { @extend .input-base; } +input[type=date] { + width: 190px; +} + .toggle-switch { display: inline-block; background-color: #BBB; diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss index 967aba76b..50c3a50b2 100644 --- a/resources/assets/sass/styles.scss +++ b/resources/assets/sass/styles.scss @@ -7,8 +7,8 @@ @import "grid"; @import "blocks"; @import "buttons"; -@import "forms"; @import "tables"; +@import "forms"; @import "animations"; @import "tinymce"; @import "highlightjs"; @@ -17,7 +17,11 @@ @import "lists"; @import "pages"; -[v-cloak], [v-show] {display: none;} +[v-cloak], [v-show] { + display: none; opacity: 0; + animation-name: none !important; +} + [ng\:cloak], [ng-cloak], .ng-cloak { display: none !important; @@ -272,8 +276,3 @@ $btt-size: 40px; - - - - - diff --git a/resources/lang/de/entities.php b/resources/lang/de/entities.php index 2859e4ec5..c9feb8497 100644 --- a/resources/lang/de/entities.php +++ b/resources/lang/de/entities.php @@ -43,18 +43,9 @@ return [ * Search */ 'search_results' => 'Suchergebnisse', - 'search_results_page' => 'Seiten-Suchergebnisse', - 'search_results_chapter' => 'Kapitel-Suchergebnisse', - 'search_results_book' => 'Buch-Suchergebnisse', 'search_clear' => 'Suche zurücksetzen', - 'search_view_pages' => 'Zeige alle passenden Seiten', - 'search_view_chapters' => 'Zeige alle passenden Kapitel', - 'search_view_books' => 'Zeige alle passenden Bücher', 'search_no_pages' => 'Es wurden keine passenden Suchergebnisse gefunden', 'search_for_term' => 'Suche nach :term', - 'search_page_for_term' => 'Suche nach :term in Seiten', - 'search_chapter_for_term' => 'Suche nach :term in Kapiteln', - 'search_book_for_term' => 'Suche nach :term in Büchern', /** * Books diff --git a/resources/lang/en/common.php b/resources/lang/en/common.php index 31ef42e97..e1d74c95e 100644 --- a/resources/lang/en/common.php +++ b/resources/lang/en/common.php @@ -33,6 +33,7 @@ return [ 'search_clear' => 'Clear Search', 'reset' => 'Reset', 'remove' => 'Remove', + 'add' => 'Add', /** diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php index f54134718..66c2e8042 100644 --- a/resources/lang/en/entities.php +++ b/resources/lang/en/entities.php @@ -43,18 +43,26 @@ return [ * Search */ 'search_results' => 'Search Results', - 'search_results_page' => 'Page Search Results', - 'search_results_chapter' => 'Chapter Search Results', - 'search_results_book' => 'Book Search Results', + 'search_total_results_found' => ':count result found|:count total results found', 'search_clear' => 'Clear Search', - 'search_view_pages' => 'View all matches pages', - 'search_view_chapters' => 'View all matches chapters', - 'search_view_books' => 'View all matches books', 'search_no_pages' => 'No pages matched this search', 'search_for_term' => 'Search for :term', - 'search_page_for_term' => 'Page search for :term', - 'search_chapter_for_term' => 'Chapter search for :term', - 'search_book_for_term' => 'Books search for :term', + 'search_more' => 'More Results', + 'search_filters' => 'Search Filters', + 'search_content_type' => 'Content Type', + 'search_exact_matches' => 'Exact Matches', + 'search_tags' => 'Tag Searches', + 'search_viewed_by_me' => 'Viewed by me', + 'search_not_viewed_by_me' => 'Not viewed by me', + 'search_permissions_set' => 'Permissions set', + 'search_created_by_me' => 'Created by me', + 'search_updated_by_me' => 'Updated by me', + 'search_updated_before' => 'Updated before', + 'search_updated_after' => 'Updated after', + 'search_created_before' => 'Created before', + 'search_created_after' => 'Created after', + 'search_set_date' => 'Set Date', + 'search_update' => 'Update Search', /** * Books diff --git a/resources/lang/es/entities.php b/resources/lang/es/entities.php index 14e952f1a..b03366da6 100644 --- a/resources/lang/es/entities.php +++ b/resources/lang/es/entities.php @@ -43,18 +43,9 @@ return [ * Search */ 'search_results' => 'Buscar resultados', - 'search_results_page' => 'resultados de búsqueda en página', - 'search_results_chapter' => 'Resultados de búsqueda en capítulo ', - 'search_results_book' => 'Resultados de búsqueda en libro', 'search_clear' => 'Limpiar resultados', - 'search_view_pages' => 'Ver todas las páginas que concuerdan', - 'search_view_chapters' => 'Ver todos los capítulos que concuerdan', - 'search_view_books' => 'Ver todos los libros que concuerdan', 'search_no_pages' => 'Ninguna página encontrada para la búsqueda', 'search_for_term' => 'Busqueda por :term', - 'search_page_for_term' => 'Búsqueda de página por :term', - 'search_chapter_for_term' => 'Búsqueda por capítulo de :term', - 'search_book_for_term' => 'Búsqueda en libro de :term', /** * Books diff --git a/resources/lang/fr/entities.php b/resources/lang/fr/entities.php index cfd206b91..5562fb0fd 100644 --- a/resources/lang/fr/entities.php +++ b/resources/lang/fr/entities.php @@ -43,18 +43,9 @@ return [ * Search */ 'search_results' => 'Résultats de recherche', - 'search_results_page' => 'Résultats de recherche des pages', - 'search_results_chapter' => 'Résultats de recherche des chapitres', - 'search_results_book' => 'Résultats de recherche des livres', 'search_clear' => 'Réinitialiser la recherche', - 'search_view_pages' => 'Voir toutes les pages correspondantes', - 'search_view_chapters' => 'Voir tous les chapitres correspondants', - 'search_view_books' => 'Voir tous les livres correspondants', 'search_no_pages' => 'Aucune page correspondant à cette recherche', 'search_for_term' => 'recherche pour :term', - 'search_page_for_term' => 'Recherche de page pour :term', - 'search_chapter_for_term' => 'Recherche de chapitre pour :term', - 'search_book_for_term' => 'Recherche de livres pour :term', /** * Books diff --git a/resources/lang/nl/entities.php b/resources/lang/nl/entities.php index 610116c8b..d6975e130 100644 --- a/resources/lang/nl/entities.php +++ b/resources/lang/nl/entities.php @@ -43,18 +43,9 @@ return [ * Search */ 'search_results' => 'Zoekresultaten', - 'search_results_page' => 'Pagina Zoekresultaten', - 'search_results_chapter' => 'Hoofdstuk Zoekresultaten', - 'search_results_book' => 'Boek Zoekresultaten', 'search_clear' => 'Zoekopdracht wissen', - 'search_view_pages' => 'Bekijk alle gevonden pagina\'s', - 'search_view_chapters' => 'Bekijk alle gevonden hoofdstukken', - 'search_view_books' => 'Bekijk alle gevonden boeken', 'search_no_pages' => 'Er zijn geen pagina\'s gevonden', 'search_for_term' => 'Zoeken op :term', - 'search_page_for_term' => 'Pagina doorzoeken op :term', - 'search_chapter_for_term' => 'Hoofdstuk doorzoeken op :term', - 'search_book_for_term' => 'Boeken doorzoeken op :term', /** * Books diff --git a/resources/lang/pt_BR/entities.php b/resources/lang/pt_BR/entities.php index 922342424..5a965fe62 100644 --- a/resources/lang/pt_BR/entities.php +++ b/resources/lang/pt_BR/entities.php @@ -43,18 +43,9 @@ return [ * Search */ 'search_results' => 'Resultado(s) da Pesquisa', - 'search_results_page' => 'Resultado(s) de Pesquisa de Página', - 'search_results_chapter' => 'Resultado(s) de Pesquisa de Capítulo', - 'search_results_book' => 'Resultado(s) de Pesquisa de Livro', 'search_clear' => 'Limpar Pesquisa', - 'search_view_pages' => 'Visualizar todas as páginas correspondentes', - 'search_view_chapters' => 'Visualizar todos os capítulos correspondentes', - 'search_view_books' => 'Visualizar todos os livros correspondentes', 'search_no_pages' => 'Nenhuma página corresponde à pesquisa', 'search_for_term' => 'Pesquisar por :term', - 'search_page_for_term' => 'Pesquisar Página por :term', - 'search_chapter_for_term' => 'Pesquisar Capítulo por :term', - 'search_book_for_term' => 'Pesquisar Livros por :term', /** * Books diff --git a/resources/views/search/all.blade.php b/resources/views/search/all.blade.php index d1f928912..1029b65fa 100644 --- a/resources/views/search/all.blade.php +++ b/resources/views/search/all.blade.php @@ -5,123 +5,203 @@
+ -
-

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

- - -
+

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

+
{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}
@include('partials/entity-list', ['entities' => $entities]) + @if ($hasNextPage) + {{ trans('entities.search_more') }} + @endif
-

Search Filters

+

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

-
-

Content Type

+ +
{{ trans('entities.search_content_type') }}
- - - + + +
-

Exact Matches

+
{{ trans('entities.search_exact_matches') }}
+
- -
-

Tag Searches

+
{{ trans('entities.search_tags') }}
+
- -
-

Options

-