Dan Brown

Merge branch 'master' into release ready for v0.14

Showing 199 changed files with 2061 additions and 1033 deletions
1 ### For Feature Requests 1 ### For Feature Requests
2 +
2 Desired Feature: 3 Desired Feature:
3 4
4 ### For Bug Reports 5 ### For Bug Reports
5 -PHP Version:
6 6
7 -MySQL Version: 7 +* BookStack Version:
8 +* PHP Version:
9 +* MySQL Version:
8 10
9 -Expected Behavior: 11 +##### Expected Behavior
10 12
11 -Actual Behavior: 13 +##### Actual Behavior
......
...@@ -13,3 +13,4 @@ _ide_helper.php ...@@ -13,3 +13,4 @@ _ide_helper.php
13 /storage/debugbar 13 /storage/debugbar
14 .phpstorm.meta.php 14 .phpstorm.meta.php
15 yarn.lock 15 yarn.lock
16 +/bin
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -17,9 +17,7 @@ addons: ...@@ -17,9 +17,7 @@ addons:
17 17
18 before_script: 18 before_script:
19 - mysql -u root -e 'create database `bookstack-test`;' 19 - mysql -u root -e 'create database `bookstack-test`;'
20 - - composer config -g github-oauth.github.com $GITHUB_ACCESS_TOKEN
21 - phpenv config-rm xdebug.ini 20 - phpenv config-rm xdebug.ini
22 - - composer self-update
23 - composer dump-autoload --no-interaction 21 - composer dump-autoload --no-interaction
24 - composer install --prefer-dist --no-interaction 22 - composer install --prefer-dist --no-interaction
25 - php artisan clear-compiled -n 23 - php artisan clear-compiled -n
......
...@@ -5,6 +5,8 @@ class Chapter extends Entity ...@@ -5,6 +5,8 @@ class Chapter extends Entity
5 { 5 {
6 protected $fillable = ['name', 'description', 'priority', 'book_id']; 6 protected $fillable = ['name', 'description', 'priority', 'book_id'];
7 7
8 + protected $with = ['book'];
9 +
8 /** 10 /**
9 * Get the book this chapter is within. 11 * Get the book this chapter is within.
10 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 12 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
...@@ -16,11 +18,12 @@ class Chapter extends Entity ...@@ -16,11 +18,12 @@ class Chapter extends Entity
16 18
17 /** 19 /**
18 * Get the pages that this chapter contains. 20 * Get the pages that this chapter contains.
21 + * @param string $dir
19 * @return mixed 22 * @return mixed
20 */ 23 */
21 - public function pages() 24 + public function pages($dir = 'ASC')
22 { 25 {
23 - return $this->hasMany(Page::class)->orderBy('priority', 'ASC'); 26 + return $this->hasMany(Page::class)->orderBy('priority', $dir);
24 } 27 }
25 28
26 /** 29 /**
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
4 class Entity extends Ownable 4 class Entity extends Ownable
5 { 5 {
6 6
7 + protected $fieldsToSearch = ['name', 'description'];
8 +
7 /** 9 /**
8 * Compares this entity to another given entity. 10 * Compares this entity to another given entity.
9 * Matches by comparing class and id. 11 * Matches by comparing class and id.
...@@ -157,7 +159,7 @@ class Entity extends Ownable ...@@ -157,7 +159,7 @@ class Entity extends Ownable
157 * @param string[] array $wheres 159 * @param string[] array $wheres
158 * @return mixed 160 * @return mixed
159 */ 161 */
160 - public function fullTextSearchQuery($fieldsToSearch, $terms, $wheres = []) 162 + public function fullTextSearchQuery($terms, $wheres = [])
161 { 163 {
162 $exactTerms = []; 164 $exactTerms = [];
163 $fuzzyTerms = []; 165 $fuzzyTerms = [];
...@@ -181,16 +183,16 @@ class Entity extends Ownable ...@@ -181,16 +183,16 @@ class Entity extends Ownable
181 // Perform fulltext search if relevant terms exist. 183 // Perform fulltext search if relevant terms exist.
182 if ($isFuzzy) { 184 if ($isFuzzy) {
183 $termString = implode(' ', $fuzzyTerms); 185 $termString = implode(' ', $fuzzyTerms);
184 - $fields = implode(',', $fieldsToSearch); 186 + $fields = implode(',', $this->fieldsToSearch);
185 $search = $search->selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]); 187 $search = $search->selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]);
186 $search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]); 188 $search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
187 } 189 }
188 190
189 // Ensure at least one exact term matches if in search 191 // Ensure at least one exact term matches if in search
190 if (count($exactTerms) > 0) { 192 if (count($exactTerms) > 0) {
191 - $search = $search->where(function ($query) use ($exactTerms, $fieldsToSearch) { 193 + $search = $search->where(function ($query) use ($exactTerms) {
192 foreach ($exactTerms as $exactTerm) { 194 foreach ($exactTerms as $exactTerm) {
193 - foreach ($fieldsToSearch as $field) { 195 + foreach ($this->fieldsToSearch as $field) {
194 $query->orWhere($field, 'like', $exactTerm); 196 $query->orWhere($field, 'like', $exactTerm);
195 } 197 }
196 } 198 }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 2
3 use BookStack\Exceptions\FileUploadException; 3 use BookStack\Exceptions\FileUploadException;
4 use BookStack\Attachment; 4 use BookStack\Attachment;
5 -use BookStack\Repos\PageRepo; 5 +use BookStack\Repos\EntityRepo;
6 use BookStack\Services\AttachmentService; 6 use BookStack\Services\AttachmentService;
7 use Illuminate\Http\Request; 7 use Illuminate\Http\Request;
8 8
...@@ -10,19 +10,19 @@ class AttachmentController extends Controller ...@@ -10,19 +10,19 @@ class AttachmentController extends Controller
10 { 10 {
11 protected $attachmentService; 11 protected $attachmentService;
12 protected $attachment; 12 protected $attachment;
13 - protected $pageRepo; 13 + protected $entityRepo;
14 14
15 /** 15 /**
16 * AttachmentController constructor. 16 * AttachmentController constructor.
17 * @param AttachmentService $attachmentService 17 * @param AttachmentService $attachmentService
18 * @param Attachment $attachment 18 * @param Attachment $attachment
19 - * @param PageRepo $pageRepo 19 + * @param EntityRepo $entityRepo
20 */ 20 */
21 - public function __construct(AttachmentService $attachmentService, Attachment $attachment, PageRepo $pageRepo) 21 + public function __construct(AttachmentService $attachmentService, Attachment $attachment, EntityRepo $entityRepo)
22 { 22 {
23 $this->attachmentService = $attachmentService; 23 $this->attachmentService = $attachmentService;
24 $this->attachment = $attachment; 24 $this->attachment = $attachment;
25 - $this->pageRepo = $pageRepo; 25 + $this->entityRepo = $entityRepo;
26 parent::__construct(); 26 parent::__construct();
27 } 27 }
28 28
...@@ -40,7 +40,7 @@ class AttachmentController extends Controller ...@@ -40,7 +40,7 @@ class AttachmentController extends Controller
40 ]); 40 ]);
41 41
42 $pageId = $request->get('uploaded_to'); 42 $pageId = $request->get('uploaded_to');
43 - $page = $this->pageRepo->getById($pageId, true); 43 + $page = $this->entityRepo->getById('page', $pageId, true);
44 44
45 $this->checkPermission('attachment-create-all'); 45 $this->checkPermission('attachment-create-all');
46 $this->checkOwnablePermission('page-update', $page); 46 $this->checkOwnablePermission('page-update', $page);
...@@ -70,14 +70,14 @@ class AttachmentController extends Controller ...@@ -70,14 +70,14 @@ class AttachmentController extends Controller
70 ]); 70 ]);
71 71
72 $pageId = $request->get('uploaded_to'); 72 $pageId = $request->get('uploaded_to');
73 - $page = $this->pageRepo->getById($pageId, true); 73 + $page = $this->entityRepo->getById('page', $pageId, true);
74 $attachment = $this->attachment->findOrFail($attachmentId); 74 $attachment = $this->attachment->findOrFail($attachmentId);
75 75
76 $this->checkOwnablePermission('page-update', $page); 76 $this->checkOwnablePermission('page-update', $page);
77 $this->checkOwnablePermission('attachment-create', $attachment); 77 $this->checkOwnablePermission('attachment-create', $attachment);
78 78
79 if (intval($pageId) !== intval($attachment->uploaded_to)) { 79 if (intval($pageId) !== intval($attachment->uploaded_to)) {
80 - return $this->jsonError('Page mismatch during attached file update'); 80 + return $this->jsonError(trans('errors.attachment_page_mismatch'));
81 } 81 }
82 82
83 $uploadedFile = $request->file('file'); 83 $uploadedFile = $request->file('file');
...@@ -106,18 +106,18 @@ class AttachmentController extends Controller ...@@ -106,18 +106,18 @@ class AttachmentController extends Controller
106 ]); 106 ]);
107 107
108 $pageId = $request->get('uploaded_to'); 108 $pageId = $request->get('uploaded_to');
109 - $page = $this->pageRepo->getById($pageId, true); 109 + $page = $this->entityRepo->getById('page', $pageId, true);
110 $attachment = $this->attachment->findOrFail($attachmentId); 110 $attachment = $this->attachment->findOrFail($attachmentId);
111 111
112 $this->checkOwnablePermission('page-update', $page); 112 $this->checkOwnablePermission('page-update', $page);
113 $this->checkOwnablePermission('attachment-create', $attachment); 113 $this->checkOwnablePermission('attachment-create', $attachment);
114 114
115 if (intval($pageId) !== intval($attachment->uploaded_to)) { 115 if (intval($pageId) !== intval($attachment->uploaded_to)) {
116 - return $this->jsonError('Page mismatch during attachment update'); 116 + return $this->jsonError(trans('errors.attachment_page_mismatch'));
117 } 117 }
118 118
119 $attachment = $this->attachmentService->updateFile($attachment, $request->all()); 119 $attachment = $this->attachmentService->updateFile($attachment, $request->all());
120 - return $attachment; 120 + return response()->json($attachment);
121 } 121 }
122 122
123 /** 123 /**
...@@ -134,7 +134,7 @@ class AttachmentController extends Controller ...@@ -134,7 +134,7 @@ class AttachmentController extends Controller
134 ]); 134 ]);
135 135
136 $pageId = $request->get('uploaded_to'); 136 $pageId = $request->get('uploaded_to');
137 - $page = $this->pageRepo->getById($pageId, true); 137 + $page = $this->entityRepo->getById('page', $pageId, true);
138 138
139 $this->checkPermission('attachment-create-all'); 139 $this->checkPermission('attachment-create-all');
140 $this->checkOwnablePermission('page-update', $page); 140 $this->checkOwnablePermission('page-update', $page);
...@@ -153,7 +153,7 @@ class AttachmentController extends Controller ...@@ -153,7 +153,7 @@ class AttachmentController extends Controller
153 */ 153 */
154 public function listForPage($pageId) 154 public function listForPage($pageId)
155 { 155 {
156 - $page = $this->pageRepo->getById($pageId, true); 156 + $page = $this->entityRepo->getById('page', $pageId, true);
157 $this->checkOwnablePermission('page-view', $page); 157 $this->checkOwnablePermission('page-view', $page);
158 return response()->json($page->attachments); 158 return response()->json($page->attachments);
159 } 159 }
...@@ -170,12 +170,12 @@ class AttachmentController extends Controller ...@@ -170,12 +170,12 @@ class AttachmentController extends Controller
170 'files' => 'required|array', 170 'files' => 'required|array',
171 'files.*.id' => 'required|integer', 171 'files.*.id' => 'required|integer',
172 ]); 172 ]);
173 - $page = $this->pageRepo->getById($pageId); 173 + $page = $this->entityRepo->getById('page', $pageId);
174 $this->checkOwnablePermission('page-update', $page); 174 $this->checkOwnablePermission('page-update', $page);
175 175
176 $attachments = $request->get('files'); 176 $attachments = $request->get('files');
177 $this->attachmentService->updateFileOrderWithinPage($attachments, $pageId); 177 $this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
178 - return response()->json(['message' => 'Attachment order updated']); 178 + return response()->json(['message' => trans('entities.attachments_order_updated')]);
179 } 179 }
180 180
181 /** 181 /**
...@@ -186,7 +186,7 @@ class AttachmentController extends Controller ...@@ -186,7 +186,7 @@ class AttachmentController extends Controller
186 public function get($attachmentId) 186 public function get($attachmentId)
187 { 187 {
188 $attachment = $this->attachment->findOrFail($attachmentId); 188 $attachment = $this->attachment->findOrFail($attachmentId);
189 - $page = $this->pageRepo->getById($attachment->uploaded_to); 189 + $page = $this->entityRepo->getById('page', $attachment->uploaded_to);
190 $this->checkOwnablePermission('page-view', $page); 190 $this->checkOwnablePermission('page-view', $page);
191 191
192 if ($attachment->external) { 192 if ($attachment->external) {
...@@ -210,6 +210,6 @@ class AttachmentController extends Controller ...@@ -210,6 +210,6 @@ class AttachmentController extends Controller
210 $attachment = $this->attachment->findOrFail($attachmentId); 210 $attachment = $this->attachment->findOrFail($attachmentId);
211 $this->checkOwnablePermission('attachment-delete', $attachment); 211 $this->checkOwnablePermission('attachment-delete', $attachment);
212 $this->attachmentService->deleteFile($attachment); 212 $this->attachmentService->deleteFile($attachment);
213 - return response()->json(['message' => 'Attachment deleted']); 213 + return response()->json(['message' => trans('entities.attachments_deleted')]);
214 } 214 }
215 } 215 }
......
...@@ -52,7 +52,7 @@ class ForgotPasswordController extends Controller ...@@ -52,7 +52,7 @@ class ForgotPasswordController extends Controller
52 ); 52 );
53 53
54 if ($response === Password::RESET_LINK_SENT) { 54 if ($response === Password::RESET_LINK_SENT) {
55 - $message = 'A password reset link has been sent to ' . $request->get('email') . '.'; 55 + $message = trans('auth.reset_password_sent_success', ['email' => $request->get('email')]);
56 session()->flash('success', $message); 56 session()->flash('success', $message);
57 return back()->with('status', trans($response)); 57 return back()->with('status', trans($response));
58 } 58 }
......
...@@ -87,7 +87,7 @@ class LoginController extends Controller ...@@ -87,7 +87,7 @@ class LoginController extends Controller
87 // Check for users with same email already 87 // Check for users with same email already
88 $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0; 88 $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
89 if ($alreadyUser) { 89 if ($alreadyUser) {
90 - throw new AuthException('A user with the email ' . $user->email . ' already exists but with different credentials.'); 90 + throw new AuthException(trans('errors.error_user_exists_different_creds', ['email' => $user->email]));
91 } 91 }
92 92
93 $user->save(); 93 $user->save();
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
3 namespace BookStack\Http\Controllers\Auth; 3 namespace BookStack\Http\Controllers\Auth;
4 4
5 use BookStack\Exceptions\ConfirmationEmailException; 5 use BookStack\Exceptions\ConfirmationEmailException;
6 +use BookStack\Exceptions\SocialSignInException;
6 use BookStack\Exceptions\UserRegistrationException; 7 use BookStack\Exceptions\UserRegistrationException;
7 use BookStack\Repos\UserRepo; 8 use BookStack\Repos\UserRepo;
8 use BookStack\Services\EmailConfirmationService; 9 use BookStack\Services\EmailConfirmationService;
...@@ -82,7 +83,7 @@ class RegisterController extends Controller ...@@ -82,7 +83,7 @@ class RegisterController extends Controller
82 protected function checkRegistrationAllowed() 83 protected function checkRegistrationAllowed()
83 { 84 {
84 if (!setting('registration-enabled')) { 85 if (!setting('registration-enabled')) {
85 - throw new UserRegistrationException('Registrations are currently disabled.', '/login'); 86 + throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
86 } 87 }
87 } 88 }
88 89
...@@ -147,7 +148,7 @@ class RegisterController extends Controller ...@@ -147,7 +148,7 @@ class RegisterController extends Controller
147 $restrictedEmailDomains = explode(',', str_replace(' ', '', setting('registration-restrict'))); 148 $restrictedEmailDomains = explode(',', str_replace(' ', '', setting('registration-restrict')));
148 $userEmailDomain = $domain = substr(strrchr($userData['email'], "@"), 1); 149 $userEmailDomain = $domain = substr(strrchr($userData['email'], "@"), 1);
149 if (!in_array($userEmailDomain, $restrictedEmailDomains)) { 150 if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
150 - throw new UserRegistrationException('That email domain does not have access to this application', '/register'); 151 + throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), '/register');
151 } 152 }
152 } 153 }
153 154
...@@ -169,7 +170,7 @@ class RegisterController extends Controller ...@@ -169,7 +170,7 @@ class RegisterController extends Controller
169 } 170 }
170 171
171 auth()->login($newUser); 172 auth()->login($newUser);
172 - session()->flash('success', 'Thanks for signing up! You are now registered and signed in.'); 173 + session()->flash('success', trans('auth.register_success'));
173 return redirect($this->redirectPath()); 174 return redirect($this->redirectPath());
174 } 175 }
175 176
...@@ -262,7 +263,7 @@ class RegisterController extends Controller ...@@ -262,7 +263,7 @@ class RegisterController extends Controller
262 return $this->socialRegisterCallback($socialDriver); 263 return $this->socialRegisterCallback($socialDriver);
263 } 264 }
264 } else { 265 } else {
265 - throw new SocialSignInException('No action defined', '/login'); 266 + throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
266 } 267 }
267 return redirect()->back(); 268 return redirect()->back();
268 } 269 }
......
...@@ -41,7 +41,7 @@ class ResetPasswordController extends Controller ...@@ -41,7 +41,7 @@ class ResetPasswordController extends Controller
41 */ 41 */
42 protected function sendResetResponse($response) 42 protected function sendResetResponse($response)
43 { 43 {
44 - $message = 'Your password has been successfully reset.'; 44 + $message = trans('auth.reset_password_success');
45 session()->flash('success', $message); 45 session()->flash('success', $message);
46 return redirect($this->redirectPath()) 46 return redirect($this->redirectPath())
47 ->with('status', trans($response)); 47 ->with('status', trans($response));
......
1 <?php namespace BookStack\Http\Controllers; 1 <?php namespace BookStack\Http\Controllers;
2 2
3 use Activity; 3 use Activity;
4 +use BookStack\Repos\EntityRepo;
4 use BookStack\Repos\UserRepo; 5 use BookStack\Repos\UserRepo;
5 use Illuminate\Http\Request; 6 use Illuminate\Http\Request;
6 -use BookStack\Http\Requests; 7 +use Illuminate\Http\Response;
7 -use BookStack\Repos\BookRepo;
8 -use BookStack\Repos\ChapterRepo;
9 -use BookStack\Repos\PageRepo;
10 use Views; 8 use Views;
11 9
12 class BookController extends Controller 10 class BookController extends Controller
13 { 11 {
14 12
15 - protected $bookRepo; 13 + protected $entityRepo;
16 - protected $pageRepo;
17 - protected $chapterRepo;
18 protected $userRepo; 14 protected $userRepo;
19 15
20 /** 16 /**
21 * BookController constructor. 17 * BookController constructor.
22 - * @param BookRepo $bookRepo 18 + * @param EntityRepo $entityRepo
23 - * @param PageRepo $pageRepo
24 - * @param ChapterRepo $chapterRepo
25 * @param UserRepo $userRepo 19 * @param UserRepo $userRepo
26 */ 20 */
27 - public function __construct(BookRepo $bookRepo, PageRepo $pageRepo, ChapterRepo $chapterRepo, UserRepo $userRepo) 21 + public function __construct(EntityRepo $entityRepo, UserRepo $userRepo)
28 { 22 {
29 - $this->bookRepo = $bookRepo; 23 + $this->entityRepo = $entityRepo;
30 - $this->pageRepo = $pageRepo;
31 - $this->chapterRepo = $chapterRepo;
32 $this->userRepo = $userRepo; 24 $this->userRepo = $userRepo;
33 parent::__construct(); 25 parent::__construct();
34 } 26 }
...@@ -39,9 +31,9 @@ class BookController extends Controller ...@@ -39,9 +31,9 @@ class BookController extends Controller
39 */ 31 */
40 public function index() 32 public function index()
41 { 33 {
42 - $books = $this->bookRepo->getAllPaginated(10); 34 + $books = $this->entityRepo->getAllPaginated('book', 10);
43 - $recents = $this->signedIn ? $this->bookRepo->getRecentlyViewed(4, 0) : false; 35 + $recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
44 - $popular = $this->bookRepo->getPopular(4, 0); 36 + $popular = $this->entityRepo->getPopular('book', 4, 0);
45 $this->setPageTitle('Books'); 37 $this->setPageTitle('Books');
46 return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular]); 38 return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular]);
47 } 39 }
...@@ -53,7 +45,7 @@ class BookController extends Controller ...@@ -53,7 +45,7 @@ class BookController extends Controller
53 public function create() 45 public function create()
54 { 46 {
55 $this->checkPermission('book-create-all'); 47 $this->checkPermission('book-create-all');
56 - $this->setPageTitle('Create New Book'); 48 + $this->setPageTitle(trans('entities.books_create'));
57 return view('books/create'); 49 return view('books/create');
58 } 50 }
59 51
...@@ -70,7 +62,7 @@ class BookController extends Controller ...@@ -70,7 +62,7 @@ class BookController extends Controller
70 'name' => 'required|string|max:255', 62 'name' => 'required|string|max:255',
71 'description' => 'string|max:1000' 63 'description' => 'string|max:1000'
72 ]); 64 ]);
73 - $book = $this->bookRepo->createFromInput($request->all()); 65 + $book = $this->entityRepo->createFromInput('book', $request->all());
74 Activity::add($book, 'book_create', $book->id); 66 Activity::add($book, 'book_create', $book->id);
75 return redirect($book->getUrl()); 67 return redirect($book->getUrl());
76 } 68 }
...@@ -82,9 +74,9 @@ class BookController extends Controller ...@@ -82,9 +74,9 @@ class BookController extends Controller
82 */ 74 */
83 public function show($slug) 75 public function show($slug)
84 { 76 {
85 - $book = $this->bookRepo->getBySlug($slug); 77 + $book = $this->entityRepo->getBySlug('book', $slug);
86 $this->checkOwnablePermission('book-view', $book); 78 $this->checkOwnablePermission('book-view', $book);
87 - $bookChildren = $this->bookRepo->getChildren($book); 79 + $bookChildren = $this->entityRepo->getBookChildren($book);
88 Views::add($book); 80 Views::add($book);
89 $this->setPageTitle($book->getShortName()); 81 $this->setPageTitle($book->getShortName());
90 return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]); 82 return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]);
...@@ -97,9 +89,9 @@ class BookController extends Controller ...@@ -97,9 +89,9 @@ class BookController extends Controller
97 */ 89 */
98 public function edit($slug) 90 public function edit($slug)
99 { 91 {
100 - $book = $this->bookRepo->getBySlug($slug); 92 + $book = $this->entityRepo->getBySlug('book', $slug);
101 $this->checkOwnablePermission('book-update', $book); 93 $this->checkOwnablePermission('book-update', $book);
102 - $this->setPageTitle('Edit Book ' . $book->getShortName()); 94 + $this->setPageTitle(trans('entities.books_edit_named',['bookName'=>$book->getShortName()]));
103 return view('books/edit', ['book' => $book, 'current' => $book]); 95 return view('books/edit', ['book' => $book, 'current' => $book]);
104 } 96 }
105 97
...@@ -111,13 +103,13 @@ class BookController extends Controller ...@@ -111,13 +103,13 @@ class BookController extends Controller
111 */ 103 */
112 public function update(Request $request, $slug) 104 public function update(Request $request, $slug)
113 { 105 {
114 - $book = $this->bookRepo->getBySlug($slug); 106 + $book = $this->entityRepo->getBySlug('book', $slug);
115 $this->checkOwnablePermission('book-update', $book); 107 $this->checkOwnablePermission('book-update', $book);
116 $this->validate($request, [ 108 $this->validate($request, [
117 'name' => 'required|string|max:255', 109 'name' => 'required|string|max:255',
118 'description' => 'string|max:1000' 110 'description' => 'string|max:1000'
119 ]); 111 ]);
120 - $book = $this->bookRepo->updateFromInput($book, $request->all()); 112 + $book = $this->entityRepo->updateFromInput('book', $book, $request->all());
121 Activity::add($book, 'book_update', $book->id); 113 Activity::add($book, 'book_update', $book->id);
122 return redirect($book->getUrl()); 114 return redirect($book->getUrl());
123 } 115 }
...@@ -129,9 +121,9 @@ class BookController extends Controller ...@@ -129,9 +121,9 @@ class BookController extends Controller
129 */ 121 */
130 public function showDelete($bookSlug) 122 public function showDelete($bookSlug)
131 { 123 {
132 - $book = $this->bookRepo->getBySlug($bookSlug); 124 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
133 $this->checkOwnablePermission('book-delete', $book); 125 $this->checkOwnablePermission('book-delete', $book);
134 - $this->setPageTitle('Delete Book ' . $book->getShortName()); 126 + $this->setPageTitle(trans('entities.books_delete_named', ['bookName'=>$book->getShortName()]));
135 return view('books/delete', ['book' => $book, 'current' => $book]); 127 return view('books/delete', ['book' => $book, 'current' => $book]);
136 } 128 }
137 129
...@@ -142,11 +134,11 @@ class BookController extends Controller ...@@ -142,11 +134,11 @@ class BookController extends Controller
142 */ 134 */
143 public function sort($bookSlug) 135 public function sort($bookSlug)
144 { 136 {
145 - $book = $this->bookRepo->getBySlug($bookSlug); 137 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
146 $this->checkOwnablePermission('book-update', $book); 138 $this->checkOwnablePermission('book-update', $book);
147 - $bookChildren = $this->bookRepo->getChildren($book, true); 139 + $bookChildren = $this->entityRepo->getBookChildren($book, true);
148 - $books = $this->bookRepo->getAll(false); 140 + $books = $this->entityRepo->getAll('book', false);
149 - $this->setPageTitle('Sort Book ' . $book->getShortName()); 141 + $this->setPageTitle(trans('entities.books_sort_named', ['bookName'=>$book->getShortName()]));
150 return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]); 142 return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]);
151 } 143 }
152 144
...@@ -158,8 +150,8 @@ class BookController extends Controller ...@@ -158,8 +150,8 @@ class BookController extends Controller
158 */ 150 */
159 public function getSortItem($bookSlug) 151 public function getSortItem($bookSlug)
160 { 152 {
161 - $book = $this->bookRepo->getBySlug($bookSlug); 153 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
162 - $bookChildren = $this->bookRepo->getChildren($book); 154 + $bookChildren = $this->entityRepo->getBookChildren($book);
163 return view('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren]); 155 return view('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren]);
164 } 156 }
165 157
...@@ -171,7 +163,7 @@ class BookController extends Controller ...@@ -171,7 +163,7 @@ class BookController extends Controller
171 */ 163 */
172 public function saveSort($bookSlug, Request $request) 164 public function saveSort($bookSlug, Request $request)
173 { 165 {
174 - $book = $this->bookRepo->getBySlug($bookSlug); 166 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
175 $this->checkOwnablePermission('book-update', $book); 167 $this->checkOwnablePermission('book-update', $book);
176 168
177 // Return if no map sent 169 // Return if no map sent
...@@ -190,13 +182,13 @@ class BookController extends Controller ...@@ -190,13 +182,13 @@ class BookController extends Controller
190 $priority = $bookChild->sort; 182 $priority = $bookChild->sort;
191 $id = intval($bookChild->id); 183 $id = intval($bookChild->id);
192 $isPage = $bookChild->type == 'page'; 184 $isPage = $bookChild->type == 'page';
193 - $bookId = $this->bookRepo->exists($bookChild->book) ? intval($bookChild->book) : $defaultBookId; 185 + $bookId = $this->entityRepo->exists('book', $bookChild->book) ? intval($bookChild->book) : $defaultBookId;
194 $chapterId = ($isPage && $bookChild->parentChapter === false) ? 0 : intval($bookChild->parentChapter); 186 $chapterId = ($isPage && $bookChild->parentChapter === false) ? 0 : intval($bookChild->parentChapter);
195 - $model = $isPage ? $this->pageRepo->getById($id) : $this->chapterRepo->getById($id); 187 + $model = $this->entityRepo->getById($isPage?'page':'chapter', $id);
196 188
197 // Update models only if there's a change in parent chain or ordering. 189 // Update models only if there's a change in parent chain or ordering.
198 if ($model->priority !== $priority || $model->book_id !== $bookId || ($isPage && $model->chapter_id !== $chapterId)) { 190 if ($model->priority !== $priority || $model->book_id !== $bookId || ($isPage && $model->chapter_id !== $chapterId)) {
199 - $isPage ? $this->pageRepo->changeBook($bookId, $model) : $this->chapterRepo->changeBook($bookId, $model); 191 + $this->entityRepo->changeBook($isPage?'page':'chapter', $bookId, $model);
200 $model->priority = $priority; 192 $model->priority = $priority;
201 if ($isPage) $model->chapter_id = $chapterId; 193 if ($isPage) $model->chapter_id = $chapterId;
202 $model->save(); 194 $model->save();
...@@ -211,12 +203,12 @@ class BookController extends Controller ...@@ -211,12 +203,12 @@ class BookController extends Controller
211 203
212 // Add activity for books 204 // Add activity for books
213 foreach ($sortedBooks as $bookId) { 205 foreach ($sortedBooks as $bookId) {
214 - $updatedBook = $this->bookRepo->getById($bookId); 206 + $updatedBook = $this->entityRepo->getById('book', $bookId);
215 Activity::add($updatedBook, 'book_sort', $updatedBook->id); 207 Activity::add($updatedBook, 'book_sort', $updatedBook->id);
216 } 208 }
217 209
218 // Update permissions on changed models 210 // Update permissions on changed models
219 - $this->bookRepo->buildJointPermissions($updatedModels); 211 + $this->entityRepo->buildJointPermissions($updatedModels);
220 212
221 return redirect($book->getUrl()); 213 return redirect($book->getUrl());
222 } 214 }
...@@ -228,11 +220,10 @@ class BookController extends Controller ...@@ -228,11 +220,10 @@ class BookController extends Controller
228 */ 220 */
229 public function destroy($bookSlug) 221 public function destroy($bookSlug)
230 { 222 {
231 - $book = $this->bookRepo->getBySlug($bookSlug); 223 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
232 $this->checkOwnablePermission('book-delete', $book); 224 $this->checkOwnablePermission('book-delete', $book);
233 Activity::addMessage('book_delete', 0, $book->name); 225 Activity::addMessage('book_delete', 0, $book->name);
234 - Activity::removeEntity($book); 226 + $this->entityRepo->destroyBook($book);
235 - $this->bookRepo->destroy($book);
236 return redirect('/books'); 227 return redirect('/books');
237 } 228 }
238 229
...@@ -243,7 +234,7 @@ class BookController extends Controller ...@@ -243,7 +234,7 @@ class BookController extends Controller
243 */ 234 */
244 public function showRestrict($bookSlug) 235 public function showRestrict($bookSlug)
245 { 236 {
246 - $book = $this->bookRepo->getBySlug($bookSlug); 237 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
247 $this->checkOwnablePermission('restrictions-manage', $book); 238 $this->checkOwnablePermission('restrictions-manage', $book);
248 $roles = $this->userRepo->getRestrictableRoles(); 239 $roles = $this->userRepo->getRestrictableRoles();
249 return view('books/restrictions', [ 240 return view('books/restrictions', [
...@@ -261,10 +252,10 @@ class BookController extends Controller ...@@ -261,10 +252,10 @@ class BookController extends Controller
261 */ 252 */
262 public function restrict($bookSlug, Request $request) 253 public function restrict($bookSlug, Request $request)
263 { 254 {
264 - $book = $this->bookRepo->getBySlug($bookSlug); 255 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
265 $this->checkOwnablePermission('restrictions-manage', $book); 256 $this->checkOwnablePermission('restrictions-manage', $book);
266 - $this->bookRepo->updateEntityPermissionsFromRequest($request, $book); 257 + $this->entityRepo->updateEntityPermissionsFromRequest($request, $book);
267 - session()->flash('success', 'Book Restrictions Updated'); 258 + session()->flash('success', trans('entities.books_permissions_updated'));
268 return redirect($book->getUrl()); 259 return redirect($book->getUrl());
269 } 260 }
270 } 261 }
......
1 <?php namespace BookStack\Http\Controllers; 1 <?php namespace BookStack\Http\Controllers;
2 2
3 use Activity; 3 use Activity;
4 +use BookStack\Repos\EntityRepo;
4 use BookStack\Repos\UserRepo; 5 use BookStack\Repos\UserRepo;
5 use Illuminate\Http\Request; 6 use Illuminate\Http\Request;
6 -use BookStack\Http\Requests; 7 +use Illuminate\Http\Response;
7 -use BookStack\Repos\BookRepo;
8 -use BookStack\Repos\ChapterRepo;
9 use Views; 8 use Views;
10 9
11 class ChapterController extends Controller 10 class ChapterController extends Controller
12 { 11 {
13 12
14 - protected $bookRepo;
15 - protected $chapterRepo;
16 protected $userRepo; 13 protected $userRepo;
14 + protected $entityRepo;
17 15
18 /** 16 /**
19 * ChapterController constructor. 17 * ChapterController constructor.
20 - * @param BookRepo $bookRepo 18 + * @param EntityRepo $entityRepo
21 - * @param ChapterRepo $chapterRepo
22 * @param UserRepo $userRepo 19 * @param UserRepo $userRepo
23 */ 20 */
24 - public function __construct(BookRepo $bookRepo, ChapterRepo $chapterRepo, UserRepo $userRepo) 21 + public function __construct(EntityRepo $entityRepo, UserRepo $userRepo)
25 { 22 {
26 - $this->bookRepo = $bookRepo; 23 + $this->entityRepo = $entityRepo;
27 - $this->chapterRepo = $chapterRepo;
28 $this->userRepo = $userRepo; 24 $this->userRepo = $userRepo;
29 parent::__construct(); 25 parent::__construct();
30 } 26 }
...@@ -36,9 +32,9 @@ class ChapterController extends Controller ...@@ -36,9 +32,9 @@ class ChapterController extends Controller
36 */ 32 */
37 public function create($bookSlug) 33 public function create($bookSlug)
38 { 34 {
39 - $book = $this->bookRepo->getBySlug($bookSlug); 35 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
40 $this->checkOwnablePermission('chapter-create', $book); 36 $this->checkOwnablePermission('chapter-create', $book);
41 - $this->setPageTitle('Create New Chapter'); 37 + $this->setPageTitle(trans('entities.chapters_create'));
42 return view('chapters/create', ['book' => $book, 'current' => $book]); 38 return view('chapters/create', ['book' => $book, 'current' => $book]);
43 } 39 }
44 40
...@@ -54,12 +50,12 @@ class ChapterController extends Controller ...@@ -54,12 +50,12 @@ class ChapterController extends Controller
54 'name' => 'required|string|max:255' 50 'name' => 'required|string|max:255'
55 ]); 51 ]);
56 52
57 - $book = $this->bookRepo->getBySlug($bookSlug); 53 + $book = $this->entityRepo->getBySlug('book', $bookSlug);
58 $this->checkOwnablePermission('chapter-create', $book); 54 $this->checkOwnablePermission('chapter-create', $book);
59 55
60 $input = $request->all(); 56 $input = $request->all();
61 - $input['priority'] = $this->bookRepo->getNewPriority($book); 57 + $input['priority'] = $this->entityRepo->getNewBookPriority($book);
62 - $chapter = $this->chapterRepo->createFromInput($input, $book); 58 + $chapter = $this->entityRepo->createFromInput('chapter', $input, $book);
63 Activity::add($chapter, 'chapter_create', $book->id); 59 Activity::add($chapter, 'chapter_create', $book->id);
64 return redirect($chapter->getUrl()); 60 return redirect($chapter->getUrl());
65 } 61 }
...@@ -72,15 +68,14 @@ class ChapterController extends Controller ...@@ -72,15 +68,14 @@ class ChapterController extends Controller
72 */ 68 */
73 public function show($bookSlug, $chapterSlug) 69 public function show($bookSlug, $chapterSlug)
74 { 70 {
75 - $book = $this->bookRepo->getBySlug($bookSlug); 71 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
76 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
77 $this->checkOwnablePermission('chapter-view', $chapter); 72 $this->checkOwnablePermission('chapter-view', $chapter);
78 - $sidebarTree = $this->bookRepo->getChildren($book); 73 + $sidebarTree = $this->entityRepo->getBookChildren($chapter->book);
79 Views::add($chapter); 74 Views::add($chapter);
80 $this->setPageTitle($chapter->getShortName()); 75 $this->setPageTitle($chapter->getShortName());
81 - $pages = $this->chapterRepo->getChildren($chapter); 76 + $pages = $this->entityRepo->getChapterChildren($chapter);
82 return view('chapters/show', [ 77 return view('chapters/show', [
83 - 'book' => $book, 78 + 'book' => $chapter->book,
84 'chapter' => $chapter, 79 'chapter' => $chapter,
85 'current' => $chapter, 80 'current' => $chapter,
86 'sidebarTree' => $sidebarTree, 81 'sidebarTree' => $sidebarTree,
...@@ -96,11 +91,10 @@ class ChapterController extends Controller ...@@ -96,11 +91,10 @@ class ChapterController extends Controller
96 */ 91 */
97 public function edit($bookSlug, $chapterSlug) 92 public function edit($bookSlug, $chapterSlug)
98 { 93 {
99 - $book = $this->bookRepo->getBySlug($bookSlug); 94 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
100 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
101 $this->checkOwnablePermission('chapter-update', $chapter); 95 $this->checkOwnablePermission('chapter-update', $chapter);
102 - $this->setPageTitle('Edit Chapter' . $chapter->getShortName()); 96 + $this->setPageTitle(trans('entities.chapters_edit_named', ['chapterName' => $chapter->getShortName()]));
103 - return view('chapters/edit', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]); 97 + return view('chapters/edit', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
104 } 98 }
105 99
106 /** 100 /**
...@@ -112,16 +106,15 @@ class ChapterController extends Controller ...@@ -112,16 +106,15 @@ class ChapterController extends Controller
112 */ 106 */
113 public function update(Request $request, $bookSlug, $chapterSlug) 107 public function update(Request $request, $bookSlug, $chapterSlug)
114 { 108 {
115 - $book = $this->bookRepo->getBySlug($bookSlug); 109 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
116 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
117 $this->checkOwnablePermission('chapter-update', $chapter); 110 $this->checkOwnablePermission('chapter-update', $chapter);
118 if ($chapter->name !== $request->get('name')) { 111 if ($chapter->name !== $request->get('name')) {
119 - $chapter->slug = $this->chapterRepo->findSuitableSlug($request->get('name'), $book->id, $chapter->id); 112 + $chapter->slug = $this->entityRepo->findSuitableSlug('chapter', $request->get('name'), $chapter->id, $chapter->book->id);
120 } 113 }
121 $chapter->fill($request->all()); 114 $chapter->fill($request->all());
122 $chapter->updated_by = user()->id; 115 $chapter->updated_by = user()->id;
123 $chapter->save(); 116 $chapter->save();
124 - Activity::add($chapter, 'chapter_update', $book->id); 117 + Activity::add($chapter, 'chapter_update', $chapter->book->id);
125 return redirect($chapter->getUrl()); 118 return redirect($chapter->getUrl());
126 } 119 }
127 120
...@@ -133,11 +126,10 @@ class ChapterController extends Controller ...@@ -133,11 +126,10 @@ class ChapterController extends Controller
133 */ 126 */
134 public function showDelete($bookSlug, $chapterSlug) 127 public function showDelete($bookSlug, $chapterSlug)
135 { 128 {
136 - $book = $this->bookRepo->getBySlug($bookSlug); 129 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
137 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
138 $this->checkOwnablePermission('chapter-delete', $chapter); 130 $this->checkOwnablePermission('chapter-delete', $chapter);
139 - $this->setPageTitle('Delete Chapter' . $chapter->getShortName()); 131 + $this->setPageTitle(trans('entities.chapters_delete_named', ['chapterName' => $chapter->getShortName()]));
140 - return view('chapters/delete', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]); 132 + return view('chapters/delete', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
141 } 133 }
142 134
143 /** 135 /**
...@@ -148,11 +140,11 @@ class ChapterController extends Controller ...@@ -148,11 +140,11 @@ class ChapterController extends Controller
148 */ 140 */
149 public function destroy($bookSlug, $chapterSlug) 141 public function destroy($bookSlug, $chapterSlug)
150 { 142 {
151 - $book = $this->bookRepo->getBySlug($bookSlug); 143 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
152 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); 144 + $book = $chapter->book;
153 $this->checkOwnablePermission('chapter-delete', $chapter); 145 $this->checkOwnablePermission('chapter-delete', $chapter);
154 Activity::addMessage('chapter_delete', $book->id, $chapter->name); 146 Activity::addMessage('chapter_delete', $book->id, $chapter->name);
155 - $this->chapterRepo->destroy($chapter); 147 + $this->entityRepo->destroyChapter($chapter);
156 return redirect($book->getUrl()); 148 return redirect($book->getUrl());
157 } 149 }
158 150
...@@ -164,12 +156,12 @@ class ChapterController extends Controller ...@@ -164,12 +156,12 @@ class ChapterController extends Controller
164 * @throws \BookStack\Exceptions\NotFoundException 156 * @throws \BookStack\Exceptions\NotFoundException
165 */ 157 */
166 public function showMove($bookSlug, $chapterSlug) { 158 public function showMove($bookSlug, $chapterSlug) {
167 - $book = $this->bookRepo->getBySlug($bookSlug); 159 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
168 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); 160 + $this->setPageTitle(trans('entities.chapters_move_named', ['chapterName' => $chapter->getShortName()]));
169 $this->checkOwnablePermission('chapter-update', $chapter); 161 $this->checkOwnablePermission('chapter-update', $chapter);
170 return view('chapters/move', [ 162 return view('chapters/move', [
171 'chapter' => $chapter, 163 'chapter' => $chapter,
172 - 'book' => $book 164 + 'book' => $chapter->book
173 ]); 165 ]);
174 } 166 }
175 167
...@@ -182,8 +174,7 @@ class ChapterController extends Controller ...@@ -182,8 +174,7 @@ class ChapterController extends Controller
182 * @throws \BookStack\Exceptions\NotFoundException 174 * @throws \BookStack\Exceptions\NotFoundException
183 */ 175 */
184 public function move($bookSlug, $chapterSlug, Request $request) { 176 public function move($bookSlug, $chapterSlug, Request $request) {
185 - $book = $this->bookRepo->getBySlug($bookSlug); 177 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
186 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
187 $this->checkOwnablePermission('chapter-update', $chapter); 178 $this->checkOwnablePermission('chapter-update', $chapter);
188 179
189 $entitySelection = $request->get('entity_selection', null); 180 $entitySelection = $request->get('entity_selection', null);
...@@ -198,17 +189,17 @@ class ChapterController extends Controller ...@@ -198,17 +189,17 @@ class ChapterController extends Controller
198 $parent = false; 189 $parent = false;
199 190
200 if ($entityType == 'book') { 191 if ($entityType == 'book') {
201 - $parent = $this->bookRepo->getById($entityId); 192 + $parent = $this->entityRepo->getById('book', $entityId);
202 } 193 }
203 194
204 if ($parent === false || $parent === null) { 195 if ($parent === false || $parent === null) {
205 - session()->flash('The selected Book was not found'); 196 + session()->flash('error', trans('errors.selected_book_not_found'));
206 return redirect()->back(); 197 return redirect()->back();
207 } 198 }
208 199
209 - $this->chapterRepo->changeBook($parent->id, $chapter, true); 200 + $this->entityRepo->changeBook('chapter', $parent->id, $chapter, true);
210 Activity::add($chapter, 'chapter_move', $chapter->book->id); 201 Activity::add($chapter, 'chapter_move', $chapter->book->id);
211 - session()->flash('success', sprintf('Chapter moved to "%s"', $parent->name)); 202 + session()->flash('success', trans('entities.chapter_move_success', ['bookName' => $parent->name]));
212 203
213 return redirect($chapter->getUrl()); 204 return redirect($chapter->getUrl());
214 } 205 }
...@@ -221,8 +212,7 @@ class ChapterController extends Controller ...@@ -221,8 +212,7 @@ class ChapterController extends Controller
221 */ 212 */
222 public function showRestrict($bookSlug, $chapterSlug) 213 public function showRestrict($bookSlug, $chapterSlug)
223 { 214 {
224 - $book = $this->bookRepo->getBySlug($bookSlug); 215 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
225 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
226 $this->checkOwnablePermission('restrictions-manage', $chapter); 216 $this->checkOwnablePermission('restrictions-manage', $chapter);
227 $roles = $this->userRepo->getRestrictableRoles(); 217 $roles = $this->userRepo->getRestrictableRoles();
228 return view('chapters/restrictions', [ 218 return view('chapters/restrictions', [
...@@ -240,11 +230,10 @@ class ChapterController extends Controller ...@@ -240,11 +230,10 @@ class ChapterController extends Controller
240 */ 230 */
241 public function restrict($bookSlug, $chapterSlug, Request $request) 231 public function restrict($bookSlug, $chapterSlug, Request $request)
242 { 232 {
243 - $book = $this->bookRepo->getBySlug($bookSlug); 233 + $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
244 - $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
245 $this->checkOwnablePermission('restrictions-manage', $chapter); 234 $this->checkOwnablePermission('restrictions-manage', $chapter);
246 - $this->chapterRepo->updateEntityPermissionsFromRequest($request, $chapter); 235 + $this->entityRepo->updateEntityPermissionsFromRequest($request, $chapter);
247 - session()->flash('success', 'Chapter Restrictions Updated'); 236 + session()->flash('success', trans('entities.chapters_permissions_success'));
248 return redirect($chapter->getUrl()); 237 return redirect($chapter->getUrl());
249 } 238 }
250 } 239 }
......
...@@ -5,6 +5,7 @@ namespace BookStack\Http\Controllers; ...@@ -5,6 +5,7 @@ namespace BookStack\Http\Controllers;
5 use Activity; 5 use Activity;
6 use BookStack\Repos\EntityRepo; 6 use BookStack\Repos\EntityRepo;
7 use BookStack\Http\Requests; 7 use BookStack\Http\Requests;
8 +use Illuminate\Http\Response;
8 use Views; 9 use Views;
9 10
10 class HomeController extends Controller 11 class HomeController extends Controller
...@@ -31,9 +32,9 @@ class HomeController extends Controller ...@@ -31,9 +32,9 @@ class HomeController extends Controller
31 $activity = Activity::latest(10); 32 $activity = Activity::latest(10);
32 $draftPages = $this->signedIn ? $this->entityRepo->getUserDraftPages(6) : []; 33 $draftPages = $this->signedIn ? $this->entityRepo->getUserDraftPages(6) : [];
33 $recentFactor = count($draftPages) > 0 ? 0.5 : 1; 34 $recentFactor = count($draftPages) > 0 ? 0.5 : 1;
34 - $recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreatedBooks(10*$recentFactor); 35 + $recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 10*$recentFactor);
35 - $recentlyCreatedPages = $this->entityRepo->getRecentlyCreatedPages(5); 36 + $recentlyCreatedPages = $this->entityRepo->getRecentlyCreated('page', 5);
36 - $recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdatedPages(5); 37 + $recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 5);
37 return view('home', [ 38 return view('home', [
38 'activity' => $activity, 39 'activity' => $activity,
39 'recents' => $recents, 40 'recents' => $recents,
...@@ -43,4 +44,39 @@ class HomeController extends Controller ...@@ -43,4 +44,39 @@ class HomeController extends Controller
43 ]); 44 ]);
44 } 45 }
45 46
47 + /**
48 + * Get a js representation of the current translations
49 + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
50 + */
51 + public function getTranslations() {
52 + $locale = trans()->getLocale();
53 + $cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale;
54 + if (cache()->has($cacheKey) && config('app.env') !== 'development') {
55 + $resp = cache($cacheKey);
56 + } else {
57 + $translations = [
58 + // Get only translations which might be used in JS
59 + 'common' => trans('common'),
60 + 'components' => trans('components'),
61 + 'entities' => trans('entities'),
62 + 'errors' => trans('errors')
63 + ];
64 + if ($locale !== 'en') {
65 + $enTrans = [
66 + 'common' => trans('common', [], null, 'en'),
67 + 'components' => trans('components', [], null, 'en'),
68 + 'entities' => trans('entities', [], null, 'en'),
69 + 'errors' => trans('errors', [], null, 'en')
70 + ];
71 + $translations = array_replace_recursive($enTrans, $translations);
72 + }
73 + $resp = 'window.translations = ' . json_encode($translations);
74 + cache()->put($cacheKey, $resp, 120);
75 + }
76 +
77 + return response($resp, 200, [
78 + 'Content-Type' => 'application/javascript'
79 + ]);
80 + }
81 +
46 } 82 }
......
1 <?php namespace BookStack\Http\Controllers; 1 <?php namespace BookStack\Http\Controllers;
2 2
3 use BookStack\Exceptions\ImageUploadException; 3 use BookStack\Exceptions\ImageUploadException;
4 +use BookStack\Repos\EntityRepo;
4 use BookStack\Repos\ImageRepo; 5 use BookStack\Repos\ImageRepo;
5 use Illuminate\Filesystem\Filesystem as File; 6 use Illuminate\Filesystem\Filesystem as File;
6 use Illuminate\Http\Request; 7 use Illuminate\Http\Request;
...@@ -73,6 +74,7 @@ class ImageController extends Controller ...@@ -73,6 +74,7 @@ class ImageController extends Controller
73 * @param $filter 74 * @param $filter
74 * @param int $page 75 * @param int $page
75 * @param Request $request 76 * @param Request $request
77 + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
76 */ 78 */
77 public function getGalleryFiltered($filter, $page = 0, Request $request) 79 public function getGalleryFiltered($filter, $page = 0, Request $request)
78 { 80 {
...@@ -149,12 +151,12 @@ class ImageController extends Controller ...@@ -149,12 +151,12 @@ class ImageController extends Controller
149 151
150 /** 152 /**
151 * Deletes an image and all thumbnail/image files 153 * Deletes an image and all thumbnail/image files
152 - * @param PageRepo $pageRepo 154 + * @param EntityRepo $entityRepo
153 * @param Request $request 155 * @param Request $request
154 * @param int $id 156 * @param int $id
155 * @return \Illuminate\Http\JsonResponse 157 * @return \Illuminate\Http\JsonResponse
156 */ 158 */
157 - public function destroy(PageRepo $pageRepo, Request $request, $id) 159 + public function destroy(EntityRepo $entityRepo, Request $request, $id)
158 { 160 {
159 $image = $this->imageRepo->getById($id); 161 $image = $this->imageRepo->getById($id);
160 $this->checkOwnablePermission('image-delete', $image); 162 $this->checkOwnablePermission('image-delete', $image);
...@@ -162,14 +164,14 @@ class ImageController extends Controller ...@@ -162,14 +164,14 @@ class ImageController extends Controller
162 // Check if this image is used on any pages 164 // Check if this image is used on any pages
163 $isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true); 165 $isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true);
164 if (!$isForced) { 166 if (!$isForced) {
165 - $pageSearch = $pageRepo->searchForImage($image->url); 167 + $pageSearch = $entityRepo->searchForImage($image->url);
166 if ($pageSearch !== false) { 168 if ($pageSearch !== false) {
167 return response()->json($pageSearch, 400); 169 return response()->json($pageSearch, 400);
168 } 170 }
169 } 171 }
170 172
171 $this->imageRepo->destroyImage($image); 173 $this->imageRepo->destroyImage($image);
172 - return response()->json('Image Deleted'); 174 + return response()->json(trans('components.images_deleted'));
173 } 175 }
174 176
175 177
......
...@@ -2,9 +2,7 @@ ...@@ -2,9 +2,7 @@
2 2
3 use BookStack\Exceptions\PermissionsException; 3 use BookStack\Exceptions\PermissionsException;
4 use BookStack\Repos\PermissionsRepo; 4 use BookStack\Repos\PermissionsRepo;
5 -use BookStack\Services\PermissionService;
6 use Illuminate\Http\Request; 5 use Illuminate\Http\Request;
7 -use BookStack\Http\Requests;
8 6
9 class PermissionController extends Controller 7 class PermissionController extends Controller
10 { 8 {
...@@ -55,7 +53,7 @@ class PermissionController extends Controller ...@@ -55,7 +53,7 @@ class PermissionController extends Controller
55 ]); 53 ]);
56 54
57 $this->permissionsRepo->saveNewRole($request->all()); 55 $this->permissionsRepo->saveNewRole($request->all());
58 - session()->flash('success', 'Role successfully created'); 56 + session()->flash('success', trans('settings.role_create_success'));
59 return redirect('/settings/roles'); 57 return redirect('/settings/roles');
60 } 58 }
61 59
...@@ -69,7 +67,7 @@ class PermissionController extends Controller ...@@ -69,7 +67,7 @@ class PermissionController extends Controller
69 { 67 {
70 $this->checkPermission('user-roles-manage'); 68 $this->checkPermission('user-roles-manage');
71 $role = $this->permissionsRepo->getRoleById($id); 69 $role = $this->permissionsRepo->getRoleById($id);
72 - if ($role->hidden) throw new PermissionsException('This role cannot be edited'); 70 + if ($role->hidden) throw new PermissionsException(trans('errors.role_cannot_be_edited'));
73 return view('settings/roles/edit', ['role' => $role]); 71 return view('settings/roles/edit', ['role' => $role]);
74 } 72 }
75 73
...@@ -88,7 +86,7 @@ class PermissionController extends Controller ...@@ -88,7 +86,7 @@ class PermissionController extends Controller
88 ]); 86 ]);
89 87
90 $this->permissionsRepo->updateRole($id, $request->all()); 88 $this->permissionsRepo->updateRole($id, $request->all());
91 - session()->flash('success', 'Role successfully updated'); 89 + session()->flash('success', trans('settings.role_update_success'));
92 return redirect('/settings/roles'); 90 return redirect('/settings/roles');
93 } 91 }
94 92
...@@ -103,7 +101,7 @@ class PermissionController extends Controller ...@@ -103,7 +101,7 @@ class PermissionController extends Controller
103 $this->checkPermission('user-roles-manage'); 101 $this->checkPermission('user-roles-manage');
104 $role = $this->permissionsRepo->getRoleById($id); 102 $role = $this->permissionsRepo->getRoleById($id);
105 $roles = $this->permissionsRepo->getAllRolesExcept($role); 103 $roles = $this->permissionsRepo->getAllRolesExcept($role);
106 - $blankRole = $role->newInstance(['display_name' => 'Don\'t migrate users']); 104 + $blankRole = $role->newInstance(['display_name' => trans('settings.role_delete_no_migration')]);
107 $roles->prepend($blankRole); 105 $roles->prepend($blankRole);
108 return view('settings/roles/delete', ['role' => $role, 'roles' => $roles]); 106 return view('settings/roles/delete', ['role' => $role, 'roles' => $roles]);
109 } 107 }
...@@ -126,7 +124,7 @@ class PermissionController extends Controller ...@@ -126,7 +124,7 @@ class PermissionController extends Controller
126 return redirect()->back(); 124 return redirect()->back();
127 } 125 }
128 126
129 - session()->flash('success', 'Role successfully deleted'); 127 + session()->flash('success', trans('settings.role_delete_success'));
130 return redirect('/settings/roles'); 128 return redirect('/settings/roles');
131 } 129 }
132 } 130 }
......
1 -<?php 1 +<?php namespace BookStack\Http\Controllers;
2 -
3 -namespace BookStack\Http\Controllers;
4 2
3 +use BookStack\Repos\EntityRepo;
5 use BookStack\Services\ViewService; 4 use BookStack\Services\ViewService;
6 use Illuminate\Http\Request; 5 use Illuminate\Http\Request;
7 6
8 -use BookStack\Http\Requests;
9 -use BookStack\Repos\BookRepo;
10 -use BookStack\Repos\ChapterRepo;
11 -use BookStack\Repos\PageRepo;
12 -
13 class SearchController extends Controller 7 class SearchController extends Controller
14 { 8 {
15 - protected $pageRepo; 9 + protected $entityRepo;
16 - protected $bookRepo;
17 - protected $chapterRepo;
18 protected $viewService; 10 protected $viewService;
19 11
20 /** 12 /**
21 * SearchController constructor. 13 * SearchController constructor.
22 - * @param PageRepo $pageRepo 14 + * @param EntityRepo $entityRepo
23 - * @param BookRepo $bookRepo
24 - * @param ChapterRepo $chapterRepo
25 * @param ViewService $viewService 15 * @param ViewService $viewService
26 */ 16 */
27 - public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ViewService $viewService) 17 + public function __construct(EntityRepo $entityRepo, ViewService $viewService)
28 { 18 {
29 - $this->pageRepo = $pageRepo; 19 + $this->entityRepo = $entityRepo;
30 - $this->bookRepo = $bookRepo;
31 - $this->chapterRepo = $chapterRepo;
32 $this->viewService = $viewService; 20 $this->viewService = $viewService;
33 parent::__construct(); 21 parent::__construct();
34 } 22 }
...@@ -46,10 +34,10 @@ class SearchController extends Controller ...@@ -46,10 +34,10 @@ class SearchController extends Controller
46 } 34 }
47 $searchTerm = $request->get('term'); 35 $searchTerm = $request->get('term');
48 $paginationAppends = $request->only('term'); 36 $paginationAppends = $request->only('term');
49 - $pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends); 37 + $pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
50 - $books = $this->bookRepo->getBySearch($searchTerm, 10, $paginationAppends); 38 + $books = $this->entityRepo->getBySearch('book', $searchTerm, [], 10, $paginationAppends);
51 - $chapters = $this->chapterRepo->getBySearch($searchTerm, [], 10, $paginationAppends); 39 + $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 10, $paginationAppends);
52 - $this->setPageTitle('Search For ' . $searchTerm); 40 + $this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
53 return view('search/all', [ 41 return view('search/all', [
54 'pages' => $pages, 42 'pages' => $pages,
55 'books' => $books, 43 'books' => $books,
...@@ -69,11 +57,11 @@ class SearchController extends Controller ...@@ -69,11 +57,11 @@ class SearchController extends Controller
69 57
70 $searchTerm = $request->get('term'); 58 $searchTerm = $request->get('term');
71 $paginationAppends = $request->only('term'); 59 $paginationAppends = $request->only('term');
72 - $pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends); 60 + $pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
73 - $this->setPageTitle('Page Search For ' . $searchTerm); 61 + $this->setPageTitle(trans('entities.search_page_for_term', ['term' => $searchTerm]));
74 return view('search/entity-search-list', [ 62 return view('search/entity-search-list', [
75 'entities' => $pages, 63 'entities' => $pages,
76 - 'title' => 'Page Search Results', 64 + 'title' => trans('entities.search_results_page'),
77 'searchTerm' => $searchTerm 65 'searchTerm' => $searchTerm
78 ]); 66 ]);
79 } 67 }
...@@ -89,11 +77,11 @@ class SearchController extends Controller ...@@ -89,11 +77,11 @@ class SearchController extends Controller
89 77
90 $searchTerm = $request->get('term'); 78 $searchTerm = $request->get('term');
91 $paginationAppends = $request->only('term'); 79 $paginationAppends = $request->only('term');
92 - $chapters = $this->chapterRepo->getBySearch($searchTerm, [], 20, $paginationAppends); 80 + $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 20, $paginationAppends);
93 - $this->setPageTitle('Chapter Search For ' . $searchTerm); 81 + $this->setPageTitle(trans('entities.search_chapter_for_term', ['term' => $searchTerm]));
94 return view('search/entity-search-list', [ 82 return view('search/entity-search-list', [
95 'entities' => $chapters, 83 'entities' => $chapters,
96 - 'title' => 'Chapter Search Results', 84 + 'title' => trans('entities.search_results_chapter'),
97 'searchTerm' => $searchTerm 85 'searchTerm' => $searchTerm
98 ]); 86 ]);
99 } 87 }
...@@ -109,11 +97,11 @@ class SearchController extends Controller ...@@ -109,11 +97,11 @@ class SearchController extends Controller
109 97
110 $searchTerm = $request->get('term'); 98 $searchTerm = $request->get('term');
111 $paginationAppends = $request->only('term'); 99 $paginationAppends = $request->only('term');
112 - $books = $this->bookRepo->getBySearch($searchTerm, 20, $paginationAppends); 100 + $books = $this->entityRepo->getBySearch('book', $searchTerm, [], 20, $paginationAppends);
113 - $this->setPageTitle('Book Search For ' . $searchTerm); 101 + $this->setPageTitle(trans('entities.search_book_for_term', ['term' => $searchTerm]));
114 return view('search/entity-search-list', [ 102 return view('search/entity-search-list', [
115 'entities' => $books, 103 'entities' => $books,
116 - 'title' => 'Book Search Results', 104 + 'title' => trans('entities.search_results_book'),
117 'searchTerm' => $searchTerm 105 'searchTerm' => $searchTerm
118 ]); 106 ]);
119 } 107 }
...@@ -132,8 +120,8 @@ class SearchController extends Controller ...@@ -132,8 +120,8 @@ class SearchController extends Controller
132 } 120 }
133 $searchTerm = $request->get('term'); 121 $searchTerm = $request->get('term');
134 $searchWhereTerms = [['book_id', '=', $bookId]]; 122 $searchWhereTerms = [['book_id', '=', $bookId]];
135 - $pages = $this->pageRepo->getBySearch($searchTerm, $searchWhereTerms); 123 + $pages = $this->entityRepo->getBySearch('page', $searchTerm, $searchWhereTerms);
136 - $chapters = $this->chapterRepo->getBySearch($searchTerm, $searchWhereTerms); 124 + $chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, $searchWhereTerms);
137 return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]); 125 return view('search/book', ['pages' => $pages, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
138 } 126 }
139 127
...@@ -152,9 +140,11 @@ class SearchController extends Controller ...@@ -152,9 +140,11 @@ class SearchController extends Controller
152 140
153 // Search for entities otherwise show most popular 141 // Search for entities otherwise show most popular
154 if ($searchTerm !== false) { 142 if ($searchTerm !== false) {
155 - if ($entityTypes->contains('page')) $entities = $entities->merge($this->pageRepo->getBySearch($searchTerm)->items()); 143 + foreach (['page', 'chapter', 'book'] as $entityType) {
156 - if ($entityTypes->contains('chapter')) $entities = $entities->merge($this->chapterRepo->getBySearch($searchTerm)->items()); 144 + if ($entityTypes->contains($entityType)) {
157 - if ($entityTypes->contains('book')) $entities = $entities->merge($this->bookRepo->getBySearch($searchTerm)->items()); 145 + $entities = $entities->merge($this->entityRepo->getBySearch($entityType, $searchTerm)->items());
146 + }
147 + }
158 $entities = $entities->sortByDesc('title_relevance'); 148 $entities = $entities->sortByDesc('title_relevance');
159 } else { 149 } else {
160 $entityNames = $entityTypes->map(function ($type) { 150 $entityNames = $entityTypes->map(function ($type) {
......
1 <?php namespace BookStack\Http\Controllers; 1 <?php namespace BookStack\Http\Controllers;
2 2
3 use Illuminate\Http\Request; 3 use Illuminate\Http\Request;
4 - 4 +use Illuminate\Http\Response;
5 -use BookStack\Http\Requests;
6 use Setting; 5 use Setting;
7 6
8 class SettingController extends Controller 7 class SettingController extends Controller
...@@ -39,7 +38,7 @@ class SettingController extends Controller ...@@ -39,7 +38,7 @@ class SettingController extends Controller
39 Setting::put($key, $value); 38 Setting::put($key, $value);
40 } 39 }
41 40
42 - session()->flash('success', 'Settings Saved'); 41 + session()->flash('success', trans('settings.settings_save_success'));
43 return redirect('/settings'); 42 return redirect('/settings');
44 } 43 }
45 44
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
2 2
3 use BookStack\Repos\TagRepo; 3 use BookStack\Repos\TagRepo;
4 use Illuminate\Http\Request; 4 use Illuminate\Http\Request;
5 -use BookStack\Http\Requests;
6 5
7 class TagController extends Controller 6 class TagController extends Controller
8 { 7 {
...@@ -16,12 +15,14 @@ class TagController extends Controller ...@@ -16,12 +15,14 @@ class TagController extends Controller
16 public function __construct(TagRepo $tagRepo) 15 public function __construct(TagRepo $tagRepo)
17 { 16 {
18 $this->tagRepo = $tagRepo; 17 $this->tagRepo = $tagRepo;
18 + parent::__construct();
19 } 19 }
20 20
21 /** 21 /**
22 * Get all the Tags for a particular entity 22 * Get all the Tags for a particular entity
23 * @param $entityType 23 * @param $entityType
24 * @param $entityId 24 * @param $entityId
25 + * @return \Illuminate\Http\JsonResponse
25 */ 26 */
26 public function getForEntity($entityType, $entityId) 27 public function getForEntity($entityType, $entityId)
27 { 28 {
...@@ -30,28 +31,9 @@ class TagController extends Controller ...@@ -30,28 +31,9 @@ class TagController extends Controller
30 } 31 }
31 32
32 /** 33 /**
33 - * Update the tags for a particular entity.
34 - * @param $entityType
35 - * @param $entityId
36 - * @param Request $request
37 - * @return mixed
38 - */
39 - public function updateForEntity($entityType, $entityId, Request $request)
40 - {
41 - $entity = $this->tagRepo->getEntity($entityType, $entityId, 'update');
42 - if ($entity === null) return $this->jsonError("Entity not found", 404);
43 -
44 - $inputTags = $request->input('tags');
45 - $tags = $this->tagRepo->saveTagsToEntity($entity, $inputTags);
46 - return response()->json([
47 - 'tags' => $tags,
48 - 'message' => 'Tags successfully updated'
49 - ]);
50 - }
51 -
52 - /**
53 * Get tag name suggestions from a given search term. 34 * Get tag name suggestions from a given search term.
54 * @param Request $request 35 * @param Request $request
36 + * @return \Illuminate\Http\JsonResponse
55 */ 37 */
56 public function getNameSuggestions(Request $request) 38 public function getNameSuggestions(Request $request)
57 { 39 {
...@@ -63,6 +45,7 @@ class TagController extends Controller ...@@ -63,6 +45,7 @@ class TagController extends Controller
63 /** 45 /**
64 * Get tag value suggestions from a given search term. 46 * Get tag value suggestions from a given search term.
65 * @param Request $request 47 * @param Request $request
48 + * @return \Illuminate\Http\JsonResponse
66 */ 49 */
67 public function getValueSuggestions(Request $request) 50 public function getValueSuggestions(Request $request)
68 { 51 {
......
1 -<?php 1 +<?php namespace BookStack\Http\Controllers;
2 2
3 -namespace BookStack\Http\Controllers;
4 -
5 -use BookStack\Activity;
6 use Exception; 3 use Exception;
7 use Illuminate\Http\Request; 4 use Illuminate\Http\Request;
8 -
9 use Illuminate\Http\Response; 5 use Illuminate\Http\Response;
10 -use BookStack\Http\Requests;
11 use BookStack\Repos\UserRepo; 6 use BookStack\Repos\UserRepo;
12 use BookStack\Services\SocialAuthService; 7 use BookStack\Services\SocialAuthService;
13 use BookStack\User; 8 use BookStack\User;
...@@ -44,7 +39,7 @@ class UserController extends Controller ...@@ -44,7 +39,7 @@ class UserController extends Controller
44 'sort' => $request->has('sort') ? $request->get('sort') : 'name', 39 'sort' => $request->has('sort') ? $request->get('sort') : 'name',
45 ]; 40 ];
46 $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails); 41 $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
47 - $this->setPageTitle('Users'); 42 + $this->setPageTitle(trans('settings.users'));
48 $users->appends($listDetails); 43 $users->appends($listDetails);
49 return view('users/index', ['users' => $users, 'listDetails' => $listDetails]); 44 return view('users/index', ['users' => $users, 'listDetails' => $listDetails]);
50 } 45 }
...@@ -83,7 +78,6 @@ class UserController extends Controller ...@@ -83,7 +78,6 @@ class UserController extends Controller
83 } 78 }
84 $this->validate($request, $validationRules); 79 $this->validate($request, $validationRules);
85 80
86 -
87 $user = $this->user->fill($request->all()); 81 $user = $this->user->fill($request->all());
88 82
89 if ($authMethod === 'standard') { 83 if ($authMethod === 'standard') {
...@@ -131,7 +125,7 @@ class UserController extends Controller ...@@ -131,7 +125,7 @@ class UserController extends Controller
131 $authMethod = ($user->system_name) ? 'system' : config('auth.method'); 125 $authMethod = ($user->system_name) ? 'system' : config('auth.method');
132 126
133 $activeSocialDrivers = $socialAuthService->getActiveDrivers(); 127 $activeSocialDrivers = $socialAuthService->getActiveDrivers();
134 - $this->setPageTitle('User Profile'); 128 + $this->setPageTitle(trans('settings.user_profile'));
135 $roles = $this->userRepo->getAllRoles(); 129 $roles = $this->userRepo->getAllRoles();
136 return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]); 130 return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
137 } 131 }
...@@ -153,9 +147,8 @@ class UserController extends Controller ...@@ -153,9 +147,8 @@ class UserController extends Controller
153 'name' => 'min:2', 147 'name' => 'min:2',
154 'email' => 'min:2|email|unique:users,email,' . $id, 148 'email' => 'min:2|email|unique:users,email,' . $id,
155 'password' => 'min:5|required_with:password_confirm', 149 'password' => 'min:5|required_with:password_confirm',
156 - 'password-confirm' => 'same:password|required_with:password' 150 + 'password-confirm' => 'same:password|required_with:password',
157 - ], [ 151 + 'setting' => 'array'
158 - 'password-confirm.required_with' => 'Password confirmation required'
159 ]); 152 ]);
160 153
161 $user = $this->user->findOrFail($id); 154 $user = $this->user->findOrFail($id);
...@@ -178,8 +171,15 @@ class UserController extends Controller ...@@ -178,8 +171,15 @@ class UserController extends Controller
178 $user->external_auth_id = $request->get('external_auth_id'); 171 $user->external_auth_id = $request->get('external_auth_id');
179 } 172 }
180 173
174 + // Save an user-specific settings
175 + if ($request->has('setting')) {
176 + foreach ($request->get('setting') as $key => $value) {
177 + setting()->putUser($user, $key, $value);
178 + }
179 + }
180 +
181 $user->save(); 181 $user->save();
182 - session()->flash('success', 'User successfully updated'); 182 + session()->flash('success', trans('settings.users_edit_success'));
183 183
184 $redirectUrl = userCan('users-manage') ? '/settings/users' : '/settings/users/' . $user->id; 184 $redirectUrl = userCan('users-manage') ? '/settings/users' : '/settings/users/' . $user->id;
185 return redirect($redirectUrl); 185 return redirect($redirectUrl);
...@@ -197,7 +197,7 @@ class UserController extends Controller ...@@ -197,7 +197,7 @@ class UserController extends Controller
197 }); 197 });
198 198
199 $user = $this->user->findOrFail($id); 199 $user = $this->user->findOrFail($id);
200 - $this->setPageTitle('Delete User ' . $user->name); 200 + $this->setPageTitle(trans('settings.users_delete_named', ['userName' => $user->name]));
201 return view('users/delete', ['user' => $user]); 201 return view('users/delete', ['user' => $user]);
202 } 202 }
203 203
...@@ -216,17 +216,17 @@ class UserController extends Controller ...@@ -216,17 +216,17 @@ class UserController extends Controller
216 $user = $this->userRepo->getById($id); 216 $user = $this->userRepo->getById($id);
217 217
218 if ($this->userRepo->isOnlyAdmin($user)) { 218 if ($this->userRepo->isOnlyAdmin($user)) {
219 - session()->flash('error', 'You cannot delete the only admin'); 219 + session()->flash('error', trans('errors.users_cannot_delete_only_admin'));
220 return redirect($user->getEditUrl()); 220 return redirect($user->getEditUrl());
221 } 221 }
222 222
223 if ($user->system_name === 'public') { 223 if ($user->system_name === 'public') {
224 - session()->flash('error', 'You cannot delete the guest user'); 224 + session()->flash('error', trans('errors.users_cannot_delete_guest'));
225 return redirect($user->getEditUrl()); 225 return redirect($user->getEditUrl());
226 } 226 }
227 227
228 $this->userRepo->destroy($user); 228 $this->userRepo->destroy($user);
229 - session()->flash('success', 'User successfully removed'); 229 + session()->flash('success', trans('settings.users_delete_success'));
230 230
231 return redirect('/settings/users'); 231 return redirect('/settings/users');
232 } 232 }
......
1 -<?php 1 +<?php namespace BookStack\Http;
2 -
3 -namespace BookStack\Http;
4 2
5 use Illuminate\Foundation\Http\Kernel as HttpKernel; 3 use Illuminate\Foundation\Http\Kernel as HttpKernel;
6 4
...@@ -30,6 +28,7 @@ class Kernel extends HttpKernel ...@@ -30,6 +28,7 @@ class Kernel extends HttpKernel
30 \Illuminate\View\Middleware\ShareErrorsFromSession::class, 28 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
31 \BookStack\Http\Middleware\VerifyCsrfToken::class, 29 \BookStack\Http\Middleware\VerifyCsrfToken::class,
32 \Illuminate\Routing\Middleware\SubstituteBindings::class, 30 \Illuminate\Routing\Middleware\SubstituteBindings::class,
31 + \BookStack\Http\Middleware\Localization::class
33 ], 32 ],
34 'api' => [ 33 'api' => [
35 'throttle:60,1', 34 'throttle:60,1',
......
...@@ -4,8 +4,6 @@ namespace BookStack\Http\Middleware; ...@@ -4,8 +4,6 @@ namespace BookStack\Http\Middleware;
4 4
5 use Closure; 5 use Closure;
6 use Illuminate\Contracts\Auth\Guard; 6 use Illuminate\Contracts\Auth\Guard;
7 -use BookStack\Exceptions\UserRegistrationException;
8 -use Setting;
9 7
10 class Authenticate 8 class Authenticate
11 { 9 {
......
1 +<?php namespace BookStack\Http\Middleware;
2 +
3 +use Carbon\Carbon;
4 +use Closure;
5 +
6 +class Localization
7 +{
8 + /**
9 + * Handle an incoming request.
10 + *
11 + * @param \Illuminate\Http\Request $request
12 + * @param \Closure $next
13 + * @return mixed
14 + */
15 + public function handle($request, Closure $next)
16 + {
17 + $defaultLang = config('app.locale');
18 + $locale = setting()->getUser(user(), 'language', $defaultLang);
19 + app()->setLocale($locale);
20 + Carbon::setLocale($locale);
21 + return $next($request);
22 + }
23 +}
1 -<?php 1 +<?php namespace BookStack\Http\Middleware;
2 -
3 -namespace BookStack\Http\Middleware;
4 2
5 use Closure; 3 use Closure;
6 use Illuminate\Contracts\Auth\Guard; 4 use Illuminate\Contracts\Auth\Guard;
......
...@@ -43,8 +43,9 @@ class ResetPassword extends Notification ...@@ -43,8 +43,9 @@ class ResetPassword extends Notification
43 public function toMail() 43 public function toMail()
44 { 44 {
45 return (new MailMessage) 45 return (new MailMessage)
46 - ->line('You are receiving this email because we received a password reset request for your account.') 46 + ->subject(trans('auth.email_reset_subject', ['appName' => setting('app-name')]))
47 - ->action('Reset Password', baseUrl('password/reset/' . $this->token)) 47 + ->line(trans('auth.email_reset_text'))
48 - ->line('If you did not request a password reset, no further action is required.'); 48 + ->action(trans('auth.reset_password'), baseUrl('password/reset/' . $this->token))
49 + ->line(trans('auth.email_reset_not_requested'));
49 } 50 }
50 } 51 }
......
...@@ -7,6 +7,10 @@ class Page extends Entity ...@@ -7,6 +7,10 @@ class Page extends Entity
7 7
8 protected $simpleAttributes = ['name', 'id', 'slug']; 8 protected $simpleAttributes = ['name', 'id', 'slug'];
9 9
10 + protected $with = ['book'];
11 +
12 + protected $fieldsToSearch = ['name', 'text'];
13 +
10 /** 14 /**
11 * Converts this page into a simplified array. 15 * Converts this page into a simplified array.
12 * @return mixed 16 * @return mixed
......
1 <?php namespace BookStack\Providers; 1 <?php namespace BookStack\Providers;
2 2
3 use Illuminate\Support\ServiceProvider; 3 use Illuminate\Support\ServiceProvider;
4 +use Validator;
4 5
5 class AppServiceProvider extends ServiceProvider 6 class AppServiceProvider extends ServiceProvider
6 { 7 {
...@@ -12,11 +13,10 @@ class AppServiceProvider extends ServiceProvider ...@@ -12,11 +13,10 @@ class AppServiceProvider extends ServiceProvider
12 public function boot() 13 public function boot()
13 { 14 {
14 // Custom validation methods 15 // Custom validation methods
15 - \Validator::extend('is_image', function($attribute, $value, $parameters, $validator) { 16 + Validator::extend('is_image', function($attribute, $value, $parameters, $validator) {
16 $imageMimes = ['image/png', 'image/bmp', 'image/gif', 'image/jpeg', 'image/jpg', 'image/tiff', 'image/webp']; 17 $imageMimes = ['image/png', 'image/bmp', 'image/gif', 'image/jpeg', 'image/jpg', 'image/tiff', 'image/webp'];
17 return in_array($value->getMimeType(), $imageMimes); 18 return in_array($value->getMimeType(), $imageMimes);
18 }); 19 });
19 -
20 } 20 }
21 21
22 /** 22 /**
......
1 -<?php namespace BookStack\Repos;
2 -
3 -use Alpha\B;
4 -use BookStack\Exceptions\NotFoundException;
5 -use Illuminate\Database\Eloquent\Collection;
6 -use Illuminate\Support\Str;
7 -use BookStack\Book;
8 -use Views;
9 -
10 -class BookRepo extends EntityRepo
11 -{
12 - protected $pageRepo;
13 - protected $chapterRepo;
14 -
15 - /**
16 - * BookRepo constructor.
17 - * @param PageRepo $pageRepo
18 - * @param ChapterRepo $chapterRepo
19 - */
20 - public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo)
21 - {
22 - $this->pageRepo = $pageRepo;
23 - $this->chapterRepo = $chapterRepo;
24 - parent::__construct();
25 - }
26 -
27 - /**
28 - * Base query for getting books.
29 - * Takes into account any restrictions.
30 - * @return mixed
31 - */
32 - private function bookQuery()
33 - {
34 - return $this->permissionService->enforceBookRestrictions($this->book, 'view');
35 - }
36 -
37 - /**
38 - * Get the book that has the given id.
39 - * @param $id
40 - * @return mixed
41 - */
42 - public function getById($id)
43 - {
44 - return $this->bookQuery()->findOrFail($id);
45 - }
46 -
47 - /**
48 - * Get all books, Limited by count.
49 - * @param int $count
50 - * @return mixed
51 - */
52 - public function getAll($count = 10)
53 - {
54 - $bookQuery = $this->bookQuery()->orderBy('name', 'asc');
55 - if (!$count) return $bookQuery->get();
56 - return $bookQuery->take($count)->get();
57 - }
58 -
59 - /**
60 - * Get all books paginated.
61 - * @param int $count
62 - * @return mixed
63 - */
64 - public function getAllPaginated($count = 10)
65 - {
66 - return $this->bookQuery()
67 - ->orderBy('name', 'asc')->paginate($count);
68 - }
69 -
70 -
71 - /**
72 - * Get the latest books.
73 - * @param int $count
74 - * @return mixed
75 - */
76 - public function getLatest($count = 10)
77 - {
78 - return $this->bookQuery()->orderBy('created_at', 'desc')->take($count)->get();
79 - }
80 -
81 - /**
82 - * Gets the most recently viewed for a user.
83 - * @param int $count
84 - * @param int $page
85 - * @return mixed
86 - */
87 - public function getRecentlyViewed($count = 10, $page = 0)
88 - {
89 - return Views::getUserRecentlyViewed($count, $page, $this->book);
90 - }
91 -
92 - /**
93 - * Gets the most viewed books.
94 - * @param int $count
95 - * @param int $page
96 - * @return mixed
97 - */
98 - public function getPopular($count = 10, $page = 0)
99 - {
100 - return Views::getPopular($count, $page, $this->book);
101 - }
102 -
103 - /**
104 - * Get a book by slug
105 - * @param $slug
106 - * @return mixed
107 - * @throws NotFoundException
108 - */
109 - public function getBySlug($slug)
110 - {
111 - $book = $this->bookQuery()->where('slug', '=', $slug)->first();
112 - if ($book === null) throw new NotFoundException('Book not found');
113 - return $book;
114 - }
115 -
116 - /**
117 - * Checks if a book exists.
118 - * @param $id
119 - * @return bool
120 - */
121 - public function exists($id)
122 - {
123 - return $this->bookQuery()->where('id', '=', $id)->exists();
124 - }
125 -
126 - /**
127 - * Get a new book instance from request input.
128 - * @param array $input
129 - * @return Book
130 - */
131 - public function createFromInput($input)
132 - {
133 - $book = $this->book->newInstance($input);
134 - $book->slug = $this->findSuitableSlug($book->name);
135 - $book->created_by = user()->id;
136 - $book->updated_by = user()->id;
137 - $book->save();
138 - $this->permissionService->buildJointPermissionsForEntity($book);
139 - return $book;
140 - }
141 -
142 - /**
143 - * Update the given book from user input.
144 - * @param Book $book
145 - * @param $input
146 - * @return Book
147 - */
148 - public function updateFromInput(Book $book, $input)
149 - {
150 - if ($book->name !== $input['name']) {
151 - $book->slug = $this->findSuitableSlug($input['name'], $book->id);
152 - }
153 - $book->fill($input);
154 - $book->updated_by = user()->id;
155 - $book->save();
156 - $this->permissionService->buildJointPermissionsForEntity($book);
157 - return $book;
158 - }
159 -
160 - /**
161 - * Destroy the given book.
162 - * @param Book $book
163 - * @throws \Exception
164 - */
165 - public function destroy(Book $book)
166 - {
167 - foreach ($book->pages as $page) {
168 - $this->pageRepo->destroy($page);
169 - }
170 - foreach ($book->chapters as $chapter) {
171 - $this->chapterRepo->destroy($chapter);
172 - }
173 - $book->views()->delete();
174 - $book->permissions()->delete();
175 - $this->permissionService->deleteJointPermissionsForEntity($book);
176 - $book->delete();
177 - }
178 -
179 - /**
180 - * Get the next child element priority.
181 - * @param Book $book
182 - * @return int
183 - */
184 - public function getNewPriority($book)
185 - {
186 - $lastElem = $this->getChildren($book)->pop();
187 - return $lastElem ? $lastElem->priority + 1 : 0;
188 - }
189 -
190 - /**
191 - * @param string $slug
192 - * @param bool|false $currentId
193 - * @return bool
194 - */
195 - public function doesSlugExist($slug, $currentId = false)
196 - {
197 - $query = $this->book->where('slug', '=', $slug);
198 - if ($currentId) {
199 - $query = $query->where('id', '!=', $currentId);
200 - }
201 - return $query->count() > 0;
202 - }
203 -
204 - /**
205 - * Provides a suitable slug for the given book name.
206 - * Ensures the returned slug is unique in the system.
207 - * @param string $name
208 - * @param bool|false $currentId
209 - * @return string
210 - */
211 - public function findSuitableSlug($name, $currentId = false)
212 - {
213 - $slug = $this->nameToSlug($name);
214 - while ($this->doesSlugExist($slug, $currentId)) {
215 - $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
216 - }
217 - return $slug;
218 - }
219 -
220 - /**
221 - * Get all child objects of a book.
222 - * Returns a sorted collection of Pages and Chapters.
223 - * Loads the book slug onto child elements to prevent access database access for getting the slug.
224 - * @param Book $book
225 - * @param bool $filterDrafts
226 - * @return mixed
227 - */
228 - public function getChildren(Book $book, $filterDrafts = false)
229 - {
230 - $pageQuery = $book->pages()->where('chapter_id', '=', 0);
231 - $pageQuery = $this->permissionService->enforcePageRestrictions($pageQuery, 'view');
232 -
233 - if ($filterDrafts) {
234 - $pageQuery = $pageQuery->where('draft', '=', false);
235 - }
236 -
237 - $pages = $pageQuery->get();
238 -
239 - $chapterQuery = $book->chapters()->with(['pages' => function ($query) use ($filterDrafts) {
240 - $this->permissionService->enforcePageRestrictions($query, 'view');
241 - if ($filterDrafts) $query->where('draft', '=', false);
242 - }]);
243 - $chapterQuery = $this->permissionService->enforceChapterRestrictions($chapterQuery, 'view');
244 - $chapters = $chapterQuery->get();
245 - $children = $pages->values();
246 - foreach ($chapters as $chapter) {
247 - $children->push($chapter);
248 - }
249 - $bookSlug = $book->slug;
250 -
251 - $children->each(function ($child) use ($bookSlug) {
252 - $child->setAttribute('bookSlug', $bookSlug);
253 - if ($child->isA('chapter')) {
254 - $child->pages->each(function ($page) use ($bookSlug) {
255 - $page->setAttribute('bookSlug', $bookSlug);
256 - });
257 - $child->pages = $child->pages->sortBy(function ($child, $key) {
258 - $score = $child->priority;
259 - if ($child->draft) $score -= 100;
260 - return $score;
261 - });
262 - }
263 - });
264 -
265 - // Sort items with drafts first then by priority.
266 - return $children->sortBy(function ($child, $key) {
267 - $score = $child->priority;
268 - if ($child->isA('page') && $child->draft) $score -= 100;
269 - return $score;
270 - });
271 - }
272 -
273 - /**
274 - * Get books by search term.
275 - * @param $term
276 - * @param int $count
277 - * @param array $paginationAppends
278 - * @return mixed
279 - */
280 - public function getBySearch($term, $count = 20, $paginationAppends = [])
281 - {
282 - $terms = $this->prepareSearchTerms($term);
283 - $bookQuery = $this->permissionService->enforceBookRestrictions($this->book->fullTextSearchQuery(['name', 'description'], $terms));
284 - $bookQuery = $this->addAdvancedSearchQueries($bookQuery, $term);
285 - $books = $bookQuery->paginate($count)->appends($paginationAppends);
286 - $words = join('|', explode(' ', preg_quote(trim($term), '/')));
287 - foreach ($books as $book) {
288 - //highlight
289 - $result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $book->getExcerpt(100));
290 - $book->searchSnippet = $result;
291 - }
292 - return $books;
293 - }
294 -
295 -}
...\ No newline at end of file ...\ No newline at end of file
1 -<?php namespace BookStack\Repos;
2 -
3 -
4 -use Activity;
5 -use BookStack\Book;
6 -use BookStack\Exceptions\NotFoundException;
7 -use Illuminate\Support\Str;
8 -use BookStack\Chapter;
9 -
10 -class ChapterRepo extends EntityRepo
11 -{
12 - protected $pageRepo;
13 -
14 - /**
15 - * ChapterRepo constructor.
16 - * @param $pageRepo
17 - */
18 - public function __construct(PageRepo $pageRepo)
19 - {
20 - $this->pageRepo = $pageRepo;
21 - parent::__construct();
22 - }
23 -
24 - /**
25 - * Base query for getting chapters, Takes permissions into account.
26 - * @return mixed
27 - */
28 - private function chapterQuery()
29 - {
30 - return $this->permissionService->enforceChapterRestrictions($this->chapter, 'view');
31 - }
32 -
33 - /**
34 - * Check if an id exists.
35 - * @param $id
36 - * @return bool
37 - */
38 - public function idExists($id)
39 - {
40 - return $this->chapterQuery()->where('id', '=', $id)->count() > 0;
41 - }
42 -
43 - /**
44 - * Get a chapter by a specific id.
45 - * @param $id
46 - * @return mixed
47 - */
48 - public function getById($id)
49 - {
50 - return $this->chapterQuery()->findOrFail($id);
51 - }
52 -
53 - /**
54 - * Get all chapters.
55 - * @return \Illuminate\Database\Eloquent\Collection|static[]
56 - */
57 - public function getAll()
58 - {
59 - return $this->chapterQuery()->all();
60 - }
61 -
62 - /**
63 - * Get a chapter that has the given slug within the given book.
64 - * @param $slug
65 - * @param $bookId
66 - * @return mixed
67 - * @throws NotFoundException
68 - */
69 - public function getBySlug($slug, $bookId)
70 - {
71 - $chapter = $this->chapterQuery()->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
72 - if ($chapter === null) throw new NotFoundException('Chapter not found');
73 - return $chapter;
74 - }
75 -
76 - /**
77 - * Get the child items for a chapter
78 - * @param Chapter $chapter
79 - */
80 - public function getChildren(Chapter $chapter)
81 - {
82 - $pages = $this->permissionService->enforcePageRestrictions($chapter->pages())->get();
83 - // Sort items with drafts first then by priority.
84 - return $pages->sortBy(function ($child, $key) {
85 - $score = $child->priority;
86 - if ($child->draft) $score -= 100;
87 - return $score;
88 - });
89 - }
90 -
91 - /**
92 - * Create a new chapter from request input.
93 - * @param $input
94 - * @param Book $book
95 - * @return Chapter
96 - */
97 - public function createFromInput($input, Book $book)
98 - {
99 - $chapter = $this->chapter->newInstance($input);
100 - $chapter->slug = $this->findSuitableSlug($chapter->name, $book->id);
101 - $chapter->created_by = user()->id;
102 - $chapter->updated_by = user()->id;
103 - $chapter = $book->chapters()->save($chapter);
104 - $this->permissionService->buildJointPermissionsForEntity($chapter);
105 - return $chapter;
106 - }
107 -
108 - /**
109 - * Destroy a chapter and its relations by providing its slug.
110 - * @param Chapter $chapter
111 - */
112 - public function destroy(Chapter $chapter)
113 - {
114 - if (count($chapter->pages) > 0) {
115 - foreach ($chapter->pages as $page) {
116 - $page->chapter_id = 0;
117 - $page->save();
118 - }
119 - }
120 - Activity::removeEntity($chapter);
121 - $chapter->views()->delete();
122 - $chapter->permissions()->delete();
123 - $this->permissionService->deleteJointPermissionsForEntity($chapter);
124 - $chapter->delete();
125 - }
126 -
127 - /**
128 - * Check if a chapter's slug exists.
129 - * @param $slug
130 - * @param $bookId
131 - * @param bool|false $currentId
132 - * @return bool
133 - */
134 - public function doesSlugExist($slug, $bookId, $currentId = false)
135 - {
136 - $query = $this->chapter->where('slug', '=', $slug)->where('book_id', '=', $bookId);
137 - if ($currentId) {
138 - $query = $query->where('id', '!=', $currentId);
139 - }
140 - return $query->count() > 0;
141 - }
142 -
143 - /**
144 - * Finds a suitable slug for the provided name.
145 - * Checks database to prevent duplicate slugs.
146 - * @param $name
147 - * @param $bookId
148 - * @param bool|false $currentId
149 - * @return string
150 - */
151 - public function findSuitableSlug($name, $bookId, $currentId = false)
152 - {
153 - $slug = $this->nameToSlug($name);
154 - while ($this->doesSlugExist($slug, $bookId, $currentId)) {
155 - $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
156 - }
157 - return $slug;
158 - }
159 -
160 - /**
161 - * Get a new priority value for a new page to be added
162 - * to the given chapter.
163 - * @param Chapter $chapter
164 - * @return int
165 - */
166 - public function getNewPriority(Chapter $chapter)
167 - {
168 - $lastPage = $chapter->pages->last();
169 - return $lastPage !== null ? $lastPage->priority + 1 : 0;
170 - }
171 -
172 - /**
173 - * Get chapters by the given search term.
174 - * @param string $term
175 - * @param array $whereTerms
176 - * @param int $count
177 - * @param array $paginationAppends
178 - * @return mixed
179 - */
180 - public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
181 - {
182 - $terms = $this->prepareSearchTerms($term);
183 - $chapterQuery = $this->permissionService->enforceChapterRestrictions($this->chapter->fullTextSearchQuery(['name', 'description'], $terms, $whereTerms));
184 - $chapterQuery = $this->addAdvancedSearchQueries($chapterQuery, $term);
185 - $chapters = $chapterQuery->paginate($count)->appends($paginationAppends);
186 - $words = join('|', explode(' ', preg_quote(trim($term), '/')));
187 - foreach ($chapters as $chapter) {
188 - //highlight
189 - $result = preg_replace('#' . $words . '#iu', "<span class=\"highlight\">\$0</span>", $chapter->getExcerpt(100));
190 - $chapter->searchSnippet = $result;
191 - }
192 - return $chapters;
193 - }
194 -
195 - /**
196 - * Changes the book relation of this chapter.
197 - * @param $bookId
198 - * @param Chapter $chapter
199 - * @param bool $rebuildPermissions
200 - * @return Chapter
201 - */
202 - public function changeBook($bookId, Chapter $chapter, $rebuildPermissions = false)
203 - {
204 - $chapter->book_id = $bookId;
205 - // Update related activity
206 - foreach ($chapter->activity as $activity) {
207 - $activity->book_id = $bookId;
208 - $activity->save();
209 - }
210 - $chapter->slug = $this->findSuitableSlug($chapter->name, $bookId, $chapter->id);
211 - $chapter->save();
212 - // Update all child pages
213 - foreach ($chapter->pages as $page) {
214 - $this->pageRepo->changeBook($bookId, $page);
215 - }
216 -
217 - // Update permissions if applicable
218 - if ($rebuildPermissions) {
219 - $chapter->load('book');
220 - $this->permissionService->buildJointPermissionsForEntity($chapter->book);
221 - }
222 -
223 - return $chapter;
224 - }
225 -
226 -}
...\ No newline at end of file ...\ No newline at end of file
...@@ -93,7 +93,7 @@ class PermissionsRepo ...@@ -93,7 +93,7 @@ class PermissionsRepo
93 $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : []; 93 $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
94 $this->assignRolePermissions($role, $permissions); 94 $this->assignRolePermissions($role, $permissions);
95 95
96 - if ($role->name === 'admin') { 96 + if ($role->system_name === 'admin') {
97 $permissions = $this->permission->all()->pluck('id')->toArray(); 97 $permissions = $this->permission->all()->pluck('id')->toArray();
98 $role->permissions()->sync($permissions); 98 $role->permissions()->sync($permissions);
99 } 99 }
...@@ -133,9 +133,9 @@ class PermissionsRepo ...@@ -133,9 +133,9 @@ class PermissionsRepo
133 133
134 // Prevent deleting admin role or default registration role. 134 // Prevent deleting admin role or default registration role.
135 if ($role->system_name && in_array($role->system_name, $this->systemRoles)) { 135 if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
136 - throw new PermissionsException('This role is a system role and cannot be deleted'); 136 + throw new PermissionsException(trans('errors.role_system_cannot_be_deleted'));
137 } else if ($role->id == setting('registration-role')) { 137 } else if ($role->id == setting('registration-role')) {
138 - throw new PermissionsException('This role cannot be deleted while set as the default registration role.'); 138 + throw new PermissionsException(trans('errors.role_registration_default_cannot_delete'));
139 } 139 }
140 140
141 if ($migrateRoleId) { 141 if ($migrateRoleId) {
......
...@@ -38,7 +38,7 @@ class TagRepo ...@@ -38,7 +38,7 @@ class TagRepo
38 { 38 {
39 $entityInstance = $this->entity->getEntityInstance($entityType); 39 $entityInstance = $this->entity->getEntityInstance($entityType);
40 $searchQuery = $entityInstance->where('id', '=', $entityId)->with('tags'); 40 $searchQuery = $entityInstance->where('id', '=', $entityId)->with('tags');
41 - $searchQuery = $this->permissionService->enforceEntityRestrictions($searchQuery, $action); 41 + $searchQuery = $this->permissionService->enforceEntityRestrictions($entityType, $searchQuery, $action);
42 return $searchQuery->first(); 42 return $searchQuery->first();
43 } 43 }
44 44
...@@ -121,7 +121,7 @@ class TagRepo ...@@ -121,7 +121,7 @@ class TagRepo
121 /** 121 /**
122 * Create a new Tag instance from user input. 122 * Create a new Tag instance from user input.
123 * @param $input 123 * @param $input
124 - * @return static 124 + * @return Tag
125 */ 125 */
126 protected function newInstanceFromInput($input) 126 protected function newInstanceFromInput($input)
127 { 127 {
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
3 use BookStack\Role; 3 use BookStack\Role;
4 use BookStack\User; 4 use BookStack\User;
5 use Exception; 5 use Exception;
6 -use Setting;
7 6
8 class UserRepo 7 class UserRepo
9 { 8 {
...@@ -169,13 +168,13 @@ class UserRepo ...@@ -169,13 +168,13 @@ class UserRepo
169 public function getRecentlyCreated(User $user, $count = 20) 168 public function getRecentlyCreated(User $user, $count = 20)
170 { 169 {
171 return [ 170 return [
172 - 'pages' => $this->entityRepo->getRecentlyCreatedPages($count, 0, function ($query) use ($user) { 171 + 'pages' => $this->entityRepo->getRecentlyCreated('page', $count, 0, function ($query) use ($user) {
173 $query->where('created_by', '=', $user->id); 172 $query->where('created_by', '=', $user->id);
174 }), 173 }),
175 - 'chapters' => $this->entityRepo->getRecentlyCreatedChapters($count, 0, function ($query) use ($user) { 174 + 'chapters' => $this->entityRepo->getRecentlyCreated('chapter', $count, 0, function ($query) use ($user) {
176 $query->where('created_by', '=', $user->id); 175 $query->where('created_by', '=', $user->id);
177 }), 176 }),
178 - 'books' => $this->entityRepo->getRecentlyCreatedBooks($count, 0, function ($query) use ($user) { 177 + 'books' => $this->entityRepo->getRecentlyCreated('book', $count, 0, function ($query) use ($user) {
179 $query->where('created_by', '=', $user->id); 178 $query->where('created_by', '=', $user->id);
180 }) 179 })
181 ]; 180 ];
......
...@@ -114,7 +114,7 @@ class ActivityService ...@@ -114,7 +114,7 @@ class ActivityService
114 114
115 $activity = $this->permissionService 115 $activity = $this->permissionService
116 ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type') 116 ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
117 - ->orderBy('created_at', 'desc')->skip($count * $page)->take($count)->get(); 117 + ->orderBy('created_at', 'desc')->with(['entity', 'user.avatar'])->skip($count * $page)->take($count)->get();
118 118
119 return $this->filterSimilar($activity); 119 return $this->filterSimilar($activity);
120 } 120 }
......
...@@ -193,7 +193,7 @@ class AttachmentService extends UploadService ...@@ -193,7 +193,7 @@ class AttachmentService extends UploadService
193 try { 193 try {
194 $storage->put($attachmentStoragePath, $attachmentData); 194 $storage->put($attachmentStoragePath, $attachmentData);
195 } catch (Exception $e) { 195 } catch (Exception $e) {
196 - throw new FileUploadException('File path ' . $attachmentStoragePath . ' could not be uploaded to. Ensure it is writable to the server.'); 196 + throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentStoragePath]));
197 } 197 }
198 return $attachmentPath; 198 return $attachmentPath;
199 } 199 }
......
...@@ -33,7 +33,7 @@ class EmailConfirmationService ...@@ -33,7 +33,7 @@ class EmailConfirmationService
33 public function sendConfirmation(User $user) 33 public function sendConfirmation(User $user)
34 { 34 {
35 if ($user->email_confirmed) { 35 if ($user->email_confirmed) {
36 - throw new ConfirmationEmailException('Email has already been confirmed, Try logging in.', '/login'); 36 + throw new ConfirmationEmailException(trans('errors.email_already_confirmed'), '/login');
37 } 37 }
38 38
39 $this->deleteConfirmationsByUser($user); 39 $this->deleteConfirmationsByUser($user);
...@@ -63,7 +63,7 @@ class EmailConfirmationService ...@@ -63,7 +63,7 @@ class EmailConfirmationService
63 * Gets an email confirmation by looking up the token, 63 * Gets an email confirmation by looking up the token,
64 * Ensures the token has not expired. 64 * Ensures the token has not expired.
65 * @param string $token 65 * @param string $token
66 - * @return EmailConfirmation 66 + * @return array|null|\stdClass
67 * @throws UserRegistrationException 67 * @throws UserRegistrationException
68 */ 68 */
69 public function getEmailConfirmationFromToken($token) 69 public function getEmailConfirmationFromToken($token)
...@@ -72,14 +72,14 @@ class EmailConfirmationService ...@@ -72,14 +72,14 @@ class EmailConfirmationService
72 72
73 // If not found show error 73 // If not found show error
74 if ($emailConfirmation === null) { 74 if ($emailConfirmation === null) {
75 - throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register'); 75 + throw new UserRegistrationException(trans('errors.email_confirmation_invalid'), '/register');
76 } 76 }
77 77
78 // If more than a day old 78 // If more than a day old
79 if (Carbon::now()->subDay()->gt(new Carbon($emailConfirmation->created_at))) { 79 if (Carbon::now()->subDay()->gt(new Carbon($emailConfirmation->created_at))) {
80 $user = $this->users->getById($emailConfirmation->user_id); 80 $user = $this->users->getById($emailConfirmation->user_id);
81 $this->sendConfirmation($user); 81 $this->sendConfirmation($user);
82 - throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm'); 82 + throw new UserRegistrationException(trans('errors.email_confirmation_expired'), '/register/confirm');
83 } 83 }
84 84
85 $emailConfirmation->user = $this->users->getById($emailConfirmation->user_id); 85 $emailConfirmation->user = $this->users->getById($emailConfirmation->user_id);
......
1 <?php namespace BookStack\Services; 1 <?php namespace BookStack\Services;
2 2
3 -
4 use BookStack\Page; 3 use BookStack\Page;
4 +use BookStack\Repos\EntityRepo;
5 5
6 class ExportService 6 class ExportService
7 { 7 {
8 8
9 + protected $entityRepo;
10 +
11 + /**
12 + * ExportService constructor.
13 + * @param $entityRepo
14 + */
15 + public function __construct(EntityRepo $entityRepo)
16 + {
17 + $this->entityRepo = $entityRepo;
18 + }
19 +
9 /** 20 /**
10 * Convert a page to a self-contained HTML file. 21 * Convert a page to a self-contained HTML file.
11 * Includes required CSS & image content. Images are base64 encoded into the HTML. 22 * Includes required CSS & image content. Images are base64 encoded into the HTML.
...@@ -15,7 +26,7 @@ class ExportService ...@@ -15,7 +26,7 @@ class ExportService
15 public function pageToContainedHtml(Page $page) 26 public function pageToContainedHtml(Page $page)
16 { 27 {
17 $cssContent = file_get_contents(public_path('/css/export-styles.css')); 28 $cssContent = file_get_contents(public_path('/css/export-styles.css'));
18 - $pageHtml = view('pages/export', ['page' => $page, 'css' => $cssContent])->render(); 29 + $pageHtml = view('pages/export', ['page' => $page, 'pageContent' => $this->entityRepo->renderPage($page), 'css' => $cssContent])->render();
19 return $this->containHtml($pageHtml); 30 return $this->containHtml($pageHtml);
20 } 31 }
21 32
...@@ -27,9 +38,15 @@ class ExportService ...@@ -27,9 +38,15 @@ class ExportService
27 public function pageToPdf(Page $page) 38 public function pageToPdf(Page $page)
28 { 39 {
29 $cssContent = file_get_contents(public_path('/css/export-styles.css')); 40 $cssContent = file_get_contents(public_path('/css/export-styles.css'));
30 - $pageHtml = view('pages/pdf', ['page' => $page, 'css' => $cssContent])->render(); 41 + $pageHtml = view('pages/pdf', ['page' => $page, 'pageContent' => $this->entityRepo->renderPage($page), 'css' => $cssContent])->render();
42 +// return $pageHtml;
43 + $useWKHTML = config('snappy.pdf.binary') !== false;
31 $containedHtml = $this->containHtml($pageHtml); 44 $containedHtml = $this->containHtml($pageHtml);
32 - $pdf = \PDF::loadHTML($containedHtml); 45 + if ($useWKHTML) {
46 + $pdf = \SnappyPDF::loadHTML($containedHtml);
47 + } else {
48 + $pdf = \PDF::loadHTML($containedHtml);
49 + }
33 return $pdf->output(); 50 return $pdf->output();
34 } 51 }
35 52
...@@ -55,9 +72,13 @@ class ExportService ...@@ -55,9 +72,13 @@ class ExportService
55 $pathString = $srcString; 72 $pathString = $srcString;
56 } 73 }
57 if ($isLocal && !file_exists($pathString)) continue; 74 if ($isLocal && !file_exists($pathString)) continue;
58 - $imageContent = file_get_contents($pathString); 75 + try {
59 - $imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent); 76 + $imageContent = file_get_contents($pathString);
60 - $newImageString = str_replace($srcString, $imageEncoded, $oldImgString); 77 + $imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent);
78 + $newImageString = str_replace($srcString, $imageEncoded, $oldImgString);
79 + } catch (\ErrorException $e) {
80 + $newImageString = '';
81 + }
61 $htmlContent = str_replace($oldImgString, $newImageString, $htmlContent); 82 $htmlContent = str_replace($oldImgString, $newImageString, $htmlContent);
62 } 83 }
63 } 84 }
...@@ -84,14 +105,14 @@ class ExportService ...@@ -84,14 +105,14 @@ class ExportService
84 105
85 /** 106 /**
86 * Converts the page contents into simple plain text. 107 * Converts the page contents into simple plain text.
87 - * This method filters any bad looking content to 108 + * This method filters any bad looking content to provide a nice final output.
88 - * provide a nice final output.
89 * @param Page $page 109 * @param Page $page
90 * @return mixed 110 * @return mixed
91 */ 111 */
92 public function pageToPlainText(Page $page) 112 public function pageToPlainText(Page $page)
93 { 113 {
94 - $text = $page->text; 114 + $html = $this->entityRepo->renderPage($page);
115 + $text = strip_tags($html);
95 // Replace multiple spaces with single spaces 116 // Replace multiple spaces with single spaces
96 $text = preg_replace('/\ {2,}/', ' ', $text); 117 $text = preg_replace('/\ {2,}/', ' ', $text);
97 // Reduce multiple horrid whitespace characters. 118 // Reduce multiple horrid whitespace characters.
......
...@@ -59,7 +59,7 @@ class ImageService extends UploadService ...@@ -59,7 +59,7 @@ class ImageService extends UploadService
59 { 59 {
60 $imageName = $imageName ? $imageName : basename($url); 60 $imageName = $imageName ? $imageName : basename($url);
61 $imageData = file_get_contents($url); 61 $imageData = file_get_contents($url);
62 - if($imageData === false) throw new \Exception('Cannot get image from ' . $url); 62 + if($imageData === false) throw new \Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
63 return $this->saveNew($imageName, $imageData, $type); 63 return $this->saveNew($imageName, $imageData, $type);
64 } 64 }
65 65
...@@ -93,7 +93,7 @@ class ImageService extends UploadService ...@@ -93,7 +93,7 @@ class ImageService extends UploadService
93 $storage->put($fullPath, $imageData); 93 $storage->put($fullPath, $imageData);
94 $storage->setVisibility($fullPath, 'public'); 94 $storage->setVisibility($fullPath, 'public');
95 } catch (Exception $e) { 95 } catch (Exception $e) {
96 - throw new ImageUploadException('Image Path ' . $fullPath . ' is not writable by the server.'); 96 + throw new ImageUploadException(trans('errors.path_not_writable', ['filePath' => $fullPath]));
97 } 97 }
98 98
99 if ($this->isLocal()) $fullPath = str_replace_first('/public', '', $fullPath); 99 if ($this->isLocal()) $fullPath = str_replace_first('/public', '', $fullPath);
...@@ -160,7 +160,7 @@ class ImageService extends UploadService ...@@ -160,7 +160,7 @@ class ImageService extends UploadService
160 $thumb = $this->imageTool->make($storage->get($imagePath)); 160 $thumb = $this->imageTool->make($storage->get($imagePath));
161 } catch (Exception $e) { 161 } catch (Exception $e) {
162 if ($e instanceof \ErrorException || $e instanceof NotSupportedException) { 162 if ($e instanceof \ErrorException || $e instanceof NotSupportedException) {
163 - throw new ImageUploadException('The server cannot create thumbnails. Please check you have the GD PHP extension installed.'); 163 + throw new ImageUploadException(trans('errors.cannot_create_thumbs'));
164 } else { 164 } else {
165 throw $e; 165 throw $e;
166 } 166 }
......
...@@ -94,4 +94,4 @@ class Ldap ...@@ -94,4 +94,4 @@ class Ldap
94 return ldap_bind($ldapConnection, $bindRdn, $bindPassword); 94 return ldap_bind($ldapConnection, $bindRdn, $bindPassword);
95 } 95 }
96 96
97 -}
...\ No newline at end of file ...\ No newline at end of file
97 +}
......
...@@ -94,7 +94,7 @@ class LdapService ...@@ -94,7 +94,7 @@ class LdapService
94 $ldapBind = $this->ldap->bind($connection, $ldapDn, $ldapPass); 94 $ldapBind = $this->ldap->bind($connection, $ldapDn, $ldapPass);
95 } 95 }
96 96
97 - if (!$ldapBind) throw new LdapException('LDAP access failed using ' . ($isAnonymous ? ' anonymous bind.' : ' given dn & pass details')); 97 + if (!$ldapBind) throw new LdapException(($isAnonymous ? trans('errors.ldap_fail_anonymous') : trans('errors.ldap_fail_authed')));
98 } 98 }
99 99
100 /** 100 /**
...@@ -109,15 +109,19 @@ class LdapService ...@@ -109,15 +109,19 @@ class LdapService
109 109
110 // Check LDAP extension in installed 110 // Check LDAP extension in installed
111 if (!function_exists('ldap_connect') && config('app.env') !== 'testing') { 111 if (!function_exists('ldap_connect') && config('app.env') !== 'testing') {
112 - throw new LdapException('LDAP PHP extension not installed'); 112 + throw new LdapException(trans('errors.ldap_extension_not_installed'));
113 } 113 }
114 114
115 - // Get port from server string if specified. 115 + // Get port from server string and protocol if specified.
116 $ldapServer = explode(':', $this->config['server']); 116 $ldapServer = explode(':', $this->config['server']);
117 - $ldapConnection = $this->ldap->connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389); 117 + $hasProtocol = preg_match('/^ldaps{0,1}\:\/\//', $this->config['server']) === 1;
118 + if (!$hasProtocol) array_unshift($ldapServer, '');
119 + $hostName = $ldapServer[0] . ($hasProtocol?':':'') . $ldapServer[1];
120 + $defaultPort = $ldapServer[0] === 'ldaps' ? 636 : 389;
121 + $ldapConnection = $this->ldap->connect($hostName, count($ldapServer) > 2 ? intval($ldapServer[2]) : $defaultPort);
118 122
119 if ($ldapConnection === false) { 123 if ($ldapConnection === false) {
120 - throw new LdapException('Cannot connect to ldap server, Initial connection failed'); 124 + throw new LdapException(trans('errors.ldap_cannot_connect'));
121 } 125 }
122 126
123 // Set any required options 127 // Set any required options
......
...@@ -8,8 +8,9 @@ use BookStack\Ownable; ...@@ -8,8 +8,9 @@ use BookStack\Ownable;
8 use BookStack\Page; 8 use BookStack\Page;
9 use BookStack\Role; 9 use BookStack\Role;
10 use BookStack\User; 10 use BookStack\User;
11 +use Illuminate\Database\Connection;
12 +use Illuminate\Database\Eloquent\Builder;
11 use Illuminate\Support\Collection; 13 use Illuminate\Support\Collection;
12 -use Illuminate\Support\Facades\Log;
13 14
14 class PermissionService 15 class PermissionService
15 { 16 {
...@@ -23,6 +24,8 @@ class PermissionService ...@@ -23,6 +24,8 @@ class PermissionService
23 public $chapter; 24 public $chapter;
24 public $page; 25 public $page;
25 26
27 + protected $db;
28 +
26 protected $jointPermission; 29 protected $jointPermission;
27 protected $role; 30 protected $role;
28 31
...@@ -31,18 +34,21 @@ class PermissionService ...@@ -31,18 +34,21 @@ class PermissionService
31 /** 34 /**
32 * PermissionService constructor. 35 * PermissionService constructor.
33 * @param JointPermission $jointPermission 36 * @param JointPermission $jointPermission
37 + * @param Connection $db
34 * @param Book $book 38 * @param Book $book
35 * @param Chapter $chapter 39 * @param Chapter $chapter
36 * @param Page $page 40 * @param Page $page
37 * @param Role $role 41 * @param Role $role
38 */ 42 */
39 - public function __construct(JointPermission $jointPermission, Book $book, Chapter $chapter, Page $page, Role $role) 43 + public function __construct(JointPermission $jointPermission, Connection $db, Book $book, Chapter $chapter, Page $page, Role $role)
40 { 44 {
45 + $this->db = $db;
41 $this->jointPermission = $jointPermission; 46 $this->jointPermission = $jointPermission;
42 $this->role = $role; 47 $this->role = $role;
43 $this->book = $book; 48 $this->book = $book;
44 $this->chapter = $chapter; 49 $this->chapter = $chapter;
45 $this->page = $page; 50 $this->page = $page;
51 + // TODO - Update so admin still goes through filters
46 } 52 }
47 53
48 /** 54 /**
...@@ -151,7 +157,7 @@ class PermissionService ...@@ -151,7 +157,7 @@ class PermissionService
151 */ 157 */
152 public function buildJointPermissionsForEntity(Entity $entity) 158 public function buildJointPermissionsForEntity(Entity $entity)
153 { 159 {
154 - $roles = $this->role->with('jointPermissions')->get(); 160 + $roles = $this->role->get();
155 $entities = collect([$entity]); 161 $entities = collect([$entity]);
156 162
157 if ($entity->isA('book')) { 163 if ($entity->isA('book')) {
...@@ -171,7 +177,7 @@ class PermissionService ...@@ -171,7 +177,7 @@ class PermissionService
171 */ 177 */
172 public function buildJointPermissionsForEntities(Collection $entities) 178 public function buildJointPermissionsForEntities(Collection $entities)
173 { 179 {
174 - $roles = $this->role->with('jointPermissions')->get(); 180 + $roles = $this->role->get();
175 $this->deleteManyJointPermissionsForEntities($entities); 181 $this->deleteManyJointPermissionsForEntities($entities);
176 $this->createManyJointPermissions($entities, $roles); 182 $this->createManyJointPermissions($entities, $roles);
177 } 183 }
...@@ -302,6 +308,10 @@ class PermissionService ...@@ -302,6 +308,10 @@ class PermissionService
302 $explodedAction = explode('-', $action); 308 $explodedAction = explode('-', $action);
303 $restrictionAction = end($explodedAction); 309 $restrictionAction = end($explodedAction);
304 310
311 + if ($role->system_name === 'admin') {
312 + return $this->createJointPermissionDataArray($entity, $role, $action, true, true);
313 + }
314 +
305 if ($entity->isA('book')) { 315 if ($entity->isA('book')) {
306 316
307 if (!$entity->restricted) { 317 if (!$entity->restricted) {
...@@ -395,7 +405,7 @@ class PermissionService ...@@ -395,7 +405,7 @@ class PermissionService
395 $action = end($explodedPermission); 405 $action = end($explodedPermission);
396 $this->currentAction = $action; 406 $this->currentAction = $action;
397 407
398 - $nonJointPermissions = ['restrictions']; 408 + $nonJointPermissions = ['restrictions', 'image', 'attachment'];
399 409
400 // Handle non entity specific jointPermissions 410 // Handle non entity specific jointPermissions
401 if (in_array($explodedPermission[0], $nonJointPermissions)) { 411 if (in_array($explodedPermission[0], $nonJointPermissions)) {
...@@ -411,7 +421,6 @@ class PermissionService ...@@ -411,7 +421,6 @@ class PermissionService
411 $this->currentAction = $permission; 421 $this->currentAction = $permission;
412 } 422 }
413 423
414 -
415 $q = $this->entityRestrictionQuery($baseQuery)->count() > 0; 424 $q = $this->entityRestrictionQuery($baseQuery)->count() > 0;
416 $this->clean(); 425 $this->clean();
417 return $q; 426 return $q;
...@@ -462,60 +471,67 @@ class PermissionService ...@@ -462,60 +471,67 @@ class PermissionService
462 } 471 }
463 472
464 /** 473 /**
465 - * Add restrictions for a page query 474 + * Get the children of a book in an efficient single query, Filtered by the permission system.
466 - * @param $query 475 + * @param integer $book_id
467 - * @param string $action 476 + * @param bool $filterDrafts
468 - * @return mixed 477 + * @return \Illuminate\Database\Query\Builder
469 */ 478 */
470 - public function enforcePageRestrictions($query, $action = 'view') 479 + public function bookChildrenQuery($book_id, $filterDrafts = false) {
471 - { 480 + $pageSelect = $this->db->table('pages')->selectRaw("'BookStack\\\\Page' as entity_type, id, slug, name, text, '' as description, book_id, priority, chapter_id, draft")->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) {
472 - // Prevent drafts being visible to others. 481 + $query->where('draft', '=', 0);
473 - $query = $query->where(function ($query) { 482 + if (!$filterDrafts) {
474 - $query->where('draft', '=', false); 483 + $query->orWhere(function($query) {
475 - if ($this->currentUser()) { 484 + $query->where('draft', '=', 1)->where('created_by', '=', $this->currentUser()->id);
476 - $query->orWhere(function ($query) {
477 - $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser()->id);
478 }); 485 });
479 } 486 }
480 }); 487 });
488 + $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);
489 + $query = $this->db->query()->select('*')->from($this->db->raw("({$pageSelect->toSql()} UNION {$chapterSelect->toSql()}) AS U"))
490 + ->mergeBindings($pageSelect)->mergeBindings($chapterSelect);
491 +
492 + if (!$this->isAdmin()) {
493 + $whereQuery = $this->db->table('joint_permissions as jp')->selectRaw('COUNT(*)')
494 + ->whereRaw('jp.entity_id=U.id')->whereRaw('jp.entity_type=U.entity_type')
495 + ->where('jp.action', '=', 'view')->whereIn('jp.role_id', $this->getRoles())
496 + ->where(function($query) {
497 + $query->where('jp.has_permission', '=', 1)->orWhere(function($query) {
498 + $query->where('jp.has_permission_own', '=', 1)->where('jp.created_by', '=', $this->currentUser()->id);
499 + });
500 + });
501 + $query->whereRaw("({$whereQuery->toSql()}) > 0")->mergeBindings($whereQuery);
502 + }
481 503
482 - return $this->enforceEntityRestrictions($query, $action); 504 + $query->orderBy('draft', 'desc')->orderBy('priority', 'asc');
483 - } 505 + $this->clean();
484 - 506 + return $query;
485 - /**
486 - * Add on permission restrictions to a chapter query.
487 - * @param $query
488 - * @param string $action
489 - * @return mixed
490 - */
491 - public function enforceChapterRestrictions($query, $action = 'view')
492 - {
493 - return $this->enforceEntityRestrictions($query, $action);
494 - }
495 -
496 - /**
497 - * Add restrictions to a book query.
498 - * @param $query
499 - * @param string $action
500 - * @return mixed
501 - */
502 - public function enforceBookRestrictions($query, $action = 'view')
503 - {
504 - return $this->enforceEntityRestrictions($query, $action);
505 } 507 }
506 508
507 /** 509 /**
508 * Add restrictions for a generic entity 510 * Add restrictions for a generic entity
509 - * @param $query 511 + * @param string $entityType
512 + * @param Builder|Entity $query
510 * @param string $action 513 * @param string $action
511 * @return mixed 514 * @return mixed
512 */ 515 */
513 - public function enforceEntityRestrictions($query, $action = 'view') 516 + public function enforceEntityRestrictions($entityType, $query, $action = 'view')
514 { 517 {
518 + if (strtolower($entityType) === 'page') {
519 + // Prevent drafts being visible to others.
520 + $query = $query->where(function ($query) {
521 + $query->where('draft', '=', false);
522 + if ($this->currentUser()) {
523 + $query->orWhere(function ($query) {
524 + $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser()->id);
525 + });
526 + }
527 + });
528 + }
529 +
515 if ($this->isAdmin()) { 530 if ($this->isAdmin()) {
516 $this->clean(); 531 $this->clean();
517 return $query; 532 return $query;
518 } 533 }
534 +
519 $this->currentAction = $action; 535 $this->currentAction = $action;
520 return $this->entityRestrictionQuery($query); 536 return $this->entityRestrictionQuery($query);
521 } 537 }
...@@ -553,6 +569,7 @@ class PermissionService ...@@ -553,6 +569,7 @@ class PermissionService
553 }); 569 });
554 }); 570 });
555 }); 571 });
572 + $this->clean();
556 return $q; 573 return $q;
557 } 574 }
558 575
...@@ -601,7 +618,7 @@ class PermissionService ...@@ -601,7 +618,7 @@ class PermissionService
601 private function isAdmin() 618 private function isAdmin()
602 { 619 {
603 if ($this->isAdminUser === null) { 620 if ($this->isAdminUser === null) {
604 - $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasRole('admin') : false; 621 + $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasSystemRole('admin') : false;
605 } 622 }
606 623
607 return $this->isAdminUser; 624 return $this->isAdminUser;
......
1 <?php namespace BookStack\Services; 1 <?php namespace BookStack\Services;
2 2
3 use BookStack\Setting; 3 use BookStack\Setting;
4 +use BookStack\User;
4 use Illuminate\Contracts\Cache\Repository as Cache; 5 use Illuminate\Contracts\Cache\Repository as Cache;
5 6
6 /** 7 /**
...@@ -38,11 +39,24 @@ class SettingService ...@@ -38,11 +39,24 @@ class SettingService
38 */ 39 */
39 public function get($key, $default = false) 40 public function get($key, $default = false)
40 { 41 {
42 + if ($default === false) $default = config('setting-defaults.' . $key, false);
41 $value = $this->getValueFromStore($key, $default); 43 $value = $this->getValueFromStore($key, $default);
42 return $this->formatValue($value, $default); 44 return $this->formatValue($value, $default);
43 } 45 }
44 46
45 /** 47 /**
48 + * Get a user-specific setting from the database or cache.
49 + * @param User $user
50 + * @param $key
51 + * @param bool $default
52 + * @return bool|string
53 + */
54 + public function getUser($user, $key, $default = false)
55 + {
56 + return $this->get($this->userKey($user->id, $key), $default);
57 + }
58 +
59 + /**
46 * Gets a setting value from the cache or database. 60 * Gets a setting value from the cache or database.
47 * Looks at the system defaults if not cached or in database. 61 * Looks at the system defaults if not cached or in database.
48 * @param $key 62 * @param $key
...@@ -69,14 +83,6 @@ class SettingService ...@@ -69,14 +83,6 @@ class SettingService
69 return $value; 83 return $value;
70 } 84 }
71 85
72 - // Check the defaults set in the app config.
73 - $configPrefix = 'setting-defaults.' . $key;
74 - if (config()->has($configPrefix)) {
75 - $value = config($configPrefix);
76 - $this->cache->forever($cacheKey, $value);
77 - return $value;
78 - }
79 -
80 return $default; 86 return $default;
81 } 87 }
82 88
...@@ -119,6 +125,16 @@ class SettingService ...@@ -119,6 +125,16 @@ class SettingService
119 } 125 }
120 126
121 /** 127 /**
128 + * Check if a user setting is in the database.
129 + * @param $key
130 + * @return bool
131 + */
132 + public function hasUser($key)
133 + {
134 + return $this->has($this->userKey($key));
135 + }
136 +
137 + /**
122 * Add a setting to the database. 138 * Add a setting to the database.
123 * @param $key 139 * @param $key
124 * @param $value 140 * @param $value
...@@ -136,6 +152,28 @@ class SettingService ...@@ -136,6 +152,28 @@ class SettingService
136 } 152 }
137 153
138 /** 154 /**
155 + * Put a user-specific setting into the database.
156 + * @param User $user
157 + * @param $key
158 + * @param $value
159 + * @return bool
160 + */
161 + public function putUser($user, $key, $value)
162 + {
163 + return $this->put($this->userKey($user->id, $key), $value);
164 + }
165 +
166 + /**
167 + * Convert a setting key into a user-specific key.
168 + * @param $key
169 + * @return string
170 + */
171 + protected function userKey($userId, $key = '')
172 + {
173 + return 'user:' . $userId . ':' . $key;
174 + }
175 +
176 + /**
139 * Removes a setting from the database. 177 * Removes a setting from the database.
140 * @param $key 178 * @param $key
141 * @return bool 179 * @return bool
...@@ -151,6 +189,16 @@ class SettingService ...@@ -151,6 +189,16 @@ class SettingService
151 } 189 }
152 190
153 /** 191 /**
192 + * Delete settings for a given user id.
193 + * @param $userId
194 + * @return mixed
195 + */
196 + public function deleteUserSettings($userId)
197 + {
198 + return $this->setting->where('setting_key', 'like', $this->userKey($userId) . '%')->delete();
199 + }
200 +
201 + /**
154 * Gets a setting model from the database for the given key. 202 * Gets a setting model from the database for the given key.
155 * @param $key 203 * @param $key
156 * @return mixed 204 * @return mixed
......
...@@ -70,12 +70,12 @@ class SocialAuthService ...@@ -70,12 +70,12 @@ class SocialAuthService
70 70
71 // Check social account has not already been used 71 // Check social account has not already been used
72 if ($this->socialAccount->where('driver_id', '=', $socialUser->getId())->exists()) { 72 if ($this->socialAccount->where('driver_id', '=', $socialUser->getId())->exists()) {
73 - throw new UserRegistrationException('This ' . $socialDriver . ' account is already in use, Try logging in via the ' . $socialDriver . ' option.', '/login'); 73 + throw new UserRegistrationException(trans('errors.social_account_in_use', ['socialAccount'=>$socialDriver]), '/login');
74 } 74 }
75 75
76 if ($this->userRepo->getByEmail($socialUser->getEmail())) { 76 if ($this->userRepo->getByEmail($socialUser->getEmail())) {
77 $email = $socialUser->getEmail(); 77 $email = $socialUser->getEmail();
78 - throw new UserRegistrationException('The email ' . $email . ' is already in use. If you already have an account you can connect your ' . $socialDriver . ' account from your profile settings.', '/login'); 78 + throw new UserRegistrationException(trans('errors.social_account_in_use', ['socialAccount'=>$socialDriver, 'email' => $email]), '/login');
79 } 79 }
80 80
81 return $socialUser; 81 return $socialUser;
...@@ -98,7 +98,6 @@ class SocialAuthService ...@@ -98,7 +98,6 @@ class SocialAuthService
98 98
99 // Get any attached social accounts or users 99 // Get any attached social accounts or users
100 $socialAccount = $this->socialAccount->where('driver_id', '=', $socialId)->first(); 100 $socialAccount = $this->socialAccount->where('driver_id', '=', $socialId)->first();
101 - $user = $this->userRepo->getByEmail($socialUser->getEmail());
102 $isLoggedIn = auth()->check(); 101 $isLoggedIn = auth()->check();
103 $currentUser = user(); 102 $currentUser = user();
104 103
...@@ -113,27 +112,26 @@ class SocialAuthService ...@@ -113,27 +112,26 @@ class SocialAuthService
113 if ($isLoggedIn && $socialAccount === null) { 112 if ($isLoggedIn && $socialAccount === null) {
114 $this->fillSocialAccount($socialDriver, $socialUser); 113 $this->fillSocialAccount($socialDriver, $socialUser);
115 $currentUser->socialAccounts()->save($this->socialAccount); 114 $currentUser->socialAccounts()->save($this->socialAccount);
116 - session()->flash('success', title_case($socialDriver) . ' account was successfully attached to your profile.'); 115 + session()->flash('success', trans('settings.users_social_connected', ['socialAccount' => title_case($socialDriver)]));
117 return redirect($currentUser->getEditUrl()); 116 return redirect($currentUser->getEditUrl());
118 } 117 }
119 118
120 // When a user is logged in and the social account exists and is already linked to the current user. 119 // When a user is logged in and the social account exists and is already linked to the current user.
121 if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) { 120 if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) {
122 - session()->flash('error', 'This ' . title_case($socialDriver) . ' account is already attached to your profile.'); 121 + session()->flash('error', trans('errors.social_account_existing', ['socialAccount' => title_case($socialDriver)]));
123 return redirect($currentUser->getEditUrl()); 122 return redirect($currentUser->getEditUrl());
124 } 123 }
125 124
126 // When a user is logged in, A social account exists but the users do not match. 125 // When a user is logged in, A social account exists but the users do not match.
127 - // Change the user that the social account is assigned to.
128 if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) { 126 if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) {
129 - session()->flash('success', 'This ' . title_case($socialDriver) . ' account is already used by another user.'); 127 + session()->flash('error', trans('errors.social_account_already_used_existing', ['socialAccount' => title_case($socialDriver)]));
130 return redirect($currentUser->getEditUrl()); 128 return redirect($currentUser->getEditUrl());
131 } 129 }
132 130
133 // Otherwise let the user know this social account is not used by anyone. 131 // Otherwise let the user know this social account is not used by anyone.
134 - $message = 'This ' . $socialDriver . ' account is not linked to any users. Please attach it in your profile settings'; 132 + $message = trans('errors.social_account_not_used', ['socialAccount' => title_case($socialDriver)]);
135 if (setting('registration-enabled')) { 133 if (setting('registration-enabled')) {
136 - $message .= ' or, If you do not yet have an account, You can register an account using the ' . $socialDriver . ' option'; 134 + $message .= trans('errors.social_account_register_instructions', ['socialAccount' => title_case($socialDriver)]);
137 } 135 }
138 136
139 throw new SocialSignInException($message . '.', '/login'); 137 throw new SocialSignInException($message . '.', '/login');
...@@ -157,8 +155,8 @@ class SocialAuthService ...@@ -157,8 +155,8 @@ class SocialAuthService
157 { 155 {
158 $driver = trim(strtolower($socialDriver)); 156 $driver = trim(strtolower($socialDriver));
159 157
160 - if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found'); 158 + if (!in_array($driver, $this->validSocialDrivers)) abort(404, trans('errors.social_driver_not_found'));
161 - if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured("Your {$driver} social settings are not configured correctly."); 159 + if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured(trans('errors.social_driver_not_configured', ['socialAccount' => title_case($socialDriver)]));
162 160
163 return $driver; 161 return $driver;
164 } 162 }
...@@ -215,7 +213,7 @@ class SocialAuthService ...@@ -215,7 +213,7 @@ class SocialAuthService
215 { 213 {
216 session(); 214 session();
217 user()->socialAccounts()->where('driver', '=', $socialDriver)->delete(); 215 user()->socialAccounts()->where('driver', '=', $socialDriver)->delete();
218 - session()->flash('success', title_case($socialDriver) . ' account successfully detached'); 216 + session()->flash('success', trans('settings.users_social_disconnected', ['socialAccount' => title_case($socialDriver)]));
219 return redirect(user()->getEditUrl()); 217 return redirect(user()->getEditUrl());
220 } 218 }
221 219
......
...@@ -5,9 +5,7 @@ use BookStack\View; ...@@ -5,9 +5,7 @@ use BookStack\View;
5 5
6 class ViewService 6 class ViewService
7 { 7 {
8 -
9 protected $view; 8 protected $view;
10 - protected $user;
11 protected $permissionService; 9 protected $permissionService;
12 10
13 /** 11 /**
...@@ -18,7 +16,6 @@ class ViewService ...@@ -18,7 +16,6 @@ class ViewService
18 public function __construct(View $view, PermissionService $permissionService) 16 public function __construct(View $view, PermissionService $permissionService)
19 { 17 {
20 $this->view = $view; 18 $this->view = $view;
21 - $this->user = user();
22 $this->permissionService = $permissionService; 19 $this->permissionService = $permissionService;
23 } 20 }
24 21
...@@ -29,8 +26,9 @@ class ViewService ...@@ -29,8 +26,9 @@ class ViewService
29 */ 26 */
30 public function add(Entity $entity) 27 public function add(Entity $entity)
31 { 28 {
32 - if ($this->user === null) return 0; 29 + $user = user();
33 - $view = $entity->views()->where('user_id', '=', $this->user->id)->first(); 30 + if ($user === null || $user->isDefault()) return 0;
31 + $view = $entity->views()->where('user_id', '=', $user->id)->first();
34 // Add view if model exists 32 // Add view if model exists
35 if ($view) { 33 if ($view) {
36 $view->increment('views'); 34 $view->increment('views');
...@@ -39,7 +37,7 @@ class ViewService ...@@ -39,7 +37,7 @@ class ViewService
39 37
40 // Otherwise create new view count 38 // Otherwise create new view count
41 $entity->views()->save($this->view->create([ 39 $entity->views()->save($this->view->create([
42 - 'user_id' => $this->user->id, 40 + 'user_id' => $user->id,
43 'views' => 1 41 'views' => 1
44 ])); 42 ]));
45 43
...@@ -78,13 +76,14 @@ class ViewService ...@@ -78,13 +76,14 @@ class ViewService
78 */ 76 */
79 public function getUserRecentlyViewed($count = 10, $page = 0, $filterModel = false) 77 public function getUserRecentlyViewed($count = 10, $page = 0, $filterModel = false)
80 { 78 {
81 - if ($this->user === null) return collect(); 79 + $user = user();
80 + if ($user === null || $user->isDefault()) return collect();
82 81
83 $query = $this->permissionService 82 $query = $this->permissionService
84 ->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type'); 83 ->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type');
85 84
86 if ($filterModel) $query = $query->where('viewable_type', '=', get_class($filterModel)); 85 if ($filterModel) $query = $query->where('viewable_type', '=', get_class($filterModel));
87 - $query = $query->where('user_id', '=', user()->id); 86 + $query = $query->where('user_id', '=', $user->id);
88 87
89 $viewables = $query->with('viewable')->orderBy('updated_at', 'desc') 88 $viewables = $query->with('viewable')->orderBy('updated_at', 'desc')
90 ->skip($count * $page)->take($count)->get()->pluck('viewable'); 89 ->skip($count * $page)->take($count)->get()->pluck('viewable');
......
...@@ -75,6 +75,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -75,6 +75,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
75 } 75 }
76 76
77 /** 77 /**
78 + * Check if the user has a role.
79 + * @param $role
80 + * @return mixed
81 + */
82 + public function hasSystemRole($role)
83 + {
84 + return $this->roles->pluck('system_name')->contains('admin');
85 + }
86 +
87 + /**
78 * Get all permissions belonging to a the current user. 88 * Get all permissions belonging to a the current user.
79 * @param bool $cache 89 * @param bool $cache
80 * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough 90 * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
...@@ -150,8 +160,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -150,8 +160,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
150 */ 160 */
151 public function getAvatar($size = 50) 161 public function getAvatar($size = 50)
152 { 162 {
153 - if ($this->image_id === 0 || $this->image_id === '0' || $this->image_id === null) return baseUrl('/user_avatar.png'); 163 + $default = baseUrl('/user_avatar.png');
154 - return baseUrl($this->avatar->getThumb($size, $size, false)); 164 + $imageId = $this->image_id;
165 + if ($imageId === 0 || $imageId === '0' || $imageId === null) return $default;
166 +
167 + try {
168 + $avatar = baseUrl($this->avatar->getThumb($size, $size, false));
169 + } catch (\Exception $err) {
170 + $avatar = $default;
171 + }
172 + return $avatar;
155 } 173 }
156 174
157 /** 175 /**
......
...@@ -60,11 +60,12 @@ function userCan($permission, Ownable $ownable = null) ...@@ -60,11 +60,12 @@ function userCan($permission, Ownable $ownable = null)
60 * Helper to access system settings. 60 * Helper to access system settings.
61 * @param $key 61 * @param $key
62 * @param bool $default 62 * @param bool $default
63 - * @return mixed 63 + * @return bool|string|\BookStack\Services\SettingService
64 */ 64 */
65 -function setting($key, $default = false) 65 +function setting($key = null, $default = false)
66 { 66 {
67 $settingService = app(\BookStack\Services\SettingService::class); 67 $settingService = app(\BookStack\Services\SettingService::class);
68 + if (is_null($key)) return $settingService;
68 return $settingService->get($key, $default); 69 return $settingService->get($key, $default);
69 } 70 }
70 71
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
15 "league/flysystem-aws-s3-v3": "^1.0", 15 "league/flysystem-aws-s3-v3": "^1.0",
16 "barryvdh/laravel-dompdf": "^0.7", 16 "barryvdh/laravel-dompdf": "^0.7",
17 "predis/predis": "^1.1", 17 "predis/predis": "^1.1",
18 - "gathercontent/htmldiff": "^0.2.1" 18 + "gathercontent/htmldiff": "^0.2.1",
19 + "barryvdh/laravel-snappy": "^0.3.1"
19 }, 20 },
20 "require-dev": { 21 "require-dev": {
21 "fzaninotto/faker": "~1.4", 22 "fzaninotto/faker": "~1.4",
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 "This file is @generated automatically" 5 "This file is @generated automatically"
6 ], 6 ],
7 - "hash": "3124d900cfe857392a94de479f3ff6d4", 7 + "hash": "2438a2f4a02adbea5f378f9e9408eb29",
8 - "content-hash": "a968767a73f77e66e865c276cf76eedf", 8 + "content-hash": "6add8bff71ecc86e0c90858590834a26",
9 "packages": [ 9 "packages": [
10 { 10 {
11 "name": "aws/aws-sdk-php", 11 "name": "aws/aws-sdk-php",
...@@ -256,6 +256,58 @@ ...@@ -256,6 +256,58 @@
256 "time": "2016-07-04 11:52:48" 256 "time": "2016-07-04 11:52:48"
257 }, 257 },
258 { 258 {
259 + "name": "barryvdh/laravel-snappy",
260 + "version": "v0.3.1",
261 + "source": {
262 + "type": "git",
263 + "url": "https://github.com/barryvdh/laravel-snappy.git",
264 + "reference": "509a4497be63d8ee7ff464a3daf00d9edde08e21"
265 + },
266 + "dist": {
267 + "type": "zip",
268 + "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/509a4497be63d8ee7ff464a3daf00d9edde08e21",
269 + "reference": "509a4497be63d8ee7ff464a3daf00d9edde08e21",
270 + "shasum": ""
271 + },
272 + "require": {
273 + "illuminate/filesystem": "5.0.x|5.1.x|5.2.x|5.3.x",
274 + "illuminate/support": "5.0.x|5.1.x|5.2.x|5.3.x",
275 + "knplabs/knp-snappy": "*",
276 + "php": ">=5.4.0"
277 + },
278 + "type": "library",
279 + "extra": {
280 + "branch-alias": {
281 + "dev-master": "0.3-dev"
282 + }
283 + },
284 + "autoload": {
285 + "psr-4": {
286 + "Barryvdh\\Snappy\\": "src/"
287 + }
288 + },
289 + "notification-url": "https://packagist.org/downloads/",
290 + "license": [
291 + "MIT"
292 + ],
293 + "authors": [
294 + {
295 + "name": "Barry vd. Heuvel",
296 + "email": "barryvdh@gmail.com"
297 + }
298 + ],
299 + "description": "Snappy PDF/Image for Laravel 4",
300 + "keywords": [
301 + "image",
302 + "laravel",
303 + "pdf",
304 + "snappy",
305 + "wkhtmltoimage",
306 + "wkhtmltopdf"
307 + ],
308 + "time": "2016-08-05 13:08:28"
309 + },
310 + {
259 "name": "barryvdh/reflection-docblock", 311 "name": "barryvdh/reflection-docblock",
260 "version": "v2.0.4", 312 "version": "v2.0.4",
261 "source": { 313 "source": {
...@@ -998,6 +1050,71 @@ ...@@ -998,6 +1050,71 @@
998 "time": "2015-12-05 17:17:57" 1050 "time": "2015-12-05 17:17:57"
999 }, 1051 },
1000 { 1052 {
1053 + "name": "knplabs/knp-snappy",
1054 + "version": "0.4.3",
1055 + "source": {
1056 + "type": "git",
1057 + "url": "https://github.com/KnpLabs/snappy.git",
1058 + "reference": "44f7a9b37d5686fd7db4c1e9569a802a5d16923f"
1059 + },
1060 + "dist": {
1061 + "type": "zip",
1062 + "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/44f7a9b37d5686fd7db4c1e9569a802a5d16923f",
1063 + "reference": "44f7a9b37d5686fd7db4c1e9569a802a5d16923f",
1064 + "shasum": ""
1065 + },
1066 + "require": {
1067 + "php": ">=5.3.3",
1068 + "symfony/process": "~2.3|~3.0"
1069 + },
1070 + "require-dev": {
1071 + "phpunit/phpunit": "~4.7"
1072 + },
1073 + "suggest": {
1074 + "h4cc/wkhtmltoimage-amd64": "Provides wkhtmltoimage-amd64 binary for Linux-compatible machines, use version `~0.12` as dependency",
1075 + "h4cc/wkhtmltoimage-i386": "Provides wkhtmltoimage-i386 binary for Linux-compatible machines, use version `~0.12` as dependency",
1076 + "h4cc/wkhtmltopdf-amd64": "Provides wkhtmltopdf-amd64 binary for Linux-compatible machines, use version `~0.12` as dependency",
1077 + "h4cc/wkhtmltopdf-i386": "Provides wkhtmltopdf-i386 binary for Linux-compatible machines, use version `~0.12` as dependency",
1078 + "wemersonjanuario/wkhtmltopdf-windows": "Provides wkhtmltopdf executable for Windows, use version `~0.12` as dependency"
1079 + },
1080 + "type": "library",
1081 + "extra": {
1082 + "branch-alias": {
1083 + "dev-master": "0.5.x-dev"
1084 + }
1085 + },
1086 + "autoload": {
1087 + "psr-0": {
1088 + "Knp\\Snappy": "src/"
1089 + }
1090 + },
1091 + "notification-url": "https://packagist.org/downloads/",
1092 + "license": [
1093 + "MIT"
1094 + ],
1095 + "authors": [
1096 + {
1097 + "name": "KNPLabs Team",
1098 + "homepage": "http://knplabs.com"
1099 + },
1100 + {
1101 + "name": "Symfony Community",
1102 + "homepage": "http://github.com/KnpLabs/snappy/contributors"
1103 + }
1104 + ],
1105 + "description": "PHP5 library allowing thumbnail, snapshot or PDF generation from a url or a html page. Wrapper for wkhtmltopdf/wkhtmltoimage.",
1106 + "homepage": "http://github.com/KnpLabs/snappy",
1107 + "keywords": [
1108 + "knp",
1109 + "knplabs",
1110 + "pdf",
1111 + "snapshot",
1112 + "thumbnail",
1113 + "wkhtmltopdf"
1114 + ],
1115 + "time": "2015-11-17 13:16:27"
1116 + },
1117 + {
1001 "name": "laravel/framework", 1118 "name": "laravel/framework",
1002 "version": "v5.3.11", 1119 "version": "v5.3.11",
1003 "source": { 1120 "source": {
......
...@@ -148,6 +148,7 @@ return [ ...@@ -148,6 +148,7 @@ return [
148 Barryvdh\DomPDF\ServiceProvider::class, 148 Barryvdh\DomPDF\ServiceProvider::class,
149 Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, 149 Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
150 Barryvdh\Debugbar\ServiceProvider::class, 150 Barryvdh\Debugbar\ServiceProvider::class,
151 + Barryvdh\Snappy\ServiceProvider::class,
151 152
152 153
153 /* 154 /*
...@@ -218,6 +219,7 @@ return [ ...@@ -218,6 +219,7 @@ return [
218 219
219 'ImageTool' => Intervention\Image\Facades\Image::class, 220 'ImageTool' => Intervention\Image\Facades\Image::class,
220 'PDF' => Barryvdh\DomPDF\Facade::class, 221 'PDF' => Barryvdh\DomPDF\Facade::class,
222 + 'SnappyPDF' => Barryvdh\Snappy\Facades\SnappyPdf::class,
221 'Debugbar' => Barryvdh\Debugbar\Facade::class, 223 'Debugbar' => Barryvdh\Debugbar\Facade::class,
222 224
223 /** 225 /**
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
6 return [ 6 return [
7 7
8 'app-name' => 'BookStack', 8 'app-name' => 'BookStack',
9 + 'app-logo' => '',
9 'app-name-header' => true, 10 'app-name-header' => true,
10 'app-editor' => 'wysiwyg', 11 'app-editor' => 'wysiwyg',
11 'app-color' => '#0288D1', 12 'app-color' => '#0288D1',
......
1 +<?php
2 +
3 +return [
4 + 'pdf' => [
5 + 'enabled' => true,
6 + 'binary' => file_exists(base_path('wkhtmltopdf')) ? base_path('wkhtmltopdf') : env('WKHTMLTOPDF', false),
7 + 'timeout' => false,
8 + 'options' => [],
9 + 'env' => [],
10 + ],
11 + 'image' => [
12 + 'enabled' => false,
13 + 'binary' => '/usr/local/bin/wkhtmltoimage',
14 + 'timeout' => false,
15 + 'options' => [],
16 + 'env' => [],
17 + ],
18 +];
...@@ -59,4 +59,14 @@ $factory->define(BookStack\Tag::class, function ($faker) { ...@@ -59,4 +59,14 @@ $factory->define(BookStack\Tag::class, function ($faker) {
59 'name' => $faker->city, 59 'name' => $faker->city,
60 'value' => $faker->sentence(3) 60 'value' => $faker->sentence(3)
61 ]; 61 ];
62 +});
63 +
64 +$factory->define(BookStack\Image::class, function ($faker) {
65 + return [
66 + 'name' => $faker->slug . '.jpg',
67 + 'url' => $faker->url,
68 + 'path' => $faker->url,
69 + 'type' => 'gallery',
70 + 'uploaded_to' => 0
71 + ];
62 }); 72 });
...\ No newline at end of file ...\ No newline at end of file
......
1 +<?php
2 +
3 +use Illuminate\Support\Facades\Schema;
4 +use Illuminate\Database\Schema\Blueprint;
5 +use Illuminate\Database\Migrations\Migration;
6 +
7 +class CreateCacheTable extends Migration
8 +{
9 + /**
10 + * Run the migrations.
11 + *
12 + * @return void
13 + */
14 + public function up()
15 + {
16 + Schema::create('cache', function (Blueprint $table) {
17 + $table->string('key')->unique();
18 + $table->text('value');
19 + $table->integer('expiration');
20 + });
21 + }
22 +
23 + /**
24 + * Reverse the migrations.
25 + *
26 + * @return void
27 + */
28 + public function down()
29 + {
30 + Schema::dropIfExists('cache');
31 + }
32 +}
1 +<?php
2 +
3 +use Illuminate\Support\Facades\Schema;
4 +use Illuminate\Database\Schema\Blueprint;
5 +use Illuminate\Database\Migrations\Migration;
6 +
7 +class CreateSessionsTable extends Migration
8 +{
9 + /**
10 + * Run the migrations.
11 + *
12 + * @return void
13 + */
14 + public function up()
15 + {
16 + Schema::create('sessions', function (Blueprint $table) {
17 + $table->string('id')->unique();
18 + $table->integer('user_id')->nullable();
19 + $table->string('ip_address', 45)->nullable();
20 + $table->text('user_agent')->nullable();
21 + $table->text('payload');
22 + $table->integer('last_activity');
23 + });
24 + }
25 +
26 + /**
27 + * Reverse the migrations.
28 + *
29 + * @return void
30 + */
31 + public function down()
32 + {
33 + Schema::dropIfExists('sessions');
34 + }
35 +}
1 { 1 {
2 "private": true, 2 "private": true,
3 "scripts": { 3 "scripts": {
4 - "prod": "gulp --production", 4 + "build": "gulp --production",
5 - "dev": "gulp watch" 5 + "dev": "gulp watch",
6 + "watch": "gulp watch"
6 }, 7 },
7 "devDependencies": { 8 "devDependencies": {
8 "angular": "^1.5.5", 9 "angular": "^1.5.5",
...@@ -15,7 +16,9 @@ ...@@ -15,7 +16,9 @@
15 "laravel-elixir": "^6.0.0-11", 16 "laravel-elixir": "^6.0.0-11",
16 "laravel-elixir-browserify-official": "^0.1.3", 17 "laravel-elixir-browserify-official": "^0.1.3",
17 "marked": "^0.3.5", 18 "marked": "^0.3.5",
18 - "moment": "^2.12.0", 19 + "moment": "^2.12.0"
19 - "zeroclipboard": "^2.2.0" 20 + },
21 + "dependencies": {
22 + "clipboard": "^1.5.16"
20 } 23 }
21 } 24 }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <php> 22 <php>
23 <env name="APP_ENV" value="testing"/> 23 <env name="APP_ENV" value="testing"/>
24 <env name="APP_DEBUG" value="false"/> 24 <env name="APP_DEBUG" value="false"/>
25 + <env name="APP_LANG" value="en"/>
25 <env name="CACHE_DRIVER" value="array"/> 26 <env name="CACHE_DRIVER" value="array"/>
26 <env name="SESSION_DRIVER" value="array"/> 27 <env name="SESSION_DRIVER" value="array"/>
27 <env name="QUEUE_DRIVER" value="sync"/> 28 <env name="QUEUE_DRIVER" value="sync"/>
......
No preview for this file type
...@@ -17,25 +17,42 @@ A platform for storing and organising information and documentation. General inf ...@@ -17,25 +17,42 @@ A platform for storing and organising information and documentation. General inf
17 17
18 All development on BookStack is currently done on the master branch. When it's time for a release the master branch is merged into release with built & minified CSS & JS then tagged at it's version. Here are the current development requirements: 18 All development on BookStack is currently done on the master branch. When it's time for a release the master branch is merged into release with built & minified CSS & JS then tagged at it's version. Here are the current development requirements:
19 19
20 -* [Node.js](https://nodejs.org/en/) 20 +* [Node.js](https://nodejs.org/en/) v6.9+
21 -* [Gulp](http://gulpjs.com/)
22 21
23 -SASS is used to help the CSS development and the JavaScript is run through browserify/babel to allow for writing ES6 code. Both of these are done using gulp. 22 +SASS is used to help the CSS development and the JavaScript is run through browserify/babel to allow for writing ES6 code. Both of these are done using gulp. To run the build task you can use the following commands:
23 +
24 +``` bash
25 +# Build and minify for production
26 +npm run-script build
27 +
28 +# Build for dev (With sourcemaps) and watch for changes
29 +npm run-script dev
30 +```
24 31
25 BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. To use you will need PHPUnit installed and accessible via command line. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the following database name, user name and password defined as `bookstack-test`. You will have to create that database and credentials before testing. 32 BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. To use you will need PHPUnit installed and accessible via command line. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the following database name, user name and password defined as `bookstack-test`. You will have to create that database and credentials before testing.
26 33
27 The testing database will also need migrating and seeding beforehand. This can be done with the following commands: 34 The testing database will also need migrating and seeding beforehand. This can be done with the following commands:
28 35
29 -``` 36 +``` bash
30 php artisan migrate --database=mysql_testing 37 php artisan migrate --database=mysql_testing
31 php artisan db:seed --class=DummyContentSeeder --database=mysql_testing 38 php artisan db:seed --class=DummyContentSeeder --database=mysql_testing
32 ``` 39 ```
33 40
34 Once done you can run `phpunit` in the application root directory to run all tests. 41 Once done you can run `phpunit` in the application root directory to run all tests.
35 42
43 +## Translations
44 +
45 +As part of BookStack v0.14 support for translations has been built in. All text strings can be found in the `resources/lang` folder where each language option has its own folder. To add a new language you should copy the `en` folder to an new folder (eg. `fr` for french) then go through and translate all text strings in those files, leaving the keys and file-names intact. If a language string is missing then the `en` translation will be used. To show the language option in the user preferences language drop-down you will need to add your language to the options found at the bottom of the `resources/lang/en/settings.php` file. A system-wide language can also be set in the `.env` file like so: `APP_LANG=en`.
46 +
47 + Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time.
48 +
49 +## Website, Docs & Blog
50 +
51 +The website project docs & Blog can be found in the [BookStackApp/website](https://github.com/BookStackApp/website) repo.
52 +
36 ## License 53 ## License
37 54
38 -BookStack is provided under the MIT License. 55 +The BookStack source is provided under the MIT License.
39 56
40 ## Attribution 57 ## Attribution
41 58
...@@ -53,5 +70,11 @@ These are the great projects used to help build BookStack: ...@@ -53,5 +70,11 @@ These are the great projects used to help build BookStack:
53 * [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html) 70 * [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html)
54 * [Marked](https://github.com/chjj/marked) 71 * [Marked](https://github.com/chjj/marked)
55 * [Moment.js](http://momentjs.com/) 72 * [Moment.js](http://momentjs.com/)
73 +* [BarryVD](https://github.com/barryvdh)
74 + * [Debugbar](https://github.com/barryvdh/laravel-debugbar)
75 + * [Dompdf](https://github.com/barryvdh/laravel-dompdf)
76 + * [Snappy (WKHTML2PDF)](https://github.com/barryvdh/laravel-snappy)
77 + * [Laravel IDE helper](https://github.com/barryvdh/laravel-ide-helper)
78 +* [WKHTMLtoPDF](http://wkhtmltopdf.org/index.html)
56 79
57 Additionally, Thank you [BrowserStack](https://www.browserstack.com/) for supporting us and making cross-browser testing easy. 80 Additionally, Thank you [BrowserStack](https://www.browserstack.com/) for supporting us and making cross-browser testing easy.
......
1 "use strict"; 1 "use strict";
2 2
3 // AngularJS - Create application and load components 3 // AngularJS - Create application and load components
4 -var angular = require('angular'); 4 +import angular from "angular";
5 -var ngResource = require('angular-resource'); 5 +import "angular-resource";
6 -var ngAnimate = require('angular-animate'); 6 +import "angular-animate";
7 -var ngSanitize = require('angular-sanitize'); 7 +import "angular-sanitize";
8 -require('angular-ui-sortable'); 8 +import "angular-ui-sortable";
9 9
10 // Url retrieval function 10 // Url retrieval function
11 window.baseUrl = function(path) { 11 window.baseUrl = function(path) {
...@@ -15,7 +15,13 @@ window.baseUrl = function(path) { ...@@ -15,7 +15,13 @@ window.baseUrl = function(path) {
15 return basePath + '/' + path; 15 return basePath + '/' + path;
16 }; 16 };
17 17
18 -var ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']); 18 +let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']);
19 +
20 +// Translation setup
21 +// Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
22 +import Translations from "./translations"
23 +let translator = new Translations(window.translations);
24 +window.trans = translator.get.bind(translator);
19 25
20 // Global Event System 26 // Global Event System
21 class EventManager { 27 class EventManager {
...@@ -25,9 +31,9 @@ class EventManager { ...@@ -25,9 +31,9 @@ class EventManager {
25 31
26 emit(eventName, eventData) { 32 emit(eventName, eventData) {
27 if (typeof this.listeners[eventName] === 'undefined') return this; 33 if (typeof this.listeners[eventName] === 'undefined') return this;
28 - var eventsToStart = this.listeners[eventName]; 34 + let eventsToStart = this.listeners[eventName];
29 for (let i = 0; i < eventsToStart.length; i++) { 35 for (let i = 0; i < eventsToStart.length; i++) {
30 - var event = eventsToStart[i]; 36 + let event = eventsToStart[i];
31 event(eventData); 37 event(eventData);
32 } 38 }
33 return this; 39 return this;
...@@ -55,10 +61,9 @@ Controllers(ngApp, window.Events); ...@@ -55,10 +61,9 @@ Controllers(ngApp, window.Events);
55 // Smooth scrolling 61 // Smooth scrolling
56 jQuery.fn.smoothScrollTo = function () { 62 jQuery.fn.smoothScrollTo = function () {
57 if (this.length === 0) return; 63 if (this.length === 0) return;
58 - let scrollElem = document.documentElement.scrollTop === 0 ? document.body : document.documentElement; 64 + $('html, body').animate({
59 - $(scrollElem).animate({
60 scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin 65 scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
61 - }, 800); // Adjust to change animations speed (ms) 66 + }, 300); // Adjust to change animations speed (ms)
62 return this; 67 return this;
63 }; 68 };
64 69
...@@ -70,93 +75,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) { ...@@ -70,93 +75,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
70 }); 75 });
71 76
72 // Global jQuery Elements 77 // Global jQuery Elements
73 -$(function () { 78 +let notifications = $('.notification');
74 - 79 +let successNotification = notifications.filter('.pos');
75 - var notifications = $('.notification'); 80 +let errorNotification = notifications.filter('.neg');
76 - var successNotification = notifications.filter('.pos'); 81 +let warningNotification = notifications.filter('.warning');
77 - var errorNotification = notifications.filter('.neg'); 82 +// Notification Events
78 - var warningNotification = notifications.filter('.warning'); 83 +window.Events.listen('success', function (text) {
79 - // Notification Events 84 + successNotification.hide();
80 - window.Events.listen('success', function (text) { 85 + successNotification.find('span').text(text);
81 - successNotification.hide(); 86 + setTimeout(() => {
82 - successNotification.find('span').text(text); 87 + successNotification.show();
88 + }, 1);
89 +});
90 +window.Events.listen('warning', function (text) {
91 + warningNotification.find('span').text(text);
92 + warningNotification.show();
93 +});
94 +window.Events.listen('error', function (text) {
95 + errorNotification.find('span').text(text);
96 + errorNotification.show();
97 +});
98 +
99 +// Notification hiding
100 +notifications.click(function () {
101 + $(this).fadeOut(100);
102 +});
103 +
104 +// Chapter page list toggles
105 +$('.chapter-toggle').click(function (e) {
106 + e.preventDefault();
107 + $(this).toggleClass('open');
108 + $(this).closest('.chapter').find('.inset-list').slideToggle(180);
109 +});
110 +
111 +// Back to top button
112 +$('#back-to-top').click(function() {
113 + $('#header').smoothScrollTo();
114 +});
115 +let scrollTopShowing = false;
116 +let scrollTop = document.getElementById('back-to-top');
117 +let scrollTopBreakpoint = 1200;
118 +window.addEventListener('scroll', function() {
119 + let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
120 + if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
121 + scrollTop.style.display = 'block';
122 + scrollTopShowing = true;
83 setTimeout(() => { 123 setTimeout(() => {
84 - successNotification.show(); 124 + scrollTop.style.opacity = 0.4;
85 }, 1); 125 }, 1);
86 - }); 126 + } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
87 - window.Events.listen('warning', function (text) { 127 + scrollTop.style.opacity = 0;
88 - warningNotification.find('span').text(text); 128 + scrollTopShowing = false;
89 - warningNotification.show(); 129 + setTimeout(() => {
90 - }); 130 + scrollTop.style.display = 'none';
91 - window.Events.listen('error', function (text) { 131 + }, 500);
92 - errorNotification.find('span').text(text);
93 - errorNotification.show();
94 - });
95 -
96 - // Notification hiding
97 - notifications.click(function () {
98 - $(this).fadeOut(100);
99 - });
100 -
101 - // Chapter page list toggles
102 - $('.chapter-toggle').click(function (e) {
103 - e.preventDefault();
104 - $(this).toggleClass('open');
105 - $(this).closest('.chapter').find('.inset-list').slideToggle(180);
106 - });
107 -
108 - // Back to top button
109 - $('#back-to-top').click(function() {
110 - $('#header').smoothScrollTo();
111 - });
112 - var scrollTopShowing = false;
113 - var scrollTop = document.getElementById('back-to-top');
114 - var scrollTopBreakpoint = 1200;
115 - window.addEventListener('scroll', function() {
116 - let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
117 - if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
118 - scrollTop.style.display = 'block';
119 - scrollTopShowing = true;
120 - setTimeout(() => {
121 - scrollTop.style.opacity = 0.4;
122 - }, 1);
123 - } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
124 - scrollTop.style.opacity = 0;
125 - scrollTopShowing = false;
126 - setTimeout(() => {
127 - scrollTop.style.display = 'none';
128 - }, 500);
129 - }
130 - });
131 -
132 - // Common jQuery actions
133 - $('[data-action="expand-entity-list-details"]').click(function() {
134 - $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
135 - });
136 -
137 - // Popup close
138 - $('.popup-close').click(function() {
139 - $(this).closest('.overlay').fadeOut(240);
140 - });
141 - $('.overlay').click(function(event) {
142 - if (!$(event.target).hasClass('overlay')) return;
143 - $(this).fadeOut(240);
144 - });
145 -
146 - // Prevent markdown display link click redirect
147 - $('.markdown-display').on('click', 'a', function(event) {
148 - event.preventDefault();
149 - window.open($(this).attr('href'));
150 - });
151 -
152 - // Detect IE for css
153 - if(navigator.userAgent.indexOf('MSIE')!==-1
154 - || navigator.appVersion.indexOf('Trident/') > 0
155 - || navigator.userAgent.indexOf('Safari') !== -1){
156 - $('body').addClass('flexbox-support');
157 } 132 }
133 +});
158 134
135 +// Common jQuery actions
136 +$('[data-action="expand-entity-list-details"]').click(function() {
137 + $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
159 }); 138 });
160 139
140 +// Popup close
141 +$('.popup-close').click(function() {
142 + $(this).closest('.overlay').fadeOut(240);
143 +});
144 +$('.overlay').click(function(event) {
145 + if (!$(event.target).hasClass('overlay')) return;
146 + $(this).fadeOut(240);
147 +});
148 +
149 +// Detect IE for css
150 +if(navigator.userAgent.indexOf('MSIE')!==-1
151 + || navigator.appVersion.indexOf('Trident/') > 0
152 + || navigator.userAgent.indexOf('Safari') !== -1){
153 + $('body').addClass('flexbox-support');
154 +}
155 +
161 // Page specific items 156 // Page specific items
162 -require('./pages/page-show'); 157 +import "./pages/page-show";
......
1 "use strict"; 1 "use strict";
2 // Configure ZeroClipboard 2 // Configure ZeroClipboard
3 -var zeroClipBoard = require('zeroclipboard'); 3 +import Clipboard from "clipboard";
4 -zeroClipBoard.config({
5 - swfPath: window.baseUrl('/ZeroClipboard.swf')
6 -});
7 4
8 -window.setupPageShow = module.exports = function (pageId) { 5 +export default window.setupPageShow = function (pageId) {
9 6
10 // Set up pointer 7 // Set up pointer
11 - var $pointer = $('#pointer').detach(); 8 + let $pointer = $('#pointer').detach();
12 - var $pointerInner = $pointer.children('div.pointer').first(); 9 + let pointerShowing = false;
13 - var isSelection = false; 10 + let $pointerInner = $pointer.children('div.pointer').first();
11 + let isSelection = false;
12 + let pointerModeLink = true;
13 + let pointerSectionId = '';
14 14
15 // Select all contents on input click 15 // Select all contents on input click
16 $pointer.on('click', 'input', function (e) { 16 $pointer.on('click', 'input', function (e) {
...@@ -18,35 +18,53 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -18,35 +18,53 @@ window.setupPageShow = module.exports = function (pageId) {
18 e.stopPropagation(); 18 e.stopPropagation();
19 }); 19 });
20 20
21 - // Set up copy-to-clipboard 21 + // Pointer mode toggle
22 - new zeroClipBoard($pointer.find('button').first()[0]); 22 + $pointer.on('click', 'span.icon', event => {
23 + let $icon = $(event.currentTarget);
24 + pointerModeLink = !pointerModeLink;
25 + $icon.html(pointerModeLink ? '<i class="zmdi zmdi-link"></i>' : '<i class="zmdi zmdi-square-down"></i>');
26 + updatePointerContent();
27 + });
28 +
29 + // Set up clipboard
30 + let clipboard = new Clipboard('#pointer button');
23 31
24 // Hide pointer when clicking away 32 // Hide pointer when clicking away
25 - $(document.body).find('*').on('click focus', function (e) { 33 + $(document.body).find('*').on('click focus', event => {
26 - if (!isSelection) { 34 + if (!pointerShowing || isSelection) return;
27 - $pointer.detach(); 35 + let target = $(event.target);
28 - } 36 + if (target.is('.zmdi') || $(event.target).closest('#pointer').length === 1) return;
37 +
38 + $pointer.detach();
39 + pointerShowing = false;
29 }); 40 });
30 41
42 + function updatePointerContent() {
43 + let inputText = pointerModeLink ? window.baseUrl(`/link/${pageId}#${pointerSectionId}`) : `{{@${pageId}#${pointerSectionId}}}`;
44 + if (pointerModeLink && inputText.indexOf('http') !== 0) inputText = window.location.protocol + "//" + window.location.host + inputText;
45 +
46 + $pointer.find('input').val(inputText);
47 + }
48 +
31 // Show pointer when selecting a single block of tagged content 49 // Show pointer when selecting a single block of tagged content
32 $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) { 50 $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) {
33 e.stopPropagation(); 51 e.stopPropagation();
34 - var selection = window.getSelection(); 52 + let selection = window.getSelection();
35 if (selection.toString().length === 0) return; 53 if (selection.toString().length === 0) return;
36 54
37 // Show pointer and set link 55 // Show pointer and set link
38 - var $elem = $(this); 56 + let $elem = $(this);
39 - let link = window.baseUrl('/link/' + pageId + '#' + $elem.attr('id')); 57 + pointerSectionId = $elem.attr('id');
40 - if (link.indexOf('http') !== 0) link = window.location.protocol + "//" + window.location.host + link; 58 + updatePointerContent();
41 - $pointer.find('input').val(link); 59 +
42 - $pointer.find('button').first().attr('data-clipboard-text', link);
43 $elem.before($pointer); 60 $elem.before($pointer);
44 $pointer.show(); 61 $pointer.show();
62 + pointerShowing = true;
45 63
46 // Set pointer to sit near mouse-up position 64 // Set pointer to sit near mouse-up position
47 - var pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2)); 65 + let pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2));
48 if (pointerLeftOffset < 0) pointerLeftOffset = 0; 66 if (pointerLeftOffset < 0) pointerLeftOffset = 0;
49 - var pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100; 67 + let pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100;
50 $pointerInner.css('left', pointerLeftOffsetPercent + '%'); 68 $pointerInner.css('left', pointerLeftOffsetPercent + '%');
51 69
52 isSelection = true; 70 isSelection = true;
...@@ -57,10 +75,12 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -57,10 +75,12 @@ window.setupPageShow = module.exports = function (pageId) {
57 75
58 // Go to, and highlight if necessary, the specified text. 76 // Go to, and highlight if necessary, the specified text.
59 function goToText(text) { 77 function goToText(text) {
60 - var idElem = $('.page-content #' + text).first(); 78 + let idElem = document.getElementById(text);
61 - if (idElem.length !== 0) { 79 + $('.page-content [data-highlighted]').attr('data-highlighted', '').css('background-color', '');
62 - idElem.smoothScrollTo(); 80 + if (idElem !== null) {
63 - idElem.css('background-color', 'rgba(244, 249, 54, 0.25)'); 81 + let $idElem = $(idElem);
82 + let color = $('#custom-styles').attr('data-color-light');
83 + $idElem.css('background-color', color).attr('data-highlighted', 'true').smoothScrollTo();
64 } else { 84 } else {
65 $('.page-content').find(':contains("' + text + '")').smoothScrollTo(); 85 $('.page-content').find(':contains("' + text + '")').smoothScrollTo();
66 } 86 }
...@@ -68,19 +88,24 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -68,19 +88,24 @@ window.setupPageShow = module.exports = function (pageId) {
68 88
69 // Check the hash on load 89 // Check the hash on load
70 if (window.location.hash) { 90 if (window.location.hash) {
71 - var text = window.location.hash.replace(/\%20/g, ' ').substr(1); 91 + let text = window.location.hash.replace(/\%20/g, ' ').substr(1);
72 goToText(text); 92 goToText(text);
73 } 93 }
74 94
95 + // Sidebar page nav click event
96 + $('.sidebar-page-nav').on('click', 'a', event => {
97 + goToText(event.target.getAttribute('href').substr(1));
98 + });
99 +
75 // Make the book-tree sidebar stick in view on scroll 100 // Make the book-tree sidebar stick in view on scroll
76 - var $window = $(window); 101 + let $window = $(window);
77 - var $bookTree = $(".book-tree"); 102 + let $bookTree = $(".book-tree");
78 - var $bookTreeParent = $bookTree.parent(); 103 + let $bookTreeParent = $bookTree.parent();
79 // Check the page is scrollable and the content is taller than the tree 104 // Check the page is scrollable and the content is taller than the tree
80 - var pageScrollable = ($(document).height() > $window.height()) && ($bookTree.height() < $('.page-content').height()); 105 + let pageScrollable = ($(document).height() > $window.height()) && ($bookTree.height() < $('.page-content').height());
81 // Get current tree's width and header height 106 // Get current tree's width and header height
82 - var headerHeight = $("#header").height() + $(".toolbar").height(); 107 + let headerHeight = $("#header").height() + $(".toolbar").height();
83 - var isFixed = $window.scrollTop() > headerHeight; 108 + let isFixed = $window.scrollTop() > headerHeight;
84 // Function to fix the tree as a sidebar 109 // Function to fix the tree as a sidebar
85 function stickTree() { 110 function stickTree() {
86 $bookTree.width($bookTreeParent.width() + 15); 111 $bookTree.width($bookTreeParent.width() + 15);
...@@ -95,7 +120,7 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -95,7 +120,7 @@ window.setupPageShow = module.exports = function (pageId) {
95 } 120 }
96 // Checks if the tree stickiness state should change 121 // Checks if the tree stickiness state should change
97 function checkTreeStickiness(skipCheck) { 122 function checkTreeStickiness(skipCheck) {
98 - var shouldBeFixed = $window.scrollTop() > headerHeight; 123 + let shouldBeFixed = $window.scrollTop() > headerHeight;
99 if (shouldBeFixed && (!isFixed || skipCheck)) { 124 if (shouldBeFixed && (!isFixed || skipCheck)) {
100 stickTree(); 125 stickTree();
101 } else if (!shouldBeFixed && (isFixed || skipCheck)) { 126 } else if (!shouldBeFixed && (isFixed || skipCheck)) {
......
1 +/**
2 + * Translation Manager
3 + * Handles the JavaScript side of translating strings
4 + * in a way which fits with Laravel.
5 + */
6 +class Translator {
7 +
8 + /**
9 + * Create an instance, Passing in the required translations
10 + * @param translations
11 + */
12 + constructor(translations) {
13 + this.store = translations;
14 + }
15 +
16 + /**
17 + * Get a translation, Same format as laravel's 'trans' helper
18 + * @param key
19 + * @param replacements
20 + * @returns {*}
21 + */
22 + get(key, replacements) {
23 + let splitKey = key.split('.');
24 + let value = splitKey.reduce((a, b) => {
25 + return a != undefined ? a[b] : a;
26 + }, this.store);
27 +
28 + if (value === undefined) {
29 + console.log(`Translation with key "${key}" does not exist`);
30 + value = key;
31 + }
32 +
33 + if (replacements === undefined) return value;
34 +
35 + let replaceMatches = value.match(/:([\S]+)/g);
36 + if (replaceMatches === null) return value;
37 + replaceMatches.forEach(match => {
38 + let key = match.substring(1);
39 + if (typeof replacements[key] === 'undefined') return;
40 + value = value.replace(match, replacements[key]);
41 + });
42 + return value;
43 + }
44 +
45 +}
46 +
47 +export default Translator
...@@ -136,9 +136,6 @@ ...@@ -136,9 +136,6 @@
136 background-color: #EEE; 136 background-color: #EEE;
137 padding: $-s; 137 padding: $-s;
138 display: block; 138 display: block;
139 - > * {
140 - display: inline-block;
141 - }
142 &:before { 139 &:before {
143 font-family: 'Material-Design-Iconic-Font'; 140 font-family: 'Material-Design-Iconic-Font';
144 padding-right: $-s; 141 padding-right: $-s;
......
...@@ -108,5 +108,4 @@ $button-border-radius: 2px; ...@@ -108,5 +108,4 @@ $button-border-radius: 2px;
108 cursor: default; 108 cursor: default;
109 box-shadow: none; 109 box-shadow: none;
110 } 110 }
111 -} 111 +}
112 -
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -70,9 +70,6 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { ...@@ -70,9 +70,6 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
70 #entity-selector-wrap .popup-body .form-group { 70 #entity-selector-wrap .popup-body .form-group {
71 margin: 0; 71 margin: 0;
72 } 72 }
73 -//body.ie #entity-selector-wrap .popup-body .form-group {
74 -// min-height: 60vh;
75 -//}
76 73
77 .image-manager-body { 74 .image-manager-body {
78 min-height: 70vh; 75 min-height: 70vh;
...@@ -465,4 +462,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { ...@@ -465,4 +462,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
465 border-bottom-width: 3px; 462 border-bottom-width: 3px;
466 } 463 }
467 } 464 }
465 +}
466 +
467 +.image-picker .none {
468 + display: none;
468 } 469 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
33 position: relative; 33 position: relative;
34 z-index: 5; 34 z-index: 5;
35 textarea { 35 textarea {
36 - font-family: 'Roboto Mono'; 36 + font-family: 'Roboto Mono', monospace;
37 font-style: normal; 37 font-style: normal;
38 font-weight: 400; 38 font-weight: 400;
39 padding: $-xs $-m; 39 padding: $-xs $-m;
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
55 display: flex; 55 display: flex;
56 flex-direction: column; 56 flex-direction: column;
57 border: 1px solid #DDD; 57 border: 1px solid #DDD;
58 + width: 50%;
58 } 59 }
59 .markdown-display { 60 .markdown-display {
60 padding: 0 $-m 0; 61 padding: 0 $-m 0;
...@@ -68,7 +69,7 @@ ...@@ -68,7 +69,7 @@
68 .editor-toolbar { 69 .editor-toolbar {
69 width: 100%; 70 width: 100%;
70 padding: $-xs $-m; 71 padding: $-xs $-m;
71 - font-family: 'Roboto Mono'; 72 + font-family: 'Roboto Mono', monospace;
72 font-size: 11px; 73 font-size: 11px;
73 line-height: 1.6; 74 line-height: 1.6;
74 border-bottom: 1px solid #DDD; 75 border-bottom: 1px solid #DDD;
...@@ -267,9 +268,4 @@ input.outline { ...@@ -267,9 +268,4 @@ input.outline {
267 268
268 .image-picker img { 269 .image-picker img {
269 background-color: #BBB; 270 background-color: #BBB;
270 -}
271 -
272 -div[toggle-switch] {
273 - height: 18px;
274 - width: 150px;
275 } 271 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -322,6 +322,9 @@ ul.pagination { ...@@ -322,6 +322,9 @@ ul.pagination {
322 font-size: 0.75em; 322 font-size: 0.75em;
323 margin-top: $-xs; 323 margin-top: $-xs;
324 } 324 }
325 + .text-muted p.text-muted {
326 + margin-top: 0;
327 + }
325 .page.draft .text-page { 328 .page.draft .text-page {
326 color: $color-page-draft; 329 color: $color-page-draft;
327 } 330 }
......
...@@ -138,6 +138,10 @@ ...@@ -138,6 +138,10 @@
138 font-size: 18px; 138 font-size: 18px;
139 padding-top: 4px; 139 padding-top: 4px;
140 } 140 }
141 + span.icon {
142 + cursor: pointer;
143 + user-select: none;
144 + }
141 .button { 145 .button {
142 line-height: 1; 146 line-height: 1;
143 margin: 0 0 0 -4px; 147 margin: 0 0 0 -4px;
......
...@@ -35,6 +35,12 @@ table.table { ...@@ -35,6 +35,12 @@ table.table {
35 tr:hover { 35 tr:hover {
36 background-color: #EEE; 36 background-color: #EEE;
37 } 37 }
38 + .text-right {
39 + text-align: right;
40 + }
41 + .text-center {
42 + text-align: center;
43 + }
38 } 44 }
39 45
40 table.no-style { 46 table.no-style {
......
...@@ -109,6 +109,9 @@ em, i, .italic { ...@@ -109,6 +109,9 @@ em, i, .italic {
109 small, p.small, span.small, .text-small { 109 small, p.small, span.small, .text-small {
110 font-size: 0.8em; 110 font-size: 0.8em;
111 color: lighten($text-dark, 20%); 111 color: lighten($text-dark, 20%);
112 + small, p.small, span.small, .text-small {
113 + font-size: 1em;
114 + }
112 } 115 }
113 116
114 sup, .superscript { 117 sup, .superscript {
...@@ -172,6 +175,7 @@ pre code { ...@@ -172,6 +175,7 @@ pre code {
172 background-color: transparent; 175 background-color: transparent;
173 border: 0; 176 border: 0;
174 font-size: 1em; 177 font-size: 1em;
178 + display: block;
175 } 179 }
176 /* 180 /*
177 * Text colors 181 * Text colors
......
1 -//@import "reset"; 1 +@import "reset";
2 @import "variables"; 2 @import "variables";
3 @import "mixins"; 3 @import "mixins";
4 @import "html"; 4 @import "html";
......
...@@ -16,7 +16,7 @@ return [ ...@@ -16,7 +16,7 @@ return [
16 'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.', 16 'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.',
17 'app_name_header' => 'Anwendungsname im Header anzeigen?', 17 'app_name_header' => 'Anwendungsname im Header anzeigen?',
18 'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?', 18 'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?',
19 - 'app_secure_images' => 'Erh&oml;hte Sicherheit f&uuml;r Bilduploads aktivieren?', 19 + 'app_secure_images' => 'Erh&ouml;hte Sicherheit f&uuml;r Bilduploads aktivieren?',
20 'app_secure_images_desc' => 'Aus Leistungsgr&uuml;nden sind alle Bilder &ouml;ffentlich sichtbar. Diese Option f&uuml;gt zuf&auml;llige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.', 20 'app_secure_images_desc' => 'Aus Leistungsgr&uuml;nden sind alle Bilder &ouml;ffentlich sichtbar. Diese Option f&uuml;gt zuf&auml;llige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.',
21 'app_editor' => 'Seiteneditor', 21 'app_editor' => 'Seiteneditor',
22 'app_editor_desc' => 'W&auml;hlen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.', 22 'app_editor_desc' => 'W&auml;hlen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.',
......
...@@ -14,7 +14,49 @@ return [ ...@@ -14,7 +14,49 @@ return [
14 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 14 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
15 15
16 /** 16 /**
17 - * Email Confirmation Text 17 + * Login & Register
18 + */
19 + 'sign_up' => 'Sign up',
20 + 'log_in' => 'Log in',
21 + 'logout' => 'Logout',
22 +
23 + 'name' => 'Name',
24 + 'username' => 'Username',
25 + 'email' => 'Email',
26 + 'password' => 'Password',
27 + 'password_confirm' => 'Confirm Password',
28 + 'password_hint' => 'Must be over 5 characters',
29 + 'forgot_password' => 'Forgot Password?',
30 + 'remember_me' => 'Remember Me',
31 + 'ldap_email_hint' => 'Please enter an email to use for this account.',
32 + 'create_account' => 'Create Account',
33 + 'social_login' => 'Social Login',
34 + 'social_registration' => 'Social Registration',
35 + 'social_registration_text' => 'Register and sign in using another service.',
36 +
37 + 'register_thanks' => 'Thanks for registering!',
38 + 'register_confirm' => 'Please check your email and click the confirmation button to access :appName.',
39 + 'registrations_disabled' => 'Registrations are currently disabled',
40 + 'registration_email_domain_invalid' => 'That email domain does not have access to this application',
41 + 'register_success' => 'Thanks for signing up! You are now registered and signed in.',
42 +
43 +
44 + /**
45 + * Password Reset
46 + */
47 + 'reset_password' => 'Reset Password',
48 + 'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.',
49 + 'reset_password_send_button' => 'Send Reset Link',
50 + 'reset_password_sent_success' => 'A password reset link has been sent to :email.',
51 + 'reset_password_success' => 'Your password has been successfully reset.',
52 +
53 + 'email_reset_subject' => 'Reset your :appName password',
54 + 'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
55 + 'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.',
56 +
57 +
58 + /**
59 + * Email Confirmation
18 */ 60 */
19 'email_confirm_subject' => 'Confirm your email on :appName', 61 'email_confirm_subject' => 'Confirm your email on :appName',
20 'email_confirm_greeting' => 'Thanks for joining :appName!', 62 'email_confirm_greeting' => 'Thanks for joining :appName!',
...@@ -23,4 +65,10 @@ return [ ...@@ -23,4 +65,10 @@ return [
23 'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.', 65 'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.',
24 'email_confirm_success' => 'Your email has been confirmed!', 66 'email_confirm_success' => 'Your email has been confirmed!',
25 'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.', 67 'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.',
68 +
69 + 'email_not_confirmed' => 'Email Address Not Confirmed',
70 + 'email_not_confirmed_text' => 'Your email address has not yet been confirmed.',
71 + 'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.',
72 + 'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.',
73 + 'email_not_confirmed_resend_button' => 'Resend Confirmation Email',
26 ]; 74 ];
...\ No newline at end of file ...\ No newline at end of file
......
1 +<?php
2 +return [
3 +
4 + /**
5 + * Buttons
6 + */
7 + 'cancel' => 'Cancel',
8 + 'confirm' => 'Confirm',
9 + 'back' => 'Back',
10 + 'save' => 'Save',
11 + 'continue' => 'Continue',
12 + 'select' => 'Select',
13 +
14 + /**
15 + * Form Labels
16 + */
17 + 'name' => 'Name',
18 + 'description' => 'Description',
19 + 'role' => 'Role',
20 +
21 + /**
22 + * Actions
23 + */
24 + 'actions' => 'Actions',
25 + 'view' => 'View',
26 + 'create' => 'Create',
27 + 'update' => 'Update',
28 + 'edit' => 'Edit',
29 + 'sort' => 'Sort',
30 + 'move' => 'Move',
31 + 'delete' => 'Delete',
32 + 'search' => 'Search',
33 + 'search_clear' => 'Clear Search',
34 + 'reset' => 'Reset',
35 + 'remove' => 'Remove',
36 +
37 +
38 + /**
39 + * Misc
40 + */
41 + 'deleted_user' => 'Deleted User',
42 + 'no_activity' => 'No activity to show',
43 + 'no_items' => 'No items available',
44 + 'back_to_top' => 'Back to top',
45 + 'toggle_details' => 'Toggle Details',
46 +
47 + /**
48 + * Header
49 + */
50 + 'view_profile' => 'View Profile',
51 + 'edit_profile' => 'Edit Profile',
52 +
53 + /**
54 + * Email Content
55 + */
56 + 'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
57 + 'email_rights' => 'All rights reserved',
58 +];
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +return [
3 +
4 + /**
5 + * Image Manager
6 + */
7 + 'image_select' => 'Image Select',
8 + 'image_all' => 'All',
9 + 'image_all_title' => 'View all images',
10 + 'image_book_title' => 'View images uploaded to this book',
11 + 'image_page_title' => 'View images uploaded to this page',
12 + 'image_search_hint' => 'Search by image name',
13 + 'image_uploaded' => 'Uploaded :uploadedDate',
14 + 'image_load_more' => 'Load More',
15 + 'image_image_name' => 'Image Name',
16 + 'image_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.',
17 + 'image_select_image' => 'Select Image',
18 + 'image_dropzone' => 'Drop images or click here to upload',
19 + 'images_deleted' => 'Images Deleted',
20 + 'image_preview' => 'Image Preview',
21 + 'image_upload_success' => 'Image uploaded successfully',
22 + 'image_update_success' => 'Image details successfully updated',
23 + 'image_delete_success' => 'Image successfully deleted'
24 +];
...\ No newline at end of file ...\ No newline at end of file
...@@ -6,7 +6,65 @@ return [ ...@@ -6,7 +6,65 @@ return [
6 * Error text strings. 6 * Error text strings.
7 */ 7 */
8 8
9 - // Pages 9 + // Permissions
10 'permission' => 'You do not have permission to access the requested page.', 10 'permission' => 'You do not have permission to access the requested page.',
11 - 'permissionJson' => 'You do not have permission to perform the requested action.' 11 + 'permissionJson' => 'You do not have permission to perform the requested action.',
12 +
13 + // Auth
14 + 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
15 + 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
16 + 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
17 + 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
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' => 'No action defined',
23 + 'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
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.',
25 + 'social_account_existing' => 'This :socialAccount is already attached to your profile.',
26 + 'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
27 + 'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
28 + 'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
29 + 'social_driver_not_found' => 'Social driver not found',
30 + 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
31 +
32 + // System
33 + 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
34 + 'cannot_get_image_from_url' => 'Cannot get image from :url',
35 + 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
36 + 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
37 + 'image_upload_error' => 'An error occurred uploading the image',
38 +
39 + // Attachments
40 + 'attachment_page_mismatch' => 'Page mismatch during attachment update',
41 +
42 + // Pages
43 + 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
44 +
45 + // Entities
46 + 'entity_not_found' => 'Entity not found',
47 + 'book_not_found' => 'Book not found',
48 + 'page_not_found' => 'Page not found',
49 + 'chapter_not_found' => 'Chapter not found',
50 + 'selected_book_not_found' => 'The selected book was not found',
51 + 'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
52 + 'guests_cannot_save_drafts' => 'Guests cannot save drafts',
53 +
54 + // Users
55 + 'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
56 + 'users_cannot_delete_guest' => 'You cannot delete the guest user',
57 +
58 + // Roles
59 + 'role_cannot_be_edited' => 'This role cannot be edited',
60 + 'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
61 + 'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
62 +
63 + // Error pages
64 + '404_page_not_found' => 'Page Not Found',
65 + 'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
66 + 'return_home' => 'Return to home',
67 + 'error_occurred' => 'An Error Occurred',
68 + 'app_down' => ':appName is down right now',
69 + 'back_soon' => 'It will be back up soon.',
12 ]; 70 ];
...\ No newline at end of file ...\ No newline at end of file
......
1 <?php 1 <?php
2 2
3 return [ 3 return [
4 - 4 +
5 /** 5 /**
6 * Settings text strings 6 * Settings text strings
7 * Contains all text strings used in the general settings sections of BookStack 7 * Contains all text strings used in the general settings sections of BookStack
8 * including users and roles. 8 * including users and roles.
9 */ 9 */
10 - 10 +
11 'settings' => 'Settings', 11 'settings' => 'Settings',
12 'settings_save' => 'Save Settings', 12 'settings_save' => 'Save Settings',
13 - 13 + 'settings_save_success' => 'Settings saved',
14 +
15 + /**
16 + * App settings
17 + */
18 +
14 'app_settings' => 'App Settings', 19 'app_settings' => 'App Settings',
15 'app_name' => 'Application name', 20 'app_name' => 'Application name',
16 'app_name_desc' => 'This name is shown in the header and any emails.', 21 'app_name_desc' => 'This name is shown in the header and any emails.',
...@@ -27,6 +32,10 @@ return [ ...@@ -27,6 +32,10 @@ return [
27 'app_primary_color' => 'Application primary color', 32 'app_primary_color' => 'Application primary color',
28 'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.', 33 'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
29 34
35 + /**
36 + * Registration settings
37 + */
38 +
30 'reg_settings' => 'Registration Settings', 39 'reg_settings' => 'Registration Settings',
31 'reg_allow' => 'Allow registration?', 40 'reg_allow' => 'Allow registration?',
32 'reg_default_role' => 'Default user role after registration', 41 'reg_default_role' => 'Default user role after registration',
...@@ -36,4 +45,79 @@ return [ ...@@ -36,4 +45,79 @@ return [
36 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.', 45 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
37 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', 46 'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
38 47
39 -];
...\ No newline at end of file ...\ No newline at end of file
48 + /**
49 + * Role settings
50 + */
51 +
52 + 'roles' => 'Roles',
53 + 'role_user_roles' => 'User Roles',
54 + 'role_create' => 'Create New Role',
55 + 'role_create_success' => 'Role successfully created',
56 + 'role_delete' => 'Delete Role',
57 + 'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
58 + 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
59 + 'role_delete_no_migration' => "Don't migrate users",
60 + 'role_delete_sure' => 'Are you sure you want to delete this role?',
61 + 'role_delete_success' => 'Role successfully deleted',
62 + 'role_edit' => 'Edit Role',
63 + 'role_details' => 'Role Details',
64 + 'role_name' => 'Role Name',
65 + 'role_desc' => 'Short Description of Role',
66 + 'role_system' => 'System Permissions',
67 + 'role_manage_users' => 'Manage users',
68 + 'role_manage_roles' => 'Manage roles & role permissions',
69 + 'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
70 + 'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
71 + 'role_manage_settings' => 'Manage app settings',
72 + 'role_asset' => 'Asset Permissions',
73 + 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
74 + 'role_all' => 'All',
75 + 'role_own' => 'Own',
76 + 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
77 + 'role_save' => 'Save Role',
78 + 'role_update_success' => 'Role successfully updated',
79 + 'role_users' => 'Users in this role',
80 + 'role_users_none' => 'No users are currently assigned to this role',
81 +
82 + /**
83 + * Users
84 + */
85 +
86 + 'users' => 'Users',
87 + 'user_profile' => 'User Profile',
88 + 'users_add_new' => 'Add New User',
89 + 'users_search' => 'Search Users',
90 + 'users_role' => 'User Roles',
91 + 'users_external_auth_id' => 'External Authentication ID',
92 + 'users_password_warning' => 'Only fill the below if you would like to change your password:',
93 + 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
94 + 'users_delete' => 'Delete User',
95 + 'users_delete_named' => 'Delete user :userName',
96 + 'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
97 + 'users_delete_confirm' => 'Are you sure you want to delete this user?',
98 + 'users_delete_success' => 'Users successfully removed',
99 + 'users_edit' => 'Edit User',
100 + 'users_edit_profile' => 'Edit Profile',
101 + 'users_edit_success' => 'User successfully updated',
102 + 'users_avatar' => 'User Avatar',
103 + 'users_avatar_desc' => 'This image should be approx 256px square.',
104 + 'users_preferred_language' => 'Preferred Language',
105 + 'users_social_accounts' => 'Social Accounts',
106 + 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
107 + 'users_social_connect' => 'Connect Account',
108 + 'users_social_disconnect' => 'Disconnect Account',
109 + 'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
110 + 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
111 +
112 + // Since these labels are already localized this array does not need to be
113 + // translated in the language-specific files.
114 + // DELETE BELOW IF COPIED FROM EN
115 + ///////////////////////////////////
116 + 'language_select' => [
117 + 'en' => 'English',
118 + 'de' => 'Deutsch',
119 + 'fr' => 'Français',
120 + 'pt_BR' => 'Português do Brasil'
121 + ]
122 + ///////////////////////////////////
123 +];
......
...@@ -87,8 +87,8 @@ return [ ...@@ -87,8 +87,8 @@ return [
87 */ 87 */
88 88
89 'custom' => [ 89 'custom' => [
90 - 'attribute-name' => [ 90 + 'password-confirm' => [
91 - 'rule-name' => 'custom-message', 91 + 'required_with' => 'Password confirmation required',
92 ], 92 ],
93 ], 93 ],
94 94
......
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' => 'a créé la page',
12 + 'page_create_notification' => 'Page créée avec succès',
13 + 'page_update' => 'a modifié la page',
14 + 'page_update_notification' => 'Page modifiée avec succès',
15 + 'page_delete' => 'a supprimé la page',
16 + 'page_delete_notification' => 'Page supprimée avec succès',
17 + 'page_restore' => 'a restauré la page',
18 + 'page_restore_notification' => 'Page réstaurée avec succès',
19 + 'page_move' => 'a déplacé la page',
20 +
21 + // Chapters
22 + 'chapter_create' => 'a créé le chapitre',
23 + 'chapter_create_notification' => 'Chapitre créé avec succès',
24 + 'chapter_update' => 'a modifié le chapitre',
25 + 'chapter_update_notification' => 'Chapitre modifié avec succès',
26 + 'chapter_delete' => 'a supprimé le chapitre',
27 + 'chapter_delete_notification' => 'Chapitre supprimé avec succès',
28 + 'chapter_move' => 'a déplacé le chapitre',
29 +
30 + // Books
31 + 'book_create' => 'a créé le livre',
32 + 'book_create_notification' => 'Livre créé avec succès',
33 + 'book_update' => 'a modifié le livre',
34 + 'book_update_notification' => 'Livre modifié avec succès',
35 + 'book_delete' => 'a supprimé le livre',
36 + 'book_delete_notification' => 'Livre supprimé avec succès',
37 + 'book_sort' => 'a réordonné le livre',
38 + 'book_sort_notification' => 'Livre réordonné avec succès',
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' => 'Ces informations ne correspondent a aucun compte.',
14 + 'throttle' => "Trop d'essais, veuillez réessayer dans :seconds secondes.",
15 +
16 + /**
17 + * Login & Register
18 + */
19 + 'sign_up' => "S'inscrire",
20 + 'log_in' => 'Se connecter',
21 + 'logout' => 'Se déconnecter',
22 +
23 + 'name' => 'Nom',
24 + 'username' => "Nom d'utilisateur",
25 + 'email' => 'E-mail',
26 + 'password' => 'Mot de passe',
27 + 'password_confirm' => 'Confirmez le mot de passe',
28 + 'password_hint' => 'Doit faire plus de 5 caractères',
29 + 'forgot_password' => 'Mot de passe oublié?',
30 + 'remember_me' => 'Se souvenir de moi',
31 + 'ldap_email_hint' => "Merci d'entrer une adresse e-mail pour ce compte",
32 + 'create_account' => 'Créer un compte',
33 + 'social_login' => 'Social Login',
34 + 'social_registration' => 'Enregistrement Social',
35 + 'social_registration_text' => "S'inscrire et se connecter avec un réseau social",
36 +
37 + 'register_thanks' => 'Merci pour votre enregistrement',
38 + 'register_confirm' => 'Vérifiez vos e-mails et cliquer sur le lien de confirmation pour rejoindre :appName.',
39 + 'registrations_disabled' => "L'inscription est désactivée pour le moment",
40 + 'registration_email_domain_invalid' => 'Cette adresse e-mail ne peux pas adcéder à l\'application',
41 + 'register_success' => 'Merci pour votre inscription. Vous êtes maintenant inscrit(e) et connecté(e)',
42 +
43 +
44 + /**
45 + * Password Reset
46 + */
47 + 'reset_password' => 'Reset Password',
48 + 'reset_password_send_instructions' => 'Entrez votre adresse e-mail ci-dessous et un e-mail avec un lien de réinitialisation de mot de passe vous sera envoyé',
49 + 'reset_password_send_button' => 'Envoyer un lien de réinitialisation',
50 + 'reset_password_sent_success' => 'Un lien de réinitialisation a été envoyé à :email.',
51 + 'reset_password_success' => 'Votre mot de passe a été réinitialisé avec succès.',
52 +
53 + 'email_reset_subject' => 'Réinitialisez votre mot de passe pour :appName',
54 + 'email_reset_text' => 'Vous recevez cet e-mail parceque nous avons reçu une demande de réinitialisation pour votre compte',
55 + 'email_reset_not_requested' => 'Si vous n\'avez pas effectué cette demande, vous pouvez ignorer cet e-mail.',
56 +
57 +
58 + /**
59 + * Email Confirmation
60 + */
61 + 'email_confirm_subject' => 'Confirmez votre adresse e-mail pour :appName',
62 + 'email_confirm_greeting' => 'Merci d\'avoir rejoint :appName!',
63 + 'email_confirm_text' => 'Merci de confirmer en cliquant sur le lien ci-dessous:',
64 + 'email_confirm_action' => 'Confirmez votre adresse e-mail',
65 + 'email_confirm_send_error' => 'La confirmation par e-mail est requise mais le système n\'a pas pu envoyer l\'e-mail. Contactez l\'administrateur système.',
66 + 'email_confirm_success' => 'Votre adresse e-mail a été confirmée!',
67 + 'email_confirm_resent' => 'L\'e-mail de confirmation a été ré-envoyé. Vérifiez votre boîte de récéption.',
68 +
69 + 'email_not_confirmed' => 'Adresse e-mail non confirmée',
70 + 'email_not_confirmed_text' => 'Votre adresse e-mail n\'a pas été confirmée.',
71 + 'email_not_confirmed_click_link' => 'Merci de cliquer sur le lien dans l\'e-mail qui vous a été envoyé après l\'enregistrement.',
72 + 'email_not_confirmed_resend' => 'Si vous ne retrouvez plus l\'e-mail, vous pouvez renvoyer un e-mail de confirmation en utilisant le formulaire ci-dessous.',
73 + 'email_not_confirmed_resend_button' => 'Renvoyez l\'e-mail de confirmation',
74 +];
1 +<?php
2 +return [
3 +
4 + /**
5 + * Buttons
6 + */
7 + 'cancel' => 'Annuler',
8 + 'confirm' => 'Confirmer',
9 + 'back' => 'Retour',
10 + 'save' => 'Enregistrer',
11 + 'continue' => 'Continuer',
12 + 'select' => 'Selectionner',
13 +
14 + /**
15 + * Form Labels
16 + */
17 + 'name' => 'Nom',
18 + 'description' => 'Description',
19 + 'role' => 'Rôle',
20 +
21 + /**
22 + * Actions
23 + */
24 + 'actions' => 'Actions',
25 + 'view' => 'Voir',
26 + 'create' => 'Créer',
27 + 'update' => 'Modifier',
28 + 'edit' => 'Editer',
29 + 'sort' => 'Trier',
30 + 'move' => 'Déplacer',
31 + 'delete' => 'Supprimer',
32 + 'search' => 'Chercher',
33 + 'search_clear' => 'Réinitialiser la recherche',
34 + 'reset' => 'Réinitialiser',
35 + 'remove' => 'Enlever',
36 +
37 +
38 + /**
39 + * Misc
40 + */
41 + 'deleted_user' => 'Utilisateur supprimé',
42 + 'no_activity' => 'Aucune activité',
43 + 'no_items' => 'Aucun élément',
44 + 'back_to_top' => 'Retour en haut',
45 + 'toggle_details' => 'Afficher les détails',
46 +
47 + /**
48 + * Header
49 + */
50 + 'view_profile' => 'Voir le profil',
51 + 'edit_profile' => 'Modifier le profil',
52 +
53 + /**
54 + * Email Content
55 + */
56 + 'email_action_help' => 'Si vous rencontrez des problèmes pour cliquer le bouton ":actionText", copiez et collez l\'adresse ci-dessous dans votre navigateur:',
57 + 'email_rights' => 'Tous droits réservés',
58 +];
1 +<?php
2 +return [
3 +
4 + /**
5 + * Image Manager
6 + */
7 + 'image_select' => 'Selectionner une image',
8 + 'image_all' => 'Toutes',
9 + 'image_all_title' => 'Voir toutes les images',
10 + 'image_book_title' => 'Voir les images ajoutées à ce livre',
11 + 'image_page_title' => 'Voir les images ajoutées à cette page',
12 + 'image_search_hint' => 'Rechercher par nom d\'image',
13 + 'image_uploaded' => 'Ajoutée le :uploadedDate',
14 + 'image_load_more' => 'Charger plus',
15 + 'image_image_name' => 'Nom de l\'image',
16 + 'image_delete_confirm' => 'Cette image est utilisée dans les pages ci-dessous. Confirmez que vous souhaitez bien supprimer cette image.',
17 + 'image_select_image' => 'Selectionner l\'image',
18 + 'image_dropzone' => 'Glissez les images ici ou cliquez pour les ajouter',
19 + 'images_deleted' => 'Images supprimées',
20 + 'image_preview' => 'Prévisualiser l\'image',
21 + 'image_upload_success' => 'Image ajoutée avec succès',
22 + 'image_update_success' => 'Détails de l\'image mis à jour',
23 + 'image_delete_success' => 'Image supprimée avec succès'
24 +];
1 +<?php
2 +
3 +return [
4 +
5 + /**
6 + * Error text strings.
7 + */
8 +
9 + // Permissions
10 + 'permission' => 'Vous n\'avez pas les droits pour accéder à cette page.',
11 + 'permissionJson' => 'Vous n\'avez pas les droits pour exécuter cette action.',
12 +
13 + // Auth
14 + 'error_user_exists_different_creds' => 'Un utilisateur avec l\'adresse :email existe déjà.',
15 + 'email_already_confirmed' => 'Cet e-mail a déjà été validé, vous pouvez vous connecter.',
16 + 'email_confirmation_invalid' => 'Cette confirmation est invalide. Veuillez essayer de vous inscrire à nouveau.',
17 + 'email_confirmation_expired' => 'Le jeton de confirmation est perimé. Un nouvel e-mail vous a été envoyé.',
18 + 'ldap_fail_anonymous' => 'L\'accès LDAP anonyme n\'a pas abouti',
19 + 'ldap_fail_authed' => 'L\'accès LDAP n\'a pas abouti avec cet utilisateur et ce mot de passe',
20 + 'ldap_extension_not_installed' => 'L\'extention LDAP PHP n\'est pas installée',
21 + 'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
22 + 'social_no_action_defined' => 'No action defined',
23 + 'social_account_in_use' => 'Cet compte :socialAccount est déjà utilisé. Essayez de vous connecter via :socialAccount.',
24 + 'social_account_email_in_use' => 'L\'email :email Est déjà utilisé. Si vous avez déjà un compte :socialAccount, vous pouvez le joindre à votre profil existant.',
25 + 'social_account_existing' => 'Ce compte :socialAccount est déjà rattaché à votre profil.',
26 + 'social_account_already_used_existing' => 'Ce compte :socialAccount est déjà utilisé par un autre utilisateur.',
27 + 'social_account_not_used' => 'Ce compte :socialAccount n\'est lié à aucun utilisateur. ',
28 + 'social_account_register_instructions' => 'Si vous n\'avez pas encore de compte, vous pouvez le lier avec l\'option :socialAccount.',
29 + 'social_driver_not_found' => 'Social driver not found',
30 + 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
31 +
32 + // System
33 + 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
34 + 'cannot_get_image_from_url' => 'Impossible de récupérer l\'image depuis :url',
35 + 'cannot_create_thumbs' => 'Le serveur ne peux pas créer de miniatures, vérifier que l\extensions GD PHP est installée.',
36 + 'server_upload_limit' => 'La taille du fichier est trop grande.',
37 + 'image_upload_error' => 'Une erreur est survenue pendant l\'envoi de l\'image',
38 +
39 + // Attachments
40 + 'attachment_page_mismatch' => 'Page mismatch during attachment update',
41 +
42 + // Pages
43 + 'page_draft_autosave_fail' => 'Le brouillon n\'a pas pu être sauvé. Vérifiez votre connexion internet',
44 +
45 + // Entities
46 + 'entity_not_found' => 'Entité non trouvée',
47 + 'book_not_found' => 'Livre non trouvé',
48 + 'page_not_found' => 'Page non trouvée',
49 + 'chapter_not_found' => 'Chapitre non trouvé',
50 + 'selected_book_not_found' => 'Ce livre n\'a pas été trouvé',
51 + 'selected_book_chapter_not_found' => 'Ce livre ou chapitre n\'a pas été trouvé',
52 + 'guests_cannot_save_drafts' => 'Les invités ne peuvent pas sauver de brouillons',
53 +
54 + // Users
55 + 'users_cannot_delete_only_admin' => 'Vous ne pouvez pas supprimer le dernier admin',
56 + 'users_cannot_delete_guest' => 'Vous ne pouvez pas supprimer l\'utilisateur invité',
57 +
58 + // Roles
59 + 'role_cannot_be_edited' => 'Ce rôle ne peut pas être modifié',
60 + 'role_system_cannot_be_deleted' => 'Ceci est un rôle du système et on ne peut pas le supprimer',
61 + 'role_registration_default_cannot_delete' => 'Ce rôle ne peut pas être supprimé tant qu\'il est le rôle par défaut',
62 +
63 + // Error pages
64 + '404_page_not_found' => 'Page non trouvée',
65 + 'sorry_page_not_found' => 'Désolé, cette page n\'a pas pu être trouvée.',
66 + 'return_home' => 'Retour à l\'accueil',
67 + 'error_occurred' => 'Une erreur est survenue',
68 + 'app_down' => ':appName n\'est pas en service pour le moment',
69 + 'back_soon' => 'Nous serons bientôt de retour.',
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; Précédent',
17 + 'next' => 'Suivant &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' => 'Les mots de passe doivent faire au moins 6 caractères et correspondre à la confirmation.',
17 + 'user' => "Nous n'avons pas trouvé d'utilisateur avec cette adresse.",
18 + 'token' => 'Le jeton de réinitialisation est invalide.',
19 + 'sent' => 'Nous vous avons envoyé un lien de réinitialisation de mot de passe!',
20 + 'reset' => 'Votre mot de passe a été réinitialisé!',
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' => 'Préférences',
12 + 'settings_save' => 'Enregistrer les préférences',
13 + 'settings_save_success' => 'Préférences enregistrées',
14 +
15 + /**
16 + * App settings
17 + */
18 +
19 + 'app_settings' => 'Préférences de l\'application',
20 + 'app_name' => 'Nom de l\'application',
21 + 'app_name_desc' => 'Ce nom est affiché dans l\'en-tête et les e-mails.',
22 + 'app_name_header' => 'Afficher le nom dans l\'en-tête?',
23 + 'app_public_viewing' => 'Accepter le visionnage public des pages?',
24 + 'app_secure_images' => 'Activer l\'ajout d\'image sécurisé?',
25 + 'app_secure_images_desc' => 'Pour des questions de performances, toutes les images sont publiques. Cette option ajoute une chaîne aléatoire difficile à deviner dans les URLs des images.',
26 + 'app_editor' => 'Editeur des pages',
27 + 'app_editor_desc' => 'Sélectionnez l\'éditeur qui sera utilisé pour modifier les pages.',
28 + 'app_custom_html' => 'HTML personnalisé dans l\'en-tête',
29 + 'app_custom_html_desc' => 'Le contenu inséré ici sera jouté en bas de la balise <head> de toutes les pages. Vous pouvez l\'utiliser pour ajouter du CSS personnalisé ou un tracker analytique.',
30 + 'app_logo' => 'Logo de l\'Application',
31 + 'app_logo_desc' => 'Cette image doit faire 43px de hauteur. <br>Les images plus larges seront réduites.',
32 + 'app_primary_color' => 'Couleur principale de l\'application',
33 + 'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
34 +
35 + /**
36 + * Registration settings
37 + */
38 +
39 + 'reg_settings' => 'Préférence pour l\'inscription',
40 + 'reg_allow' => 'Accepter l\'inscription?',
41 + 'reg_default_role' => 'Rôle par défaut lors de l\'inscription',
42 + 'reg_confirm_email' => 'Obliger la confirmation par e-mail?',
43 + 'reg_confirm_email_desc' => 'Si la restriction de domaine est activée, la confirmation sera automatiquement obligatoire et cette valeur sera ignorée.',
44 + 'reg_confirm_restrict_domain' => 'Restreindre l\'inscription à un domaine',
45 + 'reg_confirm_restrict_domain_desc' => 'Entrez une liste de domaines acceptés lors de l\'inscription, séparés par une virgule. Les utilisateur recevront un e-mail de confirmation à cette adresse. <br> Les utilisateurs pourront changer leur adresse après inscription s\'ils le souhaitent.',
46 + 'reg_confirm_restrict_domain_placeholder' => 'Aucune restriction en place',
47 +
48 + /**
49 + * Role settings
50 + */
51 +
52 + 'roles' => 'Rôles',
53 + 'role_user_roles' => 'Rôles des utilisateurs',
54 + 'role_create' => 'Créer un nouveau rôle',
55 + 'role_create_success' => 'Rôle créé avec succès',
56 + 'role_delete' => 'Supprimer le rôle',
57 + 'role_delete_confirm' => 'Ceci va supprimer le rôle \':roleName\'.',
58 + 'role_delete_users_assigned' => 'Ce rôle a :userCount utilisateurs assignés. Vous pouvez choisir un rôle de remplacement pour ces utilisateurs.',
59 + 'role_delete_no_migration' => "Ne pas assigner de nouveau rôle",
60 + 'role_delete_sure' => 'Êtes vous sûr(e) de vouloir supprimer ce rôle?',
61 + 'role_delete_success' => 'Le rôle a été supprimé avec succès',
62 + 'role_edit' => 'Modifier le rôle',
63 + 'role_details' => 'Détails du rôle',
64 + 'role_name' => 'Nom du Rôle',
65 + 'role_desc' => 'Courte description du rôle',
66 + 'role_system' => 'Permissions système',
67 + 'role_manage_users' => 'Gérer les utilisateurs',
68 + 'role_manage_roles' => 'Gérer les rôles et permissions',
69 + 'role_manage_entity_permissions' => 'Gérer les permissions sur les livres, chapitres et pages',
70 + 'role_manage_own_entity_permissions' => 'Gérer les permissions de ses propres livres chapitres et pages',
71 + 'role_manage_settings' => 'Gérer les préférences de l\'application',
72 + 'role_asset' => 'Asset Permissions',
73 + 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
74 + 'role_all' => 'Tous',
75 + 'role_own' => 'Propres',
76 + 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
77 + 'role_save' => 'Enregistrer le rôle',
78 + 'role_update_success' => 'Rôle mis à jour avec succès',
79 + 'role_users' => 'Utilisateurs ayant ce rôle',
80 + 'role_users_none' => 'Aucun utilisateur avec ce rôle actuellement',
81 +
82 + /**
83 + * Users
84 + */
85 +
86 + 'users' => 'Utilisateurs',
87 + 'user_profile' => 'Profil d\'utilisateur',
88 + 'users_add_new' => 'Ajouter un nouvel utilisateur',
89 + 'users_search' => 'Chercher les utilisateurs',
90 + 'users_role' => 'Rôles des utilisateurs',
91 + 'users_external_auth_id' => 'Identifiant d\'authentification externe',
92 + 'users_password_warning' => 'Remplissez ce fomulaire uniquement si vous souhaitez changer de mot de passe:',
93 + 'users_system_public' => 'Cet utilisateur représente les invités visitant votre instance. Il est assigné automatiquement aux invités.',
94 + 'users_delete' => 'Supprimer un utilisateur',
95 + 'users_delete_named' => 'Supprimer l\'utilisateur :userName',
96 + 'users_delete_warning' => 'Ceci va supprimer \':userName\' du système.',
97 + 'users_delete_confirm' => 'Êtes-vous sûr(e) de vouloir supprimer cet utilisateur?',
98 + 'users_delete_success' => 'Utilisateurs supprimés avec succès',
99 + 'users_edit' => 'Modifier l\'utilisateur',
100 + 'users_edit_profile' => 'Modifier le profil',
101 + 'users_edit_success' => 'Utilisateur mis à jour avec succès',
102 + 'users_avatar' => 'Avatar de l\'utilisateur',
103 + 'users_avatar_desc' => 'Cette image doit être un carré d\'environ 256px.',
104 + 'users_preferred_language' => 'Langue préférée',
105 + 'users_social_accounts' => 'Comptes sociaux',
106 + 'users_social_accounts_info' => 'Vous pouvez connecter des réseaux sociaux à votre compte pour vous connecter plus rapidement. Déconnecter un compte n\'enlèvera pas les accès autorisés précédemment sur votre compte de réseau social.',
107 + 'users_social_connect' => 'Connecter le compte',
108 + 'users_social_disconnect' => 'Déconnecter le compte',
109 + 'users_social_connected' => 'Votre compte :socialAccount a élté ajouté avec succès.',
110 + 'users_social_disconnected' => 'Votre compte :socialAccount a été déconnecté avec succès',
111 +
112 +];
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 doit être accepté.',
17 + 'active_url' => ':attribute n\'est pas une URL valide.',
18 + 'after' => ':attribute doit être supérieur à :date.',
19 + 'alpha' => ':attribute ne doit contenir que des lettres.',
20 + 'alpha_dash' => ':attribute doit contenir uniquement des lettres, chiffres et traits d\'union.',
21 + 'alpha_num' => ':attribute doit contenir uniquement des chiffres et des lettres.',
22 + 'array' => ':attribute doit être un tableau.',
23 + 'before' => ':attribute doit être inférieur à :date.',
24 + 'between' => [
25 + 'numeric' => ':attribute doit être compris entre :min et :max.',
26 + 'file' => ':attribute doit être compris entre :min et :max kilobytes.',
27 + 'string' => ':attribute doit être compris entre :min et :max caractères.',
28 + 'array' => ':attribute doit être compris entre :min et :max éléments.',
29 + ],
30 + 'boolean' => ':attribute doit être vrai ou faux.',
31 + 'confirmed' => ':attribute la confirmation n\'est pas valide.',
32 + 'date' => ':attribute n\'est pas une date valide.',
33 + 'date_format' => ':attribute ne correspond pas au format :format.',
34 + 'different' => ':attribute et :other doivent être différents l\'un de l\'autre.',
35 + 'digits' => ':attribute doit être de longueur :digits.',
36 + 'digits_between' => ':attribute doit avoir une longueur entre :min et :max.',
37 + 'email' => ':attribute doit être une adresse e-mail valide.',
38 + 'filled' => ':attribute est un champ requis.',
39 + 'exists' => 'L\'attribut :attribute est invalide.',
40 + 'image' => ':attribute doit être une image.',
41 + 'in' => 'L\'attribut :attribute est invalide.',
42 + 'integer' => ':attribute doit être un chiffre entier.',
43 + 'ip' => ':attribute doit être une adresse IP valide.',
44 + 'max' => [
45 + 'numeric' => ':attribute ne doit pas excéder :max.',
46 + 'file' => ':attribute ne doit pas excéder :max kilobytes.',
47 + 'string' => ':attribute ne doit pas excéder :max caractères.',
48 + 'array' => ':attribute ne doit pas contenir plus de :max éléments.',
49 + ],
50 + 'mimes' => ':attribute doit être un fichier de type :values.',
51 + 'min' => [
52 + 'numeric' => ':attribute doit être au moins :min.',
53 + 'file' => ':attribute doit faire au moins :min kilobytes.',
54 + 'string' => ':attribute doit contenir au moins :min caractères.',
55 + 'array' => ':attribute doit contenir au moins :min éléments.',
56 + ],
57 + 'not_in' => 'L\'attribut sélectionné :attribute est invalide.',
58 + 'numeric' => ':attribute doit être un nombre.',
59 + 'regex' => ':attribute a un format invalide.',
60 + 'required' => ':attribute est un champ requis.',
61 + 'required_if' => ':attribute est requis si :other est :value.',
62 + 'required_with' => ':attribute est requis si :values est présent.',
63 + 'required_with_all' => ':attribute est requis si :values est présent.',
64 + 'required_without' => ':attribute est requis si:values n\'est pas présent.',
65 + 'required_without_all' => ':attribute est requis si aucun des valeurs :values n\'est présente.',
66 + 'same' => ':attribute et :other doivent être identiques.',
67 + 'size' => [
68 + 'numeric' => ':attribute doit avoir la taille :size.',
69 + 'file' => ':attribute doit peser :size kilobytes.',
70 + 'string' => ':attribute doit contenir :size caractères.',
71 + 'array' => ':attribute doit contenir :size éléments.',
72 + ],
73 + 'string' => ':attribute doit être une chaîne de caractères.',
74 + 'timezone' => ':attribute doit être une zone valide.',
75 + 'unique' => ':attribute est déjà utilisé.',
76 + 'url' => ':attribute a un format invalide.',
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' => 'La confirmation du mot de passe est requise',
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 +];
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' => 'página criada',
12 + 'page_create_notification' => 'Página criada com sucesso',
13 + 'page_update' => 'página atualizada',
14 + 'page_update_notification' => 'Página atualizada com sucesso',
15 + 'page_delete' => 'página excluída',
16 + 'page_delete_notification' => 'Página excluída com sucesso',
17 + 'page_restore' => 'página restaurada',
18 + 'page_restore_notification' => 'Página restaurada com sucesso',
19 + 'page_move' => 'página movida',
20 +
21 + // Chapters
22 + 'chapter_create' => 'capítulo criado',
23 + 'chapter_create_notification' => 'Capítulo criado com sucesso',
24 + 'chapter_update' => 'capítulo atualizado',
25 + 'chapter_update_notification' => 'capítulo atualizado com sucesso',
26 + 'chapter_delete' => 'capítulo excluído',
27 + 'chapter_delete_notification' => 'Capítulo excluído com sucesso',
28 + 'chapter_move' => 'capitulo movido',
29 +
30 + // Books
31 + 'book_create' => 'livro criado',
32 + 'book_create_notification' => 'Livro criado com sucesso',
33 + 'book_update' => 'livro atualizado',
34 + 'book_update_notification' => 'Livro atualizado com sucesso',
35 + 'book_delete' => 'livro excluído',
36 + 'book_delete_notification' => 'Livro excluído com sucesso',
37 + 'book_sort' => 'livro classificado',
38 + 'book_sort_notification' => 'Livro reclassificado com sucesso',
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' => 'As credenciais fornecidas não puderam ser validadas em nossos registros..',
14 + 'throttle' => 'Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.',
15 +
16 + /**
17 + * Login & Register
18 + */
19 + 'sign_up' => 'Registrar-se',
20 + 'log_in' => 'Entrar',
21 + 'logout' => 'Sair',
22 +
23 + 'name' => 'Nome',
24 + 'username' => 'Nome de Usuário',
25 + 'email' => 'E-mail',
26 + 'password' => 'Senha',
27 + 'password_confirm' => 'Confirmar Senha',
28 + 'password_hint' => 'Senha deverá ser maior que 5 caracteres',
29 + 'forgot_password' => 'Esqueceu a senha?',
30 + 'remember_me' => 'Lembrar de mim',
31 + 'ldap_email_hint' => 'Por favor, digite um e-mail para essa conta.',
32 + 'create_account' => 'Criar conta',
33 + 'social_login' => 'Login social',
34 + 'social_registration' => 'Registro social',
35 + 'social_registration_text' => 'Registre e entre usando outro serviço.',
36 +
37 + 'register_thanks' => 'Obrigado por efetuar o registro!',
38 + 'register_confirm' => 'Por favor, verifique seu e-mail e clique no botão de confirmação para acessar :appName.',
39 + 'registrations_disabled' => 'Registros estão temporariamente desabilitados',
40 + 'registration_email_domain_invalid' => 'O domínio de e-mail usado não tem acesso permitido a essa aplicação',
41 + 'register_success' => 'Obrigado por se registrar! Você agora encontra-se registrado e logado..',
42 +
43 +
44 + /**
45 + * Password Reset
46 + */
47 + 'reset_password' => 'Resetar senha',
48 + 'reset_password_send_instructions' => 'Digite seu e-mail abaixo e o sistema enviará uma mensagem com o link de reset de senha.',
49 + 'reset_password_send_button' => 'Enviar o link de reset de senha',
50 + 'reset_password_sent_success' => 'Um link de reset de senha foi enviado para :email.',
51 + 'reset_password_success' => 'Sua senha foi resetada com sucesso.',
52 +
53 + 'email_reset_subject' => 'Resetar a senha de :appName',
54 + 'email_reset_text' => 'Você recebeu esse e-mail pois recebemos uma solicitação de reset de senha para sua conta.',
55 + 'email_reset_not_requested' => 'Caso não tenha sido você a solicitar o reset de senha, ignore esse e-mail.',
56 +
57 +
58 + /**
59 + * Email Confirmation
60 + */
61 + 'email_confirm_subject' => 'Confirme seu e-mail para :appName',
62 + 'email_confirm_greeting' => 'Obrigado por se registrar em :appName!',
63 + 'email_confirm_text' => 'Por favor, confirme seu endereço de e-mail clicando no botão abaixo:',
64 + 'email_confirm_action' => 'Confirmar E-mail',
65 + 'email_confirm_send_error' => 'E-mail de confirmação é requerido, mas o sistema não pôde enviar a mensagem. Por favor, entre em contato com o admin para se certificar que o serviço de envio de e-mails está corretamente configurado.',
66 + 'email_confirm_success' => 'Seu e-mail foi confirmado!',
67 + 'email_confirm_resent' => 'E-mail de confirmação reenviado. Por favor, cheque sua caixa postal.',
68 +
69 + 'email_not_confirmed' => 'Endereço de e-mail não foi confirmado',
70 + 'email_not_confirmed_text' => 'Seu endereço de e-mail ainda não foi confirmado.',
71 + 'email_not_confirmed_click_link' => 'Por favor, clique no link no e-mail que foi enviado após o registro.',
72 + 'email_not_confirmed_resend' => 'Caso não encontre o e-mail você poderá reenviar a confirmação usando o formulário abaixo.',
73 + 'email_not_confirmed_resend_button' => 'Reenviar o e-mail de confirmação',
74 +];
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +return [
3 +
4 + /**
5 + * Buttons
6 + */
7 + 'cancel' => 'Cancelar',
8 + 'confirm' => 'Confirmar',
9 + 'back' => 'Voltar',
10 + 'save' => 'Salvar',
11 + 'continue' => 'Continuar',
12 + 'select' => 'Selecionar',
13 +
14 + /**
15 + * Form Labels
16 + */
17 + 'name' => 'Nome',
18 + 'description' => 'Descrição',
19 + 'role' => 'Regra',
20 +
21 + /**
22 + * Actions
23 + */
24 + 'actions' => 'Ações',
25 + 'view' => 'Visualizar',
26 + 'create' => 'Criar',
27 + 'update' => 'Atualizar',
28 + 'edit' => 'Editar',
29 + 'sort' => 'Ordenar',
30 + 'move' => 'Mover',
31 + 'delete' => 'Excluir',
32 + 'search' => 'Pesquisar',
33 + 'search_clear' => 'Limpar Pesquisa',
34 + 'reset' => 'Resetar',
35 + 'remove' => 'Remover',
36 +
37 +
38 + /**
39 + * Misc
40 + */
41 + 'deleted_user' => 'Usuário excluído',
42 + 'no_activity' => 'Nenhuma atividade a mostrar',
43 + 'no_items' => 'Nenhum item disponível',
44 + 'back_to_top' => 'Voltar ao topo',
45 + 'toggle_details' => 'Alternar Detalhes',
46 +
47 + /**
48 + * Header
49 + */
50 + 'view_profile' => 'Visualizar Perfil',
51 + 'edit_profile' => 'Editar Perfil',
52 +
53 + /**
54 + * Email Content
55 + */
56 + 'email_action_help' => 'Se você estiver tendo problemas ao clicar o botão ":actionText", copie e cole a URL abaixo no seu navegador:',
57 + 'email_rights' => 'Todos os direitos reservados',
58 +];
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +return [
3 +
4 + /**
5 + * Image Manager
6 + */
7 + 'image_select' => 'Selecionar imagem',
8 + 'image_all' => 'Todos',
9 + 'image_all_title' => 'Visualizar todas as imagens',
10 + 'image_book_title' => 'Visualizar imagens relacionadas a esse livro',
11 + 'image_page_title' => 'visualizar imagens relacionadas a essa página',
12 + 'image_search_hint' => 'Pesquisar imagem por nome',
13 + 'image_uploaded' => 'Carregado :uploadedDate',
14 + 'image_load_more' => 'Carregar Mais',
15 + 'image_image_name' => 'Nome da Imagem',
16 + 'image_delete_confirm' => 'Essa imagem é usada nas páginas abaixo. Clique em Excluir novamente para confirmar que você deseja mesmo eliminar a imagem.',
17 + 'image_select_image' => 'Selecionar Imagem',
18 + 'image_dropzone' => 'Arraste imagens ou clique aqui para fazer upload',
19 + 'images_deleted' => 'Imagens excluídas',
20 + 'image_preview' => 'Virtualização de Imagem',
21 + 'image_upload_success' => 'Upload de imagem efetuado com sucesso',
22 + 'image_update_success' => 'Upload de detalhes da imagem efetuado com sucesso',
23 + 'image_delete_success' => 'Imagem excluída com sucesso'
24 +];
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +
3 +return [
4 +
5 + /**
6 + * Error text strings.
7 + */
8 +
9 + // Permissions
10 + 'permission' => 'Você não tem permissões para acessar a página requerida.',
11 + 'permissionJson' => 'Você não tem permissão para realizar a ação requerida.',
12 +
13 + // Auth
14 + 'error_user_exists_different_creds' => 'Um usuário com o e-mail :email já existe mas com credenciais diferentes.',
15 + 'email_already_confirmed' => 'E-mail já foi confirmado. Tente efetuar o login.',
16 + 'email_confirmation_invalid' => 'Esse token de confirmação não é válido ou já foi utilizado. Por favor, tente efetuar o registro novamente.',
17 + 'email_confirmation_expired' => 'O token de confirmação já expirou. Um novo e-mail foi enviado.',
18 + 'ldap_fail_anonymous' => 'O acesso LDAP falhou ao tentar usar o anonymous bind',
19 + 'ldap_fail_authed' => 'O acesso LDAPfalou ao tentar os detalhes do dn e senha fornecidos',
20 + 'ldap_extension_not_installed' => 'As extensões LDAP PHP não estão instaladas',
21 + 'ldap_cannot_connect' => 'Não foi possível conectar ao servidor LDAP. Conexão inicial falhou',
22 + 'social_no_action_defined' => 'Nenhuma ação definida',
23 + 'social_account_in_use' => 'Essa conta :socialAccount já está em uso. Por favor, tente se logar usando a opção :socialAccount',
24 + 'social_account_email_in_use' => 'O e-mail :email já está e muso. Se você já tem uma conta você poderá se conectar a conta :socialAccount a partir das configurações de seu perfil.',
25 + 'social_account_existing' => 'Essa conta :socialAccount já está atrelada a esse perfil.',
26 + 'social_account_already_used_existing' => 'Essa conta :socialAccount já está sendo usada por outro usuário.',
27 + 'social_account_not_used' => 'Essa conta :socialAccount não está atrelada a nenhum usuário. Por favor, faça o link da conta com suas configurações de perfil. ',
28 + 'social_account_register_instructions' => 'Se você não tem uma conta, você poderá fazer o registro usando a opção :socialAccount',
29 + 'social_driver_not_found' => 'Social driver não encontrado',
30 + 'social_driver_not_configured' => 'Seus parâmetros socials de :socialAccount não estão configurados corretamente.',
31 +
32 + // System
33 + 'path_not_writable' => 'O caminho de destino (:filePath) de upload de arquivo não possui permissão de escrita. Certifique-se que ele possui direitos de escrita no servidor.',
34 + 'cannot_get_image_from_url' => 'Não foi possivel capturar a imagem a partir de :url',
35 + 'cannot_create_thumbs' => 'O servidor não pôde criar as miniaturas de imagem. Por favor, verifique se a extensão GD PHP está instalada.',
36 + 'server_upload_limit' => 'O servidor não permite o upload de arquivos com esse tamanho. Por favor, tente fazer o upload de arquivos de menor tamanho.',
37 + 'image_upload_error' => 'Um erro aconteceu enquanto o servidor tentava efetuar o upload da imagem',
38 +
39 + // Attachments
40 + 'attachment_page_mismatch' => 'Erro de \'Page mismatch\' durante a atualização do anexo',
41 +
42 + // Pages
43 + 'page_draft_autosave_fail' => 'Falou ao tentar salvar o rascunho. Certifique-se que a conexão de internet está funcional antes de tentar salvar essa página',
44 +
45 + // Entities
46 + 'entity_not_found' => 'Entidade não encontrada',
47 + 'book_not_found' => 'Livro não encontrado',
48 + 'page_not_found' => 'Página não encontrada',
49 + 'chapter_not_found' => 'Capítulo não encontrado',
50 + 'selected_book_not_found' => 'O livro selecionado não foi encontrado',
51 + 'selected_book_chapter_not_found' => 'O Livro selecionado ou Capítulo não foi encontrado',
52 + 'guests_cannot_save_drafts' => 'Convidados não podem salvar rascunhos',
53 +
54 + // Users
55 + 'users_cannot_delete_only_admin' => 'Você não pode excluir o conteúdo, apenas o admin.',
56 + 'users_cannot_delete_guest' => 'Você não pode excluir o usuário convidado',
57 +
58 + // Roles
59 + 'role_cannot_be_edited' => 'Esse perfil não poed ser editado',
60 + 'role_system_cannot_be_deleted' => 'Esse perfil é um perfil de sistema e não pode ser excluído',
61 + 'role_registration_default_cannot_delete' => 'Esse perfil não poderá se excluído enquando estiver registrado como o perfil padrão',
62 +
63 + // Error pages
64 + '404_page_not_found' => 'Página não encontrada',
65 + 'sorry_page_not_found' => 'Desculpe, a página que você está procurando não pôde ser encontrada.',
66 + 'return_home' => 'Retornar à página principal',
67 + 'error_occurred' => 'Um erro ocorreu',
68 + 'app_down' => ':appName está fora do ar no momento',
69 + 'back_soon' => 'Voltaremos em seguida.',
70 +];
...\ No newline at end of file ...\ No newline at end of file
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; Anterior',
17 + 'next' => 'Próximo &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' => 'Senhas devem ter ao menos 6 caraceres e combinar com os atributos mínimos para a senha.',
17 + 'user' => "Não pudemos encontrar um usuário com o e-mail fornecido.",
18 + 'token' => 'O token de reset de senha é inválido.',
19 + 'sent' => 'Enviamos para seu e-mail o link de reset de senha!',
20 + 'reset' => 'Sua senha foi resetada com sucesso!',
21 +
22 +];