Abijeet Patro
Committed by GitHub

Merge pull request #5 from BookStackApp/master

Getting the latest from Bookstack to push the typo-fix
Showing 159 changed files with 1279 additions and 1150 deletions
...@@ -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
......
...@@ -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 {
......
...@@ -44,7 +44,7 @@ class UserController extends Controller ...@@ -44,7 +44,7 @@ class UserController extends Controller
44 'sort' => $request->has('sort') ? $request->get('sort') : 'name', 44 'sort' => $request->has('sort') ? $request->get('sort') : 'name',
45 ]; 45 ];
46 $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails); 46 $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
47 - $this->setPageTitle('Users'); 47 + $this->setPageTitle(trans('settings.users'));
48 $users->appends($listDetails); 48 $users->appends($listDetails);
49 return view('users/index', ['users' => $users, 'listDetails' => $listDetails]); 49 return view('users/index', ['users' => $users, 'listDetails' => $listDetails]);
50 } 50 }
...@@ -83,7 +83,6 @@ class UserController extends Controller ...@@ -83,7 +83,6 @@ class UserController extends Controller
83 } 83 }
84 $this->validate($request, $validationRules); 84 $this->validate($request, $validationRules);
85 85
86 -
87 $user = $this->user->fill($request->all()); 86 $user = $this->user->fill($request->all());
88 87
89 if ($authMethod === 'standard') { 88 if ($authMethod === 'standard') {
...@@ -131,7 +130,7 @@ class UserController extends Controller ...@@ -131,7 +130,7 @@ class UserController extends Controller
131 $authMethod = ($user->system_name) ? 'system' : config('auth.method'); 130 $authMethod = ($user->system_name) ? 'system' : config('auth.method');
132 131
133 $activeSocialDrivers = $socialAuthService->getActiveDrivers(); 132 $activeSocialDrivers = $socialAuthService->getActiveDrivers();
134 - $this->setPageTitle('User Profile'); 133 + $this->setPageTitle(trans('settings.user_profile'));
135 $roles = $this->userRepo->getAllRoles(); 134 $roles = $this->userRepo->getAllRoles();
136 return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]); 135 return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
137 } 136 }
...@@ -154,8 +153,6 @@ class UserController extends Controller ...@@ -154,8 +153,6 @@ class UserController extends Controller
154 'email' => 'min:2|email|unique:users,email,' . $id, 153 'email' => 'min:2|email|unique:users,email,' . $id,
155 'password' => 'min:5|required_with:password_confirm', 154 'password' => 'min:5|required_with:password_confirm',
156 'password-confirm' => 'same:password|required_with:password' 155 'password-confirm' => 'same:password|required_with:password'
157 - ], [
158 - 'password-confirm.required_with' => 'Password confirmation required'
159 ]); 156 ]);
160 157
161 $user = $this->user->findOrFail($id); 158 $user = $this->user->findOrFail($id);
...@@ -179,7 +176,7 @@ class UserController extends Controller ...@@ -179,7 +176,7 @@ class UserController extends Controller
179 } 176 }
180 177
181 $user->save(); 178 $user->save();
182 - session()->flash('success', 'User successfully updated'); 179 + session()->flash('success', trans('settings.users_edit_success'));
183 180
184 $redirectUrl = userCan('users-manage') ? '/settings/users' : '/settings/users/' . $user->id; 181 $redirectUrl = userCan('users-manage') ? '/settings/users' : '/settings/users/' . $user->id;
185 return redirect($redirectUrl); 182 return redirect($redirectUrl);
...@@ -197,7 +194,7 @@ class UserController extends Controller ...@@ -197,7 +194,7 @@ class UserController extends Controller
197 }); 194 });
198 195
199 $user = $this->user->findOrFail($id); 196 $user = $this->user->findOrFail($id);
200 - $this->setPageTitle('Delete User ' . $user->name); 197 + $this->setPageTitle(trans('settings.users_delete_named', ['userName' => $user->name]));
201 return view('users/delete', ['user' => $user]); 198 return view('users/delete', ['user' => $user]);
202 } 199 }
203 200
...@@ -216,17 +213,17 @@ class UserController extends Controller ...@@ -216,17 +213,17 @@ class UserController extends Controller
216 $user = $this->userRepo->getById($id); 213 $user = $this->userRepo->getById($id);
217 214
218 if ($this->userRepo->isOnlyAdmin($user)) { 215 if ($this->userRepo->isOnlyAdmin($user)) {
219 - session()->flash('error', 'You cannot delete the only admin'); 216 + session()->flash('error', trans('errors.users_cannot_delete_only_admin'));
220 return redirect($user->getEditUrl()); 217 return redirect($user->getEditUrl());
221 } 218 }
222 219
223 if ($user->system_name === 'public') { 220 if ($user->system_name === 'public') {
224 - session()->flash('error', 'You cannot delete the guest user'); 221 + session()->flash('error', trans('errors.users_cannot_delete_guest'));
225 return redirect($user->getEditUrl()); 222 return redirect($user->getEditUrl());
226 } 223 }
227 224
228 $this->userRepo->destroy($user); 225 $this->userRepo->destroy($user);
229 - session()->flash('success', 'User successfully removed'); 226 + session()->flash('success', trans('settings.users_delete_success'));
230 227
231 return redirect('/settings/users'); 228 return redirect('/settings/users');
232 } 229 }
......
...@@ -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 {
......
...@@ -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 Carbon\Carbon;
3 use Illuminate\Support\ServiceProvider; 4 use Illuminate\Support\ServiceProvider;
5 +use Validator;
4 6
5 class AppServiceProvider extends ServiceProvider 7 class AppServiceProvider extends ServiceProvider
6 { 8 {
...@@ -12,11 +14,12 @@ class AppServiceProvider extends ServiceProvider ...@@ -12,11 +14,12 @@ class AppServiceProvider extends ServiceProvider
12 public function boot() 14 public function boot()
13 { 15 {
14 // Custom validation methods 16 // Custom validation methods
15 - \Validator::extend('is_image', function($attribute, $value, $parameters, $validator) { 17 + 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']; 18 $imageMimes = ['image/png', 'image/bmp', 'image/gif', 'image/jpeg', 'image/jpg', 'image/tiff', 'image/webp'];
17 return in_array($value->getMimeType(), $imageMimes); 19 return in_array($value->getMimeType(), $imageMimes);
18 }); 20 });
19 21
22 + Carbon::setLocale(config('app.locale'));
20 } 23 }
21 24
22 /** 25 /**
......
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
...@@ -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;
5 4
6 class ExportService 5 class ExportService
...@@ -28,8 +27,13 @@ class ExportService ...@@ -28,8 +27,13 @@ class ExportService
28 { 27 {
29 $cssContent = file_get_contents(public_path('/css/export-styles.css')); 28 $cssContent = file_get_contents(public_path('/css/export-styles.css'));
30 $pageHtml = view('pages/pdf', ['page' => $page, 'css' => $cssContent])->render(); 29 $pageHtml = view('pages/pdf', ['page' => $page, 'css' => $cssContent])->render();
30 + $useWKHTML = config('snappy.pdf.binary') !== false;
31 $containedHtml = $this->containHtml($pageHtml); 31 $containedHtml = $this->containHtml($pageHtml);
32 - $pdf = \PDF::loadHTML($containedHtml); 32 + if ($useWKHTML) {
33 + $pdf = \SnappyPDF::loadHTML($containedHtml);
34 + } else {
35 + $pdf = \PDF::loadHTML($containedHtml);
36 + }
33 return $pdf->output(); 37 return $pdf->output();
34 } 38 }
35 39
......
...@@ -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,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,7 +109,7 @@ class LdapService ...@@ -109,7 +109,7 @@ 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 if specified.
...@@ -117,7 +117,7 @@ class LdapService ...@@ -117,7 +117,7 @@ class LdapService
117 $ldapConnection = $this->ldap->connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389); 117 $ldapConnection = $this->ldap->connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389);
118 118
119 if ($ldapConnection === false) { 119 if ($ldapConnection === false) {
120 - throw new LdapException('Cannot connect to ldap server, Initial connection failed'); 120 + throw new LdapException(trans('errors.ldap_cannot_connect'));
121 } 121 }
122 122
123 // Set any required options 123 // 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 /**
...@@ -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) {
...@@ -461,61 +471,77 @@ class PermissionService ...@@ -461,61 +471,77 @@ class PermissionService
461 return $q; 471 return $q;
462 } 472 }
463 473
464 - /** 474 + public function bookChildrenQuery($book_id, $filterDrafts = false) {
465 - * Add restrictions for a page query
466 - * @param $query
467 - * @param string $action
468 - * @return mixed
469 - */
470 - public function enforcePageRestrictions($query, $action = 'view')
471 - {
472 - // Prevent drafts being visible to others.
473 - $query = $query->where(function ($query) {
474 - $query->where('draft', '=', false);
475 - if ($this->currentUser()) {
476 - $query->orWhere(function ($query) {
477 - $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser()->id);
478 - });
479 - }
480 - });
481 475
482 - return $this->enforceEntityRestrictions($query, $action); 476 + // Draft setup
483 - } 477 + $params = [
484 - 478 + 'userId' => $this->currentUser()->id,
485 - /** 479 + 'bookIdPage' => $book_id,
486 - * Add on permission restrictions to a chapter query. 480 + 'bookIdChapter' => $book_id
487 - * @param $query 481 + ];
488 - * @param string $action 482 + if (!$filterDrafts) {
489 - * @return mixed 483 + $params['userIdDrafts'] = $this->currentUser()->id;
490 - */ 484 + }
491 - public function enforceChapterRestrictions($query, $action = 'view') 485 + // Role setup
492 - { 486 + $userRoles = $this->getRoles();
493 - return $this->enforceEntityRestrictions($query, $action); 487 + $roleBindings = [];
494 - } 488 + $roleValues = [];
489 + foreach ($userRoles as $index => $roleId) {
490 + $roleBindings[':role'.$index] = $roleId;
491 + $roleValues['role'.$index] = $roleId;
492 + }
493 + // TODO - Clean this up, Maybe extract into a nice class for doing these kind of manual things
494 + // Something which will handle the above role crap in a nice clean way
495 + $roleBindingString = implode(',', array_keys($roleBindings));
496 + $query = "SELECT * from (
497 +(SELECT 'Bookstack\\\Page' as entity_type, id, slug, name, text, '' as description, book_id, priority, chapter_id, draft FROM {$this->page->getTable()}
498 + where book_id = :bookIdPage AND ". ($filterDrafts ? '(draft = 0)' : '(draft = 0 OR (draft = 1 AND created_by = :userIdDrafts))') .")
499 +UNION
500 +(SELECT 'Bookstack\\\Chapter' as entity_type, id, slug, name, '' as text, description, book_id, priority, 0 as chapter_id, 0 as draft FROM {$this->chapter->getTable()} WHERE book_id = :bookIdChapter)
501 +) as U WHERE (
502 + SELECT COUNT(*) FROM {$this->jointPermission->getTable()} jp
503 + WHERE
504 + jp.entity_id=U.id AND
505 + jp.entity_type=U.entity_type AND
506 + jp.action = 'view' AND
507 + jp.role_id IN ({$roleBindingString}) AND
508 + (
509 + jp.has_permission = 1 OR
510 + (jp.has_permission_own = 1 AND jp.created_by = :userId)
511 + )
512 +) > 0
513 +ORDER BY draft desc, priority asc";
495 514
496 - /** 515 + $this->clean();
497 - * Add restrictions to a book query. 516 + return $this->db->select($query, array_replace($roleValues, $params));
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 } 517 }
506 518
507 /** 519 /**
508 * Add restrictions for a generic entity 520 * Add restrictions for a generic entity
509 - * @param $query 521 + * @param string $entityType
522 + * @param Builder|Entity $query
510 * @param string $action 523 * @param string $action
511 * @return mixed 524 * @return mixed
512 */ 525 */
513 - public function enforceEntityRestrictions($query, $action = 'view') 526 + public function enforceEntityRestrictions($entityType, $query, $action = 'view')
514 { 527 {
528 + if (strtolower($entityType) === 'page') {
529 + // Prevent drafts being visible to others.
530 + $query = $query->where(function ($query) {
531 + $query->where('draft', '=', false);
532 + if ($this->currentUser()) {
533 + $query->orWhere(function ($query) {
534 + $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser()->id);
535 + });
536 + }
537 + });
538 + }
539 +
515 if ($this->isAdmin()) { 540 if ($this->isAdmin()) {
516 $this->clean(); 541 $this->clean();
517 return $query; 542 return $query;
518 } 543 }
544 +
519 $this->currentAction = $action; 545 $this->currentAction = $action;
520 return $this->entityRestrictionQuery($query); 546 return $this->entityRestrictionQuery($query);
521 } 547 }
...@@ -601,7 +627,7 @@ class PermissionService ...@@ -601,7 +627,7 @@ class PermissionService
601 private function isAdmin() 627 private function isAdmin()
602 { 628 {
603 if ($this->isAdminUser === null) { 629 if ($this->isAdminUser === null) {
604 - $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasRole('admin') : false; 630 + $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasSystemRole('admin') : false;
605 } 631 }
606 632
607 return $this->isAdminUser; 633 return $this->isAdminUser;
......
...@@ -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
......
...@@ -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 +];
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",
......
...@@ -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"/>
......
...@@ -17,22 +17,33 @@ A platform for storing and organising information and documentation. General inf ...@@ -17,22 +17,33 @@ 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 +## Website and Docs
44 +
45 +The website and project docs are currently stored in the [BookStackApp/website](https://github.com/BookStackApp/website) repo. The docs are stored as markdown files in the `resources/docs` folder
46 +
36 ## License 47 ## License
37 48
38 BookStack is provided under the MIT License. 49 BookStack is provided under the MIT License.
...@@ -53,5 +64,11 @@ These are the great projects used to help build BookStack: ...@@ -53,5 +64,11 @@ These are the great projects used to help build BookStack:
53 * [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html) 64 * [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html)
54 * [Marked](https://github.com/chjj/marked) 65 * [Marked](https://github.com/chjj/marked)
55 * [Moment.js](http://momentjs.com/) 66 * [Moment.js](http://momentjs.com/)
67 +* [BarryVD](https://github.com/barryvdh)
68 + * [Debugbar](https://github.com/barryvdh/laravel-debugbar)
69 + * [Dompdf](https://github.com/barryvdh/laravel-dompdf)
70 + * [Snappy (WKHTML2PDF)](https://github.com/barryvdh/laravel-snappy)
71 + * [Laravel IDE helper](https://github.com/barryvdh/laravel-ide-helper)
72 +* [WKHTMLtoPDF](http://wkhtmltopdf.org/index.html)
56 73
57 Additionally, Thank you [BrowserStack](https://www.browserstack.com/) for supporting us and making cross-browser testing easy. 74 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;
...@@ -70,93 +76,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) { ...@@ -70,93 +76,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
70 }); 76 });
71 77
72 // Global jQuery Elements 78 // Global jQuery Elements
73 -$(function () { 79 +let notifications = $('.notification');
74 - 80 +let successNotification = notifications.filter('.pos');
75 - var notifications = $('.notification'); 81 +let errorNotification = notifications.filter('.neg');
76 - var successNotification = notifications.filter('.pos'); 82 +let warningNotification = notifications.filter('.warning');
77 - var errorNotification = notifications.filter('.neg'); 83 +// Notification Events
78 - var warningNotification = notifications.filter('.warning'); 84 +window.Events.listen('success', function (text) {
79 - // Notification Events 85 + successNotification.hide();
80 - window.Events.listen('success', function (text) { 86 + successNotification.find('span').text(text);
81 - successNotification.hide(); 87 + setTimeout(() => {
82 - successNotification.find('span').text(text); 88 + successNotification.show();
89 + }, 1);
90 +});
91 +window.Events.listen('warning', function (text) {
92 + warningNotification.find('span').text(text);
93 + warningNotification.show();
94 +});
95 +window.Events.listen('error', function (text) {
96 + errorNotification.find('span').text(text);
97 + errorNotification.show();
98 +});
99 +
100 +// Notification hiding
101 +notifications.click(function () {
102 + $(this).fadeOut(100);
103 +});
104 +
105 +// Chapter page list toggles
106 +$('.chapter-toggle').click(function (e) {
107 + e.preventDefault();
108 + $(this).toggleClass('open');
109 + $(this).closest('.chapter').find('.inset-list').slideToggle(180);
110 +});
111 +
112 +// Back to top button
113 +$('#back-to-top').click(function() {
114 + $('#header').smoothScrollTo();
115 +});
116 +let scrollTopShowing = false;
117 +let scrollTop = document.getElementById('back-to-top');
118 +let scrollTopBreakpoint = 1200;
119 +window.addEventListener('scroll', function() {
120 + let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
121 + if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
122 + scrollTop.style.display = 'block';
123 + scrollTopShowing = true;
83 setTimeout(() => { 124 setTimeout(() => {
84 - successNotification.show(); 125 + scrollTop.style.opacity = 0.4;
85 }, 1); 126 }, 1);
86 - }); 127 + } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
87 - window.Events.listen('warning', function (text) { 128 + scrollTop.style.opacity = 0;
88 - warningNotification.find('span').text(text); 129 + scrollTopShowing = false;
89 - warningNotification.show(); 130 + setTimeout(() => {
90 - }); 131 + scrollTop.style.display = 'none';
91 - window.Events.listen('error', function (text) { 132 + }, 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 } 133 }
134 +});
158 135
136 +// Common jQuery actions
137 +$('[data-action="expand-entity-list-details"]').click(function() {
138 + $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
159 }); 139 });
160 140
141 +// Popup close
142 +$('.popup-close').click(function() {
143 + $(this).closest('.overlay').fadeOut(240);
144 +});
145 +$('.overlay').click(function(event) {
146 + if (!$(event.target).hasClass('overlay')) return;
147 + $(this).fadeOut(240);
148 +});
149 +
150 +// Detect IE for css
151 +if(navigator.userAgent.indexOf('MSIE')!==-1
152 + || navigator.appVersion.indexOf('Trident/') > 0
153 + || navigator.userAgent.indexOf('Safari') !== -1){
154 + $('body').addClass('flexbox-support');
155 +}
156 +
161 // Page specific items 157 // Page specific items
162 -require('./pages/page-show'); 158 +import "./pages/page-show";
......
1 "use strict"; 1 "use strict";
2 // Configure ZeroClipboard 2 // Configure ZeroClipboard
3 -var zeroClipBoard = require('zeroclipboard'); 3 +import zeroClipBoard from "zeroclipboard";
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 $pointerInner = $pointer.children('div.pointer').first();
13 - var isSelection = false; 10 + let isSelection = false;
14 11
15 // Select all contents on input click 12 // Select all contents on input click
16 $pointer.on('click', 'input', function (e) { 13 $pointer.on('click', 'input', function (e) {
...@@ -19,6 +16,9 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -19,6 +16,9 @@ window.setupPageShow = module.exports = function (pageId) {
19 }); 16 });
20 17
21 // Set up copy-to-clipboard 18 // Set up copy-to-clipboard
19 + zeroClipBoard.config({
20 + swfPath: window.baseUrl('/ZeroClipboard.swf')
21 + });
22 new zeroClipBoard($pointer.find('button').first()[0]); 22 new zeroClipBoard($pointer.find('button').first()[0]);
23 23
24 // Hide pointer when clicking away 24 // Hide pointer when clicking away
...@@ -31,11 +31,11 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -31,11 +31,11 @@ window.setupPageShow = module.exports = function (pageId) {
31 // Show pointer when selecting a single block of tagged content 31 // Show pointer when selecting a single block of tagged content
32 $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) { 32 $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) {
33 e.stopPropagation(); 33 e.stopPropagation();
34 - var selection = window.getSelection(); 34 + let selection = window.getSelection();
35 if (selection.toString().length === 0) return; 35 if (selection.toString().length === 0) return;
36 36
37 // Show pointer and set link 37 // Show pointer and set link
38 - var $elem = $(this); 38 + let $elem = $(this);
39 let link = window.baseUrl('/link/' + pageId + '#' + $elem.attr('id')); 39 let link = window.baseUrl('/link/' + pageId + '#' + $elem.attr('id'));
40 if (link.indexOf('http') !== 0) link = window.location.protocol + "//" + window.location.host + link; 40 if (link.indexOf('http') !== 0) link = window.location.protocol + "//" + window.location.host + link;
41 $pointer.find('input').val(link); 41 $pointer.find('input').val(link);
...@@ -44,9 +44,9 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -44,9 +44,9 @@ window.setupPageShow = module.exports = function (pageId) {
44 $pointer.show(); 44 $pointer.show();
45 45
46 // Set pointer to sit near mouse-up position 46 // Set pointer to sit near mouse-up position
47 - var pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2)); 47 + let pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2));
48 if (pointerLeftOffset < 0) pointerLeftOffset = 0; 48 if (pointerLeftOffset < 0) pointerLeftOffset = 0;
49 - var pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100; 49 + let pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100;
50 $pointerInner.css('left', pointerLeftOffsetPercent + '%'); 50 $pointerInner.css('left', pointerLeftOffsetPercent + '%');
51 51
52 isSelection = true; 52 isSelection = true;
...@@ -57,7 +57,7 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -57,7 +57,7 @@ window.setupPageShow = module.exports = function (pageId) {
57 57
58 // Go to, and highlight if necessary, the specified text. 58 // Go to, and highlight if necessary, the specified text.
59 function goToText(text) { 59 function goToText(text) {
60 - var idElem = $('.page-content #' + text).first(); 60 + let idElem = $('.page-content #' + text).first();
61 if (idElem.length !== 0) { 61 if (idElem.length !== 0) {
62 idElem.smoothScrollTo(); 62 idElem.smoothScrollTo();
63 idElem.css('background-color', 'rgba(244, 249, 54, 0.25)'); 63 idElem.css('background-color', 'rgba(244, 249, 54, 0.25)');
...@@ -68,19 +68,19 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -68,19 +68,19 @@ window.setupPageShow = module.exports = function (pageId) {
68 68
69 // Check the hash on load 69 // Check the hash on load
70 if (window.location.hash) { 70 if (window.location.hash) {
71 - var text = window.location.hash.replace(/\%20/g, ' ').substr(1); 71 + let text = window.location.hash.replace(/\%20/g, ' ').substr(1);
72 goToText(text); 72 goToText(text);
73 } 73 }
74 74
75 // Make the book-tree sidebar stick in view on scroll 75 // Make the book-tree sidebar stick in view on scroll
76 - var $window = $(window); 76 + let $window = $(window);
77 - var $bookTree = $(".book-tree"); 77 + let $bookTree = $(".book-tree");
78 - var $bookTreeParent = $bookTree.parent(); 78 + let $bookTreeParent = $bookTree.parent();
79 // Check the page is scrollable and the content is taller than the tree 79 // 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()); 80 + let pageScrollable = ($(document).height() > $window.height()) && ($bookTree.height() < $('.page-content').height());
81 // Get current tree's width and header height 81 // Get current tree's width and header height
82 - var headerHeight = $("#header").height() + $(".toolbar").height(); 82 + let headerHeight = $("#header").height() + $(".toolbar").height();
83 - var isFixed = $window.scrollTop() > headerHeight; 83 + let isFixed = $window.scrollTop() > headerHeight;
84 // Function to fix the tree as a sidebar 84 // Function to fix the tree as a sidebar
85 function stickTree() { 85 function stickTree() {
86 $bookTree.width($bookTreeParent.width() + 15); 86 $bookTree.width($bookTreeParent.width() + 15);
...@@ -95,7 +95,7 @@ window.setupPageShow = module.exports = function (pageId) { ...@@ -95,7 +95,7 @@ window.setupPageShow = module.exports = function (pageId) {
95 } 95 }
96 // Checks if the tree stickiness state should change 96 // Checks if the tree stickiness state should change
97 function checkTreeStickiness(skipCheck) { 97 function checkTreeStickiness(skipCheck) {
98 - var shouldBeFixed = $window.scrollTop() > headerHeight; 98 + let shouldBeFixed = $window.scrollTop() > headerHeight;
99 if (shouldBeFixed && (!isFixed || skipCheck)) { 99 if (shouldBeFixed && (!isFixed || skipCheck)) {
100 stickTree(); 100 stickTree();
101 } else if (!shouldBeFixed && (isFixed || skipCheck)) { 101 } 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
...@@ -465,4 +465,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { ...@@ -465,4 +465,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
465 border-bottom-width: 3px; 465 border-bottom-width: 3px;
466 } 466 }
467 } 467 }
468 +}
469 +
470 +.image-picker .none {
471 + display: none;
468 } 472 }
...\ 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 }
......
...@@ -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 {
......
...@@ -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
......
...@@ -10,7 +10,12 @@ return [ ...@@ -10,7 +10,12 @@ return [
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,96 @@ return [ ...@@ -36,4 +45,96 @@ 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 ser :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_social_accounts' => 'Social Accounts',
105 + '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.',
106 + 'users_social_connect' => 'Connect Account',
107 + 'users_social_disconnect' => 'Disconnect Account',
108 + 'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
109 + 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
110 +];
111 +
112 +
113 +
114 +
115 +
116 +
117 +
118 +
119 +
120 +
121 +
122 +
123 +
124 +
125 +
126 +
127 +
128 +
129 +
130 +
131 +
132 +
133 +
134 +
135 +
136 +
137 +
138 +
139 +
140 +
......
...@@ -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 <div class="form-group"> 1 <div class="form-group">
2 - <label for="username">Username</label> 2 + <label for="username">{{ trans('auth.username') }}</label>
3 @include('form/text', ['name' => 'username', 'tabindex' => 1]) 3 @include('form/text', ['name' => 'username', 'tabindex' => 1])
4 </div> 4 </div>
5 5
6 @if(session('request-email', false) === true) 6 @if(session('request-email', false) === true)
7 <div class="form-group"> 7 <div class="form-group">
8 - <label for="email">Email</label> 8 + <label for="email">{{ trans('auth.email') }}</label>
9 @include('form/text', ['name' => 'email', 'tabindex' => 1]) 9 @include('form/text', ['name' => 'email', 'tabindex' => 1])
10 <span class="text-neg"> 10 <span class="text-neg">
11 - Please enter an email to use for this account. 11 + {{ trans('auth.ldap_email_hint') }}
12 </span> 12 </span>
13 </div> 13 </div>
14 @endif 14 @endif
15 15
16 <div class="form-group"> 16 <div class="form-group">
17 - <label for="password">Password</label> 17 + <label for="password">{{ trans('auth.password') }}</label>
18 @include('form/password', ['name' => 'password', 'tabindex' => 2]) 18 @include('form/password', ['name' => 'password', 'tabindex' => 2])
19 </div> 19 </div>
...\ No newline at end of file ...\ No newline at end of file
......
1 <div class="form-group"> 1 <div class="form-group">
2 - <label for="email">Email</label> 2 + <label for="email">{{ trans('auth.email') }}</label>
3 @include('form/text', ['name' => 'email', 'tabindex' => 1]) 3 @include('form/text', ['name' => 'email', 'tabindex' => 1])
4 </div> 4 </div>
5 5
6 <div class="form-group"> 6 <div class="form-group">
7 - <label for="password">Password</label> 7 + <label for="password">{{ trans('auth.password') }}</label>
8 @include('form/password', ['name' => 'password', 'tabindex' => 2]) 8 @include('form/password', ['name' => 'password', 'tabindex' => 2])
9 - <span class="block small"><a href="{{ baseUrl('/password/email') }}">Forgot Password?</a></span> 9 + <span class="block small"><a href="{{ baseUrl('/password/email') }}">{{ trans('auth.forgot_password') }}</a></span>
10 </div> 10 </div>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 2
3 @section('header-buttons') 3 @section('header-buttons')
4 @if(setting('registration-enabled', false)) 4 @if(setting('registration-enabled', false))
5 - <a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>Sign up</a> 5 + <a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>{{ trans('auth.sign_up') }}</a>
6 @endif 6 @endif
7 @stop 7 @stop
8 8
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
10 10
11 <div class="text-center"> 11 <div class="text-center">
12 <div class="center-box"> 12 <div class="center-box">
13 - <h1>Log In</h1> 13 + <h1>{{ title_case(trans('auth.log_in')) }}</h1>
14 14
15 <form action="{{ baseUrl("/login") }}" method="POST" id="login-form"> 15 <form action="{{ baseUrl("/login") }}" method="POST" id="login-form">
16 {!! csrf_field() !!} 16 {!! csrf_field() !!}
...@@ -19,25 +19,25 @@ ...@@ -19,25 +19,25 @@
19 @include('auth/forms/login/' . $authMethod) 19 @include('auth/forms/login/' . $authMethod)
20 20
21 <div class="form-group"> 21 <div class="form-group">
22 - <label for="remember" class="inline">Remember Me</label> 22 + <label for="remember" class="inline">{{ trans('auth.remember_me') }}</label>
23 <input type="checkbox" id="remember" name="remember" class="toggle-switch-checkbox"> 23 <input type="checkbox" id="remember" name="remember" class="toggle-switch-checkbox">
24 <label for="remember" class="toggle-switch"></label> 24 <label for="remember" class="toggle-switch"></label>
25 </div> 25 </div>
26 26
27 27
28 <div class="from-group"> 28 <div class="from-group">
29 - <button class="button block pos" tabindex="3"><i class="zmdi zmdi-sign-in"></i> Sign In</button> 29 + <button class="button block pos" tabindex="3"><i class="zmdi zmdi-sign-in"></i> {{ title_case(trans('auth.log_in')) }}</button>
30 </div> 30 </div>
31 </form> 31 </form>
32 32
33 @if(count($socialDrivers) > 0) 33 @if(count($socialDrivers) > 0)
34 <hr class="margin-top"> 34 <hr class="margin-top">
35 - <h3 class="text-muted">Social Login</h3> 35 + <h3 class="text-muted">{{ trans('auth.social_login') }}</h3>
36 @if(isset($socialDrivers['google'])) 36 @if(isset($socialDrivers['google']))
37 - <a href="{{ baseUrl("/login/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a> 37 + <a id="social-login-google" href="{{ baseUrl("/login/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
38 @endif 38 @endif
39 @if(isset($socialDrivers['github'])) 39 @if(isset($socialDrivers['github']))
40 - <a href="{{ baseUrl("/login/service/github") }}" style="color:#444;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a> 40 + <a id="social-login-github" href="{{ baseUrl("/login/service/github") }}" style="color:#444;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a>
41 @endif 41 @endif
42 @endif 42 @endif
43 </div> 43 </div>
......
1 @extends('public') 1 @extends('public')
2 2
3 @section('header-buttons') 3 @section('header-buttons')
4 - <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a> 4 + <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
5 @if(setting('registration-enabled')) 5 @if(setting('registration-enabled'))
6 - <a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>Sign up</a> 6 + <a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>{{ trans('auth.sign_up') }}</a>
7 @endif 7 @endif
8 @stop 8 @stop
9 9
...@@ -12,20 +12,20 @@ ...@@ -12,20 +12,20 @@
12 12
13 <div class="text-center"> 13 <div class="text-center">
14 <div class="center-box text-left"> 14 <div class="center-box text-left">
15 - <h1>Reset Password</h1> 15 + <h1>{{ trans('auth.reset_password') }}</h1>
16 16
17 - <p class="muted small">Enter your email below and you will be sent an email with a password reset link.</p> 17 + <p class="muted small">{{ trans('auth.reset_password_send_instructions') }}</p>
18 18
19 <form action="{{ baseUrl("/password/email") }}" method="POST"> 19 <form action="{{ baseUrl("/password/email") }}" method="POST">
20 {!! csrf_field() !!} 20 {!! csrf_field() !!}
21 21
22 <div class="form-group"> 22 <div class="form-group">
23 - <label for="email">Email</label> 23 + <label for="email">{{ trans('auth.email') }}</label>
24 @include('form/text', ['name' => 'email']) 24 @include('form/text', ['name' => 'email'])
25 </div> 25 </div>
26 26
27 <div class="from-group"> 27 <div class="from-group">
28 - <button class="button block pos">Send Reset Link</button> 28 + <button class="button block pos">{{ trans('auth.reset_password_send_button') }}</button>
29 </div> 29 </div>
30 </form> 30 </form>
31 </div> 31 </div>
......
1 @extends('public') 1 @extends('public')
2 2
3 @section('header-buttons') 3 @section('header-buttons')
4 - <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a> 4 + <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
5 @if(setting('registration-enabled')) 5 @if(setting('registration-enabled'))
6 - <a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>Sign up</a> 6 + <a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>{{ trans('auth.sign_up') }}</a>
7 @endif 7 @endif
8 @stop 8 @stop
9 9
...@@ -14,29 +14,29 @@ ...@@ -14,29 +14,29 @@
14 14
15 <div class="text-center"> 15 <div class="text-center">
16 <div class="center-box text-left"> 16 <div class="center-box text-left">
17 - <h1>Reset Password</h1> 17 + <h1>{{ trans('auth.reset_password') }}</h1>
18 18
19 <form action="{{ baseUrl("/password/reset") }}" method="POST"> 19 <form action="{{ baseUrl("/password/reset") }}" method="POST">
20 {!! csrf_field() !!} 20 {!! csrf_field() !!}
21 <input type="hidden" name="token" value="{{ $token }}"> 21 <input type="hidden" name="token" value="{{ $token }}">
22 22
23 <div class="form-group"> 23 <div class="form-group">
24 - <label for="email">Email</label> 24 + <label for="email">{{ trans('auth.email') }}</label>
25 @include('form/text', ['name' => 'email']) 25 @include('form/text', ['name' => 'email'])
26 </div> 26 </div>
27 27
28 <div class="form-group"> 28 <div class="form-group">
29 - <label for="password">Password</label> 29 + <label for="password">{{ trans('auth.password') }}</label>
30 @include('form/password', ['name' => 'password']) 30 @include('form/password', ['name' => 'password'])
31 </div> 31 </div>
32 32
33 <div class="form-group"> 33 <div class="form-group">
34 - <label for="password_confirmation">Confirm Password</label> 34 + <label for="password_confirmation">{{ trans('auth.password_confirm') }}</label>
35 @include('form/password', ['name' => 'password_confirmation']) 35 @include('form/password', ['name' => 'password_confirmation'])
36 </div> 36 </div>
37 37
38 <div class="from-group"> 38 <div class="from-group">
39 - <button class="button block pos">Reset Password</button> 39 + <button class="button block pos">{{ trans('auth.reset_password') }}</button>
40 </div> 40 </div>
41 </form> 41 </form>
42 </div> 42 </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 2
3 @section('header-buttons') 3 @section('header-buttons')
4 @if(!$signedIn) 4 @if(!$signedIn)
5 - <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a> 5 + <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
6 @endif 6 @endif
7 @stop 7 @stop
8 8
...@@ -10,10 +10,9 @@ ...@@ -10,10 +10,9 @@
10 10
11 <div class="text-center"> 11 <div class="text-center">
12 <div class="center-box"> 12 <div class="center-box">
13 - <h2>Thanks for registering!</h2> 13 + <h2>{{ trans('auth.register_thanks') }}</h2>
14 - <p>Please check your email and click the confirmation button to access {{ setting('app-name', 'BookStack') }}.</p> 14 + <p>{{ trans('auth.register_confirm', ['appName' => setting('app-name')]) }}</p>
15 </div> 15 </div>
16 </div> 16 </div>
17 17
18 -
19 @stop 18 @stop
......
1 @extends('public') 1 @extends('public')
2 2
3 @section('header-buttons') 3 @section('header-buttons')
4 - <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a> 4 + <a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
5 @stop 5 @stop
6 6
7 @section('content') 7 @section('content')
8 8
9 <div class="text-center"> 9 <div class="text-center">
10 <div class="center-box"> 10 <div class="center-box">
11 - <h1>Sign Up</h1> 11 + <h1>{{ title_case(trans('auth.sign_up')) }}</h1>
12 12
13 <form action="{{ baseUrl("/register") }}" method="POST"> 13 <form action="{{ baseUrl("/register") }}" method="POST">
14 {!! csrf_field() !!} 14 {!! csrf_field() !!}
15 15
16 <div class="form-group"> 16 <div class="form-group">
17 - <label for="email">Name</label> 17 + <label for="email">{{ trans('auth.name') }}</label>
18 @include('form/text', ['name' => 'name']) 18 @include('form/text', ['name' => 'name'])
19 </div> 19 </div>
20 20
21 <div class="form-group"> 21 <div class="form-group">
22 - <label for="email">Email</label> 22 + <label for="email">{{ trans('auth.email') }}</label>
23 @include('form/text', ['name' => 'email']) 23 @include('form/text', ['name' => 'email'])
24 </div> 24 </div>
25 25
26 <div class="form-group"> 26 <div class="form-group">
27 - <label for="password">Password</label> 27 + <label for="password">{{ trans('auth.password') }}</label>
28 - @include('form/password', ['name' => 'password', 'placeholder' => 'Must be over 5 characters']) 28 + @include('form/password', ['name' => 'password', 'placeholder' => trans('auth.password_hint')])
29 </div> 29 </div>
30 30
31 <div class="from-group"> 31 <div class="from-group">
32 - <button class="button block pos">Create Account</button> 32 + <button class="button block pos">{{ trans('auth.create_account') }}</button>
33 </div> 33 </div>
34 </form> 34 </form>
35 35
36 @if(count($socialDrivers) > 0) 36 @if(count($socialDrivers) > 0)
37 <hr class="margin-top"> 37 <hr class="margin-top">
38 - <h3 class="text-muted">Social Registration</h3> 38 + <h3 class="text-muted">{{ trans('auth.social_registration') }}</h3>
39 - <p class="text-small">Register and sign in using another service.</p> 39 + <p class="text-small">{{ trans('auth.social_registration_text') }}</p>
40 @if(isset($socialDrivers['google'])) 40 @if(isset($socialDrivers['google']))
41 <a href="{{ baseUrl("/register/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a> 41 <a href="{{ baseUrl("/register/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
42 @endif 42 @endif
......
...@@ -4,16 +4,16 @@ ...@@ -4,16 +4,16 @@
4 4
5 <div class="row"> 5 <div class="row">
6 <div class="col-md-6 col-md-offset-3"> 6 <div class="col-md-6 col-md-offset-3">
7 - <h2>Email Address not confirmed</h2> 7 + <h2>{{ trans('auth.email_not_confirmed') }}</h2>
8 - <p class="text-muted">Your email address has not yet been confirmed. <br> 8 + <p class="text-muted">{{ trans('auth.email_not_confirmed_text') }}<br>
9 - Please click the link in the email that was sent shortly after you registered. <br> 9 + {{ trans('auth.email_not_confirmed_click_link') }} <br>
10 - If you cannot find the email you can re-send the confirmation email by submitting the form below. 10 + {{ trans('auth.email_not_confirmed_resend') }}
11 </p> 11 </p>
12 <hr> 12 <hr>
13 <form action="{{ baseUrl("/register/confirm/resend") }}" method="POST"> 13 <form action="{{ baseUrl("/register/confirm/resend") }}" method="POST">
14 {!! csrf_field() !!} 14 {!! csrf_field() !!}
15 <div class="form-group"> 15 <div class="form-group">
16 - <label for="email">Email Address</label> 16 + <label for="email">{{ trans('auth.email') }}</label>
17 @if(auth()->check()) 17 @if(auth()->check())
18 @include('form/text', ['name' => 'email', 'model' => auth()->user()]) 18 @include('form/text', ['name' => 'email', 'model' => auth()->user()])
19 @else 19 @else
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
21 @endif 21 @endif
22 </div> 22 </div>
23 <div class="form-group"> 23 <div class="form-group">
24 - <button type="submit" class="button pos">Resend Confirmation Email</button> 24 + <button type="submit" class="button pos">{{ trans('auth.email_not_confirmed_resend_button') }}</button>
25 </div> 25 </div>
26 </form> 26 </form>
27 </div> 27 </div>
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
17 <!-- Scripts --> 17 <!-- Scripts -->
18 <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script> 18 <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script>
19 <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script> 19 <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script>
20 + <script src="{{ baseUrl('/translations.js') }}"></script>
20 21
21 @yield('head') 22 @yield('head')
22 23
...@@ -53,32 +54,16 @@ ...@@ -53,32 +54,16 @@
53 <div class="col-lg-4 col-sm-5"> 54 <div class="col-lg-4 col-sm-5">
54 <div class="float right"> 55 <div class="float right">
55 <div class="links text-center"> 56 <div class="links text-center">
56 - <a href="{{ baseUrl('/books') }}"><i class="zmdi zmdi-book"></i>Books</a> 57 + <a href="{{ baseUrl('/books') }}"><i class="zmdi zmdi-book"></i>{{ trans('entities.books') }}</a>
57 @if(isset($currentUser) && userCan('settings-manage')) 58 @if(isset($currentUser) && userCan('settings-manage'))
58 - <a href="{{ baseUrl('/settings') }}"><i class="zmdi zmdi-settings"></i>Settings</a> 59 + <a href="{{ baseUrl('/settings') }}"><i class="zmdi zmdi-settings"></i>{{ trans('settings.settings') }}</a>
59 @endif 60 @endif
60 @if(!isset($signedIn) || !$signedIn) 61 @if(!isset($signedIn) || !$signedIn)
61 - <a href="{{ baseUrl('/login') }}"><i class="zmdi zmdi-sign-in"></i>Sign In</a> 62 + <a href="{{ baseUrl('/login') }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
62 @endif 63 @endif
63 </div> 64 </div>
64 @if(isset($signedIn) && $signedIn) 65 @if(isset($signedIn) && $signedIn)
65 - <div class="dropdown-container" dropdown> 66 + @include('partials._header-dropdown', ['currentUser' => $currentUser])
66 - <span class="user-name" dropdown-toggle>
67 - <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
68 - <span class="name" ng-non-bindable>{{ $currentUser->getShortName(9) }}</span> <i class="zmdi zmdi-caret-down"></i>
69 - </span>
70 - <ul>
71 - <li>
72 - <a href="{{ baseUrl("/user/{$currentUser->id}") }}" class="text-primary"><i class="zmdi zmdi-account zmdi-hc-fw zmdi-hc-lg"></i>View Profile</a>
73 - </li>
74 - <li>
75 - <a href="{{ baseUrl("/settings/users/{$currentUser->id}") }}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-fw zmdi-hc-lg"></i>Edit Profile</a>
76 - </li>
77 - <li>
78 - <a href="{{ baseUrl('/logout') }}" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-fw zmdi-hc-lg"></i>Logout</a>
79 - </li>
80 - </ul>
81 - </div>
82 @endif 67 @endif
83 68
84 </div> 69 </div>
...@@ -93,7 +78,7 @@ ...@@ -93,7 +78,7 @@
93 78
94 <div id="back-to-top"> 79 <div id="back-to-top">
95 <div class="inner"> 80 <div class="inner">
96 - <i class="zmdi zmdi-chevron-up"></i> <span>Back to top</span> 81 + <i class="zmdi zmdi-chevron-up"></i> <span>{{ trans('common.back_to_top') }}</span>
97 </div> 82 </div>
98 </div> 83 </div>
99 @yield('bottom') 84 @yield('bottom')
......
1 +<div class="breadcrumbs">
2 + <a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
3 +</div>
...\ No newline at end of file ...\ No newline at end of file
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 @section('content') 3 @section('content')
4 4
5 <div class="container small" ng-non-bindable> 5 <div class="container small" ng-non-bindable>
6 - <h1>Create New Book</h1> 6 + <h1>{{ trans('entities.books_create') }}</h1>
7 <form action="{{ baseUrl("/books") }}" method="POST"> 7 <form action="{{ baseUrl("/books") }}" method="POST">
8 @include('books/form') 8 @include('books/form')
9 </form> 9 </form>
......
...@@ -2,16 +2,26 @@ ...@@ -2,16 +2,26 @@
2 2
3 @section('content') 3 @section('content')
4 4
5 + <div class="faded-small toolbar">
6 + <div class="container">
7 + <div class="row">
8 + <div class="col-sm-12 faded">
9 + @include('books._breadcrumbs', ['book' => $book])
10 + </div>
11 + </div>
12 + </div>
13 + </div>
14 +
5 <div class="container small" ng-non-bindable> 15 <div class="container small" ng-non-bindable>
6 - <h1>Delete Book</h1> 16 + <h1>{{ trans('entities.books_delete') }}</h1>
7 - <p>This will delete the book with the name '{{$book->name}}', All pages and chapters will be removed.</p> 17 + <p>{{ trans('entities.books_delete_explain', ['bookName' => $book->name]) }}</p>
8 - <p class="text-neg">Are you sure you want to delete this book?</p> 18 + <p class="text-neg">{{ trans('entities.books_delete_confirmation') }}</p>
9 19
10 <form action="{{$book->getUrl()}}" method="POST"> 20 <form action="{{$book->getUrl()}}" method="POST">
11 {!! csrf_field() !!} 21 {!! csrf_field() !!}
12 <input type="hidden" name="_method" value="DELETE"> 22 <input type="hidden" name="_method" value="DELETE">
13 - <a href="{{$book->getUrl()}}" class="button">Cancel</a> 23 + <a href="{{$book->getUrl()}}" class="button">{{ trans('common.cancel') }}</a>
14 - <button type="submit" class="button neg">Confirm</button> 24 + <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
15 </form> 25 </form>
16 </div> 26 </div>
17 27
......
...@@ -2,8 +2,18 @@ ...@@ -2,8 +2,18 @@
2 2
3 @section('content') 3 @section('content')
4 4
5 + <div class="faded-small toolbar">
6 + <div class="container">
7 + <div class="row">
8 + <div class="col-sm-12 faded">
9 + @include('books._breadcrumbs', ['book' => $book])
10 + </div>
11 + </div>
12 + </div>
13 + </div>
14 +
5 <div class="container small" ng-non-bindable> 15 <div class="container small" ng-non-bindable>
6 - <h1>Edit Book</h1> 16 + <h1>{{ trans('entities.books_edit') }}</h1>
7 <form action="{{ $book->getUrl() }}" method="POST"> 17 <form action="{{ $book->getUrl() }}" method="POST">
8 <input type="hidden" name="_method" value="PUT"> 18 <input type="hidden" name="_method" value="PUT">
9 @include('books/form', ['model' => $book]) 19 @include('books/form', ['model' => $book])
......
1 1
2 {{ csrf_field() }} 2 {{ csrf_field() }}
3 <div class="form-group title-input"> 3 <div class="form-group title-input">
4 - <label for="name">Book Name</label> 4 + <label for="name">{{ trans('common.name') }}</label>
5 @include('form/text', ['name' => 'name']) 5 @include('form/text', ['name' => 'name'])
6 </div> 6 </div>
7 7
8 <div class="form-group description-input"> 8 <div class="form-group description-input">
9 - <label for="description">Description</label> 9 + <label for="description">{{ trans('common.description') }}</label>
10 @include('form/textarea', ['name' => 'description']) 10 @include('form/textarea', ['name' => 'description'])
11 </div> 11 </div>
12 12
13 <div class="form-group"> 13 <div class="form-group">
14 - <a href="{{ back()->getTargetUrl() }}" class="button muted">Cancel</a> 14 + <a href="{{ back()->getTargetUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
15 - <button type="submit" class="button pos">Save Book</button> 15 + <button type="submit" class="button pos">{{ trans('entities.books_save') }}</button>
16 </div> 16 </div>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
9 <div class="col-xs-11 faded"> 9 <div class="col-xs-11 faded">
10 <div class="action-buttons"> 10 <div class="action-buttons">
11 @if($currentUser->can('book-create-all')) 11 @if($currentUser->can('book-create-all'))
12 - <a href="{{ baseUrl("/books/create") }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>Add new book</a> 12 + <a href="{{ baseUrl("/books/create") }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.books_create') }}</a>
13 @endif 13 @endif
14 </div> 14 </div>
15 </div> 15 </div>
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
21 <div class="container" ng-non-bindable> 21 <div class="container" ng-non-bindable>
22 <div class="row"> 22 <div class="row">
23 <div class="col-sm-7"> 23 <div class="col-sm-7">
24 - <h1>Books</h1> 24 + <h1>{{ trans('entities.books') }}</h1>
25 @if(count($books) > 0) 25 @if(count($books) > 0)
26 @foreach($books as $book) 26 @foreach($books as $book)
27 @include('books/list-item', ['book' => $book]) 27 @include('books/list-item', ['book' => $book])
...@@ -29,27 +29,27 @@ ...@@ -29,27 +29,27 @@
29 @endforeach 29 @endforeach
30 {!! $books->render() !!} 30 {!! $books->render() !!}
31 @else 31 @else
32 - <p class="text-muted">No books have been created.</p> 32 + <p class="text-muted">{{ trans('entities.books_empty') }}</p>
33 @if(userCan('books-create-all')) 33 @if(userCan('books-create-all'))
34 - <a href="{{ baseUrl("/books/create") }}" class="text-pos"><i class="zmdi zmdi-edit"></i>Create one now</a> 34 + <a href="{{ baseUrl("/books/create") }}" class="text-pos"><i class="zmdi zmdi-edit"></i>{{ trans('entities.create_one_now') }}</a>
35 @endif 35 @endif
36 @endif 36 @endif
37 </div> 37 </div>
38 <div class="col-sm-4 col-sm-offset-1"> 38 <div class="col-sm-4 col-sm-offset-1">
39 <div id="recents"> 39 <div id="recents">
40 @if($recents) 40 @if($recents)
41 - <div class="margin-top large">&nbsp;</div> 41 + <div class="margin-top">&nbsp;</div>
42 - <h3>Recently Viewed</h3> 42 + <h3>{{ trans('entities.recently_viewed') }}</h3>
43 @include('partials/entity-list', ['entities' => $recents]) 43 @include('partials/entity-list', ['entities' => $recents])
44 @endif 44 @endif
45 </div> 45 </div>
46 <div class="margin-top large">&nbsp;</div> 46 <div class="margin-top large">&nbsp;</div>
47 <div id="popular"> 47 <div id="popular">
48 - <h3>Popular Books</h3> 48 + <h3>{{ trans('entities.books_popular') }}</h3>
49 @if(count($popular) > 0) 49 @if(count($popular) > 0)
50 @include('partials/entity-list', ['entities' => $popular]) 50 @include('partials/entity-list', ['entities' => $popular])
51 @else 51 @else
52 - <p class="text-muted">The most popular books will appear here.</p> 52 + <p class="text-muted">{{ trans('entities.books_popular_empty') }}</p>
53 @endif 53 @endif
54 </div> 54 </div>
55 </div> 55 </div>
......
...@@ -6,9 +6,7 @@ ...@@ -6,9 +6,7 @@
6 <div class="container"> 6 <div class="container">
7 <div class="row"> 7 <div class="row">
8 <div class="col-sm-12 faded"> 8 <div class="col-sm-12 faded">
9 - <div class="breadcrumbs"> 9 + @include('books._breadcrumbs', ['book' => $book])
10 - <a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
11 - </div>
12 </div> 10 </div>
13 </div> 11 </div>
14 </div> 12 </div>
...@@ -16,7 +14,7 @@ ...@@ -16,7 +14,7 @@
16 14
17 15
18 <div class="container" ng-non-bindable> 16 <div class="container" ng-non-bindable>
19 - <h1>Book Permissions</h1> 17 + <h1>{{ trans('entities.books_permissions') }}</h1>
20 @include('form/restriction-form', ['model' => $book]) 18 @include('form/restriction-form', ['model' => $book])
21 </div> 19 </div>
22 20
......
...@@ -5,29 +5,32 @@ ...@@ -5,29 +5,32 @@
5 <div class="faded-small toolbar"> 5 <div class="faded-small toolbar">
6 <div class="container"> 6 <div class="container">
7 <div class="row"> 7 <div class="row">
8 - <div class="col-md-12"> 8 + <div class="col-md-6 faded">
9 + @include('books._breadcrumbs', ['book' => $book])
10 + </div>
11 + <div class="col-md-6">
9 <div class="action-buttons faded"> 12 <div class="action-buttons faded">
10 @if(userCan('page-create', $book)) 13 @if(userCan('page-create', $book))
11 - <a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Page</a> 14 + <a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
12 @endif 15 @endif
13 @if(userCan('chapter-create', $book)) 16 @if(userCan('chapter-create', $book))
14 - <a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Chapter</a> 17 + <a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.chapters_new') }}</a>
15 @endif 18 @endif
16 @if(userCan('book-update', $book)) 19 @if(userCan('book-update', $book))
17 - <a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> 20 + <a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
18 @endif 21 @endif
19 @if(userCan('book-update', $book) || userCan('restrictions-manage', $book) || userCan('book-delete', $book)) 22 @if(userCan('book-update', $book) || userCan('restrictions-manage', $book) || userCan('book-delete', $book))
20 <div dropdown class="dropdown-container"> 23 <div dropdown class="dropdown-container">
21 <a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a> 24 <a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
22 <ul> 25 <ul>
23 @if(userCan('book-update', $book)) 26 @if(userCan('book-update', $book))
24 - <li><a href="{{ $book->getUrl('/sort') }}" class="text-primary"><i class="zmdi zmdi-sort"></i>Sort</a></li> 27 + <li><a href="{{ $book->getUrl('/sort') }}" class="text-primary"><i class="zmdi zmdi-sort"></i>{{ trans('common.sort') }}</a></li>
25 @endif 28 @endif
26 @if(userCan('restrictions-manage', $book)) 29 @if(userCan('restrictions-manage', $book))
27 - <li><a href="{{ $book->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li> 30 + <li><a href="{{ $book->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.permissions') }}</a></li>
28 @endif 31 @endif
29 @if(userCan('book-delete', $book)) 32 @if(userCan('book-delete', $book))
30 - <li><a href="{{ $book->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li> 33 + <li><a href="{{ $book->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
31 @endif 34 @endif
32 </ul> 35 </ul>
33 </div> 36 </div>
...@@ -59,23 +62,19 @@ ...@@ -59,23 +62,19 @@
59 <hr> 62 <hr>
60 @endforeach 63 @endforeach
61 @else 64 @else
62 - <p class="text-muted">No pages or chapters have been created for this book.</p> 65 + <p class="text-muted">{{ trans('entities.books_empty_contents') }}</p>
63 <p> 66 <p>
64 - <a href="{{ $book->getUrl('/page/create') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a> 67 + <a href="{{ $book->getUrl('/page/create') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
65 - &nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp; 68 + &nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
66 - <a href="{{ $book->getUrl('/chapter/create') }}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>Add a chapter</a> 69 + <a href="{{ $book->getUrl('/chapter/create') }}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>{{ trans('entities.books_empty_add_chapter') }}</a>
67 </p> 70 </p>
68 <hr> 71 <hr>
69 @endif 72 @endif
70 - <p class="text-muted small"> 73 + @include('partials.entity-meta', ['entity' => $book])
71 - Created {{$book->created_at->diffForHumans()}} @if($book->createdBy) by <a href="{{ $book->createdBy->getProfileUrl() }}">{{$book->createdBy->name}}</a> @endif
72 - <br>
73 - Last Updated {{$book->updated_at->diffForHumans()}} @if($book->updatedBy) by <a href="{{ $book->updatedBy->getProfileUrl() }}">{{$book->updatedBy->name}}</a> @endif
74 - </p>
75 </div> 74 </div>
76 </div> 75 </div>
77 <div class="search-results" ng-cloak ng-show="searching"> 76 <div class="search-results" ng-cloak ng-show="searching">
78 - <h3 class="text-muted">Search Results <a ng-if="searching" ng-click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>Clear Search</a></h3> 77 + <h3 class="text-muted">{{ trans('entities.search_results') }} <a ng-if="searching" ng-click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
79 <div ng-if="!searchResults"> 78 <div ng-if="!searchResults">
80 @include('partials/loading-icon') 79 @include('partials/loading-icon')
81 </div> 80 </div>
...@@ -90,21 +89,21 @@ ...@@ -90,21 +89,21 @@
90 @if($book->restricted) 89 @if($book->restricted)
91 <p class="text-muted"> 90 <p class="text-muted">
92 @if(userCan('restrictions-manage', $book)) 91 @if(userCan('restrictions-manage', $book))
93 - <a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a> 92 + <a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
94 @else 93 @else
95 - <i class="zmdi zmdi-lock-outline"></i>Book Permissions Active 94 + <i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
96 @endif 95 @endif
97 </p> 96 </p>
98 @endif 97 @endif
99 <div class="search-box"> 98 <div class="search-box">
100 <form ng-submit="searchBook($event)"> 99 <form ng-submit="searchBook($event)">
101 - <input ng-model="searchTerm" ng-change="checkSearchForm()" type="text" name="term" placeholder="Search This Book"> 100 + <input ng-model="searchTerm" ng-change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
102 <button type="submit"><i class="zmdi zmdi-search"></i></button> 101 <button type="submit"><i class="zmdi zmdi-search"></i></button>
103 <button ng-if="searching" ng-click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button> 102 <button ng-if="searching" ng-click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
104 </form> 103 </form>
105 </div> 104 </div>
106 <div class="activity anim fadeIn"> 105 <div class="activity anim fadeIn">
107 - <h3>Recent Activity</h3> 106 + <h3>{{ trans('entities.recent_activity') }}</h3>
108 @include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)]) 107 @include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)])
109 </div> 108 </div>
110 </div> 109 </div>
......
...@@ -6,8 +6,18 @@ ...@@ -6,8 +6,18 @@
6 6
7 @section('content') 7 @section('content')
8 8
9 + <div class="faded-small toolbar">
10 + <div class="container">
11 + <div class="row">
12 + <div class="col-sm-12 faded">
13 + @include('books._breadcrumbs', ['book' => $book])
14 + </div>
15 + </div>
16 + </div>
17 + </div>
18 +
9 <div class="container" ng-non-bindable> 19 <div class="container" ng-non-bindable>
10 - <h1>Sorting Pages & Chapters<span class="subheader">For {{ $book->name }}</span></h1> 20 + <h1>{{ trans('entities.books_sort') }}</h1>
11 <div class="row"> 21 <div class="row">
12 <div class="col-md-8" id="sort-boxes"> 22 <div class="col-md-8" id="sort-boxes">
13 23
...@@ -17,7 +27,7 @@ ...@@ -17,7 +27,7 @@
17 27
18 @if(count($books) > 1) 28 @if(count($books) > 1)
19 <div class="col-md-4"> 29 <div class="col-md-4">
20 - <h3>Show Other Books</h3> 30 + <h3>{{ trans('entities.books_sort_show_other') }}</h3>
21 <div id="additional-books"> 31 <div id="additional-books">
22 @foreach($books as $otherBook) 32 @foreach($books as $otherBook)
23 @if($otherBook->id !== $book->id) 33 @if($otherBook->id !== $book->id)
...@@ -37,8 +47,8 @@ ...@@ -37,8 +47,8 @@
37 <input type="hidden" name="_method" value="PUT"> 47 <input type="hidden" name="_method" value="PUT">
38 <input type="hidden" id="sort-tree-input" name="sort-tree"> 48 <input type="hidden" id="sort-tree-input" name="sort-tree">
39 <div class="list"> 49 <div class="list">
40 - <a href="{{ $book->getUrl() }}" class="button muted">Cancel</a> 50 + <a href="{{ $book->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
41 - <button class="button pos" type="submit">Save Order</button> 51 + <button class="button pos" type="submit">{{ trans('entities.books_sort_save') }}</button>
42 </div> 52 </div>
43 </form> 53 </form>
44 54
......
1 +<div class="breadcrumbs">
2 + <a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
3 + <span class="sep">&raquo;</span>
4 + <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a>
5 +</div>
...\ No newline at end of file ...\ No newline at end of file
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 @section('content') 3 @section('content')
4 4
5 <div class="container small" ng-non-bindable> 5 <div class="container small" ng-non-bindable>
6 - <h1>Create New Chapter</h1> 6 + <h1>{{ trans('entities.chapters_create') }}</h1>
7 <form action="{{ $book->getUrl('/chapter/create') }}" method="POST"> 7 <form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
8 @include('chapters/form') 8 @include('chapters/form')
9 </form> 9 </form>
......
...@@ -2,17 +2,26 @@ ...@@ -2,17 +2,26 @@
2 2
3 @section('content') 3 @section('content')
4 4
5 + <div class="faded-small toolbar">
6 + <div class="container">
7 + <div class="row">
8 + <div class="col-sm-12 faded">
9 + @include('chapters._breadcrumbs', ['chapter' => $chapter])
10 + </div>
11 + </div>
12 + </div>
13 + </div>
14 +
5 <div class="container small" ng-non-bindable> 15 <div class="container small" ng-non-bindable>
6 - <h1>Delete Chapter</h1> 16 + <h1>{{ trans('entities.chapters_delete') }}</h1>
7 - <p>This will delete the chapter with the name '{{$chapter->name}}', All pages will be removed 17 + <p>{{ trans('entities.chapters_delete_explain', ['chapterName' => $chapter->name]) }}</p>
8 - and added directly to the book.</p> 18 + <p class="text-neg">{{ trans('entities.chapters_delete_confirm') }}</p>
9 - <p class="text-neg">Are you sure you want to delete this chapter?</p>
10 19
11 <form action="{{ $chapter->getUrl() }}" method="POST"> 20 <form action="{{ $chapter->getUrl() }}" method="POST">
12 {!! csrf_field() !!} 21 {!! csrf_field() !!}
13 <input type="hidden" name="_method" value="DELETE"> 22 <input type="hidden" name="_method" value="DELETE">
14 - <a href="{{ $chapter->getUrl() }}" class="button primary">Cancel</a> 23 + <a href="{{ $chapter->getUrl() }}" class="button primary">{{ trans('common.cancel') }}</a>
15 - <button type="submit" class="button neg">Confirm</button> 24 + <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
16 </form> 25 </form>
17 </div> 26 </div>
18 27
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 @section('content') 3 @section('content')
4 4
5 <div class="container small" ng-non-bindable> 5 <div class="container small" ng-non-bindable>
6 - <h1>Edit Chapter</h1> 6 + <h1>{{ trans('entities.chapters_edit') }}</h1>
7 <form action="{{ $chapter->getUrl() }}" method="POST"> 7 <form action="{{ $chapter->getUrl() }}" method="POST">
8 <input type="hidden" name="_method" value="PUT"> 8 <input type="hidden" name="_method" value="PUT">
9 @include('chapters/form', ['model' => $chapter]) 9 @include('chapters/form', ['model' => $chapter])
......
...@@ -2,16 +2,16 @@ ...@@ -2,16 +2,16 @@
2 {!! csrf_field() !!} 2 {!! csrf_field() !!}
3 3
4 <div class="form-group title-input"> 4 <div class="form-group title-input">
5 - <label for="name">Chapter Name</label> 5 + <label for="name">{{ trans('common.name') }}</label>
6 @include('form/text', ['name' => 'name']) 6 @include('form/text', ['name' => 'name'])
7 </div> 7 </div>
8 8
9 <div class="form-group description-input"> 9 <div class="form-group description-input">
10 - <label for="description">Description</label> 10 + <label for="description">{{ trans('common.description') }}</label>
11 @include('form/textarea', ['name' => 'description']) 11 @include('form/textarea', ['name' => 'description'])
12 </div> 12 </div>
13 13
14 <div class="form-group"> 14 <div class="form-group">
15 - <a href="{{ back()->getTargetUrl() }}" class="button muted">Cancel</a> 15 + <a href="{{ back()->getTargetUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
16 - <button type="submit" class="button pos">Save Chapter</button> 16 + <button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
17 </div> 17 </div>
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
17 @endif 17 @endif
18 18
19 @if(!isset($hidePages) && count($chapter->pages) > 0) 19 @if(!isset($hidePages) && count($chapter->pages) > 0)
20 - <p class="text-muted chapter-toggle"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ count($chapter->pages) }} Pages</span></p> 20 + <p class="text-muted chapter-toggle"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans('entities.x_pages', ['count' => $chapter->pages->count()]) }}</span></p>
21 <div class="inset-list"> 21 <div class="inset-list">
22 @foreach($chapter->pages as $page) 22 @foreach($chapter->pages as $page)
23 <h5 class="@if($page->draft) draft @endif"><a href="{{ $page->getUrl() }}" class="text-page @if($page->draft) draft @endif"><i class="zmdi zmdi-file-text"></i>{{$page->name}}</a></h5> 23 <h5 class="@if($page->draft) draft @endif"><a href="{{ $page->getUrl() }}" class="text-page @if($page->draft) draft @endif"><i class="zmdi zmdi-file-text"></i>{{$page->name}}</a></h5>
......
...@@ -6,27 +6,23 @@ ...@@ -6,27 +6,23 @@
6 <div class="container"> 6 <div class="container">
7 <div class="row"> 7 <div class="row">
8 <div class="col-sm-12 faded"> 8 <div class="col-sm-12 faded">
9 - <div class="breadcrumbs"> 9 + @include('chapters._breadcrumbs', ['chapter' => $chapter])
10 - <a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
11 - <span class="sep">&raquo;</span>
12 - <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{ $chapter->getShortName() }}</a>
13 - </div>
14 </div> 10 </div>
15 </div> 11 </div>
16 </div> 12 </div>
17 </div> 13 </div>
18 14
19 <div class="container"> 15 <div class="container">
20 - <h1>Move Chapter <small class="subheader">{{$chapter->name}}</small></h1> 16 + <h1>{{ trans('entities.chapters_move') }}</h1>
21 17
22 <form action="{{ $chapter->getUrl('/move') }}" method="POST"> 18 <form action="{{ $chapter->getUrl('/move') }}" method="POST">
23 {!! csrf_field() !!} 19 {!! csrf_field() !!}
24 <input type="hidden" name="_method" value="PUT"> 20 <input type="hidden" name="_method" value="PUT">
25 21
26 - @include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book']) 22 + @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
27 23
28 - <a href="{{ $chapter->getUrl() }}" class="button muted">Cancel</a> 24 + <a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
29 - <button type="submit" class="button pos">Move Chapter</button> 25 + <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button>
30 </form> 26 </form>
31 </div> 27 </div>
32 28
......
...@@ -6,18 +6,14 @@ ...@@ -6,18 +6,14 @@
6 <div class="container"> 6 <div class="container">
7 <div class="row"> 7 <div class="row">
8 <div class="col-sm-12 faded"> 8 <div class="col-sm-12 faded">
9 - <div class="breadcrumbs"> 9 + @include('chapters._breadcrumbs', ['chapter' => $chapter])
10 - <a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
11 - <span class="sep">&raquo;</span>
12 - <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a>
13 - </div>
14 </div> 10 </div>
15 </div> 11 </div>
16 </div> 12 </div>
17 </div> 13 </div>
18 14
19 <div class="container" ng-non-bindable> 15 <div class="container" ng-non-bindable>
20 - <h1>Chapter Permissions</h1> 16 + <h1>{{ trans('entities.chapters_permissions') }}</h1>
21 @include('form/restriction-form', ['model' => $chapter]) 17 @include('form/restriction-form', ['model' => $chapter])
22 </div> 18 </div>
23 19
......
...@@ -6,30 +6,28 @@ ...@@ -6,30 +6,28 @@
6 <div class="container"> 6 <div class="container">
7 <div class="row"> 7 <div class="row">
8 <div class="col-sm-8 faded" ng-non-bindable> 8 <div class="col-sm-8 faded" ng-non-bindable>
9 - <div class="breadcrumbs"> 9 + @include('chapters._breadcrumbs', ['chapter' => $chapter])
10 - <a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
11 - </div>
12 </div> 10 </div>
13 <div class="col-sm-4 faded"> 11 <div class="col-sm-4 faded">
14 <div class="action-buttons"> 12 <div class="action-buttons">
15 @if(userCan('page-create', $chapter)) 13 @if(userCan('page-create', $chapter))
16 - <a href="{{ $chapter->getUrl('/create-page') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>New Page</a> 14 + <a href="{{ $chapter->getUrl('/create-page') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
17 @endif 15 @endif
18 @if(userCan('chapter-update', $chapter)) 16 @if(userCan('chapter-update', $chapter))
19 - <a href="{{ $chapter->getUrl('/edit') }}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a> 17 + <a href="{{ $chapter->getUrl('/edit') }}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
20 @endif 18 @endif
21 @if(userCan('chapter-update', $chapter) || userCan('restrictions-manage', $chapter) || userCan('chapter-delete', $chapter)) 19 @if(userCan('chapter-update', $chapter) || userCan('restrictions-manage', $chapter) || userCan('chapter-delete', $chapter))
22 <div dropdown class="dropdown-container"> 20 <div dropdown class="dropdown-container">
23 <a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a> 21 <a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
24 <ul> 22 <ul>
25 @if(userCan('chapter-update', $chapter)) 23 @if(userCan('chapter-update', $chapter))
26 - <li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary"><i class="zmdi zmdi-folder"></i>Move</a></li> 24 + <li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary"><i class="zmdi zmdi-folder"></i>{{ trans('common.move') }}</a></li>
27 @endif 25 @endif
28 @if(userCan('restrictions-manage', $chapter)) 26 @if(userCan('restrictions-manage', $chapter))
29 - <li><a href="{{ $chapter->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li> 27 + <li><a href="{{ $chapter->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.permissions') }}</a></li>
30 @endif 28 @endif
31 @if(userCan('chapter-delete', $chapter)) 29 @if(userCan('chapter-delete', $chapter))
32 - <li><a href="{{ $chapter->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li> 30 + <li><a href="{{ $chapter->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
33 @endif 31 @endif
34 </ul> 32 </ul>
35 </div> 33 </div>
...@@ -57,26 +55,22 @@ ...@@ -57,26 +55,22 @@
57 </div> 55 </div>
58 @else 56 @else
59 <hr> 57 <hr>
60 - <p class="text-muted">No pages are currently in this chapter.</p> 58 + <p class="text-muted">{{ trans('entities.chapters_empty') }}</p>
61 <p> 59 <p>
62 @if(userCan('page-create', $chapter)) 60 @if(userCan('page-create', $chapter))
63 - <a href="{{ $chapter->getUrl('/create-page') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a> 61 + <a href="{{ $chapter->getUrl('/create-page') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
64 @endif 62 @endif
65 @if(userCan('page-create', $chapter) && userCan('book-update', $book)) 63 @if(userCan('page-create', $chapter) && userCan('book-update', $book))
66 - &nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp; 64 + &nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
67 @endif 65 @endif
68 @if(userCan('book-update', $book)) 66 @if(userCan('book-update', $book))
69 - <a href="{{ $book->getUrl('/sort') }}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a> 67 + <a href="{{ $book->getUrl('/sort') }}" class="text-book"><i class="zmdi zmdi-book"></i>{{ trans('entities.books_empty_sort_current_book') }}</a>
70 @endif 68 @endif
71 </p> 69 </p>
72 <hr> 70 <hr>
73 @endif 71 @endif
74 72
75 - <p class="text-muted small"> 73 + @include('partials.entity-meta', ['entity' => $chapter])
76 - Created {{ $chapter->created_at->diffForHumans() }} @if($chapter->createdBy) by <a href="{{ $chapter->createdBy->getProfileUrl() }}">{{ $chapter->createdBy->name}}</a> @endif
77 - <br>
78 - Last Updated {{ $chapter->updated_at->diffForHumans() }} @if($chapter->updatedBy) by <a href="{{ $chapter->updatedBy->getProfileUrl() }}">{{ $chapter->updatedBy->name}}</a> @endif
79 - </p>
80 </div> 74 </div>
81 <div class="col-md-3 col-md-offset-1"> 75 <div class="col-md-3 col-md-offset-1">
82 <div class="margin-top large"></div> 76 <div class="margin-top large"></div>
...@@ -84,19 +78,20 @@ ...@@ -84,19 +78,20 @@
84 <div class="text-muted"> 78 <div class="text-muted">
85 79
86 @if($book->restricted) 80 @if($book->restricted)
87 - @if(userCan('restrictions-manage', $book)) 81 + <p class="text-muted">
88 - <a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a> 82 + @if(userCan('restrictions-manage', $book))
89 - @else 83 + <a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
90 - <i class="zmdi zmdi-lock-outline"></i>Book Permissions Active 84 + @else
91 - @endif 85 + <i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
92 - <br> 86 + @endif
87 + </p>
93 @endif 88 @endif
94 89
95 @if($chapter->restricted) 90 @if($chapter->restricted)
96 @if(userCan('restrictions-manage', $chapter)) 91 @if(userCan('restrictions-manage', $chapter))
97 - <a href="{{ $chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active</a> 92 + <a href="{{ $chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}</a>
98 @else 93 @else
99 - <i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active 94 + <i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}
100 @endif 95 @endif
101 @endif 96 @endif
102 </div> 97 </div>
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
2 <div class="overlay" entity-link-selector> 2 <div class="overlay" entity-link-selector>
3 <div class="popup-body small flex-child"> 3 <div class="popup-body small flex-child">
4 <div class="popup-header primary-background"> 4 <div class="popup-header primary-background">
5 - <div class="popup-title">Entity Select</div> 5 + <div class="popup-title">{{ trans('entities.entity_select') }}</div>
6 <button type="button" class="corner-button neg button popup-close">x</button> 6 <button type="button" class="corner-button neg button popup-close">x</button>
7 </div> 7 </div>
8 - @include('partials/entity-selector', ['name' => 'entity-selector']) 8 + @include('components.entity-selector', ['name' => 'entity-selector'])
9 <div class="popup-footer"> 9 <div class="popup-footer">
10 - <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">Select</button> 10 + <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button>
11 </div> 11 </div>
12 </div> 12 </div>
13 </div> 13 </div>
......
1 <div class="form-group"> 1 <div class="form-group">
2 <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}"> 2 <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}">
3 <input type="hidden" entity-selector-input name="{{$name}}" value=""> 3 <input type="hidden" entity-selector-input name="{{$name}}" value="">
4 - <input type="text" placeholder="Search" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()"> 4 + <input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()">
5 - <div class="text-center loading" ng-show="loading">@include('partials/loading-icon')</div> 5 + <div class="text-center loading" ng-show="loading">@include('partials.loading-icon')</div>
6 <div ng-show="!loading" ng-bind-html="entityResults"></div> 6 <div ng-show="!loading" ng-bind-html="entityResults"></div>
7 </div> 7 </div>
8 </div> 8 </div>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 <div class="popup-body" ng-click="$event.stopPropagation()"> 3 <div class="popup-body" ng-click="$event.stopPropagation()">
4 4
5 <div class="popup-header primary-background"> 5 <div class="popup-header primary-background">
6 - <div class="popup-title">Image Select</div> 6 + <div class="popup-title">{{ trans('components.image_select') }}</div>
7 <button class="popup-close neg corner-button button">x</button> 7 <button class="popup-close neg corner-button button">x</button>
8 </div> 8 </div>
9 9
...@@ -12,16 +12,16 @@ ...@@ -12,16 +12,16 @@
12 <div class="image-manager-content"> 12 <div class="image-manager-content">
13 <div ng-if="imageType === 'gallery'" class="container"> 13 <div ng-if="imageType === 'gallery'" class="container">
14 <div class="image-manager-header row faded-small nav-tabs"> 14 <div class="image-manager-header row faded-small nav-tabs">
15 - <div class="col-xs-4 tab-item" title="View all images" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> All</div> 15 + <div class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.image_all') }}</div>
16 - <div class="col-xs-4 tab-item" title="View images uploaded to this book" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> Book</div> 16 + <div class="col-xs-4 tab-item" title="{{ trans('components.image_book_title') }}" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
17 - <div class="col-xs-4 tab-item" title="View images uploaded to this page" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> Page</div> 17 + <div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
18 </div> 18 </div>
19 </div> 19 </div>
20 <div ng-show="view === 'all'" > 20 <div ng-show="view === 'all'" >
21 <form ng-submit="searchImages()" class="contained-search-box"> 21 <form ng-submit="searchImages()" class="contained-search-box">
22 - <input type="text" placeholder="Search by image name" ng-model="searchTerm"> 22 + <input type="text" placeholder="{{ trans('components.image_search_hint') }}" ng-model="searchTerm">
23 - <button ng-class="{active: searching}" title="Clear Search" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button> 23 + <button ng-class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
24 - <button title="Search" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button> 24 + <button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
25 </form> 25 </form>
26 </div> 26 </div>
27 <div class="image-manager-list"> 27 <div class="image-manager-list">
...@@ -31,11 +31,11 @@ ...@@ -31,11 +31,11 @@
31 <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}"> 31 <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
32 <div class="image-meta"> 32 <div class="image-meta">
33 <span class="name" ng-bind="image.name"></span> 33 <span class="name" ng-bind="image.name"></span>
34 - <span class="date">Uploaded @{{ getDate(image.created_at) }}</span> 34 + <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
35 </div> 35 </div>
36 </div> 36 </div>
37 </div> 37 </div>
38 - <div class="load-more" ng-show="hasMore" ng-click="fetchData()">Load More</div> 38 + <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.image_load_more') }}</div>
39 </div> 39 </div>
40 </div> 40 </div>
41 41
...@@ -51,15 +51,14 @@ ...@@ -51,15 +51,14 @@
51 </a> 51 </a>
52 </div> 52 </div>
53 <div class="form-group"> 53 <div class="form-group">
54 - <label for="name">Image Name</label> 54 + <label for="name">{{ trans('components.image_image_name') }}</label>
55 <input type="text" id="name" name="name" ng-model="selectedImage.name"> 55 <input type="text" id="name" name="name" ng-model="selectedImage.name">
56 </div> 56 </div>
57 </form> 57 </form>
58 58
59 <div ng-show="dependantPages"> 59 <div ng-show="dependantPages">
60 <p class="text-neg text-small"> 60 <p class="text-neg text-small">
61 - This image is used in the pages below, Click delete again to confirm you want to delete 61 + {{ trans('components.image_delete_confirm') }}
62 - this image.
63 </p> 62 </p>
64 <ul class="text-neg"> 63 <ul class="text-neg">
65 <li ng-repeat="page in dependantPages"> 64 <li ng-repeat="page in dependantPages">
...@@ -73,13 +72,13 @@ ...@@ -73,13 +72,13 @@
73 <button class="button icon neg"><i class="zmdi zmdi-delete"></i></button> 72 <button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
74 </form> 73 </form>
75 <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()"> 74 <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()">
76 - <i class="zmdi zmdi-square-right"></i>Select Image 75 + <i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
77 </button> 76 </button>
78 </div> 77 </div>
79 78
80 </div> 79 </div>
81 80
82 - <drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone> 81 + <drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
83 82
84 83
85 </div> 84 </div>
......
1 +<div class="image-picker" image-picker="{{$name}}" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId or '' }}" data-resize-crop="{{ $resizeCrop or '' }}">
2 +
3 + <div>
4 + <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}">
5 + </div>
6 +
7 + <button class="button" type="button" data-action="show-image-manager">{{ trans('components.image_select_image') }}</button>
8 + <br>
9 + <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button>
10 +
11 + @if ($showRemove)
12 + <span class="sep">|</span>
13 + <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button>
14 + @endif
15 +
16 + <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{ isset($currentId) && ($currentId !== '' && $currentId !== false) ? $currentId : $currentImage}}">
17 +</div>
18 +
19 +<script>
20 + (function(){
21 +
22 + var picker = document.querySelector('[image-picker="{{$name}}"]');
23 + picker.addEventListener('click', function(event) {
24 + if (event.target.nodeName.toLowerCase() !== 'button') return;
25 + var button = event.target;
26 + var action = button.getAttribute('data-action');
27 + var resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width');
28 + var usingIds = picker.getAttribute('data-current-id') !== '';
29 + var resizeCrop = picker.getAttribute('data-resize-crop') !== '';
30 + var imageElem = picker.querySelector('img');
31 + var input = picker.querySelector('input');
32 +
33 + function setImage(image) {
34 + if (image === 'none') {
35 + imageElem.src = picker.getAttribute('data-default-image');
36 + imageElem.classList.add('none');
37 + input.value = 'none';
38 + return;
39 + }
40 + imageElem.src = image.url;
41 + input.value = usingIds ? image.id : image.url;
42 + imageElem.classList.remove('none');
43 + }
44 +
45 + if (action === 'show-image-manager') {
46 + window.ImageManager.showExternal((image) => {
47 + if (!resize) {
48 + setImage(image);
49 + return;
50 + }
51 + var requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false');
52 + $.get(window.baseUrl(requestString), resp => {
53 + image.url = resp.url;
54 + setImage(image);
55 + });
56 + });
57 + } else if (action === 'reset-image') {
58 + setImage({id: 0, url: picker.getAttribute('data-default-image')});
59 + } else if (action === 'remove-image') {
60 + setImage('none');
61 + }
62 +
63 + });
64 +
65 + })();
66 +</script>
...\ No newline at end of file ...\ No newline at end of file
1 +<div toggle-switch="{{$name}}" class="toggle-switch @if($value) active @endif">
2 + <input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/>
3 + <div class="switch-handle"></div>
4 +</div>
5 +<script>
6 + (function() {
7 + var toggle = document.querySelector('[toggle-switch="{{$name}}"]');
8 + var toggleInput = toggle.querySelector('input');
9 + toggle.onclick = function(event) {
10 + var checked = toggleInput.value !== 'true';
11 + toggleInput.value = checked ? 'true' : 'false';
12 + checked ? toggle.classList.add('active') : toggle.classList.remove('active');
13 + };
14 + })()
15 +</script>
...\ No newline at end of file ...\ No newline at end of file
...@@ -4,9 +4,28 @@ ...@@ -4,9 +4,28 @@
4 4
5 5
6 <div class="container"> 6 <div class="container">
7 - <h1 class="text-muted">{{ $message or 'Page Not Found' }}</h1> 7 +
8 - <p>Sorry, The page you were looking for could not be found.</p> 8 +
9 - <a href="{{ baseUrl('/') }}" class="button">Return To Home</a> 9 + <h1>{{ $message or trans('errors.404_page_not_found') }}</h1>
10 + <p>{{ trans('errors.sorry_page_not_found') }}</p>
11 + <p><a href="{{ baseUrl('/') }}" class="button">{{ trans('errors.return_home') }}</a></p>
12 +
13 + <hr>
14 +
15 + <div class="row">
16 + <div class="col-md-4">
17 + <h3 class="text-muted">{{ trans('entities.pages_popular') }}</h3>
18 + @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Page::class]), 'style' => 'compact'])
19 + </div>
20 + <div class="col-md-4">
21 + <h3 class="text-muted">{{ trans('entities.books_popular') }}</h3>
22 + @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Book::class]), 'style' => 'compact'])
23 + </div>
24 + <div class="col-md-4">
25 + <h3 class="text-muted">{{ trans('entities.chapters_popular') }}</h3>
26 + @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Chapter::class]), 'style' => 'compact'])
27 + </div>
28 + </div>
10 </div> 29 </div>
11 30
12 @stop 31 @stop
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 @section('content') 3 @section('content')
4 4
5 <div class="container"> 5 <div class="container">
6 - <h1 class="text-muted">An Error Occurred</h1> 6 + <h1 class="text-muted">{{ trans('errors.error_occurred') }}</h1>
7 <p>{{ $message }}</p> 7 <p>{{ $message }}</p>
8 </div> 8 </div>
9 9
......