Dan Brown

Merge branch 'master' into release

Showing 78 changed files with 1622 additions and 539 deletions
...@@ -56,4 +56,13 @@ class Book extends Entity ...@@ -56,4 +56,13 @@ class Book extends Entity
56 return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description; 56 return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description;
57 } 57 }
58 58
59 + /**
60 + * Return a generalised, common raw query that can be 'unioned' across entities.
61 + * @return string
62 + */
63 + public function entityRawQuery()
64 + {
65 + return "'BookStack\\\\Book' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text,'' as html, '0' as book_id, '0' as priority, '0' as chapter_id, '0' as draft, created_by, updated_by, updated_at, created_at";
66 + }
67 +
59 } 68 }
......
...@@ -51,4 +51,13 @@ class Chapter extends Entity ...@@ -51,4 +51,13 @@ class Chapter extends Entity
51 return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description; 51 return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description;
52 } 52 }
53 53
54 + /**
55 + * Return a generalised, common raw query that can be 'unioned' across entities.
56 + * @return string
57 + */
58 + public function entityRawQuery()
59 + {
60 + return "'BookStack\\\\Chapter' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text, '' as html, book_id, priority, '0' as chapter_id, '0' as draft, created_by, updated_by, updated_at, created_at";
61 + }
62 +
54 } 63 }
......
...@@ -12,7 +12,7 @@ class RegeneratePermissions extends Command ...@@ -12,7 +12,7 @@ class RegeneratePermissions extends Command
12 * 12 *
13 * @var string 13 * @var string
14 */ 14 */
15 - protected $signature = 'bookstack:regenerate-permissions'; 15 + protected $signature = 'bookstack:regenerate-permissions {--database= : The database connection to use.}';
16 16
17 /** 17 /**
18 * The console command description. 18 * The console command description.
...@@ -46,7 +46,14 @@ class RegeneratePermissions extends Command ...@@ -46,7 +46,14 @@ class RegeneratePermissions extends Command
46 */ 46 */
47 public function handle() 47 public function handle()
48 { 48 {
49 + $connection = \DB::getDefaultConnection();
50 + if ($this->option('database') !== null) {
51 + \DB::setDefaultConnection($this->option('database'));
52 + }
53 +
49 $this->permissionService->buildJointPermissions(); 54 $this->permissionService->buildJointPermissions();
55 +
56 + \DB::setDefaultConnection($connection);
50 $this->comment('Permissions regenerated'); 57 $this->comment('Permissions regenerated');
51 } 58 }
52 } 59 }
......
1 +<?php
2 +
3 +namespace BookStack\Console\Commands;
4 +
5 +use BookStack\Services\SearchService;
6 +use Illuminate\Console\Command;
7 +
8 +class RegenerateSearch extends Command
9 +{
10 + /**
11 + * The name and signature of the console command.
12 + *
13 + * @var string
14 + */
15 + protected $signature = 'bookstack:regenerate-search {--database= : The database connection to use.}';
16 +
17 + /**
18 + * The console command description.
19 + *
20 + * @var string
21 + */
22 + protected $description = 'Command description';
23 +
24 + protected $searchService;
25 +
26 + /**
27 + * Create a new command instance.
28 + *
29 + * @param SearchService $searchService
30 + */
31 + public function __construct(SearchService $searchService)
32 + {
33 + parent::__construct();
34 + $this->searchService = $searchService;
35 + }
36 +
37 + /**
38 + * Execute the console command.
39 + *
40 + * @return mixed
41 + */
42 + public function handle()
43 + {
44 + $connection = \DB::getDefaultConnection();
45 + if ($this->option('database') !== null) {
46 + \DB::setDefaultConnection($this->option('database'));
47 + }
48 +
49 + $this->searchService->indexAllEntities();
50 + \DB::setDefaultConnection($connection);
51 + $this->comment('Search index regenerated');
52 + }
53 +}
1 -<?php 1 +<?php namespace BookStack\Console;
2 -
3 -namespace BookStack\Console;
4 2
5 use Illuminate\Console\Scheduling\Schedule; 3 use Illuminate\Console\Scheduling\Schedule;
6 use Illuminate\Foundation\Console\Kernel as ConsoleKernel; 4 use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
...@@ -13,10 +11,11 @@ class Kernel extends ConsoleKernel ...@@ -13,10 +11,11 @@ class Kernel extends ConsoleKernel
13 * @var array 11 * @var array
14 */ 12 */
15 protected $commands = [ 13 protected $commands = [
16 - \BookStack\Console\Commands\ClearViews::class, 14 + Commands\ClearViews::class,
17 - \BookStack\Console\Commands\ClearActivity::class, 15 + Commands\ClearActivity::class,
18 - \BookStack\Console\Commands\ClearRevisions::class, 16 + Commands\ClearRevisions::class,
19 - \BookStack\Console\Commands\RegeneratePermissions::class, 17 + Commands\RegeneratePermissions::class,
18 + Commands\RegenerateSearch::class
20 ]; 19 ];
21 20
22 /** 21 /**
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
4 class Entity extends Ownable 4 class Entity extends Ownable
5 { 5 {
6 6
7 - protected $fieldsToSearch = ['name', 'description']; 7 + public $textField = 'description';
8 8
9 /** 9 /**
10 * Compares this entity to another given entity. 10 * Compares this entity to another given entity.
...@@ -66,6 +66,15 @@ class Entity extends Ownable ...@@ -66,6 +66,15 @@ class Entity extends Ownable
66 } 66 }
67 67
68 /** 68 /**
69 + * Get the related search terms.
70 + * @return \Illuminate\Database\Eloquent\Relations\MorphMany
71 + */
72 + public function searchTerms()
73 + {
74 + return $this->morphMany(SearchTerm::class, 'entity');
75 + }
76 +
77 + /**
69 * Get this entities restrictions. 78 * Get this entities restrictions.
70 */ 79 */
71 public function permissions() 80 public function permissions()
...@@ -153,67 +162,19 @@ class Entity extends Ownable ...@@ -153,67 +162,19 @@ class Entity extends Ownable
153 } 162 }
154 163
155 /** 164 /**
156 - * Perform a full-text search on this entity. 165 + * Get the body text of this entity.
157 - * @param string[] $fieldsToSearch
158 - * @param string[] $terms
159 - * @param string[] array $wheres
160 * @return mixed 166 * @return mixed
161 */ 167 */
162 - public function fullTextSearchQuery($terms, $wheres = []) 168 + public function getText()
163 { 169 {
164 - $exactTerms = []; 170 + return $this->{$this->textField};
165 - $fuzzyTerms = [];
166 - $search = static::newQuery();
167 -
168 - foreach ($terms as $key => $term) {
169 - $term = htmlentities($term, ENT_QUOTES);
170 - $term = preg_replace('/[+\-><\(\)~*\"@]+/', ' ', $term);
171 - if (preg_match('/&quot;.*?&quot;/', $term) || is_numeric($term)) {
172 - $term = str_replace('&quot;', '', $term);
173 - $exactTerms[] = '%' . $term . '%';
174 - } else {
175 - $term = '' . $term . '*';
176 - if ($term !== '*') $fuzzyTerms[] = $term;
177 - }
178 - }
179 -
180 - $isFuzzy = count($exactTerms) === 0 && count($fuzzyTerms) > 0;
181 -
182 -
183 - // Perform fulltext search if relevant terms exist.
184 - if ($isFuzzy) {
185 - $termString = implode(' ', $fuzzyTerms);
186 - $fields = implode(',', $this->fieldsToSearch);
187 - $search = $search->selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]);
188 - $search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
189 } 171 }
190 172
191 - // Ensure at least one exact term matches if in search 173 + /**
192 - if (count($exactTerms) > 0) { 174 + * Return a generalised, common raw query that can be 'unioned' across entities.
193 - $search = $search->where(function ($query) use ($exactTerms) { 175 + * @return string
194 - foreach ($exactTerms as $exactTerm) { 176 + */
195 - foreach ($this->fieldsToSearch as $field) { 177 + public function entityRawQuery(){return '';}
196 - $query->orWhere($field, 'like', $exactTerm);
197 - }
198 - }
199 - });
200 - }
201 -
202 - $orderBy = $isFuzzy ? 'title_relevance' : 'updated_at';
203 -
204 - // Add additional where terms
205 - foreach ($wheres as $whereTerm) {
206 - $search->where($whereTerm[0], $whereTerm[1], $whereTerm[2]);
207 - }
208 -
209 - // Load in relations
210 - if ($this->isA('page')) {
211 - $search = $search->with('book', 'chapter', 'createdBy', 'updatedBy');
212 - } else if ($this->isA('chapter')) {
213 - $search = $search->with('book');
214 - }
215 178
216 - return $search->orderBy($orderBy, 'desc');
217 - }
218 179
219 } 180 }
......
1 <?php namespace BookStack\Http\Controllers; 1 <?php namespace BookStack\Http\Controllers;
2 2
3 use BookStack\Repos\EntityRepo; 3 use BookStack\Repos\EntityRepo;
4 +use BookStack\Services\SearchService;
4 use BookStack\Services\ViewService; 5 use BookStack\Services\ViewService;
5 use Illuminate\Http\Request; 6 use Illuminate\Http\Request;
6 7
...@@ -8,16 +9,19 @@ class SearchController extends Controller ...@@ -8,16 +9,19 @@ class SearchController extends Controller
8 { 9 {
9 protected $entityRepo; 10 protected $entityRepo;
10 protected $viewService; 11 protected $viewService;
12 + protected $searchService;
11 13
12 /** 14 /**
13 * SearchController constructor. 15 * SearchController constructor.
14 * @param EntityRepo $entityRepo 16 * @param EntityRepo $entityRepo
15 * @param ViewService $viewService 17 * @param ViewService $viewService
18 + * @param SearchService $searchService
16 */ 19 */
17 - public function __construct(EntityRepo $entityRepo, ViewService $viewService) 20 + public function __construct(EntityRepo $entityRepo, ViewService $viewService, SearchService $searchService)
18 { 21 {
19 $this->entityRepo = $entityRepo; 22 $this->entityRepo = $entityRepo;
20 $this->viewService = $viewService; 23 $this->viewService = $viewService;
24 + $this->searchService = $searchService;
21 parent::__construct(); 25 parent::__construct();
22 } 26 }
23 27
...@@ -27,105 +31,55 @@ class SearchController extends Controller ...@@ -27,105 +31,55 @@ class SearchController extends Controller
27 * @return \Illuminate\View\View 31 * @return \Illuminate\View\View
28 * @internal param string $searchTerm 32 * @internal param string $searchTerm
29 */ 33 */
30 - public function searchAll(Request $request) 34 + public function search(Request $request)
31 { 35 {
32 - if (!$request->has('term')) {
33 - return redirect()->back();
34 - }
35 $searchTerm = $request->get('term'); 36 $searchTerm = $request->get('term');
36 - $paginationAppends = $request->only('term');
37 - $pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
38 - $books = $this->entityRepo->getBySearch('book', $searchTerm, [], 10, $paginationAppends);
39 - $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 10, $paginationAppends);
40 $this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm])); 37 $this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
41 - return view('search/all', [
42 - 'pages' => $pages,
43 - 'books' => $books,
44 - 'chapters' => $chapters,
45 - 'searchTerm' => $searchTerm
46 - ]);
47 - }
48 -
49 - /**
50 - * Search only the pages in the system.
51 - * @param Request $request
52 - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
53 - */
54 - public function searchPages(Request $request)
55 - {
56 - if (!$request->has('term')) return redirect()->back();
57 38
58 - $searchTerm = $request->get('term'); 39 + $page = $request->has('page') && is_int(intval($request->get('page'))) ? intval($request->get('page')) : 1;
59 - $paginationAppends = $request->only('term'); 40 + $nextPageLink = baseUrl('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
60 - $pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
61 - $this->setPageTitle(trans('entities.search_page_for_term', ['term' => $searchTerm]));
62 - return view('search/entity-search-list', [
63 - 'entities' => $pages,
64 - 'title' => trans('entities.search_results_page'),
65 - 'searchTerm' => $searchTerm
66 - ]);
67 - }
68 41
69 - /** 42 + $results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
70 - * Search only the chapters in the system. 43 + $hasNextPage = $this->searchService->searchEntities($searchTerm, 'all', $page+1, 20)['count'] > 0;
71 - * @param Request $request
72 - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
73 - */
74 - public function searchChapters(Request $request)
75 - {
76 - if (!$request->has('term')) return redirect()->back();
77 44
78 - $searchTerm = $request->get('term'); 45 + return view('search/all', [
79 - $paginationAppends = $request->only('term'); 46 + 'entities' => $results['results'],
80 - $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 20, $paginationAppends); 47 + 'totalResults' => $results['total'],
81 - $this->setPageTitle(trans('entities.search_chapter_for_term', ['term' => $searchTerm])); 48 + 'searchTerm' => $searchTerm,
82 - return view('search/entity-search-list', [ 49 + 'hasNextPage' => $hasNextPage,
83 - 'entities' => $chapters, 50 + 'nextPageLink' => $nextPageLink
84 - 'title' => trans('entities.search_results_chapter'),
85 - 'searchTerm' => $searchTerm
86 ]); 51 ]);
87 } 52 }
88 53
54 +
89 /** 55 /**
90 - * Search only the books in the system. 56 + * Searches all entities within a book.
91 * @param Request $request 57 * @param Request $request
92 - * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View 58 + * @param integer $bookId
59 + * @return \Illuminate\View\View
60 + * @internal param string $searchTerm
93 */ 61 */
94 - public function searchBooks(Request $request) 62 + public function searchBook(Request $request, $bookId)
95 { 63 {
96 - if (!$request->has('term')) return redirect()->back(); 64 + $term = $request->get('term', '');
97 - 65 + $results = $this->searchService->searchBook($bookId, $term);
98 - $searchTerm = $request->get('term'); 66 + return view('partials/entity-list', ['entities' => $results]);
99 - $paginationAppends = $request->only('term');
100 - $books = $this->entityRepo->getBySearch('book', $searchTerm, [], 20, $paginationAppends);
101 - $this->setPageTitle(trans('entities.search_book_for_term', ['term' => $searchTerm]));
102 - return view('search/entity-search-list', [
103 - 'entities' => $books,
104 - 'title' => trans('entities.search_results_book'),
105 - 'searchTerm' => $searchTerm
106 - ]);
107 } 67 }
108 68
109 /** 69 /**
110 - * Searches all entities within a book. 70 + * Searches all entities within a chapter.
111 * @param Request $request 71 * @param Request $request
112 - * @param integer $bookId 72 + * @param integer $chapterId
113 * @return \Illuminate\View\View 73 * @return \Illuminate\View\View
114 * @internal param string $searchTerm 74 * @internal param string $searchTerm
115 */ 75 */
116 - public function searchBook(Request $request, $bookId) 76 + public function searchChapter(Request $request, $chapterId)
117 { 77 {
118 - if (!$request->has('term')) { 78 + $term = $request->get('term', '');
119 - return redirect()->back(); 79 + $results = $this->searchService->searchChapter($chapterId, $term);
120 - } 80 + return view('partials/entity-list', ['entities' => $results]);
121 - $searchTerm = $request->get('term');
122 - $searchWhereTerms = [['book_id', '=', $bookId]];
123 - $pages = $this->entityRepo->getBySearch('page', $searchTerm, $searchWhereTerms);
124 - $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, $searchWhereTerms);
125 - return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
126 } 81 }
127 82
128 -
129 /** 83 /**
130 * Search for a list of entities and return a partial HTML response of matching entities. 84 * Search for a list of entities and return a partial HTML response of matching entities.
131 * Returns the most popular entities if no search is provided. 85 * Returns the most popular entities if no search is provided.
...@@ -134,18 +88,13 @@ class SearchController extends Controller ...@@ -134,18 +88,13 @@ class SearchController extends Controller
134 */ 88 */
135 public function searchEntitiesAjax(Request $request) 89 public function searchEntitiesAjax(Request $request)
136 { 90 {
137 - $entities = collect();
138 $entityTypes = $request->has('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']); 91 $entityTypes = $request->has('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']);
139 $searchTerm = ($request->has('term') && trim($request->get('term')) !== '') ? $request->get('term') : false; 92 $searchTerm = ($request->has('term') && trim($request->get('term')) !== '') ? $request->get('term') : false;
140 93
141 // Search for entities otherwise show most popular 94 // Search for entities otherwise show most popular
142 if ($searchTerm !== false) { 95 if ($searchTerm !== false) {
143 - foreach (['page', 'chapter', 'book'] as $entityType) { 96 + $searchTerm .= ' {type:'. implode('|', $entityTypes->toArray()) .'}';
144 - if ($entityTypes->contains($entityType)) { 97 + $entities = $this->searchService->searchEntities($searchTerm)['results'];
145 - $entities = $entities->merge($this->entityRepo->getBySearch($entityType, $searchTerm)->items());
146 - }
147 - }
148 - $entities = $entities->sortByDesc('title_relevance');
149 } else { 98 } else {
150 $entityNames = $entityTypes->map(function ($type) { 99 $entityNames = $entityTypes->map(function ($type) {
151 return 'BookStack\\' . ucfirst($type); 100 return 'BookStack\\' . ucfirst($type);
......
...@@ -2,12 +2,16 @@ ...@@ -2,12 +2,16 @@
2 2
3 namespace BookStack\Notifications; 3 namespace BookStack\Notifications;
4 4
5 +use Illuminate\Bus\Queueable;
5 use Illuminate\Notifications\Notification; 6 use Illuminate\Notifications\Notification;
7 +use Illuminate\Contracts\Queue\ShouldQueue;
6 use Illuminate\Notifications\Messages\MailMessage; 8 use Illuminate\Notifications\Messages\MailMessage;
7 9
8 -class ConfirmEmail extends Notification 10 +class ConfirmEmail extends Notification implements ShouldQueue
9 { 11 {
10 12
13 + use Queueable;
14 +
11 public $token; 15 public $token;
12 16
13 /** 17 /**
......
...@@ -8,8 +8,7 @@ class Page extends Entity ...@@ -8,8 +8,7 @@ class Page extends Entity
8 protected $simpleAttributes = ['name', 'id', 'slug']; 8 protected $simpleAttributes = ['name', 'id', 'slug'];
9 9
10 protected $with = ['book']; 10 protected $with = ['book'];
11 - 11 + public $textField = 'text';
12 - protected $fieldsToSearch = ['name', 'text'];
13 12
14 /** 13 /**
15 * Converts this page into a simplified array. 14 * Converts this page into a simplified array.
...@@ -96,4 +95,14 @@ class Page extends Entity ...@@ -96,4 +95,14 @@ class Page extends Entity
96 return mb_convert_encoding($text, 'UTF-8'); 95 return mb_convert_encoding($text, 'UTF-8');
97 } 96 }
98 97
98 + /**
99 + * Return a generalised, common raw query that can be 'unioned' across entities.
100 + * @param bool $withContent
101 + * @return string
102 + */
103 + public function entityRawQuery($withContent = false)
104 + { $htmlQuery = $withContent ? 'html' : "'' as html";
105 + return "'BookStack\\\\Page' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text, {$htmlQuery}, book_id, priority, chapter_id, draft, created_by, updated_by, updated_at, created_at";
106 + }
107 +
99 } 108 }
......
1 +<?php namespace BookStack;
2 +
3 +class SearchTerm extends Model
4 +{
5 +
6 + protected $fillable = ['term', 'entity_id', 'entity_type', 'score'];
7 + public $timestamps = false;
8 +
9 + /**
10 + * Get the entity that this term belongs to
11 + * @return \Illuminate\Database\Eloquent\Relations\MorphTo
12 + */
13 + public function entity()
14 + {
15 + return $this->morphTo('entity');
16 + }
17 +
18 +}
...@@ -479,8 +479,7 @@ class PermissionService ...@@ -479,8 +479,7 @@ class PermissionService
479 * @return \Illuminate\Database\Query\Builder 479 * @return \Illuminate\Database\Query\Builder
480 */ 480 */
481 public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false) { 481 public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false) {
482 - $pageContentSelect = $fetchPageContent ? 'html' : "''"; 482 + $pageSelect = $this->db->table('pages')->selectRaw($this->page->entityRawQuery($fetchPageContent))->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) {
483 - $pageSelect = $this->db->table('pages')->selectRaw("'BookStack\\\\Page' as entity_type, id, slug, name, text, {$pageContentSelect} as description, book_id, priority, chapter_id, draft")->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) {
484 $query->where('draft', '=', 0); 483 $query->where('draft', '=', 0);
485 if (!$filterDrafts) { 484 if (!$filterDrafts) {
486 $query->orWhere(function($query) { 485 $query->orWhere(function($query) {
...@@ -488,7 +487,7 @@ class PermissionService ...@@ -488,7 +487,7 @@ class PermissionService
488 }); 487 });
489 } 488 }
490 }); 489 });
491 - $chapterSelect = $this->db->table('chapters')->selectRaw("'BookStack\\\\Chapter' as entity_type, id, slug, name, '' as text, description, book_id, priority, 0 as chapter_id, 0 as draft")->where('book_id', '=', $book_id); 490 + $chapterSelect = $this->db->table('chapters')->selectRaw($this->chapter->entityRawQuery())->where('book_id', '=', $book_id);
492 $query = $this->db->query()->select('*')->from($this->db->raw("({$pageSelect->toSql()} UNION {$chapterSelect->toSql()}) AS U")) 491 $query = $this->db->query()->select('*')->from($this->db->raw("({$pageSelect->toSql()} UNION {$chapterSelect->toSql()}) AS U"))
493 ->mergeBindings($pageSelect)->mergeBindings($chapterSelect); 492 ->mergeBindings($pageSelect)->mergeBindings($chapterSelect);
494 493
...@@ -514,7 +513,7 @@ class PermissionService ...@@ -514,7 +513,7 @@ class PermissionService
514 * @param string $entityType 513 * @param string $entityType
515 * @param Builder|Entity $query 514 * @param Builder|Entity $query
516 * @param string $action 515 * @param string $action
517 - * @return mixed 516 + * @return Builder
518 */ 517 */
519 public function enforceEntityRestrictions($entityType, $query, $action = 'view') 518 public function enforceEntityRestrictions($entityType, $query, $action = 'view')
520 { 519 {
...@@ -540,7 +539,7 @@ class PermissionService ...@@ -540,7 +539,7 @@ class PermissionService
540 } 539 }
541 540
542 /** 541 /**
543 - * Filter items that have entities set a a polymorphic relation. 542 + * Filter items that have entities set as a polymorphic relation.
544 * @param $query 543 * @param $query
545 * @param string $tableName 544 * @param string $tableName
546 * @param string $entityIdColumn 545 * @param string $entityIdColumn
......
...@@ -64,6 +64,10 @@ ...@@ -64,6 +64,10 @@
64 "post-update-cmd": [ 64 "post-update-cmd": [
65 "Illuminate\\Foundation\\ComposerScripts::postUpdate", 65 "Illuminate\\Foundation\\ComposerScripts::postUpdate",
66 "php artisan optimize" 66 "php artisan optimize"
67 + ],
68 + "refresh-test-database": [
69 + "php artisan migrate:refresh --database=mysql_testing",
70 + "php artisan db:seed --class=DummyContentSeeder --database=mysql_testing"
67 ] 71 ]
68 }, 72 },
69 "config": { 73 "config": {
......
...@@ -100,7 +100,7 @@ return [ ...@@ -100,7 +100,7 @@ return [
100 | 100 |
101 */ 101 */
102 102
103 - 'log' => 'single', 103 + 'log' => env('APP_LOGGING', 'single'),
104 104
105 /* 105 /*
106 |-------------------------------------------------------------------------- 106 |--------------------------------------------------------------------------
......
...@@ -43,7 +43,8 @@ $factory->define(BookStack\Page::class, function ($faker) { ...@@ -43,7 +43,8 @@ $factory->define(BookStack\Page::class, function ($faker) {
43 'name' => $faker->sentence, 43 'name' => $faker->sentence,
44 'slug' => str_random(10), 44 'slug' => str_random(10),
45 'html' => $html, 45 'html' => $html,
46 - 'text' => strip_tags($html) 46 + 'text' => strip_tags($html),
47 + 'revision_count' => 1
47 ]; 48 ];
48 }); 49 });
49 50
......
...@@ -12,9 +12,10 @@ class AddSearchIndexes extends Migration ...@@ -12,9 +12,10 @@ class AddSearchIndexes extends Migration
12 */ 12 */
13 public function up() 13 public function up()
14 { 14 {
15 - DB::statement('ALTER TABLE pages ADD FULLTEXT search(name, text)'); 15 + $prefix = DB::getTablePrefix();
16 - DB::statement('ALTER TABLE books ADD FULLTEXT search(name, description)'); 16 + DB::statement("ALTER TABLE {$prefix}pages ADD FULLTEXT search(name, text)");
17 - DB::statement('ALTER TABLE chapters ADD FULLTEXT search(name, description)'); 17 + DB::statement("ALTER TABLE {$prefix}books ADD FULLTEXT search(name, description)");
18 + DB::statement("ALTER TABLE {$prefix}chapters ADD FULLTEXT search(name, description)");
18 } 19 }
19 20
20 /** 21 /**
......
...@@ -12,9 +12,10 @@ class FulltextWeighting extends Migration ...@@ -12,9 +12,10 @@ class FulltextWeighting extends Migration
12 */ 12 */
13 public function up() 13 public function up()
14 { 14 {
15 - DB::statement('ALTER TABLE pages ADD FULLTEXT name_search(name)'); 15 + $prefix = DB::getTablePrefix();
16 - DB::statement('ALTER TABLE books ADD FULLTEXT name_search(name)'); 16 + DB::statement("ALTER TABLE {$prefix}pages ADD FULLTEXT name_search(name)");
17 - DB::statement('ALTER TABLE chapters ADD FULLTEXT name_search(name)'); 17 + DB::statement("ALTER TABLE {$prefix}books ADD FULLTEXT name_search(name)");
18 + DB::statement("ALTER TABLE {$prefix}chapters ADD FULLTEXT name_search(name)");
18 } 19 }
19 20
20 /** 21 /**
......
1 +<?php
2 +
3 +use Illuminate\Support\Facades\Schema;
4 +use Illuminate\Database\Schema\Blueprint;
5 +use Illuminate\Database\Migrations\Migration;
6 +
7 +class CreateSearchIndexTable extends Migration
8 +{
9 + /**
10 + * Run the migrations.
11 + *
12 + * @return void
13 + */
14 + public function up()
15 + {
16 + Schema::create('search_terms', function (Blueprint $table) {
17 + $table->increments('id');
18 + $table->string('term', 200);
19 + $table->string('entity_type', 100);
20 + $table->integer('entity_id');
21 + $table->integer('score');
22 +
23 + $table->index('term');
24 + $table->index('entity_type');
25 + $table->index(['entity_type', 'entity_id']);
26 + $table->index('score');
27 + });
28 +
29 + // Drop search indexes
30 + Schema::table('pages', function(Blueprint $table) {
31 + $table->dropIndex('search');
32 + $table->dropIndex('name_search');
33 + });
34 + Schema::table('books', function(Blueprint $table) {
35 + $table->dropIndex('search');
36 + $table->dropIndex('name_search');
37 + });
38 + Schema::table('chapters', function(Blueprint $table) {
39 + $table->dropIndex('search');
40 + $table->dropIndex('name_search');
41 + });
42 +
43 + app(\BookStack\Services\SearchService::class)->indexAllEntities();
44 + }
45 +
46 + /**
47 + * Reverse the migrations.
48 + *
49 + * @return void
50 + */
51 + public function down()
52 + {
53 + $prefix = DB::getTablePrefix();
54 + DB::statement("ALTER TABLE {$prefix}pages ADD FULLTEXT search(name, text)");
55 + DB::statement("ALTER TABLE {$prefix}books ADD FULLTEXT search(name, description)");
56 + DB::statement("ALTER TABLE {$prefix}chapters ADD FULLTEXT search(name, description)");
57 + DB::statement("ALTER TABLE {$prefix}pages ADD FULLTEXT name_search(name)");
58 + DB::statement("ALTER TABLE {$prefix}books ADD FULLTEXT name_search(name)");
59 + DB::statement("ALTER TABLE {$prefix}chapters ADD FULLTEXT name_search(name)");
60 +
61 + Schema::dropIfExists('search_terms');
62 + }
63 +}
1 +<?php
2 +
3 +use Illuminate\Support\Facades\Schema;
4 +use Illuminate\Database\Schema\Blueprint;
5 +use Illuminate\Database\Migrations\Migration;
6 +
7 +class AddRevisionCounts extends Migration
8 +{
9 + /**
10 + * Run the migrations.
11 + *
12 + * @return void
13 + */
14 + public function up()
15 + {
16 + Schema::table('pages', function (Blueprint $table) {
17 + $table->integer('revision_count');
18 + });
19 + Schema::table('page_revisions', function (Blueprint $table) {
20 + $table->integer('revision_number');
21 + $table->index('revision_number');
22 + });
23 +
24 + // Update revision count
25 + $pTable = DB::getTablePrefix() . 'pages';
26 + $rTable = DB::getTablePrefix() . 'page_revisions';
27 + DB::statement("UPDATE ${pTable} SET ${pTable}.revision_count=(SELECT count(*) FROM ${rTable} WHERE ${rTable}.page_id=${pTable}.id)");
28 + }
29 +
30 + /**
31 + * Reverse the migrations.
32 + *
33 + * @return void
34 + */
35 + public function down()
36 + {
37 + Schema::table('pages', function (Blueprint $table) {
38 + $table->dropColumn('revision_count');
39 + });
40 + Schema::table('page_revisions', function (Blueprint $table) {
41 + $table->dropColumn('revision_number');
42 + });
43 + }
44 +}
...@@ -16,7 +16,7 @@ class DummyContentSeeder extends Seeder ...@@ -16,7 +16,7 @@ class DummyContentSeeder extends Seeder
16 $user->attachRole($role); 16 $user->attachRole($role);
17 17
18 18
19 - $books = factory(\BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id]) 19 + factory(\BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id])
20 ->each(function($book) use ($user) { 20 ->each(function($book) use ($user) {
21 $chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id]) 21 $chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id])
22 ->each(function($chapter) use ($user, $book){ 22 ->each(function($chapter) use ($user, $book){
...@@ -28,7 +28,7 @@ class DummyContentSeeder extends Seeder ...@@ -28,7 +28,7 @@ class DummyContentSeeder extends Seeder
28 $book->pages()->saveMany($pages); 28 $book->pages()->saveMany($pages);
29 }); 29 });
30 30
31 - $restrictionService = app(\BookStack\Services\PermissionService::class); 31 + app(\BookStack\Services\PermissionService::class)->buildJointPermissions();
32 - $restrictionService->buildJointPermissions(); 32 + app(\BookStack\Services\SearchService::class)->indexAllEntities();
33 } 33 }
34 } 34 }
......
1 -var elixir = require('laravel-elixir'); 1 +const argv = require('yargs').argv;
2 +const gulp = require('gulp'),
3 + plumber = require('gulp-plumber');
4 +const autoprefixer = require('gulp-autoprefixer');
5 +const uglify = require('gulp-uglify');
6 +const minifycss = require('gulp-clean-css');
7 +const sass = require('gulp-sass');
8 +const browserify = require("browserify");
9 +const source = require('vinyl-source-stream');
10 +const buffer = require('vinyl-buffer');
11 +const babelify = require("babelify");
12 +const watchify = require("watchify");
13 +const envify = require("envify");
14 +const gutil = require("gulp-util");
2 15
3 -elixir(mix => { 16 +if (argv.production) process.env.NODE_ENV = 'production';
4 - mix.sass('styles.scss'); 17 +
5 - mix.sass('print-styles.scss'); 18 +gulp.task('styles', () => {
6 - mix.sass('export-styles.scss'); 19 + let chain = gulp.src(['resources/assets/sass/**/*.scss'])
7 - mix.browserify('global.js', './public/js/common.js'); 20 + .pipe(plumber({
21 + errorHandler: function (error) {
22 + console.log(error.message);
23 + this.emit('end');
24 + }}))
25 + .pipe(sass())
26 + .pipe(autoprefixer('last 2 versions'));
27 + if (argv.production) chain = chain.pipe(minifycss());
28 + return chain.pipe(gulp.dest('public/css/'));
8 }); 29 });
30 +
31 +
32 +function scriptTask(watch=false) {
33 +
34 + let props = {
35 + basedir: 'resources/assets/js',
36 + debug: true,
37 + entries: ['global.js']
38 + };
39 +
40 + let bundler = watch ? watchify(browserify(props), { poll: true }) : browserify(props);
41 + bundler.transform(envify, {global: true}).transform(babelify, {presets: ['es2015']});
42 + function rebundle() {
43 + let stream = bundler.bundle();
44 + stream = stream.pipe(source('common.js'));
45 + if (argv.production) stream = stream.pipe(buffer()).pipe(uglify());
46 + return stream.pipe(gulp.dest('public/js/'));
47 + }
48 + bundler.on('update', function() {
49 + rebundle();
50 + gutil.log('Rebundle...');
51 + });
52 + bundler.on('log', gutil.log);
53 + return rebundle();
54 +}
55 +
56 +gulp.task('scripts', () => {scriptTask(false)});
57 +gulp.task('scripts-watch', () => {scriptTask(true)});
58 +
59 +gulp.task('default', ['styles', 'scripts-watch'], () => {
60 + gulp.watch("resources/assets/sass/**/*.scss", ['styles']);
61 +});
62 +
63 +gulp.task('build', ['styles', 'scripts']);
...\ No newline at end of file ...\ No newline at end of file
......
1 { 1 {
2 "private": true, 2 "private": true,
3 "scripts": { 3 "scripts": {
4 - "build": "gulp --production", 4 + "build": "gulp build",
5 - "dev": "gulp watch", 5 + "production": "gulp build --production",
6 - "watch": "gulp watch" 6 + "dev": "gulp",
7 + "watch": "gulp"
7 }, 8 },
8 "devDependencies": { 9 "devDependencies": {
10 + "babelify": "^7.3.0",
11 + "browserify": "^14.3.0",
12 + "envify": "^4.0.0",
13 + "gulp": "3.9.1",
14 + "gulp-autoprefixer": "3.1.1",
15 + "gulp-clean-css": "^3.0.4",
16 + "gulp-minify-css": "1.2.4",
17 + "gulp-plumber": "1.1.0",
18 + "gulp-sass": "3.1.0",
19 + "gulp-uglify": "2.1.2",
20 + "vinyl-buffer": "^1.0.0",
21 + "vinyl-source-stream": "^1.1.0",
22 + "watchify": "^3.9.0",
23 + "yargs": "^7.1.0"
24 + },
25 + "dependencies": {
9 "angular": "^1.5.5", 26 "angular": "^1.5.5",
10 "angular-animate": "^1.5.5", 27 "angular-animate": "^1.5.5",
11 "angular-resource": "^1.5.5", 28 "angular-resource": "^1.5.5",
12 "angular-sanitize": "^1.5.5", 29 "angular-sanitize": "^1.5.5",
13 - "angular-ui-sortable": "^0.15.0", 30 + "angular-ui-sortable": "^0.17.0",
31 + "axios": "^0.16.1",
32 + "babel-preset-es2015": "^6.24.1",
33 + "clipboard": "^1.5.16",
14 "dropzone": "^4.0.1", 34 "dropzone": "^4.0.1",
15 - "gulp": "^3.9.0", 35 + "gulp-util": "^3.0.8",
16 - "laravel-elixir": "^6.0.0-11", 36 + "markdown-it": "^8.3.1",
17 - "laravel-elixir-browserify-official": "^0.1.3", 37 + "markdown-it-task-lists": "^2.0.0",
18 - "marked": "^0.3.5", 38 + "moment": "^2.12.0",
19 - "moment": "^2.12.0" 39 + "vue": "^2.2.6"
20 }, 40 },
21 - "dependencies": { 41 + "browser": {
22 - "clipboard": "^1.5.16" 42 + "vue": "vue/dist/vue.common.js"
23 } 43 }
24 } 44 }
......
...@@ -74,7 +74,7 @@ These are the great projects used to help build BookStack: ...@@ -74,7 +74,7 @@ These are the great projects used to help build BookStack:
74 * [Dropzone.js](http://www.dropzonejs.com/) 74 * [Dropzone.js](http://www.dropzonejs.com/)
75 * [ZeroClipboard](http://zeroclipboard.org/) 75 * [ZeroClipboard](http://zeroclipboard.org/)
76 * [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html) 76 * [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html)
77 -* [Marked](https://github.com/chjj/marked) 77 +* [markdown-it](https://github.com/markdown-it/markdown-it) and [markdown-it-task-lists](https://github.com/revin/markdown-it-task-lists)
78 * [Moment.js](http://momentjs.com/) 78 * [Moment.js](http://momentjs.com/)
79 * [BarryVD](https://github.com/barryvdh) 79 * [BarryVD](https://github.com/barryvdh)
80 * [Debugbar](https://github.com/barryvdh/laravel-debugbar) 80 * [Debugbar](https://github.com/barryvdh/laravel-debugbar)
......
1 "use strict"; 1 "use strict";
2 2
3 -import moment from 'moment'; 3 +const moment = require('moment');
4 -import 'moment/locale/en-gb'; 4 +require('moment/locale/en-gb');
5 -import editorOptions from "./pages/page-form"; 5 +const editorOptions = require("./pages/page-form");
6 6
7 moment.locale('en-gb'); 7 moment.locale('en-gb');
8 8
9 -export default function (ngApp, events) { 9 +module.exports = function (ngApp, events) {
10 10
11 ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService', 11 ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService',
12 function ($scope, $attrs, $http, $timeout, imageManagerService) { 12 function ($scope, $attrs, $http, $timeout, imageManagerService) {
...@@ -259,39 +259,6 @@ export default function (ngApp, events) { ...@@ -259,39 +259,6 @@ export default function (ngApp, events) {
259 259
260 }]); 260 }]);
261 261
262 -
263 - ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', '$sce', function ($scope, $http, $attrs, $sce) {
264 - $scope.searching = false;
265 - $scope.searchTerm = '';
266 - $scope.searchResults = '';
267 -
268 - $scope.searchBook = function (e) {
269 - e.preventDefault();
270 - let term = $scope.searchTerm;
271 - if (term.length == 0) return;
272 - $scope.searching = true;
273 - $scope.searchResults = '';
274 - let searchUrl = window.baseUrl('/search/book/' + $attrs.bookId);
275 - searchUrl += '?term=' + encodeURIComponent(term);
276 - $http.get(searchUrl).then((response) => {
277 - $scope.searchResults = $sce.trustAsHtml(response.data);
278 - });
279 - };
280 -
281 - $scope.checkSearchForm = function () {
282 - if ($scope.searchTerm.length < 1) {
283 - $scope.searching = false;
284 - }
285 - };
286 -
287 - $scope.clearSearch = function () {
288 - $scope.searching = false;
289 - $scope.searchTerm = '';
290 - };
291 -
292 - }]);
293 -
294 -
295 ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce', 262 ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce',
296 function ($scope, $http, $attrs, $interval, $timeout, $sce) { 263 function ($scope, $http, $attrs, $interval, $timeout, $sce) {
297 264
......
1 "use strict"; 1 "use strict";
2 -import DropZone from "dropzone"; 2 +const DropZone = require("dropzone");
3 -import markdown from "marked"; 3 +const MarkdownIt = require("markdown-it");
4 +const mdTasksLists = require('markdown-it-task-lists');
4 5
5 -export default function (ngApp, events) { 6 +module.exports = function (ngApp, events) {
6 7
7 /** 8 /**
8 * Common tab controls using simple jQuery functions. 9 * Common tab controls using simple jQuery functions.
...@@ -214,18 +215,8 @@ export default function (ngApp, events) { ...@@ -214,18 +215,8 @@ export default function (ngApp, events) {
214 } 215 }
215 }]); 216 }]);
216 217
217 - let renderer = new markdown.Renderer(); 218 + const md = new MarkdownIt();
218 - // Custom markdown checkbox list item 219 + md.use(mdTasksLists, {label: true});
219 - // Attribution: https://github.com/chjj/marked/issues/107#issuecomment-44542001
220 - renderer.listitem = function(text) {
221 - if (/^\s*\[[x ]\]\s*/.test(text)) {
222 - text = text
223 - .replace(/^\s*\[ \]\s*/, '<input type="checkbox"/>')
224 - .replace(/^\s*\[x\]\s*/, '<input type="checkbox" checked/>');
225 - return `<li class="checkbox-item">${text}</li>`;
226 - }
227 - return `<li>${text}</li>`;
228 - };
229 220
230 /** 221 /**
231 * Markdown input 222 * Markdown input
...@@ -244,20 +235,20 @@ export default function (ngApp, events) { ...@@ -244,20 +235,20 @@ export default function (ngApp, events) {
244 element = element.find('textarea').first(); 235 element = element.find('textarea').first();
245 let content = element.val(); 236 let content = element.val();
246 scope.mdModel = content; 237 scope.mdModel = content;
247 - scope.mdChange(markdown(content, {renderer: renderer})); 238 + scope.mdChange(md.render(content));
248 239
249 element.on('change input', (event) => { 240 element.on('change input', (event) => {
250 content = element.val(); 241 content = element.val();
251 $timeout(() => { 242 $timeout(() => {
252 scope.mdModel = content; 243 scope.mdModel = content;
253 - scope.mdChange(markdown(content, {renderer: renderer})); 244 + scope.mdChange(md.render(content));
254 }); 245 });
255 }); 246 });
256 247
257 scope.$on('markdown-update', (event, value) => { 248 scope.$on('markdown-update', (event, value) => {
258 element.val(value); 249 element.val(value);
259 scope.mdModel = value; 250 scope.mdModel = value;
260 - scope.mdChange(markdown(value)); 251 + scope.mdChange(md.render(value));
261 }); 252 });
262 253
263 } 254 }
......
1 "use strict"; 1 "use strict";
2 2
3 -// AngularJS - Create application and load components
4 -import angular from "angular";
5 -import "angular-resource";
6 -import "angular-animate";
7 -import "angular-sanitize";
8 -import "angular-ui-sortable";
9 -
10 // Url retrieval function 3 // Url retrieval function
11 window.baseUrl = function(path) { 4 window.baseUrl = function(path) {
12 let basePath = document.querySelector('meta[name="base-url"]').getAttribute('content'); 5 let basePath = document.querySelector('meta[name="base-url"]').getAttribute('content');
...@@ -15,11 +8,33 @@ window.baseUrl = function(path) { ...@@ -15,11 +8,33 @@ window.baseUrl = function(path) {
15 return basePath + '/' + path; 8 return basePath + '/' + path;
16 }; 9 };
17 10
11 +const Vue = require("vue");
12 +const axios = require("axios");
13 +
14 +let axiosInstance = axios.create({
15 + headers: {
16 + 'X-CSRF-TOKEN': document.querySelector('meta[name=token]').getAttribute('content'),
17 + 'baseURL': window.baseUrl('')
18 + }
19 +});
20 +
21 +Vue.prototype.$http = axiosInstance;
22 +
23 +require("./vues/vues");
24 +
25 +
26 +// AngularJS - Create application and load components
27 +const angular = require("angular");
28 +require("angular-resource");
29 +require("angular-animate");
30 +require("angular-sanitize");
31 +require("angular-ui-sortable");
32 +
18 let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']); 33 let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']);
19 34
20 // Translation setup 35 // Translation setup
21 // Creates a global function with name 'trans' to be used in the same way as Laravel's translation system 36 // Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
22 -import Translations from "./translations" 37 +const Translations = require("./translations");
23 let translator = new Translations(window.translations); 38 let translator = new Translations(window.translations);
24 window.trans = translator.get.bind(translator); 39 window.trans = translator.get.bind(translator);
25 40
...@@ -47,11 +62,12 @@ class EventManager { ...@@ -47,11 +62,12 @@ class EventManager {
47 } 62 }
48 63
49 window.Events = new EventManager(); 64 window.Events = new EventManager();
65 +Vue.prototype.$events = window.Events;
50 66
51 // Load in angular specific items 67 // Load in angular specific items
52 -import Services from './services'; 68 +const Services = require('./services');
53 -import Directives from './directives'; 69 +const Directives = require('./directives');
54 -import Controllers from './controllers'; 70 +const Controllers = require('./controllers');
55 Services(ngApp, window.Events); 71 Services(ngApp, window.Events);
56 Directives(ngApp, window.Events); 72 Directives(ngApp, window.Events);
57 Controllers(ngApp, window.Events); 73 Controllers(ngApp, window.Events);
...@@ -154,4 +170,4 @@ if(navigator.userAgent.indexOf('MSIE')!==-1 ...@@ -154,4 +170,4 @@ if(navigator.userAgent.indexOf('MSIE')!==-1
154 } 170 }
155 171
156 // Page specific items 172 // Page specific items
157 -import "./pages/page-show"; 173 +require("./pages/page-show");
......
...@@ -60,7 +60,7 @@ function registerEditorShortcuts(editor) { ...@@ -60,7 +60,7 @@ function registerEditorShortcuts(editor) {
60 editor.addShortcut('meta+shift+E', '', ['FormatBlock', false, 'code']); 60 editor.addShortcut('meta+shift+E', '', ['FormatBlock', false, 'code']);
61 } 61 }
62 62
63 -export default function() { 63 +module.exports = function() {
64 let settings = { 64 let settings = {
65 selector: '#html-editor', 65 selector: '#html-editor',
66 content_css: [ 66 content_css: [
...@@ -68,6 +68,7 @@ export default function() { ...@@ -68,6 +68,7 @@ export default function() {
68 window.baseUrl('/libs/material-design-iconic-font/css/material-design-iconic-font.min.css') 68 window.baseUrl('/libs/material-design-iconic-font/css/material-design-iconic-font.min.css')
69 ], 69 ],
70 body_class: 'page-content', 70 body_class: 'page-content',
71 + browser_spellcheck: true,
71 relative_urls: false, 72 relative_urls: false,
72 remove_script_host: false, 73 remove_script_host: false,
73 document_base_url: window.baseUrl('/'), 74 document_base_url: window.baseUrl('/'),
...@@ -213,4 +214,4 @@ export default function() { ...@@ -213,4 +214,4 @@ export default function() {
213 } 214 }
214 }; 215 };
215 return settings; 216 return settings;
216 -}
...\ No newline at end of file ...\ No newline at end of file
217 +};
...\ No newline at end of file ...\ No newline at end of file
......
1 "use strict"; 1 "use strict";
2 // Configure ZeroClipboard 2 // Configure ZeroClipboard
3 -import Clipboard from "clipboard"; 3 +const Clipboard = require("clipboard");
4 4
5 -export default window.setupPageShow = function (pageId) { 5 +let setupPageShow = window.setupPageShow = function (pageId) {
6 6
7 // Set up pointer 7 // Set up pointer
8 let $pointer = $('#pointer').detach(); 8 let $pointer = $('#pointer').detach();
...@@ -81,6 +81,12 @@ export default window.setupPageShow = function (pageId) { ...@@ -81,6 +81,12 @@ export default window.setupPageShow = function (pageId) {
81 let $idElem = $(idElem); 81 let $idElem = $(idElem);
82 let color = $('#custom-styles').attr('data-color-light'); 82 let color = $('#custom-styles').attr('data-color-light');
83 $idElem.css('background-color', color).attr('data-highlighted', 'true').smoothScrollTo(); 83 $idElem.css('background-color', color).attr('data-highlighted', 'true').smoothScrollTo();
84 + setTimeout(() => {
85 + $idElem.addClass('anim').addClass('selectFade').css('background-color', '');
86 + setTimeout(() => {
87 + $idElem.removeClass('selectFade');
88 + }, 3000);
89 + }, 100);
84 } else { 90 } else {
85 $('.page-content').find(':contains("' + text + '")').smoothScrollTo(); 91 $('.page-content').find(':contains("' + text + '")').smoothScrollTo();
86 } 92 }
...@@ -151,3 +157,5 @@ export default window.setupPageShow = function (pageId) { ...@@ -151,3 +157,5 @@ export default window.setupPageShow = function (pageId) {
151 }); 157 });
152 158
153 }; 159 };
160 +
161 +module.exports = setupPageShow;
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -44,4 +44,4 @@ class Translator { ...@@ -44,4 +44,4 @@ class Translator {
44 44
45 } 45 }
46 46
47 -export default Translator 47 +module.exports = Translator;
......
1 +let data = {
2 + id: null,
3 + type: '',
4 + searching: false,
5 + searchTerm: '',
6 + searchResults: '',
7 +};
8 +
9 +let computed = {
10 +
11 +};
12 +
13 +let methods = {
14 +
15 + searchBook() {
16 + if (this.searchTerm.trim().length === 0) return;
17 + this.searching = true;
18 + this.searchResults = '';
19 + let url = window.baseUrl(`/search/${this.type}/${this.id}`);
20 + url += `?term=${encodeURIComponent(this.searchTerm)}`;
21 + this.$http.get(url).then(resp => {
22 + this.searchResults = resp.data;
23 + });
24 + },
25 +
26 + checkSearchForm() {
27 + this.searching = this.searchTerm > 0;
28 + },
29 +
30 + clearSearch() {
31 + this.searching = false;
32 + this.searchTerm = '';
33 + }
34 +
35 +};
36 +
37 +function mounted() {
38 + this.id = Number(this.$el.getAttribute('entity-id'));
39 + this.type = this.$el.getAttribute('entity-type');
40 +}
41 +
42 +module.exports = {
43 + data, computed, methods, mounted
44 +};
...\ No newline at end of file ...\ No newline at end of file
1 +const moment = require('moment');
2 +
3 +let data = {
4 + terms: '',
5 + termString : '',
6 + search: {
7 + type: {
8 + page: true,
9 + chapter: true,
10 + book: true
11 + },
12 + exactTerms: [],
13 + tagTerms: [],
14 + option: {},
15 + dates: {
16 + updated_after: false,
17 + updated_before: false,
18 + created_after: false,
19 + created_before: false,
20 + }
21 + }
22 +};
23 +
24 +let computed = {
25 +
26 +};
27 +
28 +let methods = {
29 +
30 + appendTerm(term) {
31 + this.termString += ' ' + term;
32 + this.termString = this.termString.replace(/\s{2,}/g, ' ');
33 + this.termString = this.termString.replace(/^\s+/, '');
34 + this.termString = this.termString.replace(/\s+$/, '');
35 + },
36 +
37 + exactParse(searchString) {
38 + this.search.exactTerms = [];
39 + let exactFilter = /"(.+?)"/g;
40 + let matches;
41 + while ((matches = exactFilter.exec(searchString)) !== null) {
42 + this.search.exactTerms.push(matches[1]);
43 + }
44 + },
45 +
46 + exactChange() {
47 + let exactFilter = /"(.+?)"/g;
48 + this.termString = this.termString.replace(exactFilter, '');
49 + let matchesTerm = this.search.exactTerms.filter(term => {
50 + return term.trim() !== '';
51 + }).map(term => {
52 + return `"${term}"`
53 + }).join(' ');
54 + this.appendTerm(matchesTerm);
55 + },
56 +
57 + addExact() {
58 + this.search.exactTerms.push('');
59 + setTimeout(() => {
60 + let exactInputs = document.querySelectorAll('.exact-input');
61 + exactInputs[exactInputs.length - 1].focus();
62 + }, 100);
63 + },
64 +
65 + removeExact(index) {
66 + this.search.exactTerms.splice(index, 1);
67 + this.exactChange();
68 + },
69 +
70 + tagParse(searchString) {
71 + this.search.tagTerms = [];
72 + let tagFilter = /\[(.+?)\]/g;
73 + let matches;
74 + while ((matches = tagFilter.exec(searchString)) !== null) {
75 + this.search.tagTerms.push(matches[1]);
76 + }
77 + },
78 +
79 + tagChange() {
80 + let tagFilter = /\[(.+?)\]/g;
81 + this.termString = this.termString.replace(tagFilter, '');
82 + let matchesTerm = this.search.tagTerms.filter(term => {
83 + return term.trim() !== '';
84 + }).map(term => {
85 + return `[${term}]`
86 + }).join(' ');
87 + this.appendTerm(matchesTerm);
88 + },
89 +
90 + addTag() {
91 + this.search.tagTerms.push('');
92 + setTimeout(() => {
93 + let tagInputs = document.querySelectorAll('.tag-input');
94 + tagInputs[tagInputs.length - 1].focus();
95 + }, 100);
96 + },
97 +
98 + removeTag(index) {
99 + this.search.tagTerms.splice(index, 1);
100 + this.tagChange();
101 + },
102 +
103 + typeParse(searchString) {
104 + let typeFilter = /{\s?type:\s?(.*?)\s?}/;
105 + let match = searchString.match(typeFilter);
106 + let type = this.search.type;
107 + if (!match) {
108 + type.page = type.book = type.chapter = true;
109 + return;
110 + }
111 + let splitTypes = match[1].replace(/ /g, '').split('|');
112 + type.page = (splitTypes.indexOf('page') !== -1);
113 + type.chapter = (splitTypes.indexOf('chapter') !== -1);
114 + type.book = (splitTypes.indexOf('book') !== -1);
115 + },
116 +
117 + typeChange() {
118 + let typeFilter = /{\s?type:\s?(.*?)\s?}/;
119 + let type = this.search.type;
120 + if (type.page === type.chapter && type.page === type.book) {
121 + this.termString = this.termString.replace(typeFilter, '');
122 + return;
123 + }
124 + let selectedTypes = Object.keys(type).filter(type => {return this.search.type[type];}).join('|');
125 + let typeTerm = '{type:'+selectedTypes+'}';
126 + if (this.termString.match(typeFilter)) {
127 + this.termString = this.termString.replace(typeFilter, typeTerm);
128 + return;
129 + }
130 + this.appendTerm(typeTerm);
131 + },
132 +
133 + optionParse(searchString) {
134 + let optionFilter = /{([a-z_\-:]+?)}/gi;
135 + let matches;
136 + while ((matches = optionFilter.exec(searchString)) !== null) {
137 + this.search.option[matches[1].toLowerCase()] = true;
138 + }
139 + },
140 +
141 + optionChange(optionName) {
142 + let isChecked = this.search.option[optionName];
143 + if (isChecked) {
144 + this.appendTerm(`{${optionName}}`);
145 + } else {
146 + this.termString = this.termString.replace(`{${optionName}}`, '');
147 + }
148 + },
149 +
150 + updateSearch(e) {
151 + e.preventDefault();
152 + window.location = '/search?term=' + encodeURIComponent(this.termString);
153 + },
154 +
155 + enableDate(optionName) {
156 + this.search.dates[optionName.toLowerCase()] = moment().format('YYYY-MM-DD');
157 + this.dateChange(optionName);
158 + },
159 +
160 + dateParse(searchString) {
161 + let dateFilter = /{([a-z_\-]+?):([a-z_\-0-9]+?)}/gi;
162 + let dateTags = Object.keys(this.search.dates);
163 + let matches;
164 + while ((matches = dateFilter.exec(searchString)) !== null) {
165 + if (dateTags.indexOf(matches[1]) === -1) continue;
166 + this.search.dates[matches[1].toLowerCase()] = matches[2];
167 + }
168 + },
169 +
170 + dateChange(optionName) {
171 + let dateFilter = new RegExp('{\\s?'+optionName+'\\s?:([a-z_\\-0-9]+?)}', 'gi');
172 + this.termString = this.termString.replace(dateFilter, '');
173 + if (!this.search.dates[optionName]) return;
174 + this.appendTerm(`{${optionName}:${this.search.dates[optionName]}}`);
175 + },
176 +
177 + dateRemove(optionName) {
178 + this.search.dates[optionName] = false;
179 + this.dateChange(optionName);
180 + }
181 +
182 +};
183 +
184 +function created() {
185 + this.termString = document.querySelector('[name=searchTerm]').value;
186 + this.typeParse(this.termString);
187 + this.exactParse(this.termString);
188 + this.tagParse(this.termString);
189 + this.optionParse(this.termString);
190 + this.dateParse(this.termString);
191 +}
192 +
193 +module.exports = {
194 + data, computed, methods, created
195 +};
...\ No newline at end of file ...\ No newline at end of file
1 +const Vue = require("vue");
2 +
3 +function exists(id) {
4 + return document.getElementById(id) !== null;
5 +}
6 +
7 +let vueMapping = {
8 + 'search-system': require('./search'),
9 + 'entity-dashboard': require('./entity-search'),
10 +};
11 +
12 +Object.keys(vueMapping).forEach(id => {
13 + if (exists(id)) {
14 + let config = vueMapping[id];
15 + config.el = '#' + id;
16 + new Vue(config);
17 + }
18 +});
...\ No newline at end of file ...\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 .anim.fadeIn { 2 .anim.fadeIn {
3 opacity: 0; 3 opacity: 0;
4 animation-name: fadeIn; 4 animation-name: fadeIn;
5 - animation-duration: 160ms; 5 + animation-duration: 180ms;
6 animation-timing-function: ease-in-out; 6 animation-timing-function: ease-in-out;
7 animation-fill-mode: forwards; 7 animation-fill-mode: forwards;
8 } 8 }
...@@ -127,3 +127,7 @@ ...@@ -127,3 +127,7 @@
127 animation-delay: 0s; 127 animation-delay: 0s;
128 animation-timing-function: cubic-bezier(.62, .28, .23, .99); 128 animation-timing-function: cubic-bezier(.62, .28, .23, .99);
129 } 129 }
130 +
131 +.anim.selectFade {
132 + transition: background-color ease-in-out 3000ms;
133 +}
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -98,19 +98,36 @@ label { ...@@ -98,19 +98,36 @@ label {
98 98
99 label.radio, label.checkbox { 99 label.radio, label.checkbox {
100 font-weight: 400; 100 font-weight: 400;
101 + user-select: none;
101 input[type="radio"], input[type="checkbox"] { 102 input[type="radio"], input[type="checkbox"] {
102 margin-right: $-xs; 103 margin-right: $-xs;
103 } 104 }
104 } 105 }
105 106
107 +label.inline.checkbox {
108 + margin-right: $-m;
109 +}
110 +
106 label + p.small { 111 label + p.small {
107 margin-bottom: 0.8em; 112 margin-bottom: 0.8em;
108 } 113 }
109 114
110 -input[type="text"], input[type="number"], input[type="email"], input[type="search"], input[type="url"], input[type="password"], select, textarea { 115 +table.form-table {
116 + max-width: 100%;
117 + td {
118 + overflow: hidden;
119 + padding: $-xxs/2 0;
120 + }
121 +}
122 +
123 +input[type="text"], input[type="number"], input[type="email"], input[type="date"], input[type="search"], input[type="url"], input[type="password"], select, textarea {
111 @extend .input-base; 124 @extend .input-base;
112 } 125 }
113 126
127 +input[type=date] {
128 + width: 190px;
129 +}
130 +
114 .toggle-switch { 131 .toggle-switch {
115 display: inline-block; 132 display: inline-block;
116 background-color: #BBB; 133 background-color: #BBB;
......
...@@ -109,6 +109,7 @@ ...@@ -109,6 +109,7 @@
109 transition-property: right, border; 109 transition-property: right, border;
110 border-left: 0px solid #FFF; 110 border-left: 0px solid #FFF;
111 background-color: #FFF; 111 background-color: #FFF;
112 + max-width: 320px;
112 &.fixed { 113 &.fixed {
113 background-color: #FFF; 114 background-color: #FFF;
114 z-index: 5; 115 z-index: 5;
......
...@@ -269,19 +269,31 @@ span.highlight { ...@@ -269,19 +269,31 @@ span.highlight {
269 /* 269 /*
270 * Lists 270 * Lists
271 */ 271 */
272 +ul, ol {
273 + overflow: hidden;
274 + p {
275 + margin: 0;
276 + }
277 +}
272 ul { 278 ul {
273 padding-left: $-m * 1.3; 279 padding-left: $-m * 1.3;
274 list-style: disc; 280 list-style: disc;
275 - overflow: hidden; 281 + ul {
282 + list-style: circle;
283 + margin-top: 0;
284 + margin-bottom: 0;
285 + }
286 + label {
287 + margin: 0;
288 + }
276 } 289 }
277 290
278 ol { 291 ol {
279 list-style: decimal; 292 list-style: decimal;
280 padding-left: $-m * 2; 293 padding-left: $-m * 2;
281 - overflow: hidden;
282 } 294 }
283 295
284 -li.checkbox-item { 296 +li.checkbox-item, li.task-list-item {
285 list-style: none; 297 list-style: none;
286 margin-left: - ($-m * 1.3); 298 margin-left: - ($-m * 1.3);
287 input[type="checkbox"] { 299 input[type="checkbox"] {
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
7 @import "grid"; 7 @import "grid";
8 @import "blocks"; 8 @import "blocks";
9 @import "buttons"; 9 @import "buttons";
10 -@import "forms";
11 @import "tables"; 10 @import "tables";
11 +@import "forms";
12 @import "animations"; 12 @import "animations";
13 @import "tinymce"; 13 @import "tinymce";
14 @import "highlightjs"; 14 @import "highlightjs";
...@@ -17,7 +17,11 @@ ...@@ -17,7 +17,11 @@
17 @import "lists"; 17 @import "lists";
18 @import "pages"; 18 @import "pages";
19 19
20 -[v-cloak], [v-show] {display: none;} 20 +[v-cloak], [v-show] {
21 + display: none; opacity: 0;
22 + animation-name: none !important;
23 +}
24 +
21 25
22 [ng\:cloak], [ng-cloak], .ng-cloak { 26 [ng\:cloak], [ng-cloak], .ng-cloak {
23 display: none !important; 27 display: none !important;
...@@ -272,8 +276,3 @@ $btt-size: 40px; ...@@ -272,8 +276,3 @@ $btt-size: 40px;
272 276
273 277
274 278
275 -
276 -
277 -
278 -
279 -
......
...@@ -43,18 +43,9 @@ return [ ...@@ -43,18 +43,9 @@ return [
43 * Search 43 * Search
44 */ 44 */
45 'search_results' => 'Suchergebnisse', 45 'search_results' => 'Suchergebnisse',
46 - 'search_results_page' => 'Seiten-Suchergebnisse',
47 - 'search_results_chapter' => 'Kapitel-Suchergebnisse',
48 - 'search_results_book' => 'Buch-Suchergebnisse',
49 'search_clear' => 'Suche zur&uuml;cksetzen', 46 'search_clear' => 'Suche zur&uuml;cksetzen',
50 - 'search_view_pages' => 'Zeige alle passenden Seiten',
51 - 'search_view_chapters' => 'Zeige alle passenden Kapitel',
52 - 'search_view_books' => 'Zeige alle passenden B&uuml;cher',
53 'search_no_pages' => 'Es wurden keine passenden Suchergebnisse gefunden', 47 'search_no_pages' => 'Es wurden keine passenden Suchergebnisse gefunden',
54 'search_for_term' => 'Suche nach :term', 48 'search_for_term' => 'Suche nach :term',
55 - 'search_page_for_term' => 'Suche nach :term in Seiten',
56 - 'search_chapter_for_term' => 'Suche nach :term in Kapiteln',
57 - 'search_book_for_term' => 'Suche nach :term in B&uuml;chern',
58 49
59 /** 50 /**
60 * Books 51 * Books
......
...@@ -33,6 +33,7 @@ return [ ...@@ -33,6 +33,7 @@ return [
33 'search_clear' => 'Clear Search', 33 'search_clear' => 'Clear Search',
34 'reset' => 'Reset', 34 'reset' => 'Reset',
35 'remove' => 'Remove', 35 'remove' => 'Remove',
36 + 'add' => 'Add',
36 37
37 38
38 /** 39 /**
......
...@@ -14,6 +14,7 @@ return [ ...@@ -14,6 +14,7 @@ return [
14 'recent_activity' => 'Recent Activity', 14 'recent_activity' => 'Recent Activity',
15 'create_now' => 'Create one now', 15 'create_now' => 'Create one now',
16 'revisions' => 'Revisions', 16 'revisions' => 'Revisions',
17 + 'meta_revision' => 'Revision #:revisionCount',
17 'meta_created' => 'Created :timeLength', 18 'meta_created' => 'Created :timeLength',
18 'meta_created_name' => 'Created :timeLength by :user', 19 'meta_created_name' => 'Created :timeLength by :user',
19 'meta_updated' => 'Updated :timeLength', 20 'meta_updated' => 'Updated :timeLength',
...@@ -43,18 +44,26 @@ return [ ...@@ -43,18 +44,26 @@ return [
43 * Search 44 * Search
44 */ 45 */
45 'search_results' => 'Search Results', 46 'search_results' => 'Search Results',
46 - 'search_results_page' => 'Page Search Results', 47 + 'search_total_results_found' => ':count result found|:count total results found',
47 - 'search_results_chapter' => 'Chapter Search Results',
48 - 'search_results_book' => 'Book Search Results',
49 'search_clear' => 'Clear Search', 48 'search_clear' => 'Clear Search',
50 - 'search_view_pages' => 'View all matches pages',
51 - 'search_view_chapters' => 'View all matches chapters',
52 - 'search_view_books' => 'View all matches books',
53 'search_no_pages' => 'No pages matched this search', 49 'search_no_pages' => 'No pages matched this search',
54 'search_for_term' => 'Search for :term', 50 'search_for_term' => 'Search for :term',
55 - 'search_page_for_term' => 'Page search for :term', 51 + 'search_more' => 'More Results',
56 - 'search_chapter_for_term' => 'Chapter search for :term', 52 + 'search_filters' => 'Search Filters',
57 - 'search_book_for_term' => 'Books search for :term', 53 + 'search_content_type' => 'Content Type',
54 + 'search_exact_matches' => 'Exact Matches',
55 + 'search_tags' => 'Tag Searches',
56 + 'search_viewed_by_me' => 'Viewed by me',
57 + 'search_not_viewed_by_me' => 'Not viewed by me',
58 + 'search_permissions_set' => 'Permissions set',
59 + 'search_created_by_me' => 'Created by me',
60 + 'search_updated_by_me' => 'Updated by me',
61 + 'search_updated_before' => 'Updated before',
62 + 'search_updated_after' => 'Updated after',
63 + 'search_created_before' => 'Created before',
64 + 'search_created_after' => 'Created after',
65 + 'search_set_date' => 'Set Date',
66 + 'search_update' => 'Update Search',
58 67
59 /** 68 /**
60 * Books 69 * Books
...@@ -112,6 +121,7 @@ return [ ...@@ -112,6 +121,7 @@ return [
112 'chapters_empty' => 'No pages are currently in this chapter.', 121 'chapters_empty' => 'No pages are currently in this chapter.',
113 'chapters_permissions_active' => 'Chapter Permissions Active', 122 'chapters_permissions_active' => 'Chapter Permissions Active',
114 'chapters_permissions_success' => 'Chapter Permissions Updated', 123 'chapters_permissions_success' => 'Chapter Permissions Updated',
124 + 'chapters_search_this' => 'Search this chapter',
115 125
116 /** 126 /**
117 * Pages 127 * Pages
...@@ -159,6 +169,7 @@ return [ ...@@ -159,6 +169,7 @@ return [
159 'pages_revision_named' => 'Page Revision for :pageName', 169 'pages_revision_named' => 'Page Revision for :pageName',
160 'pages_revisions_created_by' => 'Created By', 170 'pages_revisions_created_by' => 'Created By',
161 'pages_revisions_date' => 'Revision Date', 171 'pages_revisions_date' => 'Revision Date',
172 + 'pages_revisions_number' => '#',
162 'pages_revisions_changelog' => 'Changelog', 173 'pages_revisions_changelog' => 'Changelog',
163 'pages_revisions_changes' => 'Changes', 174 'pages_revisions_changes' => 'Changes',
164 'pages_revisions_current' => 'Current Version', 175 'pages_revisions_current' => 'Current Version',
......
...@@ -120,6 +120,7 @@ return [ ...@@ -120,6 +120,7 @@ return [
120 'fr' => 'Français', 120 'fr' => 'Français',
121 'nl' => 'Nederlands', 121 'nl' => 'Nederlands',
122 'pt_BR' => 'Português do Brasil', 122 'pt_BR' => 'Português do Brasil',
123 + 'sk' => 'Slovensky',
123 ] 124 ]
124 /////////////////////////////////// 125 ///////////////////////////////////
125 ]; 126 ];
......
...@@ -11,7 +11,7 @@ return [ ...@@ -11,7 +11,7 @@ return [
11 | 11 |
12 */ 12 */
13 'failed' => 'Las credenciales no concuerdan con nuestros registros.', 13 'failed' => 'Las credenciales no concuerdan con nuestros registros.',
14 - 'throttle' => 'Demasiados intentos fallidos de conexiÃn. Por favor intente nuevamente en :seconds segundos.', 14 + 'throttle' => 'Demasiados intentos fallidos de conexión. Por favor intente nuevamente en :seconds segundos.',
15 15
16 /** 16 /**
17 * Login & Register 17 * Login & Register
......
...@@ -18,7 +18,7 @@ return [ ...@@ -18,7 +18,7 @@ return [
18 'image_dropzone' => 'Arrastre las imágenes o hacer click aquí para Subir', 18 'image_dropzone' => 'Arrastre las imágenes o hacer click aquí para Subir',
19 'images_deleted' => 'Imágenes borradas', 19 'images_deleted' => 'Imágenes borradas',
20 'image_preview' => 'Preview de la imagen', 20 'image_preview' => 'Preview de la imagen',
21 - 'image_upload_success' => 'Imagen subida exitosamente', 21 + 'image_upload_success' => 'Imagen subida éxitosamente',
22 'image_update_success' => 'Detalles de la imagen actualizados exitosamente', 22 'image_update_success' => 'Detalles de la imagen actualizados exitosamente',
23 'image_delete_success' => 'Imagen borrada exitosamente' 23 'image_delete_success' => 'Imagen borrada exitosamente'
24 ]; 24 ];
......
...@@ -4,7 +4,7 @@ return [ ...@@ -4,7 +4,7 @@ return [
4 /** 4 /**
5 * Shared 5 * Shared
6 */ 6 */
7 - 'recently_created' => 'Recientemente creadod', 7 + 'recently_created' => 'Recientemente creado',
8 'recently_created_pages' => 'Páginas recientemente creadas', 8 'recently_created_pages' => 'Páginas recientemente creadas',
9 'recently_updated_pages' => 'Páginas recientemente actualizadas', 9 'recently_updated_pages' => 'Páginas recientemente actualizadas',
10 'recently_created_chapters' => 'Capítulos recientemente creados', 10 'recently_created_chapters' => 'Capítulos recientemente creados',
...@@ -43,18 +43,9 @@ return [ ...@@ -43,18 +43,9 @@ return [
43 * Search 43 * Search
44 */ 44 */
45 'search_results' => 'Buscar resultados', 45 'search_results' => 'Buscar resultados',
46 - 'search_results_page' => 'resultados de búsqueda en página',
47 - 'search_results_chapter' => 'Resultados de búsqueda en capítulo ',
48 - 'search_results_book' => 'Resultados de búsqueda en libro',
49 'search_clear' => 'Limpiar resultados', 46 'search_clear' => 'Limpiar resultados',
50 - 'search_view_pages' => 'Ver todas las páginas que concuerdan',
51 - 'search_view_chapters' => 'Ver todos los capítulos que concuerdan',
52 - 'search_view_books' => 'Ver todos los libros que concuerdan',
53 'search_no_pages' => 'Ninguna página encontrada para la búsqueda', 47 'search_no_pages' => 'Ninguna página encontrada para la búsqueda',
54 'search_for_term' => 'Busqueda por :term', 48 'search_for_term' => 'Busqueda por :term',
55 - 'search_page_for_term' => 'Búsqueda de página por :term',
56 - 'search_chapter_for_term' => 'Búsqueda por capítulo de :term',
57 - 'search_book_for_term' => 'Búsqueda en libro de :term',
58 49
59 /** 50 /**
60 * Books 51 * Books
...@@ -148,79 +139,79 @@ return [ ...@@ -148,79 +139,79 @@ return [
148 'pages_md_editor' => 'Editor', 139 'pages_md_editor' => 'Editor',
149 'pages_md_preview' => 'Preview', 140 'pages_md_preview' => 'Preview',
150 'pages_md_insert_image' => 'Insertar Imagen', 141 'pages_md_insert_image' => 'Insertar Imagen',
151 - 'pages_md_insert_link' => 'Insert Entity Link', 142 + 'pages_md_insert_link' => 'Insertar link de entidad',
152 - 'pages_not_in_chapter' => 'Page is not in a chapter', 143 + 'pages_not_in_chapter' => 'La página no esá en el caítulo',
153 - 'pages_move' => 'Move Page', 144 + 'pages_move' => 'Mover página',
154 - 'pages_move_success' => 'Page moved to ":parentName"', 145 + 'pages_move_success' => 'Página movida a ":parentName"',
155 - 'pages_permissions' => 'Page Permissions', 146 + 'pages_permissions' => 'Permisos de página',
156 - 'pages_permissions_success' => 'Page permissions updated', 147 + 'pages_permissions_success' => 'Permisos de página actualizados',
157 - 'pages_revisions' => 'Page Revisions', 148 + 'pages_revisions' => 'Revisiones de página',
158 - 'pages_revisions_named' => 'Page Revisions for :pageName', 149 + 'pages_revisions_named' => 'Revisiones de página para :pageName',
159 - 'pages_revision_named' => 'Page Revision for :pageName', 150 + 'pages_revision_named' => 'Revisión de ágina para :pageName',
160 - 'pages_revisions_created_by' => 'Created By', 151 + 'pages_revisions_created_by' => 'Creado por',
161 - 'pages_revisions_date' => 'Revision Date', 152 + 'pages_revisions_date' => 'Fecha de revisión',
162 'pages_revisions_changelog' => 'Changelog', 153 'pages_revisions_changelog' => 'Changelog',
163 - 'pages_revisions_changes' => 'Changes', 154 + 'pages_revisions_changes' => 'Cambios',
164 - 'pages_revisions_current' => 'Current Version', 155 + 'pages_revisions_current' => 'Versión actual',
165 'pages_revisions_preview' => 'Preview', 156 'pages_revisions_preview' => 'Preview',
166 - 'pages_revisions_restore' => 'Restore', 157 + 'pages_revisions_restore' => 'Restaurar',
167 - 'pages_revisions_none' => 'This page has no revisions', 158 + 'pages_revisions_none' => 'Esta página no tiene revisiones',
168 - 'pages_copy_link' => 'Copy Link', 159 + 'pages_copy_link' => 'Copiar Link',
169 - 'pages_permissions_active' => 'Page Permissions Active', 160 + 'pages_permissions_active' => 'Permisos de página activos',
170 - 'pages_initial_revision' => 'Initial publish', 161 + 'pages_initial_revision' => 'Publicación inicial',
171 - 'pages_initial_name' => 'New Page', 162 + 'pages_initial_name' => 'Página nueva',
172 - 'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.', 163 + 'pages_editing_draft_notification' => 'Ud. está actualmente editando un borrador que fue guardado porúltima vez el :timeDiff.',
173 - 'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.', 164 + 'pages_draft_edited_notification' => 'Esta página ha sido actualizada desde aquel momento. Se recomienda que cancele este borrador.',
174 'pages_draft_edit_active' => [ 165 'pages_draft_edit_active' => [
175 - 'start_a' => ':count users have started editing this page', 166 + 'start_a' => ':count usuarios han comenzado a editar esta página',
176 - 'start_b' => ':userName has started editing this page', 167 + 'start_b' => ':userName ha comenzado a editar esta página',
177 - 'time_a' => 'since the pages was last updated', 168 + 'time_a' => 'desde que las página fue actualizada',
178 - 'time_b' => 'in the last :minCount minutes', 169 + 'time_b' => 'en los Ãltimos :minCount minutos',
179 - 'message' => ':start :time. Take care not to overwrite each other\'s updates!', 170 + 'message' => ':start :time. Ten cuidado de no sobreescribir los cambios del otro usuario',
180 ], 171 ],
181 - 'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content', 172 + 'pages_draft_discarded' => 'Borrador descartado, el editor ha sido actualizado con el contenido de la página actual',
182 173
183 /** 174 /**
184 * Editor sidebar 175 * Editor sidebar
185 */ 176 */
186 - 'page_tags' => 'Page Tags', 177 + 'page_tags' => 'Etiquetas de página',
187 - 'tag' => 'Tag', 178 + 'tag' => 'Etiqueta',
188 - 'tags' => '', 179 + 'tags' => 'Etiquetas',
189 - 'tag_value' => 'Tag Value (Optional)', 180 + 'tag_value' => 'Valor de la etiqueta (Opcional)',
190 - 'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.", 181 + 'tags_explain' => "Agregar algunas etiquetas para mejorar la categorización de su contenido. \n Ud. puede asignar un valor a una etiqueta para una organizacón a mayor detalle.",
191 - 'tags_add' => 'Add another tag', 182 + 'tags_add' => 'Agregar otra etiqueta',
192 - 'attachments' => 'Attachments', 183 + 'attachments' => 'Adjuntos',
193 - 'attachments_explain' => 'Upload some files or attach some link to display on your page. These are visible in the page sidebar.', 184 + 'attachments_explain' => 'Subir ficheros o agregar links para mostrar en la página. Estos son visibles en la barra lateral de la página.',
194 - 'attachments_explain_instant_save' => 'Changes here are saved instantly.', 185 + 'attachments_explain_instant_save' => 'Los cambios son guardados de manera instantánea .',
195 - 'attachments_items' => 'Attached Items', 186 + 'attachments_items' => 'Items adjuntados',
196 - 'attachments_upload' => 'Upload File', 187 + 'attachments_upload' => 'Fichero adjuntado',
197 - 'attachments_link' => 'Attach Link', 188 + 'attachments_link' => 'Adjuntar Link',
198 - 'attachments_set_link' => 'Set Link', 189 + 'attachments_set_link' => 'Setear Link',
199 - 'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.', 190 + 'attachments_delete_confirm' => 'Haga click en borrar nuevamente para confirmar que quiere borrar este adjunto.',
200 - 'attachments_dropzone' => 'Drop files or click here to attach a file', 191 + 'attachments_dropzone' => 'Arrastre ficheros aquío haga click aquípara adjuntar un fichero',
201 - 'attachments_no_files' => 'No files have been uploaded', 192 + 'attachments_no_files' => 'NingÃn fichero ha sido adjuntado',
202 - 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', 193 + 'attachments_explain_link' => 'Ud. puede agregar un link o si lo prefiere puede agregar un fichero. Esto puede ser un link a otra página o un link a un fichero en la nube.',
203 - 'attachments_link_name' => 'Link Name', 194 + 'attachments_link_name' => 'Nombre de Link',
204 - 'attachment_link' => 'Attachment link', 195 + 'attachment_link' => 'Link adjunto',
205 - 'attachments_link_url' => 'Link to file', 196 + 'attachments_link_url' => 'Link a fichero',
206 - 'attachments_link_url_hint' => 'Url of site or file', 197 + 'attachments_link_url_hint' => 'Url del sitio o fichero',
207 - 'attach' => 'Attach', 198 + 'attach' => 'Adjuntar',
208 - 'attachments_edit_file' => 'Edit File', 199 + 'attachments_edit_file' => 'Editar fichero',
209 - 'attachments_edit_file_name' => 'File Name', 200 + 'attachments_edit_file_name' => 'Nombre del fichero',
210 - 'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite', 201 + 'attachments_edit_drop_upload' => 'Arrastre a los ficheros o haga click aquípara subir o sobreescribir',
211 - 'attachments_order_updated' => 'Attachment order updated', 202 + 'attachments_order_updated' => 'Orden de adjuntos actualizado',
212 - 'attachments_updated_success' => 'Attachment details updated', 203 + 'attachments_updated_success' => 'Detalles de adjuntos actualizados',
213 - 'attachments_deleted' => 'Attachment deleted', 204 + 'attachments_deleted' => 'Adjunto borrado',
214 - 'attachments_file_uploaded' => 'File successfully uploaded', 205 + 'attachments_file_uploaded' => 'Fichero subido éxitosamente',
215 - 'attachments_file_updated' => 'File successfully updated', 206 + 'attachments_file_updated' => 'Fichero actualizado éxitosamente',
216 - 'attachments_link_attached' => 'Link successfully attached to page', 207 + 'attachments_link_attached' => 'Link agregado éxitosamente a la ágina',
217 208
218 /** 209 /**
219 * Profile View 210 * Profile View
220 */ 211 */
221 - 'profile_user_for_x' => 'User for :time', 212 + 'profile_user_for_x' => 'Usuario para :time',
222 - 'profile_created_content' => 'Created Content', 213 + 'profile_created_content' => 'Contenido creado',
223 - 'profile_not_created_pages' => ':userName has not created any pages', 214 + 'profile_not_created_pages' => ':userName no ha creado ninguna página',
224 - 'profile_not_created_chapters' => ':userName has not created any chapters', 215 + 'profile_not_created_chapters' => ':userName no ha creado ningún capítulo',
225 - 'profile_not_created_books' => ':userName has not created any books', 216 + 'profile_not_created_books' => ':userName no ha creado ningún libro',
226 ]; 217 ];
......
...@@ -7,64 +7,64 @@ return [ ...@@ -7,64 +7,64 @@ return [
7 */ 7 */
8 8
9 // Permissions 9 // Permissions
10 - 'permission' => 'You do not have permission to access the requested page.', 10 + 'permission' => 'Ud. no tiene permisos para visualizar la página solicitada.',
11 - 'permissionJson' => 'You do not have permission to perform the requested action.', 11 + 'permissionJson' => 'Ud. no tiene permisos para ejecutar la acción solicitada.',
12 12
13 // Auth 13 // Auth
14 - 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.', 14 + 'error_user_exists_different_creds' => 'Un usuario con el email :email ya existe pero con credenciales diferentes.',
15 - 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', 15 + 'email_already_confirmed' => 'El email ya ha sido confirmado, Intente loguearse en la aplicación.',
16 - 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', 16 + 'email_confirmation_invalid' => 'Este token de confirmación no e válido o ya ha sido usado,Intente registrar uno nuevamente.',
17 - 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', 17 + 'email_confirmation_expired' => 'El token de confirmación ha expirado, Un nuevo email de confirmacón ha sido enviado.',
18 - 'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind', 18 + 'ldap_fail_anonymous' => 'El acceso con LDAP ha fallado usando binding anónimo',
19 - 'ldap_fail_authed' => 'LDAP access failed using given dn & password details', 19 + 'ldap_fail_authed' => 'El acceso LDAP usando el dn & password detallados',
20 - 'ldap_extension_not_installed' => 'LDAP PHP extension not installed', 20 + 'ldap_extension_not_installed' => 'La extensión LDAP PHP no se encuentra instalada',
21 - 'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed', 21 + 'ldap_cannot_connect' => 'No se puede conectar con el servidor ldap, la conexión inicial ha fallado',
22 - 'social_no_action_defined' => 'No action defined', 22 + 'social_no_action_defined' => 'Acción no definida',
23 - 'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.', 23 + 'social_account_in_use' => 'la cuenta :socialAccount ya se encuentra en uso, intente loguearse a través de la opcón :socialAccount .',
24 - 'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.', 24 + 'social_account_email_in_use' => 'El email :email ya se encuentra en uso. Si ud. ya dispone de una cuenta puede loguearse a través de su cuenta :socialAccount desde la configuración de perfil.',
25 - 'social_account_existing' => 'This :socialAccount is already attached to your profile.', 25 + 'social_account_existing' => 'La cuenta :socialAccount ya se encuentra asignada a su perfil.',
26 - 'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.', 26 + 'social_account_already_used_existing' => 'La cuenta :socialAccount ya se encuentra usada por otro usuario.',
27 - 'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ', 27 + 'social_account_not_used' => 'La cuenta :socialAccount no está asociada a ningún usuario. Por favor adjuntela a su configuración de perfil. ',
28 - 'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.', 28 + 'social_account_register_instructions' => 'Si no dispone de una cuenta, puede registrar una cuenta usando la opción de :socialAccount .',
29 - 'social_driver_not_found' => 'Social driver not found', 29 + 'social_driver_not_found' => 'Driver social no encontrado',
30 - 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', 30 + 'social_driver_not_configured' => 'Su configuración :socialAccount no es correcta.',
31 31
32 // System 32 // System
33 - 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', 33 + 'path_not_writable' => 'La ruta :filePath no pudo ser cargada. Asegurese de que es escribible por el servidor.',
34 - 'cannot_get_image_from_url' => 'Cannot get image from :url', 34 + 'cannot_get_image_from_url' => 'No se puede obtener la imagen desde :url',
35 - 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', 35 + 'cannot_create_thumbs' => 'El servidor no puede crear la imagen miniatura. Por favor chequee que tiene la extensión GD instalada.',
36 - 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.', 36 + 'server_upload_limit' => 'El servidor no permite la subida de ficheros de este tamañ. Por favor intente con un fichero de menor tamañ.',
37 - 'image_upload_error' => 'An error occurred uploading the image', 37 + 'image_upload_error' => 'Ha ocurrido un error al subir la imagen',
38 38
39 // Attachments 39 // Attachments
40 - 'attachment_page_mismatch' => 'Page mismatch during attachment update', 40 + 'attachment_page_mismatch' => 'Página no coincidente durante la subida del adjunto ',
41 41
42 // Pages 42 // Pages
43 - 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page', 43 + 'page_draft_autosave_fail' => 'Fallo al guardar borrador. Asegurese de que tiene conexión a Internet antes de guardar este borrador',
44 44
45 // Entities 45 // Entities
46 - 'entity_not_found' => 'Entity not found', 46 + 'entity_not_found' => 'Entidad no encontrada',
47 - 'book_not_found' => 'Book not found', 47 + 'book_not_found' => 'Libro no encontrado',
48 - 'page_not_found' => 'Page not found', 48 + 'page_not_found' => 'Página no encontrada',
49 - 'chapter_not_found' => 'Chapter not found', 49 + 'chapter_not_found' => 'Capítulo no encontrado',
50 - 'selected_book_not_found' => 'The selected book was not found', 50 + 'selected_book_not_found' => 'El libro seleccionado no fue encontrado',
51 - 'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found', 51 + 'selected_book_chapter_not_found' => 'El libro o capítulo seleccionado no fue encontrado',
52 - 'guests_cannot_save_drafts' => 'Guests cannot save drafts', 52 + 'guests_cannot_save_drafts' => 'Los invitados no pueden guardar los borradores',
53 53
54 // Users 54 // Users
55 - 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', 55 + 'users_cannot_delete_only_admin' => 'No se puede borrar el único administrador',
56 - 'users_cannot_delete_guest' => 'You cannot delete the guest user', 56 + 'users_cannot_delete_guest' => 'No se puede borrar el usuario invitado',
57 57
58 // Roles 58 // Roles
59 - 'role_cannot_be_edited' => 'This role cannot be edited', 59 + 'role_cannot_be_edited' => 'Este rol no puede ser editado',
60 - 'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted', 60 + 'role_system_cannot_be_deleted' => 'Este rol es un rol de sistema y no puede ser borrado',
61 - 'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role', 61 + 'role_registration_default_cannot_delete' => 'Este rol no puede ser borrado mientras sea el rol por defecto de registro',
62 62
63 // Error pages 63 // Error pages
64 - '404_page_not_found' => 'Page Not Found', 64 + '404_page_not_found' => 'Página no encontrada',
65 - 'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.', 65 + 'sorry_page_not_found' => 'Lo sentimos, la página que intenta acceder no pudo ser encontrada.',
66 - 'return_home' => 'Return to home', 66 + 'return_home' => 'Volver al home',
67 - 'error_occurred' => 'An Error Occurred', 67 + 'error_occurred' => 'Ha ocurrido un error',
68 - 'app_down' => ':appName is down right now', 68 + 'app_down' => 'La aplicación :appName se encuentra caída en este momento',
69 - 'back_soon' => 'It will be back up soon.', 69 + 'back_soon' => 'Volverá a estar operativa en corto tiempo.',
70 ]; 70 ];
......
...@@ -13,67 +13,67 @@ return [ ...@@ -13,67 +13,67 @@ return [
13 | 13 |
14 */ 14 */
15 15
16 - 'accepted' => 'The :attribute must be accepted.', 16 + 'accepted' => 'El :attribute debe ser aceptado.',
17 - 'active_url' => 'The :attribute is not a valid URL.', 17 + 'active_url' => 'El :attribute no es una URl válida.',
18 - 'after' => 'The :attribute must be a date after :date.', 18 + 'after' => 'El :attribute debe ser una fecha posterior :date.',
19 - 'alpha' => 'The :attribute may only contain letters.', 19 + 'alpha' => 'El :attribute solo puede contener letras.',
20 - 'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.', 20 + 'alpha_dash' => 'El :attribute solo puede contener letras, números y guiones.',
21 - 'alpha_num' => 'The :attribute may only contain letters and numbers.', 21 + 'alpha_num' => 'El :attribute solo puede contener letras y número.',
22 - 'array' => 'The :attribute must be an array.', 22 + 'array' => 'El :attribute debe de ser un array.',
23 - 'before' => 'The :attribute must be a date before :date.', 23 + 'before' => 'El :attribute debe ser una fecha anterior a :date.',
24 'between' => [ 24 'between' => [
25 - 'numeric' => 'The :attribute must be between :min and :max.', 25 + 'numeric' => 'El :attribute debe estar entre :min y :max.',
26 - 'file' => 'The :attribute must be between :min and :max kilobytes.', 26 + 'file' => 'El :attribute debe estar entre :min y :max kilobytes.',
27 - 'string' => 'The :attribute must be between :min and :max characters.', 27 + 'string' => 'El :attribute debe estar entre :min y :max carácteres.',
28 - 'array' => 'The :attribute must have between :min and :max items.', 28 + 'array' => 'El :attribute debe estar entre :min y :max items.',
29 ], 29 ],
30 - 'boolean' => 'The :attribute field must be true or false.', 30 + 'boolean' => 'El campo :attribute debe ser true o false.',
31 - 'confirmed' => 'The :attribute confirmation does not match.', 31 + 'confirmed' => 'La confirmación de :attribute no concuerda.',
32 - 'date' => 'The :attribute is not a valid date.', 32 + 'date' => 'El :attribute no es una fecha válida.',
33 - 'date_format' => 'The :attribute does not match the format :format.', 33 + 'date_format' => 'El :attribute no coincide con el formato :format.',
34 - 'different' => 'The :attribute and :other must be different.', 34 + 'different' => ':attribute y :other deben ser diferentes.',
35 - 'digits' => 'The :attribute must be :digits digits.', 35 + 'digits' => ':attribute debe ser de :digits dígitos.',
36 - 'digits_between' => 'The :attribute must be between :min and :max digits.', 36 + 'digits_between' => ':attribute debe ser un valor entre :min y :max dígios.',
37 - 'email' => 'The :attribute must be a valid email address.', 37 + 'email' => ':attribute debe ser una dirección álida.',
38 - 'filled' => 'The :attribute field is required.', 38 + 'filled' => 'El campo :attribute es requerido.',
39 - 'exists' => 'The selected :attribute is invalid.', 39 + 'exists' => 'El :attribute seleccionado es inválido.',
40 - 'image' => 'The :attribute must be an image.', 40 + 'image' => 'El :attribute debe ser una imagen.',
41 - 'in' => 'The selected :attribute is invalid.', 41 + 'in' => 'El selected :attribute es inválio.',
42 - 'integer' => 'The :attribute must be an integer.', 42 + 'integer' => 'El :attribute debe ser un entero.',
43 - 'ip' => 'The :attribute must be a valid IP address.', 43 + 'ip' => 'El :attribute debe ser una dirección IP álida.',
44 'max' => [ 44 'max' => [
45 - 'numeric' => 'The :attribute may not be greater than :max.', 45 + 'numeric' => ':attribute no puede ser mayor que :max.',
46 - 'file' => 'The :attribute may not be greater than :max kilobytes.', 46 + 'file' => ':attribute no puede ser mayor que :max kilobytes.',
47 - 'string' => 'The :attribute may not be greater than :max characters.', 47 + 'string' => ':attribute no puede ser mayor que :max carácteres.',
48 - 'array' => 'The :attribute may not have more than :max items.', 48 + 'array' => ':attribute no puede contener más de :max items.',
49 ], 49 ],
50 - 'mimes' => 'The :attribute must be a file of type: :values.', 50 + 'mimes' => ':attribute debe ser un fichero de tipo: :values.',
51 'min' => [ 51 'min' => [
52 - 'numeric' => 'The :attribute must be at least :min.', 52 + 'numeric' => ':attribute debe ser al menos de :min.',
53 - 'file' => 'The :attribute must be at least :min kilobytes.', 53 + 'file' => ':attribute debe ser al menos :min kilobytes.',
54 - 'string' => 'The :attribute must be at least :min characters.', 54 + 'string' => ':attribute debe ser al menos :min caracteres.',
55 - 'array' => 'The :attribute must have at least :min items.', 55 + 'array' => ':attribute debe tener como mínimo :min items.',
56 ], 56 ],
57 - 'not_in' => 'The selected :attribute is invalid.', 57 + 'not_in' => ':attribute seleccionado es inválio.',
58 - 'numeric' => 'The :attribute must be a number.', 58 + 'numeric' => ':attribute debe ser numérico.',
59 - 'regex' => 'The :attribute format is invalid.', 59 + 'regex' => ':attribute con formato inválido',
60 - 'required' => 'The :attribute field is required.', 60 + 'required' => ':attribute es requerido.',
61 - 'required_if' => 'The :attribute field is required when :other is :value.', 61 + 'required_if' => ':attribute es requerido cuando :other vale :value.',
62 - 'required_with' => 'The :attribute field is required when :values is present.', 62 + 'required_with' => 'El campo :attribute es requerido cuando se encuentre entre los valores :values.',
63 - 'required_with_all' => 'The :attribute field is required when :values is present.', 63 + 'required_with_all' => 'El campo :attribute es requerido cuando los valores sean :values.',
64 - 'required_without' => 'The :attribute field is required when :values is not present.', 64 + 'required_without' => ':attribute es requerido cuando no se encuentre entre los valores :values.',
65 - 'required_without_all' => 'The :attribute field is required when none of :values are present.', 65 + 'required_without_all' => ':attribute es requerido cuando ninguno de los valores :values están presentes.',
66 - 'same' => 'The :attribute and :other must match.', 66 + 'same' => ':attribute y :other deben coincidir.',
67 'size' => [ 67 'size' => [
68 - 'numeric' => 'The :attribute must be :size.', 68 + 'numeric' => ':attribute debe ser :size.',
69 - 'file' => 'The :attribute must be :size kilobytes.', 69 + 'file' => ':attribute debe ser :size kilobytes.',
70 - 'string' => 'The :attribute must be :size characters.', 70 + 'string' => ':attribute debe ser :size caracteres.',
71 - 'array' => 'The :attribute must contain :size items.', 71 + 'array' => ':attribute debe contener :size items.',
72 ], 72 ],
73 - 'string' => 'The :attribute must be a string.', 73 + 'string' => 'El atributo :attribute debe ser una cadena.',
74 - 'timezone' => 'The :attribute must be a valid zone.', 74 + 'timezone' => 'El atributo :attribute debe ser una zona válida.',
75 - 'unique' => 'The :attribute has already been taken.', 75 + 'unique' => 'El atributo :attribute ya ha sido tomado.',
76 - 'url' => 'The :attribute format is invalid.', 76 + 'url' => 'El atributo :attribute tiene un formato inválid.',
77 77
78 /* 78 /*
79 |-------------------------------------------------------------------------- 79 |--------------------------------------------------------------------------
...@@ -88,7 +88,7 @@ return [ ...@@ -88,7 +88,7 @@ return [
88 88
89 'custom' => [ 89 'custom' => [
90 'password-confirm' => [ 90 'password-confirm' => [
91 - 'required_with' => 'Password confirmation required', 91 + 'required_with' => 'Confirmación de Password requerida',
92 ], 92 ],
93 ], 93 ],
94 94
......
...@@ -43,18 +43,9 @@ return [ ...@@ -43,18 +43,9 @@ return [
43 * Search 43 * Search
44 */ 44 */
45 'search_results' => 'Résultats de recherche', 45 'search_results' => 'Résultats de recherche',
46 - 'search_results_page' => 'Résultats de recherche des pages',
47 - 'search_results_chapter' => 'Résultats de recherche des chapitres',
48 - 'search_results_book' => 'Résultats de recherche des livres',
49 'search_clear' => 'Réinitialiser la recherche', 46 'search_clear' => 'Réinitialiser la recherche',
50 - 'search_view_pages' => 'Voir toutes les pages correspondantes',
51 - 'search_view_chapters' => 'Voir tous les chapitres correspondants',
52 - 'search_view_books' => 'Voir tous les livres correspondants',
53 'search_no_pages' => 'Aucune page correspondant à cette recherche', 47 'search_no_pages' => 'Aucune page correspondant à cette recherche',
54 'search_for_term' => 'recherche pour :term', 48 'search_for_term' => 'recherche pour :term',
55 - 'search_page_for_term' => 'Recherche de page pour :term',
56 - 'search_chapter_for_term' => 'Recherche de chapitre pour :term',
57 - 'search_book_for_term' => 'Recherche de livres pour :term',
58 49
59 /** 50 /**
60 * Books 51 * Books
......
...@@ -43,18 +43,9 @@ return [ ...@@ -43,18 +43,9 @@ return [
43 * Search 43 * Search
44 */ 44 */
45 'search_results' => 'Zoekresultaten', 45 'search_results' => 'Zoekresultaten',
46 - 'search_results_page' => 'Pagina Zoekresultaten',
47 - 'search_results_chapter' => 'Hoofdstuk Zoekresultaten',
48 - 'search_results_book' => 'Boek Zoekresultaten',
49 'search_clear' => 'Zoekopdracht wissen', 46 'search_clear' => 'Zoekopdracht wissen',
50 - 'search_view_pages' => 'Bekijk alle gevonden pagina\'s',
51 - 'search_view_chapters' => 'Bekijk alle gevonden hoofdstukken',
52 - 'search_view_books' => 'Bekijk alle gevonden boeken',
53 'search_no_pages' => 'Er zijn geen pagina\'s gevonden', 47 'search_no_pages' => 'Er zijn geen pagina\'s gevonden',
54 'search_for_term' => 'Zoeken op :term', 48 'search_for_term' => 'Zoeken op :term',
55 - 'search_page_for_term' => 'Pagina doorzoeken op :term',
56 - 'search_chapter_for_term' => 'Hoofdstuk doorzoeken op :term',
57 - 'search_book_for_term' => 'Boeken doorzoeken op :term',
58 49
59 /** 50 /**
60 * Books 51 * Books
......
...@@ -43,18 +43,9 @@ return [ ...@@ -43,18 +43,9 @@ return [
43 * Search 43 * Search
44 */ 44 */
45 'search_results' => 'Resultado(s) da Pesquisa', 45 'search_results' => 'Resultado(s) da Pesquisa',
46 - 'search_results_page' => 'Resultado(s) de Pesquisa de Página',
47 - 'search_results_chapter' => 'Resultado(s) de Pesquisa de Capítulo',
48 - 'search_results_book' => 'Resultado(s) de Pesquisa de Livro',
49 'search_clear' => 'Limpar Pesquisa', 46 'search_clear' => 'Limpar Pesquisa',
50 - 'search_view_pages' => 'Visualizar todas as páginas correspondentes',
51 - 'search_view_chapters' => 'Visualizar todos os capítulos correspondentes',
52 - 'search_view_books' => 'Visualizar todos os livros correspondentes',
53 'search_no_pages' => 'Nenhuma página corresponde à pesquisa', 47 'search_no_pages' => 'Nenhuma página corresponde à pesquisa',
54 'search_for_term' => 'Pesquisar por :term', 48 'search_for_term' => 'Pesquisar por :term',
55 - 'search_page_for_term' => 'Pesquisar Página por :term',
56 - 'search_chapter_for_term' => 'Pesquisar Capítulo por :term',
57 - 'search_book_for_term' => 'Pesquisar Livros por :term',
58 49
59 /** 50 /**
60 * Books 51 * Books
......
1 +<?php
2 +
3 +return [
4 +
5 + /**
6 + * Activity text strings.
7 + * Is used for all the text within activity logs & notifications.
8 + */
9 +
10 + // Pages
11 + 'page_create' => 'vytvoril stránku',
12 + 'page_create_notification' => 'Stránka úspešne vytvorená',
13 + 'page_update' => 'aktualizoval stránku',
14 + 'page_update_notification' => 'Stránka úspešne aktualizovaná',
15 + 'page_delete' => 'odstránil stránku',
16 + 'page_delete_notification' => 'Stránka úspešne odstránená',
17 + 'page_restore' => 'obnovil stránku',
18 + 'page_restore_notification' => 'Stránka úspešne obnovená',
19 + 'page_move' => 'presunul stránku',
20 +
21 + // Chapters
22 + 'chapter_create' => 'vytvoril kapitolu',
23 + 'chapter_create_notification' => 'Kapitola úspešne vytvorená',
24 + 'chapter_update' => 'aktualizoval kapitolu',
25 + 'chapter_update_notification' => 'Kapitola úspešne aktualizovaná',
26 + 'chapter_delete' => 'odstránil kapitolu',
27 + 'chapter_delete_notification' => 'Kapitola úspešne odstránená',
28 + 'chapter_move' => 'presunul kapitolu',
29 +
30 + // Books
31 + 'book_create' => 'vytvoril knihu',
32 + 'book_create_notification' => 'Kniha úspešne vytvorená',
33 + 'book_update' => 'aktualizoval knihu',
34 + 'book_update_notification' => 'Kniha úspešne aktualizovaná',
35 + 'book_delete' => 'odstránil knihu',
36 + 'book_delete_notification' => 'Kniha úspešne odstránená',
37 + 'book_sort' => 'zoradil knihu',
38 + 'book_sort_notification' => 'Kniha úspešne znovu zoradená',
39 +
40 +];
1 +<?php
2 +return [
3 + /*
4 + |--------------------------------------------------------------------------
5 + | Authentication Language Lines
6 + |--------------------------------------------------------------------------
7 + |
8 + | The following language lines are used during authentication for various
9 + | messages that we need to display to the user. You are free to modify
10 + | these language lines according to your application's requirements.
11 + |
12 + */
13 + 'failed' => 'Tieto údaje nesedia s našimi záznamami.',
14 + 'throttle' => 'Priveľa pokusov o prihlásenie. Skúste znova o :seconds sekúnd.',
15 +
16 + /**
17 + * Login & Register
18 + */
19 + 'sign_up' => 'Registrácia',
20 + 'log_in' => 'Prihlásenie',
21 + 'log_in_with' => 'Prihlásiť sa cez :socialDriver',
22 + 'sign_up_with' => 'Registrovať sa cez :socialDriver',
23 + 'logout' => 'Odhlásenie',
24 +
25 + 'name' => 'Meno',
26 + 'username' => 'Používateľské meno',
27 + 'email' => 'Email',
28 + 'password' => 'Heslo',
29 + 'password_confirm' => 'Potvrdiť heslo',
30 + 'password_hint' => 'Musí mať viac ako 5 znakov',
31 + 'forgot_password' => 'Zabudli ste heslo?',
32 + 'remember_me' => 'Zapamätať si ma',
33 + 'ldap_email_hint' => 'Zadajte prosím email, ktorý sa má použiť pre tento účet.',
34 + 'create_account' => 'Vytvoriť účet',
35 + 'social_login' => 'Sociálne prihlásenie',
36 + 'social_registration' => 'Sociálna registrácia',
37 + 'social_registration_text' => 'Registrovať sa a prihlásiť sa použitím inej služby.',
38 +
39 + 'register_thanks' => 'Ďakujeme zaregistráciu!',
40 + 'register_confirm' => 'Skontrolujte prosím svoj email a kliknite na potvrdzujúce tlačidlo pre prístup k :appName.',
41 + 'registrations_disabled' => 'Registrácie sú momentálne zablokované',
42 + 'registration_email_domain_invalid' => 'Táto emailová doména nemá prístup k tejto aplikácii',
43 + 'register_success' => 'Ďakujeme za registráciu! Teraz ste registrovaný a prihlásený.',
44 +
45 +
46 + /**
47 + * Password Reset
48 + */
49 + 'reset_password' => 'Reset hesla',
50 + 'reset_password_send_instructions' => 'Zadajte svoj email nižšie a bude Vám odoslaný email s odkazom pre reset hesla.',
51 + 'reset_password_send_button' => 'Poslať odkaz na reset hesla',
52 + 'reset_password_sent_success' => 'Odkaz na reset hesla bol poslaný na :email.',
53 + 'reset_password_success' => 'Vaše heslo bolo úspešne resetované.',
54 +
55 + 'email_reset_subject' => 'Reset Vášho :appName hesla',
56 + 'email_reset_text' => 'Tento email Ste dostali pretože sme dostali požiadavku na reset hesla pre Váš účet.',
57 + 'email_reset_not_requested' => 'Ak ste nepožiadali o reset hesla, nemusíte nič robiť.',
58 +
59 +
60 + /**
61 + * Email Confirmation
62 + */
63 + 'email_confirm_subject' => 'Potvrdiť email na :appName',
64 + 'email_confirm_greeting' => 'Ďakujeme za pridanie sa k :appName!',
65 + 'email_confirm_text' => 'Prosím potvrďte Vašu emailovú adresu kliknutím na tlačidlo nižšie:',
66 + 'email_confirm_action' => 'Potvrdiť email',
67 + 'email_confirm_send_error' => 'Je požadované overenie emailu, ale systém nemohol odoslať email. Kontaktujte administrátora by ste sa uistili, že email je nastavený správne.',
68 + 'email_confirm_success' => 'Váš email bol overený!',
69 + 'email_confirm_resent' => 'Potvrdzujúci email bol poslaný znovu, skontrolujte prosím svoju emailovú schránku.',
70 +
71 + 'email_not_confirmed' => 'Emailová adresa nebola overená',
72 + 'email_not_confirmed_text' => 'Vaša emailová adresa nebola zatiaľ overená.',
73 + 'email_not_confirmed_click_link' => 'Prosím, kliknite na odkaz v emaili, ktorý bol poslaný krátko po Vašej registrácii.',
74 + 'email_not_confirmed_resend' => 'Ak nemôžete násť email, môžete znova odoslať overovací email odoslaním doleuvedeného formulára.',
75 + 'email_not_confirmed_resend_button' => 'Znova odoslať overovací email',
76 +];
1 +<?php
2 +return [
3 +
4 + /**
5 + * Buttons
6 + */
7 + 'cancel' => 'Zrušiť',
8 + 'confirm' => 'Potvrdiť',
9 + 'back' => 'Späť',
10 + 'save' => 'Uložiť',
11 + 'continue' => 'Pokračovať',
12 + 'select' => 'Vybrať',
13 +
14 + /**
15 + * Form Labels
16 + */
17 + 'name' => 'Meno',
18 + 'description' => 'Popis',
19 + 'role' => 'Rola',
20 +
21 + /**
22 + * Actions
23 + */
24 + 'actions' => 'Akcie',
25 + 'view' => 'Zobraziť',
26 + 'create' => 'Vytvoriť',
27 + 'update' => 'Aktualizovať',
28 + 'edit' => 'Editovať',
29 + 'sort' => 'Zoradiť',
30 + 'move' => 'Presunúť',
31 + 'delete' => 'Zmazať',
32 + 'search' => 'Hľadť',
33 + 'search_clear' => 'Vyčistiť hľadanie',
34 + 'reset' => 'Reset',
35 + 'remove' => 'Odstrániť',
36 +
37 +
38 + /**
39 + * Misc
40 + */
41 + 'deleted_user' => 'Odstránený používateľ',
42 + 'no_activity' => 'Žiadna aktivita na zobrazenie',
43 + 'no_items' => 'Žiadne položky nie sú dostupné',
44 + 'back_to_top' => 'Späť nahor',
45 + 'toggle_details' => 'Prepnúť detaily',
46 +
47 + /**
48 + * Header
49 + */
50 + 'view_profile' => 'Zobraziť profil',
51 + 'edit_profile' => 'Upraviť profil',
52 +
53 + /**
54 + * Email Content
55 + */
56 + 'email_action_help' => 'Ak máte problém klinkúť na tlačidlo ":actionText", skopírujte a vložte URL uvedenú nižšie do Vášho prehliadača:',
57 + 'email_rights' => 'Všetky práva vyhradené',
58 +];
1 +<?php
2 +return [
3 +
4 + /**
5 + * Image Manager
6 + */
7 + 'image_select' => 'Vybrať obrázok',
8 + 'image_all' => 'Všetko',
9 + 'image_all_title' => 'Zobraziť všetky obrázky',
10 + 'image_book_title' => 'Zobraziť obrázky nahrané do tejto knihy',
11 + 'image_page_title' => 'Zobraziť obrázky nahrané do tejto stránky',
12 + 'image_search_hint' => 'Hľadať obrázok podľa názvu',
13 + 'image_uploaded' => 'Nahrané :uploadedDate',
14 + 'image_load_more' => 'Načítať viac',
15 + 'image_image_name' => 'Názov obrázka',
16 + 'image_delete_confirm' => 'Tento obrázok je použitý na stránkach uvedených nižšie, kliknite znova na zmazať pre potvrdenie zmazania tohto obrázka.',
17 + 'image_select_image' => 'Vybrať obrázok',
18 + 'image_dropzone' => 'Presuňte obrázky sem alebo kliknite sem pre nahranie',
19 + 'images_deleted' => 'Obrázky zmazané',
20 + 'image_preview' => 'Náhľad obrázka',
21 + 'image_upload_success' => 'Obrázok úspešne nahraný',
22 + 'image_update_success' => 'Detaily obrázka úspešne aktualizované',
23 + 'image_delete_success' => 'Obrázok úspešne zmazaný'
24 +];
1 +<?php
2 +
3 +return [
4 +
5 + /**
6 + * Error text strings.
7 + */
8 +
9 + // Permissions
10 + 'permission' => 'Nemáte oprávnenie pre prístup k požadovanej stránke.',
11 + 'permissionJson' => 'Nemáte oprávnenie pre vykonanie požadovaného úkonu.',
12 +
13 + // Auth
14 + 'error_user_exists_different_creds' => 'Používateľ s emailom :email už existuje, ale s inými údajmi.',
15 + 'email_already_confirmed' => 'Email bol už overený, skúste sa prihlásiť.',
16 + 'email_confirmation_invalid' => 'Tento potvrdzujúci token nie je platný alebo už bol použitý, skúste sa prosím registrovať znova.',
17 + 'email_confirmation_expired' => 'Potvrdzujúci token expiroval, bol odoslaný nový potvrdzujúci email.',
18 + 'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
19 + 'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
20 + 'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
21 + 'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
22 + 'social_no_action_defined' => 'Nebola definovaná žiadna akcia',
23 + 'social_account_in_use' => 'Tento :socialAccount účet sa už používa, skúste sa prihlásiť pomocou možnosti :socialAccount.',
24 + 'social_account_email_in_use' => 'Email :email sa už používa. Ak už máte účet, môžete pripojiť svoj :socialAccount účet v nastaveniach profilu.',
25 + 'social_account_existing' => 'Tento :socialAccount účet je už spojený s Vaším profilom.',
26 + 'social_account_already_used_existing' => 'Tento :socialAccount účet už používa iný používateľ.',
27 + 'social_account_not_used' => 'Tento :socialAccount účet nie je spojený so žiadnym používateľom. Pripojte ho prosím v nastaveniach Vášho profilu. ',
28 + 'social_account_register_instructions' => 'Ak zatiaľ nemáte účet, môžete sa registrovať pomocou možnosti :socialAccount.',
29 + 'social_driver_not_found' => 'Ovládač socialnych sietí nebol nájdený',
30 + 'social_driver_not_configured' => 'Nastavenia Vášho :socialAccount účtu nie sú správne.',
31 +
32 + // System
33 + 'path_not_writable' => 'Do cesty :filePath sa nedá nahrávať. Uistite sa, že je zapisovateľná serverom.',
34 + 'cannot_get_image_from_url' => 'Nedá sa získať obrázok z :url',
35 + 'cannot_create_thumbs' => 'Server nedokáže vytvoriť náhľady. Skontrolujte prosím, či máte nainštalované GD rozšírenie PHP.',
36 + 'server_upload_limit' => 'Server nedovoľuje nahrávanie súborov s takouto veľkosťou. Skúste prosím menší súbor.',
37 + 'image_upload_error' => 'Pri nahrávaní obrázka nastala chyba',
38 +
39 + // Attachments
40 + 'attachment_page_mismatch' => 'Page mismatch during attachment update',
41 +
42 + // Pages
43 + 'page_draft_autosave_fail' => 'Koncept nemohol byť uložený. Uistite sa, že máte pripojenie k internetu pre uložením tejto stránky',
44 +
45 + // Entities
46 + 'entity_not_found' => 'Entita nenájdená',
47 + 'book_not_found' => 'Kniha nenájdená',
48 + 'page_not_found' => 'Stránka nenájdená',
49 + 'chapter_not_found' => 'Kapitola nenájdená',
50 + 'selected_book_not_found' => 'Vybraná kniha nebola nájdená',
51 + 'selected_book_chapter_not_found' => 'Vybraná kniha alebo kapitola nebola nájdená',
52 + 'guests_cannot_save_drafts' => 'Hosť nemôže ukladať koncepty',
53 +
54 + // Users
55 + 'users_cannot_delete_only_admin' => 'Nemôžete zmazať posledného správcu',
56 + 'users_cannot_delete_guest' => 'Nemôžete zmazať hosťa',
57 +
58 + // Roles
59 + 'role_cannot_be_edited' => 'Táto rola nemôže byť upravovaná',
60 + 'role_system_cannot_be_deleted' => 'Táto rola je systémová rola a nemôže byť zmazaná',
61 + 'role_registration_default_cannot_delete' => 'Táto rola nemôže byť zmazaná, pretože je nastavená ako prednastavená rola pri registrácii',
62 +
63 + // Error pages
64 + '404_page_not_found' => 'Stránka nenájdená',
65 + 'sorry_page_not_found' => 'Prepáčte, stránka ktorú hľadáte nebola nájdená.',
66 + 'return_home' => 'Vrátiť sa domov',
67 + 'error_occurred' => 'Nastala chyba',
68 + 'app_down' => ':appName je momentálne nedostupná',
69 + 'back_soon' => 'Čoskoro bude opäť dostupná.',
70 +];
1 +<?php
2 +
3 +return [
4 +
5 + /*
6 + |--------------------------------------------------------------------------
7 + | Pagination Language Lines
8 + |--------------------------------------------------------------------------
9 + |
10 + | The following language lines are used by the paginator library to build
11 + | the simple pagination links. You are free to change them to anything
12 + | you want to customize your views to better match your application.
13 + |
14 + */
15 +
16 + 'previous' => '&laquo; Predchádzajúca',
17 + 'next' => 'Ďalšia &raquo;',
18 +
19 +];
1 +<?php
2 +
3 +return [
4 +
5 + /*
6 + |--------------------------------------------------------------------------
7 + | Password Reminder Language Lines
8 + |--------------------------------------------------------------------------
9 + |
10 + | The following language lines are the default lines which match reasons
11 + | that are given by the password broker for a password update attempt
12 + | has failed, such as for an invalid token or invalid new password.
13 + |
14 + */
15 +
16 + 'password' => 'Heslo musí obsahovať aspoň šesť znakov a musí byť rovnaké ako potvrdzujúce.',
17 + 'user' => "Nenašli sme používateľa s takou emailovou adresou.",
18 + 'token' => 'Tento token pre reset hesla je neplatný.',
19 + 'sent' => 'Poslali sme Vám email s odkazom na reset hesla!',
20 + 'reset' => 'Vaše heslo bolo resetované!',
21 +
22 +];
1 +<?php
2 +
3 +return [
4 +
5 + /**
6 + * Settings text strings
7 + * Contains all text strings used in the general settings sections of BookStack
8 + * including users and roles.
9 + */
10 +
11 + 'settings' => 'Nastavenia',
12 + 'settings_save' => 'Uložiť nastavenia',
13 + 'settings_save_success' => 'Nastavenia uložené',
14 +
15 + /**
16 + * App settings
17 + */
18 +
19 + 'app_settings' => 'Nastavenia aplikácie',
20 + 'app_name' => 'Názov aplikácia',
21 + 'app_name_desc' => 'Tento názov sa zobrazuje v hlavičke a v emailoch.',
22 + 'app_name_header' => 'Zobraziť názov aplikácie v hlavičke?',
23 + 'app_public_viewing' => 'Povoliť verejné zobrazenie?',
24 + 'app_secure_images' => 'Povoliť nahrávanie súborov so zvýšeným zabezpečením?',
25 + 'app_secure_images_desc' => 'Kvôli výkonu sú všetky obrázky verejné. Táto možnosť pridá pred URL obrázka náhodný, ťažko uhádnuteľný reťazec. Aby ste zabránili jednoduchému prístupu, uistite sa, že indexy priečinkov nie sú povolené.',
26 + 'app_editor' => 'Editor stránky',
27 + 'app_editor_desc' => 'Vyberte editor, ktorý bude používaný všetkými používateľmi na editáciu stránok.',
28 + 'app_custom_html' => 'Vlastný HTML obsah hlavičky',
29 + 'app_custom_html_desc' => 'Všetok text pridaný sem bude vložený naspodok <head> sekcie na každej stránke. Môže sa to zísť pri zmene štýlu alebo pre pridanie analytického kódu.',
30 + 'app_logo' => 'Logo aplikácie',
31 + 'app_logo_desc' => 'Tento obrázok by mal mať 43px na výšku. <br>Veľké obrázky budú preškálované na menší rozmer.',
32 + 'app_primary_color' => 'Primárna farba pre aplikáciu',
33 + 'app_primary_color_desc' => 'Toto by mala byť hodnota v hex tvare. <br>Nechajte prázdne ak chcete použiť prednastavenú farbu.',
34 +
35 + /**
36 + * Registration settings
37 + */
38 +
39 + 'reg_settings' => 'Nastavenia registrácie',
40 + 'reg_allow' => 'Povoliť registráciu?',
41 + 'reg_default_role' => 'Prednastavená používateľská rola po registrácii',
42 + 'reg_confirm_email' => 'Vyžadovať overenie emailu?',
43 + 'reg_confirm_email_desc' => 'Ak je použité obmedzenie domény, potom bude vyžadované overenie emailu a hodnota nižšie bude ignorovaná.',
44 + 'reg_confirm_restrict_domain' => 'Obmedziť registráciu na doménu',
45 + 'reg_confirm_restrict_domain_desc' => 'Zadajte zoznam domén, pre ktoré chcete povoliť registráciu oddelených čiarkou. Používatelia dostanú email kvôli overeniu adresy predtým ako im bude dovolené používať aplikáciu. <br> Používatelia si budú môcť po úspešnej registrácii zmeniť svoju emailovú adresu.',
46 + 'reg_confirm_restrict_domain_placeholder' => 'Nie sú nastavené žiadne obmedzenia',
47 +
48 + /**
49 + * Role settings
50 + */
51 +
52 + 'roles' => 'Roly',
53 + 'role_user_roles' => 'Používateľské roly',
54 + 'role_create' => 'Vytvoriť novú rolu',
55 + 'role_create_success' => 'Rola úspešne vytvorená',
56 + 'role_delete' => 'Zmazať rolu',
57 + 'role_delete_confirm' => 'Toto zmaže rolu menom \':roleName\'.',
58 + 'role_delete_users_assigned' => 'Túto rolu má priradenú :userCount používateľov. Ak chcete premigrovať používateľov z tejto roly, vyberte novú rolu nižšie.',
59 + 'role_delete_no_migration' => "Nemigrovať používateľov",
60 + 'role_delete_sure' => 'Ste si istý, že chcete zmazať túto rolu?',
61 + 'role_delete_success' => 'Rola úspešne zmazaná',
62 + 'role_edit' => 'Upraviť rolu',
63 + 'role_details' => 'Detaily roly',
64 + 'role_name' => 'Názov roly',
65 + 'role_desc' => 'Krátky popis roly',
66 + 'role_system' => 'Systémové oprávnenia',
67 + 'role_manage_users' => 'Spravovať používateľov',
68 + 'role_manage_roles' => 'Spravovať role a oprávnenia rolí',
69 + 'role_manage_entity_permissions' => 'Spravovať všetky oprávnenia kníh, kapitol a stránok',
70 + 'role_manage_own_entity_permissions' => 'Spravovať oprávnenia vlastných kníh, kapitol a stránok',
71 + 'role_manage_settings' => 'Spravovať nastavenia aplikácie',
72 + 'role_asset' => 'Oprávnenia majetku',
73 + 'role_asset_desc' => 'Tieto oprávnenia regulujú prednastavený prístup k zdroju v systéme. Oprávnenia pre knihy, kapitoly a stránky majú vyššiu prioritu.',
74 + 'role_all' => 'Všetko',
75 + 'role_own' => 'Vlastné',
76 + 'role_controlled_by_asset' => 'Regulované zdrojom, do ktorého sú nahrané',
77 + 'role_save' => 'Uložiť rolu',
78 + 'role_update_success' => 'Roly úspešne aktualizované',
79 + 'role_users' => 'Používatelia s touto rolou',
80 + 'role_users_none' => 'Žiadni používatelia nemajú priradenú túto rolu',
81 +
82 + /**
83 + * Users
84 + */
85 +
86 + 'users' => 'Používatelia',
87 + 'user_profile' => 'Profil používateľa',
88 + 'users_add_new' => 'Pridať nového používateľa',
89 + 'users_search' => 'Hľadať medzi používateľmi',
90 + 'users_role' => 'Používateľské roly',
91 + 'users_external_auth_id' => 'Externé autentifikačné ID',
92 + 'users_password_warning' => 'Pole nižšie vyplňte iba ak chcete zmeniť heslo:',
93 + 'users_system_public' => 'Tento účet reprezentuje každého hosťovského používateľa, ktorý navštívi Vašu inštanciu. Nedá sa pomocou neho prihlásiť a je priradený automaticky.',
94 + 'users_delete' => 'Zmazať používateľa',
95 + 'users_delete_named' => 'Zmazať používateľa :userName',
96 + 'users_delete_warning' => ' Toto úplne odstráni používateľa menom \':userName\' zo systému.',
97 + 'users_delete_confirm' => 'Ste si istý, že chcete zmazať tohoto používateľa?',
98 + 'users_delete_success' => 'Používateľ úspešne zmazaný',
99 + 'users_edit' => 'Upraviť používateľa',
100 + 'users_edit_profile' => 'Upraviť profil',
101 + 'users_edit_success' => 'Používateľ úspešne upravený',
102 + 'users_avatar' => 'Avatar používateľa',
103 + 'users_avatar_desc' => 'Tento obrázok by mal byť štvorec s rozmerom približne 256px.',
104 + 'users_preferred_language' => 'Preferovaný jazyk',
105 + 'users_social_accounts' => 'Sociálne účty',
106 + 'users_social_accounts_info' => 'Tu si môžete pripojiť iné účty pre rýchlejšie a jednoduchšie prihlásenie. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
107 + 'users_social_connect' => 'Pripojiť účet',
108 + 'users_social_disconnect' => 'Odpojiť účet',
109 + 'users_social_connected' => ':socialAccount účet bol úspešne pripojený k Vášmu profilu.',
110 + 'users_social_disconnected' => ':socialAccount účet bol úspešne odpojený od Vášho profilu.',
111 +];
1 +<?php
2 +
3 +return [
4 +
5 + /*
6 + |--------------------------------------------------------------------------
7 + | Validation Language Lines
8 + |--------------------------------------------------------------------------
9 + |
10 + | The following language lines contain the default error messages used by
11 + | the validator class. Some of these rules have multiple versions such
12 + | as the size rules. Feel free to tweak each of these messages here.
13 + |
14 + */
15 +
16 + 'accepted' => ':attribute musí byť akceptovaný.',
17 + 'active_url' => ':attribute nie je platná URL.',
18 + 'after' => ':attribute musí byť dátum po :date.',
19 + 'alpha' => ':attribute môže obsahovať iba písmená.',
20 + 'alpha_dash' => ':attribute môže obsahovať iba písmená, čísla a pomlčky.',
21 + 'alpha_num' => ':attribute môže obsahovať iba písmená a čísla.',
22 + 'array' => ':attribute musí byť pole.',
23 + 'before' => ':attribute musí byť dátum pred :date.',
24 + 'between' => [
25 + 'numeric' => ':attribute musí byť medzi :min a :max.',
26 + 'file' => ':attribute musí byť medzi :min a :max kilobajtmi.',
27 + 'string' => ':attribute musí byť medzi :min a :max znakmi.',
28 + 'array' => ':attribute musí byť medzi :min a :max položkami.',
29 + ],
30 + 'boolean' => ':attribute pole musí byť true alebo false.',
31 + 'confirmed' => ':attribute potvrdenie nesedí.',
32 + 'date' => ':attribute nie je platný dátum.',
33 + 'date_format' => ':attribute nesedí s formátom :format.',
34 + 'different' => ':attribute a :other musia byť rozdielne.',
35 + 'digits' => ':attribute musí mať :digits číslic.',
36 + 'digits_between' => ':attribute musí mať medzi :min a :max číslicami.',
37 + 'email' => ':attribute musí byť platná emailová adresa.',
38 + 'filled' => 'Políčko :attribute je povinné.',
39 + 'exists' => 'Vybraný :attribute nie je platný.',
40 + 'image' => ':attribute musí byť obrázok.',
41 + 'in' => 'Vybraný :attribute je neplatný.',
42 + 'integer' => ':attribute musí byť celé číslo.',
43 + 'ip' => ':attribute musí byť platná IP adresa.',
44 + 'max' => [
45 + 'numeric' => ':attribute nesmie byť väčší ako :max.',
46 + 'file' => ':attribute nesmie byť väčší ako :max kilobajtov.',
47 + 'string' => ':attribute nesmie byť dlhší ako :max znakov.',
48 + 'array' => ':attribute nesmie mať viac ako :max položiek.',
49 + ],
50 + 'mimes' => ':attribute musí byť súbor typu: :values.',
51 + 'min' => [
52 + 'numeric' => ':attribute musí byť aspoň :min.',
53 + 'file' => ':attribute musí mať aspoň :min kilobajtov.',
54 + 'string' => ':attribute musí mať aspoň :min znakov.',
55 + 'array' => ':attribute musí mať aspoň :min položiek.',
56 + ],
57 + 'not_in' => 'Vybraný :attribute je neplatný.',
58 + 'numeric' => ':attribute musí byť číslo.',
59 + 'regex' => ':attribute formát je neplatný.',
60 + 'required' => 'Políčko :attribute je povinné.',
61 + 'required_if' => 'Políčko :attribute je povinné ak :other je :value.',
62 + 'required_with' => 'Políčko :attribute je povinné ak :values existuje.',
63 + 'required_with_all' => 'Políčko :attribute je povinné ak :values existuje.',
64 + 'required_without' => 'Políčko :attribute je povinné aj :values neexistuje.',
65 + 'required_without_all' => 'Políčko :attribute je povinné ak ani jedno z :values neexistuje.',
66 + 'same' => ':attribute a :other musia byť rovnaké.',
67 + 'size' => [
68 + 'numeric' => ':attribute musí byť :size.',
69 + 'file' => ':attribute musí mať :size kilobajtov.',
70 + 'string' => ':attribute musí mať :size znakov.',
71 + 'array' => ':attribute musí obsahovať :size položiek.',
72 + ],
73 + 'string' => ':attribute musí byť reťazec.',
74 + 'timezone' => ':attribute musí byť plantá časová zóna.',
75 + 'unique' => ':attribute je už použité.',
76 + 'url' => ':attribute formát je neplatný.',
77 +
78 + /*
79 + |--------------------------------------------------------------------------
80 + | Custom Validation Language Lines
81 + |--------------------------------------------------------------------------
82 + |
83 + | Here you may specify custom validation messages for attributes using the
84 + | convention "attribute.rule" to name the lines. This makes it quick to
85 + | specify a specific custom language line for a given attribute rule.
86 + |
87 + */
88 +
89 + 'custom' => [
90 + 'password-confirm' => [
91 + 'required_with' => 'Vyžaduje sa potvrdenie hesla',
92 + ],
93 + ],
94 +
95 + /*
96 + |--------------------------------------------------------------------------
97 + | Custom Validation Attributes
98 + |--------------------------------------------------------------------------
99 + |
100 + | The following language lines are used to swap attribute place-holders
101 + | with something more reader friendly such as E-Mail Address instead
102 + | of "email". This simply helps us make messages a little cleaner.
103 + |
104 + */
105 +
106 + 'attributes' => [],
107 +
108 +];
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
47 </a> 47 </a>
48 </div> 48 </div>
49 <div class="col-lg-4 col-sm-3 text-center"> 49 <div class="col-lg-4 col-sm-3 text-center">
50 - <form action="{{ baseUrl('/search/all') }}" method="GET" class="search-box"> 50 + <form action="{{ baseUrl('/search') }}" method="GET" class="search-box">
51 <input id="header-search-box-input" type="text" name="term" tabindex="2" value="{{ isset($searchTerm) ? $searchTerm : '' }}"> 51 <input id="header-search-box-input" type="text" name="term" tabindex="2" value="{{ isset($searchTerm) ? $searchTerm : '' }}">
52 <button id="header-search-box-button" type="submit" class="text-button"><i class="zmdi zmdi-search"></i></button> 52 <button id="header-search-box-button" type="submit" class="text-button"><i class="zmdi zmdi-search"></i></button>
53 </form> 53 </form>
......
...@@ -50,15 +50,15 @@ ...@@ -50,15 +50,15 @@
50 </div> 50 </div>
51 51
52 52
53 - <div class="container" id="book-dashboard" ng-controller="BookShowController" book-id="{{ $book->id }}"> 53 + <div class="container" id="entity-dashboard" entity-id="{{ $book->id }}" entity-type="book">
54 <div class="row"> 54 <div class="row">
55 <div class="col-md-7"> 55 <div class="col-md-7">
56 56
57 <h1>{{$book->name}}</h1> 57 <h1>{{$book->name}}</h1>
58 - <div class="book-content" ng-show="!searching"> 58 + <div class="book-content" v-if="!searching">
59 - <p class="text-muted" ng-non-bindable>{{$book->description}}</p> 59 + <p class="text-muted" v-pre>{{$book->description}}</p>
60 60
61 - <div class="page-list" ng-non-bindable> 61 + <div class="page-list" v-pre>
62 <hr> 62 <hr>
63 @if(count($bookChildren) > 0) 63 @if(count($bookChildren) > 0)
64 @foreach($bookChildren as $childElement) 64 @foreach($bookChildren as $childElement)
...@@ -81,12 +81,12 @@ ...@@ -81,12 +81,12 @@
81 @include('partials.entity-meta', ['entity' => $book]) 81 @include('partials.entity-meta', ['entity' => $book])
82 </div> 82 </div>
83 </div> 83 </div>
84 - <div class="search-results" ng-cloak ng-show="searching"> 84 + <div class="search-results" v-cloak v-if="searching">
85 - <h3 class="text-muted">{{ trans('entities.search_results') }} <a ng-if="searching" ng-click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3> 85 + <h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" v-on:click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
86 - <div ng-if="!searchResults"> 86 + <div v-if="!searchResults">
87 @include('partials/loading-icon') 87 @include('partials/loading-icon')
88 </div> 88 </div>
89 - <div ng-bind-html="searchResults"></div> 89 + <div v-html="searchResults"></div>
90 </div> 90 </div>
91 91
92 92
...@@ -94,6 +94,7 @@ ...@@ -94,6 +94,7 @@
94 94
95 <div class="col-md-4 col-md-offset-1"> 95 <div class="col-md-4 col-md-offset-1">
96 <div class="margin-top large"></div> 96 <div class="margin-top large"></div>
97 +
97 @if($book->restricted) 98 @if($book->restricted)
98 <p class="text-muted"> 99 <p class="text-muted">
99 @if(userCan('restrictions-manage', $book)) 100 @if(userCan('restrictions-manage', $book))
...@@ -103,14 +104,16 @@ ...@@ -103,14 +104,16 @@
103 @endif 104 @endif
104 </p> 105 </p>
105 @endif 106 @endif
107 +
106 <div class="search-box"> 108 <div class="search-box">
107 - <form ng-submit="searchBook($event)"> 109 + <form v-on:submit="searchBook">
108 - <input ng-model="searchTerm" ng-change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}"> 110 + <input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
109 <button type="submit"><i class="zmdi zmdi-search"></i></button> 111 <button type="submit"><i class="zmdi zmdi-search"></i></button>
110 - <button ng-if="searching" ng-click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button> 112 + <button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
111 </form> 113 </form>
112 </div> 114 </div>
113 - <div class="activity anim fadeIn"> 115 +
116 + <div class="activity">
114 <h3>{{ trans('entities.recent_activity') }}</h3> 117 <h3>{{ trans('entities.recent_activity') }}</h3>
115 @include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)]) 118 @include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)])
116 </div> 119 </div>
......
1 <div class="breadcrumbs"> 1 <div class="breadcrumbs">
2 + @if (userCan('view', $chapter->book))
2 <a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a> 3 <a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
3 <span class="sep">&raquo;</span> 4 <span class="sep">&raquo;</span>
5 + @endif
4 <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a> 6 <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a>
5 </div> 7 </div>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -47,10 +47,11 @@ ...@@ -47,10 +47,11 @@
47 </div> 47 </div>
48 48
49 49
50 - <div class="container" ng-non-bindable> 50 + <div class="container" id="entity-dashboard" entity-id="{{ $chapter->id }}" entity-type="chapter">
51 <div class="row"> 51 <div class="row">
52 - <div class="col-md-8"> 52 + <div class="col-md-7">
53 <h1>{{ $chapter->name }}</h1> 53 <h1>{{ $chapter->name }}</h1>
54 + <div class="chapter-content" v-if="!searching">
54 <p class="text-muted">{{ $chapter->description }}</p> 55 <p class="text-muted">{{ $chapter->description }}</p>
55 56
56 @if(count($pages) > 0) 57 @if(count($pages) > 0)
...@@ -80,7 +81,16 @@ ...@@ -80,7 +81,16 @@
80 81
81 @include('partials.entity-meta', ['entity' => $chapter]) 82 @include('partials.entity-meta', ['entity' => $chapter])
82 </div> 83 </div>
83 - <div class="col-md-3 col-md-offset-1"> 84 +
85 + <div class="search-results" v-cloak v-if="searching">
86 + <h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" v-on:click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
87 + <div v-if="!searchResults">
88 + @include('partials/loading-icon')
89 + </div>
90 + <div v-html="searchResults"></div>
91 + </div>
92 + </div>
93 + <div class="col-md-4 col-md-offset-1">
84 <div class="margin-top large"></div> 94 <div class="margin-top large"></div>
85 @if($book->restricted || $chapter->restricted) 95 @if($book->restricted || $chapter->restricted)
86 <div class="text-muted"> 96 <div class="text-muted">
...@@ -105,7 +115,16 @@ ...@@ -105,7 +115,16 @@
105 </div> 115 </div>
106 @endif 116 @endif
107 117
118 + <div class="search-box">
119 + <form v-on:submit="searchBook">
120 + <input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.chapters_search_this') }}">
121 + <button type="submit"><i class="zmdi zmdi-search"></i></button>
122 + <button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
123 + </form>
124 + </div>
125 +
108 @include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree]) 126 @include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree])
127 +
109 </div> 128 </div>
110 </div> 129 </div>
111 </div> 130 </div>
......
1 <div class="breadcrumbs"> 1 <div class="breadcrumbs">
2 + @if (userCan('view', $page->book))
2 <a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a> 3 <a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
3 - @if($page->hasChapter())
4 <span class="sep">&raquo;</span> 4 <span class="sep">&raquo;</span>
5 + @endif
6 + @if($page->hasChapter() && userCan('view', $page->chapter))
5 <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button"> 7 <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
6 <i class="zmdi zmdi-collection-bookmark"></i> 8 <i class="zmdi zmdi-collection-bookmark"></i>
7 {{ $page->chapter->getShortName() }} 9 {{ $page->chapter->getShortName() }}
8 </a> 10 </a>
9 - @endif
10 <span class="sep">&raquo;</span> 11 <span class="sep">&raquo;</span>
12 + @endif
11 <a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a> 13 <a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
12 </div> 14 </div>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 19
20 <table class="table"> 20 <table class="table">
21 <tr> 21 <tr>
22 + <th width="3%">{{ trans('entities.pages_revisions_number') }}</th>
22 <th width="23%">{{ trans('entities.pages_name') }}</th> 23 <th width="23%">{{ trans('entities.pages_name') }}</th>
23 <th colspan="2" width="8%">{{ trans('entities.pages_revisions_created_by') }}</th> 24 <th colspan="2" width="8%">{{ trans('entities.pages_revisions_created_by') }}</th>
24 <th width="15%">{{ trans('entities.pages_revisions_date') }}</th> 25 <th width="15%">{{ trans('entities.pages_revisions_date') }}</th>
...@@ -27,6 +28,7 @@ ...@@ -27,6 +28,7 @@
27 </tr> 28 </tr>
28 @foreach($page->revisions as $index => $revision) 29 @foreach($page->revisions as $index => $revision)
29 <tr> 30 <tr>
31 + <td>{{ $revision->revision_number == 0 ? '' : $revision->revision_number }}</td>
30 <td>{{ $revision->name }}</td> 32 <td>{{ $revision->name }}</td>
31 <td style="line-height: 0;"> 33 <td style="line-height: 0;">
32 @if($revision->createdBy) 34 @if($revision->createdBy)
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
3 3
4 @if(isset($page) && $page->tags->count() > 0) 4 @if(isset($page) && $page->tags->count() > 0)
5 <div class="tag-display"> 5 <div class="tag-display">
6 - <h6 class="text-muted">Page Tags</h6> 6 + <h6 class="text-muted">{{ trans('entities.page_tags') }}</h6>
7 <table> 7 <table>
8 <tbody> 8 <tbody>
9 @foreach($page->tags as $tag) 9 @foreach($page->tags as $tag)
10 <tr class="tag"> 10 <tr class="tag">
11 - <td @if(!$tag->value) colspan="2" @endif><a href="{{ baseUrl('/search/all?term=%5B' . urlencode($tag->name) .'%5D') }}">{{ $tag->name }}</a></td> 11 + <td @if(!$tag->value) colspan="2" @endif><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%5D') }}">{{ $tag->name }}</a></td>
12 - @if($tag->value) <td class="tag-value"><a href="{{ baseUrl('/search/all?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></td> @endif 12 + @if($tag->value) <td class="tag-value"><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></td> @endif
13 </tr> 13 </tr>
14 @endforeach 14 @endforeach
15 </tbody> 15 </tbody>
...@@ -39,8 +39,10 @@ ...@@ -39,8 +39,10 @@
39 39
40 <h6 class="text-muted">{{ trans('entities.books_navigation') }}</h6> 40 <h6 class="text-muted">{{ trans('entities.books_navigation') }}</h6>
41 <ul class="sidebar-page-list menu"> 41 <ul class="sidebar-page-list menu">
42 - <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
43 42
43 + @if (userCan('view', $book))
44 + <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
45 + @endif
44 46
45 @foreach($sidebarTree as $bookChild) 47 @foreach($sidebarTree as $bookChild)
46 <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}"> 48 <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
......
1 <p class="text-muted small"> 1 <p class="text-muted small">
2 + @if ($entity->isA('page')) {{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br> @endif
2 @if ($entity->createdBy) 3 @if ($entity->createdBy)
3 - {!! trans('entities.meta_created_name', ['timeLength' => $entity->created_at->diffForHumans(), 'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"]) !!} 4 + {!! trans('entities.meta_created_name', [
5 + 'timeLength' => '<span title="'.$entity->created_at->toDayDateTimeString().'">'.$entity->created_at->diffForHumans() . '</span>',
6 + 'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"
7 + ]) !!}
4 @else 8 @else
5 - {{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }} 9 + <span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
6 @endif 10 @endif
7 <br> 11 <br>
8 @if ($entity->updatedBy) 12 @if ($entity->updatedBy)
9 - {!! trans('entities.meta_updated_name', ['timeLength' => $entity->updated_at->diffForHumans(), 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"]) !!} 13 + {!! trans('entities.meta_updated_name', [
14 + 'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
15 + 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
16 + ]) !!}
10 @else 17 @else
11 - {{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }} 18 + <span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
12 @endif 19 @endif
13 </p> 20 </p>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -123,11 +123,9 @@ Route::group(['middleware' => 'auth'], function () { ...@@ -123,11 +123,9 @@ Route::group(['middleware' => 'auth'], function () {
123 Route::get('/link/{id}', 'PageController@redirectFromLink'); 123 Route::get('/link/{id}', 'PageController@redirectFromLink');
124 124
125 // Search 125 // Search
126 - Route::get('/search/all', 'SearchController@searchAll'); 126 + Route::get('/search', 'SearchController@search');
127 - Route::get('/search/pages', 'SearchController@searchPages');
128 - Route::get('/search/books', 'SearchController@searchBooks');
129 - Route::get('/search/chapters', 'SearchController@searchChapters');
130 Route::get('/search/book/{bookId}', 'SearchController@searchBook'); 127 Route::get('/search/book/{bookId}', 'SearchController@searchBook');
128 + Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter');
131 129
132 // Other Pages 130 // Other Pages
133 Route::get('/', 'HomeController@index'); 131 Route::get('/', 'HomeController@index');
......
...@@ -22,6 +22,12 @@ abstract class BrowserKitTest extends TestCase ...@@ -22,6 +22,12 @@ abstract class BrowserKitTest extends TestCase
22 private $admin; 22 private $admin;
23 private $editor; 23 private $editor;
24 24
25 + public function tearDown()
26 + {
27 + \DB::disconnect();
28 + parent::tearDown();
29 + }
30 +
25 /** 31 /**
26 * Creates the application. 32 * Creates the application.
27 * 33 *
......
1 <?php namespace Tests; 1 <?php namespace Tests;
2 2
3 +use BookStack\Book;
4 +use BookStack\Chapter;
5 +use BookStack\Page;
6 +use BookStack\Repos\EntityRepo;
7 +use BookStack\Repos\UserRepo;
8 +
3 class EntityTest extends BrowserKitTest 9 class EntityTest extends BrowserKitTest
4 { 10 {
5 11
...@@ -18,7 +24,7 @@ class EntityTest extends BrowserKitTest ...@@ -18,7 +24,7 @@ class EntityTest extends BrowserKitTest
18 $this->bookDelete($book); 24 $this->bookDelete($book);
19 } 25 }
20 26
21 - public function bookDelete(\BookStack\Book $book) 27 + public function bookDelete(Book $book)
22 { 28 {
23 $this->asAdmin() 29 $this->asAdmin()
24 ->visit($book->getUrl()) 30 ->visit($book->getUrl())
...@@ -32,7 +38,7 @@ class EntityTest extends BrowserKitTest ...@@ -32,7 +38,7 @@ class EntityTest extends BrowserKitTest
32 ->notSeeInDatabase('books', ['id' => $book->id]); 38 ->notSeeInDatabase('books', ['id' => $book->id]);
33 } 39 }
34 40
35 - public function bookUpdate(\BookStack\Book $book) 41 + public function bookUpdate(Book $book)
36 { 42 {
37 $newName = $book->name . ' Updated'; 43 $newName = $book->name . ' Updated';
38 $this->asAdmin() 44 $this->asAdmin()
...@@ -46,12 +52,12 @@ class EntityTest extends BrowserKitTest ...@@ -46,12 +52,12 @@ class EntityTest extends BrowserKitTest
46 ->seePageIs($book->getUrl() . '-updated') 52 ->seePageIs($book->getUrl() . '-updated')
47 ->see($newName); 53 ->see($newName);
48 54
49 - return \BookStack\Book::find($book->id); 55 + return Book::find($book->id);
50 } 56 }
51 57
52 public function test_book_sort_page_shows() 58 public function test_book_sort_page_shows()
53 { 59 {
54 - $books = \BookStack\Book::all(); 60 + $books = Book::all();
55 $bookToSort = $books[0]; 61 $bookToSort = $books[0];
56 $this->asAdmin() 62 $this->asAdmin()
57 ->visit($bookToSort->getUrl()) 63 ->visit($bookToSort->getUrl())
...@@ -65,7 +71,7 @@ class EntityTest extends BrowserKitTest ...@@ -65,7 +71,7 @@ class EntityTest extends BrowserKitTest
65 71
66 public function test_book_sort_item_returns_book_content() 72 public function test_book_sort_item_returns_book_content()
67 { 73 {
68 - $books = \BookStack\Book::all(); 74 + $books = Book::all();
69 $bookToSort = $books[0]; 75 $bookToSort = $books[0];
70 $firstPage = $bookToSort->pages[0]; 76 $firstPage = $bookToSort->pages[0];
71 $firstChapter = $bookToSort->chapters[0]; 77 $firstChapter = $bookToSort->chapters[0];
...@@ -79,7 +85,7 @@ class EntityTest extends BrowserKitTest ...@@ -79,7 +85,7 @@ class EntityTest extends BrowserKitTest
79 85
80 public function pageCreation($chapter) 86 public function pageCreation($chapter)
81 { 87 {
82 - $page = factory(\BookStack\Page::class)->make([ 88 + $page = factory(Page::class)->make([
83 'name' => 'My First Page' 89 'name' => 'My First Page'
84 ]); 90 ]);
85 91
...@@ -88,7 +94,7 @@ class EntityTest extends BrowserKitTest ...@@ -88,7 +94,7 @@ class EntityTest extends BrowserKitTest
88 ->visit($chapter->getUrl()) 94 ->visit($chapter->getUrl())
89 ->click('New Page'); 95 ->click('New Page');
90 96
91 - $draftPage = \BookStack\Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first(); 97 + $draftPage = Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first();
92 98
93 $this->seePageIs($draftPage->getUrl()) 99 $this->seePageIs($draftPage->getUrl())
94 // Fill out form 100 // Fill out form
...@@ -99,13 +105,13 @@ class EntityTest extends BrowserKitTest ...@@ -99,13 +105,13 @@ class EntityTest extends BrowserKitTest
99 ->seePageIs($chapter->book->getUrl() . '/page/my-first-page') 105 ->seePageIs($chapter->book->getUrl() . '/page/my-first-page')
100 ->see($page->name); 106 ->see($page->name);
101 107
102 - $page = \BookStack\Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first(); 108 + $page = Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first();
103 return $page; 109 return $page;
104 } 110 }
105 111
106 - public function chapterCreation(\BookStack\Book $book) 112 + public function chapterCreation(Book $book)
107 { 113 {
108 - $chapter = factory(\BookStack\Chapter::class)->make([ 114 + $chapter = factory(Chapter::class)->make([
109 'name' => 'My First Chapter' 115 'name' => 'My First Chapter'
110 ]); 116 ]);
111 117
...@@ -122,13 +128,13 @@ class EntityTest extends BrowserKitTest ...@@ -122,13 +128,13 @@ class EntityTest extends BrowserKitTest
122 ->seePageIs($book->getUrl() . '/chapter/my-first-chapter') 128 ->seePageIs($book->getUrl() . '/chapter/my-first-chapter')
123 ->see($chapter->name)->see($chapter->description); 129 ->see($chapter->name)->see($chapter->description);
124 130
125 - $chapter = \BookStack\Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first(); 131 + $chapter = Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first();
126 return $chapter; 132 return $chapter;
127 } 133 }
128 134
129 public function bookCreation() 135 public function bookCreation()
130 { 136 {
131 - $book = factory(\BookStack\Book::class)->make([ 137 + $book = factory(Book::class)->make([
132 'name' => 'My First Book' 138 'name' => 'My First Book'
133 ]); 139 ]);
134 $this->asAdmin() 140 $this->asAdmin()
...@@ -154,7 +160,7 @@ class EntityTest extends BrowserKitTest ...@@ -154,7 +160,7 @@ class EntityTest extends BrowserKitTest
154 $expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/'; 160 $expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/';
155 $this->assertRegExp($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n"); 161 $this->assertRegExp($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n");
156 162
157 - $book = \BookStack\Book::where('slug', '=', 'my-first-book')->first(); 163 + $book = Book::where('slug', '=', 'my-first-book')->first();
158 return $book; 164 return $book;
159 } 165 }
160 166
...@@ -165,8 +171,8 @@ class EntityTest extends BrowserKitTest ...@@ -165,8 +171,8 @@ class EntityTest extends BrowserKitTest
165 $updater = $this->getEditor(); 171 $updater = $this->getEditor();
166 $entities = $this->createEntityChainBelongingToUser($creator, $updater); 172 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
167 $this->actingAs($creator); 173 $this->actingAs($creator);
168 - app('BookStack\Repos\UserRepo')->destroy($creator); 174 + app(UserRepo::class)->destroy($creator);
169 - app('BookStack\Repos\EntityRepo')->savePageRevision($entities['page']); 175 + app(EntityRepo::class)->savePageRevision($entities['page']);
170 176
171 $this->checkEntitiesViewable($entities); 177 $this->checkEntitiesViewable($entities);
172 } 178 }
...@@ -178,8 +184,8 @@ class EntityTest extends BrowserKitTest ...@@ -178,8 +184,8 @@ class EntityTest extends BrowserKitTest
178 $updater = $this->getEditor(); 184 $updater = $this->getEditor();
179 $entities = $this->createEntityChainBelongingToUser($creator, $updater); 185 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
180 $this->actingAs($updater); 186 $this->actingAs($updater);
181 - app('BookStack\Repos\UserRepo')->destroy($updater); 187 + app(UserRepo::class)->destroy($updater);
182 - app('BookStack\Repos\EntityRepo')->savePageRevision($entities['page']); 188 + app(EntityRepo::class)->savePageRevision($entities['page']);
183 189
184 $this->checkEntitiesViewable($entities); 190 $this->checkEntitiesViewable($entities);
185 } 191 }
...@@ -216,7 +222,7 @@ class EntityTest extends BrowserKitTest ...@@ -216,7 +222,7 @@ class EntityTest extends BrowserKitTest
216 222
217 public function test_old_page_slugs_redirect_to_new_pages() 223 public function test_old_page_slugs_redirect_to_new_pages()
218 { 224 {
219 - $page = \BookStack\Page::first(); 225 + $page = Page::first();
220 $pageUrl = $page->getUrl(); 226 $pageUrl = $page->getUrl();
221 $newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page'; 227 $newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page';
222 // Need to save twice since revisions are not generated in seeder. 228 // Need to save twice since revisions are not generated in seeder.
...@@ -225,7 +231,7 @@ class EntityTest extends BrowserKitTest ...@@ -225,7 +231,7 @@ class EntityTest extends BrowserKitTest
225 ->type('super test', '#name') 231 ->type('super test', '#name')
226 ->press('Save Page'); 232 ->press('Save Page');
227 233
228 - $page = \BookStack\Page::first(); 234 + $page = Page::first();
229 $pageUrl = $page->getUrl(); 235 $pageUrl = $page->getUrl();
230 236
231 // Second Save 237 // Second Save
...@@ -242,7 +248,7 @@ class EntityTest extends BrowserKitTest ...@@ -242,7 +248,7 @@ class EntityTest extends BrowserKitTest
242 248
243 public function test_recently_updated_pages_on_home() 249 public function test_recently_updated_pages_on_home()
244 { 250 {
245 - $page = \BookStack\Page::orderBy('updated_at', 'asc')->first(); 251 + $page = Page::orderBy('updated_at', 'asc')->first();
246 $this->asAdmin()->visit('/') 252 $this->asAdmin()->visit('/')
247 ->dontSeeInElement('#recently-updated-pages', $page->name); 253 ->dontSeeInElement('#recently-updated-pages', $page->name);
248 $this->visit($page->getUrl() . '/edit') 254 $this->visit($page->getUrl() . '/edit')
......
1 +<?php namespace Entity;
2 +
3 +
4 +use BookStack\Page;
5 +use Tests\TestCase;
6 +
7 +class PageRevisionTest extends TestCase
8 +{
9 +
10 + public function test_page_revision_count_increments_on_update()
11 + {
12 + $page = Page::first();
13 + $startCount = $page->revision_count;
14 +
15 + $resp = $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
16 + $resp->assertStatus(302);
17 +
18 + $this->assertTrue(Page::find($page->id)->revision_count === $startCount+1);
19 + }
20 +
21 + public function test_revision_count_shown_in_page_meta()
22 + {
23 + $page = Page::first();
24 + $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
25 + $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
26 + $page = Page::find($page->id);
27 +
28 + $pageView = $this->get($page->getUrl());
29 + $pageView->assertSee('Revision #' . $page->revision_count);
30 + }
31 +
32 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -522,4 +522,21 @@ class RestrictionsTest extends BrowserKitTest ...@@ -522,4 +522,21 @@ class RestrictionsTest extends BrowserKitTest
522 ->see('Delete Chapter'); 522 ->see('Delete Chapter');
523 } 523 }
524 524
525 + public function test_page_visible_if_has_permissions_when_book_not_visible()
526 + {
527 + $book = \BookStack\Book::first();
528 + $bookChapter = $book->chapters->first();
529 + $bookPage = $bookChapter->pages->first();
530 +
531 + $this->setEntityRestrictions($book, []);
532 + $this->setEntityRestrictions($bookPage, ['view']);
533 +
534 + $this->actingAs($this->viewer);
535 + $this->get($bookPage->getUrl());
536 + $this->assertResponseOk();
537 + $this->see($bookPage->name);
538 + $this->dontSee(substr($book->name, 0, 15));
539 + $this->dontSee(substr($bookChapter->name, 0, 15));
540 + }
541 +
525 } 542 }
......
...@@ -76,4 +76,16 @@ abstract class TestCase extends BaseTestCase ...@@ -76,4 +76,16 @@ abstract class TestCase extends BaseTestCase
76 public function newChapter($input = ['name' => 'test chapter', 'description' => 'My new test chapter'], Book $book) { 76 public function newChapter($input = ['name' => 'test chapter', 'description' => 'My new test chapter'], Book $book) {
77 return $this->app[EntityRepo::class]->createFromInput('chapter', $input, $book); 77 return $this->app[EntityRepo::class]->createFromInput('chapter', $input, $book);
78 } 78 }
79 +
80 + /**
81 + * Create and return a new test page
82 + * @param array $input
83 + * @return Chapter
84 + */
85 + public function newPage($input = ['name' => 'test page', 'html' => 'My new test page']) {
86 + $book = Book::first();
87 + $entityRepo = $this->app[EntityRepo::class];
88 + $draftPage = $entityRepo->getDraftPage($book);
89 + return $entityRepo->publishPageDraft($draftPage, $input);
90 + }
79 } 91 }
...\ No newline at end of file ...\ No newline at end of file
......
1 -v0.15.3 1 +v0.16
......