Dan Brown
Committed by GitHub

Merge pull request #234 from BookStackApp/translations

Setup for translations
Showing 137 changed files with 1007 additions and 575 deletions
......@@ -77,7 +77,7 @@ class AttachmentController extends Controller
$this->checkOwnablePermission('attachment-create', $attachment);
if (intval($pageId) !== intval($attachment->uploaded_to)) {
return $this->jsonError('Page mismatch during attached file update');
return $this->jsonError(trans('errors.attachment_page_mismatch'));
}
$uploadedFile = $request->file('file');
......@@ -113,11 +113,11 @@ class AttachmentController extends Controller
$this->checkOwnablePermission('attachment-create', $attachment);
if (intval($pageId) !== intval($attachment->uploaded_to)) {
return $this->jsonError('Page mismatch during attachment update');
return $this->jsonError(trans('errors.attachment_page_mismatch'));
}
$attachment = $this->attachmentService->updateFile($attachment, $request->all());
return $attachment;
return response()->json($attachment);
}
/**
......@@ -175,7 +175,7 @@ class AttachmentController extends Controller
$attachments = $request->get('files');
$this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
return response()->json(['message' => 'Attachment order updated']);
return response()->json(['message' => trans('entities.attachments_order_updated')]);
}
/**
......@@ -210,6 +210,6 @@ class AttachmentController extends Controller
$attachment = $this->attachment->findOrFail($attachmentId);
$this->checkOwnablePermission('attachment-delete', $attachment);
$this->attachmentService->deleteFile($attachment);
return response()->json(['message' => 'Attachment deleted']);
return response()->json(['message' => trans('entities.attachments_deleted')]);
}
}
......
......@@ -52,7 +52,7 @@ class ForgotPasswordController extends Controller
);
if ($response === Password::RESET_LINK_SENT) {
$message = 'A password reset link has been sent to ' . $request->get('email') . '.';
$message = trans('auth.reset_password_sent_success', ['email' => $request->get('email')]);
session()->flash('success', $message);
return back()->with('status', trans($response));
}
......
......@@ -87,7 +87,7 @@ class LoginController extends Controller
// Check for users with same email already
$alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
if ($alreadyUser) {
throw new AuthException('A user with the email ' . $user->email . ' already exists but with different credentials.');
throw new AuthException(trans('errors.error_user_exists_different_creds', ['email' => $user->email]));
}
$user->save();
......
......@@ -3,6 +3,7 @@
namespace BookStack\Http\Controllers\Auth;
use BookStack\Exceptions\ConfirmationEmailException;
use BookStack\Exceptions\SocialSignInException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Repos\UserRepo;
use BookStack\Services\EmailConfirmationService;
......@@ -82,7 +83,7 @@ class RegisterController extends Controller
protected function checkRegistrationAllowed()
{
if (!setting('registration-enabled')) {
throw new UserRegistrationException('Registrations are currently disabled.', '/login');
throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
}
}
......@@ -147,7 +148,7 @@ class RegisterController extends Controller
$restrictedEmailDomains = explode(',', str_replace(' ', '', setting('registration-restrict')));
$userEmailDomain = $domain = substr(strrchr($userData['email'], "@"), 1);
if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
throw new UserRegistrationException('That email domain does not have access to this application', '/register');
throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), '/register');
}
}
......@@ -169,7 +170,7 @@ class RegisterController extends Controller
}
auth()->login($newUser);
session()->flash('success', 'Thanks for signing up! You are now registered and signed in.');
session()->flash('success', trans('auth.register_success'));
return redirect($this->redirectPath());
}
......@@ -262,7 +263,7 @@ class RegisterController extends Controller
return $this->socialRegisterCallback($socialDriver);
}
} else {
throw new SocialSignInException('No action defined', '/login');
throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
}
return redirect()->back();
}
......
......@@ -41,7 +41,7 @@ class ResetPasswordController extends Controller
*/
protected function sendResetResponse($response)
{
$message = 'Your password has been successfully reset.';
$message = trans('auth.reset_password_success');
session()->flash('success', $message);
return redirect($this->redirectPath())
->with('status', trans($response));
......
......@@ -7,6 +7,7 @@ use BookStack\Http\Requests;
use BookStack\Repos\BookRepo;
use BookStack\Repos\ChapterRepo;
use BookStack\Repos\PageRepo;
use Illuminate\Http\Response;
use Views;
class BookController extends Controller
......@@ -53,7 +54,7 @@ class BookController extends Controller
public function create()
{
$this->checkPermission('book-create-all');
$this->setPageTitle('Create New Book');
$this->setPageTitle(trans('entities.books_create'));
return view('books/create');
}
......@@ -99,7 +100,7 @@ class BookController extends Controller
{
$book = $this->bookRepo->getBySlug($slug);
$this->checkOwnablePermission('book-update', $book);
$this->setPageTitle('Edit Book ' . $book->getShortName());
$this->setPageTitle(trans('entities.books_edit_named',['bookName'=>$book->getShortName()]));
return view('books/edit', ['book' => $book, 'current' => $book]);
}
......@@ -131,7 +132,7 @@ class BookController extends Controller
{
$book = $this->bookRepo->getBySlug($bookSlug);
$this->checkOwnablePermission('book-delete', $book);
$this->setPageTitle('Delete Book ' . $book->getShortName());
$this->setPageTitle(trans('entities.books_delete_named', ['bookName'=>$book->getShortName()]));
return view('books/delete', ['book' => $book, 'current' => $book]);
}
......@@ -146,7 +147,7 @@ class BookController extends Controller
$this->checkOwnablePermission('book-update', $book);
$bookChildren = $this->bookRepo->getChildren($book, true);
$books = $this->bookRepo->getAll(false);
$this->setPageTitle('Sort Book ' . $book->getShortName());
$this->setPageTitle(trans('entities.books_sort_named', ['bookName'=>$book->getShortName()]));
return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]);
}
......@@ -264,7 +265,7 @@ class BookController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$this->checkOwnablePermission('restrictions-manage', $book);
$this->bookRepo->updateEntityPermissionsFromRequest($request, $book);
session()->flash('success', 'Book Restrictions Updated');
session()->flash('success', trans('entities.books_permissions_updated'));
return redirect($book->getUrl());
}
}
......
......@@ -3,9 +3,9 @@
use Activity;
use BookStack\Repos\UserRepo;
use Illuminate\Http\Request;
use BookStack\Http\Requests;
use BookStack\Repos\BookRepo;
use BookStack\Repos\ChapterRepo;
use Illuminate\Http\Response;
use Views;
class ChapterController extends Controller
......@@ -38,7 +38,7 @@ class ChapterController extends Controller
{
$book = $this->bookRepo->getBySlug($bookSlug);
$this->checkOwnablePermission('chapter-create', $book);
$this->setPageTitle('Create New Chapter');
$this->setPageTitle(trans('entities.chapters_create'));
return view('chapters/create', ['book' => $book, 'current' => $book]);
}
......@@ -99,7 +99,7 @@ class ChapterController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
$this->checkOwnablePermission('chapter-update', $chapter);
$this->setPageTitle('Edit Chapter' . $chapter->getShortName());
$this->setPageTitle(trans('entities.chapters_edit_named', ['chapterName' => $chapter->getShortName()]));
return view('chapters/edit', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]);
}
......@@ -136,7 +136,7 @@ class ChapterController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
$this->checkOwnablePermission('chapter-delete', $chapter);
$this->setPageTitle('Delete Chapter' . $chapter->getShortName());
$this->setPageTitle(trans('entities.chapters_delete_named', ['chapterName' => $chapter->getShortName()]));
return view('chapters/delete', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]);
}
......@@ -166,6 +166,7 @@ class ChapterController extends Controller
public function showMove($bookSlug, $chapterSlug) {
$book = $this->bookRepo->getBySlug($bookSlug);
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
$this->setPageTitle(trans('entities.chapters_move_named', ['chapterName' => $chapter->getShortName()]));
$this->checkOwnablePermission('chapter-update', $chapter);
return view('chapters/move', [
'chapter' => $chapter,
......@@ -202,13 +203,13 @@ class ChapterController extends Controller
}
if ($parent === false || $parent === null) {
session()->flash('The selected Book was not found');
session()->flash('error', trans('errors.selected_book_not_found'));
return redirect()->back();
}
$this->chapterRepo->changeBook($parent->id, $chapter, true);
Activity::add($chapter, 'chapter_move', $chapter->book->id);
session()->flash('success', sprintf('Chapter moved to "%s"', $parent->name));
session()->flash('success', trans('entities.chapter_move_success', ['bookName' => $parent->name]));
return redirect($chapter->getUrl());
}
......@@ -244,7 +245,7 @@ class ChapterController extends Controller
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
$this->checkOwnablePermission('restrictions-manage', $chapter);
$this->chapterRepo->updateEntityPermissionsFromRequest($request, $chapter);
session()->flash('success', 'Chapter Restrictions Updated');
session()->flash('success', trans('entities.chapters_permissions_success'));
return redirect($chapter->getUrl());
}
}
......
......@@ -43,4 +43,39 @@ class HomeController extends Controller
]);
}
/**
* Get a js representation of the current translations
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
*/
public function getTranslations() {
$locale = trans()->getLocale();
$cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale;
if (cache()->has($cacheKey) && config('app.env') !== 'development') {
$resp = cache($cacheKey);
} else {
$translations = [
// Get only translations which might be used in JS
'common' => trans('common'),
'components' => trans('components'),
'entities' => trans('entities'),
'errors' => trans('errors')
];
if ($locale !== 'en') {
$enTrans = [
'common' => trans('common', [], null, 'en'),
'components' => trans('components', [], null, 'en'),
'entities' => trans('entities', [], null, 'en'),
'errors' => trans('errors', [], null, 'en')
];
$translations = array_replace_recursive($enTrans, $translations);
}
$resp = 'window.translations = ' . json_encode($translations);
cache()->put($cacheKey, $resp, 120);
}
return response($resp, 200, [
'Content-Type' => 'application/javascript'
]);
}
}
......
......@@ -73,6 +73,7 @@ class ImageController extends Controller
* @param $filter
* @param int $page
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
*/
public function getGalleryFiltered($filter, $page = 0, Request $request)
{
......@@ -169,7 +170,7 @@ class ImageController extends Controller
}
$this->imageRepo->destroyImage($image);
return response()->json('Image Deleted');
return response()->json(trans('components.images_deleted'));
}
......
......@@ -10,6 +10,7 @@ use BookStack\Http\Requests;
use BookStack\Repos\BookRepo;
use BookStack\Repos\ChapterRepo;
use BookStack\Repos\PageRepo;
use Illuminate\Http\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Views;
use GatherContent\Htmldiff\Htmldiff;
......@@ -62,7 +63,7 @@ class PageController extends Controller
}
// Otherwise show edit view
$this->setPageTitle('Create New Page');
$this->setPageTitle(trans('entities.pages_new'));
return view('pages/guest-create', ['parent' => $parent]);
}
......@@ -104,7 +105,7 @@ class PageController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$draft = $this->pageRepo->getById($pageId, true);
$this->checkOwnablePermission('page-create', $book);
$this->setPageTitle('Edit Page Draft');
$this->setPageTitle(trans('entities.pages_edit_draft'));
$draftsEnabled = $this->signedIn;
return view('pages/edit', [
......@@ -119,6 +120,7 @@ class PageController extends Controller
* Store a new page by changing a draft into a page.
* @param Request $request
* @param string $bookSlug
* @param int $pageId
* @return Response
*/
public function store(Request $request, $bookSlug, $pageId)
......@@ -201,7 +203,7 @@ class PageController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
$this->checkOwnablePermission('page-update', $page);
$this->setPageTitle('Editing Page ' . $page->getShortName());
$this->setPageTitle(trans('entities.pages_editing_named', ['pageName'=>$page->getShortName()]));
$page->isDraft = false;
// Check for active editing
......@@ -265,7 +267,7 @@ class PageController extends Controller
if (!$this->signedIn) {
return response()->json([
'status' => 'error',
'message' => 'Guests cannot save drafts',
'message' => trans('errors.guests_cannot_save_drafts'),
], 500);
}
......@@ -279,7 +281,7 @@ class PageController extends Controller
$utcUpdateTimestamp = $updateTime + Carbon::createFromTimestamp(0)->offset;
return response()->json([
'status' => 'success',
'message' => 'Draft saved at ',
'message' => trans('entities.pages_edit_draft_save_at'),
'timestamp' => $utcUpdateTimestamp
]);
}
......@@ -307,7 +309,7 @@ class PageController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
$this->checkOwnablePermission('page-delete', $page);
$this->setPageTitle('Delete Page ' . $page->getShortName());
$this->setPageTitle(trans('entities.pages_delete_named', ['pageName'=>$page->getShortName()]));
return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
}
......@@ -324,7 +326,7 @@ class PageController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getById($pageId, true);
$this->checkOwnablePermission('page-update', $page);
$this->setPageTitle('Delete Draft Page ' . $page->getShortName());
$this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName'=>$page->getShortName()]));
return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
}
......@@ -341,7 +343,7 @@ class PageController extends Controller
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
$this->checkOwnablePermission('page-delete', $page);
Activity::addMessage('page_delete', $book->id, $page->name);
session()->flash('success', 'Page deleted');
session()->flash('success', trans('entities.pages_delete_success'));
$this->pageRepo->destroy($page);
return redirect($book->getUrl());
}
......@@ -358,7 +360,7 @@ class PageController extends Controller
$book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getById($pageId, true);
$this->checkOwnablePermission('page-update', $page);
session()->flash('success', 'Draft deleted');
session()->flash('success', trans('entities.pages_delete_draft_success'));
$this->pageRepo->destroy($page);
return redirect($book->getUrl());
}
......@@ -373,7 +375,7 @@ class PageController extends Controller
{
$book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
$this->setPageTitle('Revisions For ' . $page->getShortName());
$this->setPageTitle(trans('entities.pages_revisions_named', ['pageName'=>$page->getShortName()]));
return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]);
}
......@@ -391,7 +393,7 @@ class PageController extends Controller
$revision = $this->pageRepo->getRevisionById($revisionId);
$page->fill($revision->toArray());
$this->setPageTitle('Page Revision For ' . $page->getShortName());
$this->setPageTitle(trans('entities.pages_revision_named', ['pageName'=>$page->getShortName()]));
return view('pages/revision', [
'page' => $page,
......@@ -417,7 +419,7 @@ class PageController extends Controller
$diff = (new Htmldiff)->diff($prevContent, $revision->html);
$page->fill($revision->toArray());
$this->setPageTitle('Page Revision For ' . $page->getShortName());
$this->setPageTitle(trans('entities.pages_revision_named', ['pageName'=>$page->getShortName()]));
return view('pages/revision', [
'page' => $page,
......@@ -503,7 +505,7 @@ class PageController extends Controller
{
$pages = $this->pageRepo->getRecentlyCreatedPaginated(20)->setPath(baseUrl('/pages/recently-created'));
return view('pages/detailed-listing', [
'title' => 'Recently Created Pages',
'title' => trans('entities.recently_created_pages'),
'pages' => $pages
]);
}
......@@ -516,7 +518,7 @@ class PageController extends Controller
{
$pages = $this->pageRepo->getRecentlyUpdatedPaginated(20)->setPath(baseUrl('/pages/recently-updated'));
return view('pages/detailed-listing', [
'title' => 'Recently Updated Pages',
'title' => trans('entities.recently_updated_pages'),
'pages' => $pages
]);
}
......@@ -589,13 +591,13 @@ class PageController extends Controller
}
if ($parent === false || $parent === null) {
session()->flash('The selected Book or Chapter was not found');
session()->flash(trans('entities.selected_book_chapter_not_found'));
return redirect()->back();
}
$this->pageRepo->changePageParent($page, $parent);
Activity::add($page, 'page_move', $page->book->id);
session()->flash('success', sprintf('Page moved to "%s"', $parent->name));
session()->flash('success', trans('entities.pages_move_success', ['parentName' => $parent->name]));
return redirect($page->getUrl());
}
......@@ -613,7 +615,7 @@ class PageController extends Controller
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
$this->checkOwnablePermission('restrictions-manage', $page);
$this->pageRepo->updateEntityPermissionsFromRequest($request, $page);
session()->flash('success', 'Page Permissions Updated');
session()->flash('success', trans('entities.pages_permissions_success'));
return redirect($page->getUrl());
}
......
......@@ -2,9 +2,7 @@
use BookStack\Exceptions\PermissionsException;
use BookStack\Repos\PermissionsRepo;
use BookStack\Services\PermissionService;
use Illuminate\Http\Request;
use BookStack\Http\Requests;
class PermissionController extends Controller
{
......@@ -55,7 +53,7 @@ class PermissionController extends Controller
]);
$this->permissionsRepo->saveNewRole($request->all());
session()->flash('success', 'Role successfully created');
session()->flash('success', trans('settings.role_create_success'));
return redirect('/settings/roles');
}
......@@ -69,7 +67,7 @@ class PermissionController extends Controller
{
$this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id);
if ($role->hidden) throw new PermissionsException('This role cannot be edited');
if ($role->hidden) throw new PermissionsException(trans('errors.role_cannot_be_edited'));
return view('settings/roles/edit', ['role' => $role]);
}
......@@ -88,7 +86,7 @@ class PermissionController extends Controller
]);
$this->permissionsRepo->updateRole($id, $request->all());
session()->flash('success', 'Role successfully updated');
session()->flash('success', trans('settings.role_update_success'));
return redirect('/settings/roles');
}
......@@ -103,7 +101,7 @@ class PermissionController extends Controller
$this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id);
$roles = $this->permissionsRepo->getAllRolesExcept($role);
$blankRole = $role->newInstance(['display_name' => 'Don\'t migrate users']);
$blankRole = $role->newInstance(['display_name' => trans('settings.role_delete_no_migration')]);
$roles->prepend($blankRole);
return view('settings/roles/delete', ['role' => $role, 'roles' => $roles]);
}
......@@ -126,7 +124,7 @@ class PermissionController extends Controller
return redirect()->back();
}
session()->flash('success', 'Role successfully deleted');
session()->flash('success', trans('settings.role_delete_success'));
return redirect('/settings/roles');
}
}
......
<?php
namespace BookStack\Http\Controllers;
<?php namespace BookStack\Http\Controllers;
use BookStack\Services\ViewService;
use Illuminate\Http\Request;
use BookStack\Http\Requests;
use BookStack\Repos\BookRepo;
use BookStack\Repos\ChapterRepo;
use BookStack\Repos\PageRepo;
......@@ -49,7 +45,7 @@ class SearchController extends Controller
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
$books = $this->bookRepo->getBySearch($searchTerm, 10, $paginationAppends);
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 10, $paginationAppends);
$this->setPageTitle('Search For ' . $searchTerm);
$this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
return view('search/all', [
'pages' => $pages,
'books' => $books,
......@@ -70,10 +66,10 @@ class SearchController extends Controller
$searchTerm = $request->get('term');
$paginationAppends = $request->only('term');
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
$this->setPageTitle('Page Search For ' . $searchTerm);
$this->setPageTitle(trans('entities.search_page_for_term', ['term' => $searchTerm]));
return view('search/entity-search-list', [
'entities' => $pages,
'title' => 'Page Search Results',
'title' => trans('entities.search_results_page'),
'searchTerm' => $searchTerm
]);
}
......@@ -90,10 +86,10 @@ class SearchController extends Controller
$searchTerm = $request->get('term');
$paginationAppends = $request->only('term');
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
$this->setPageTitle('Chapter Search For ' . $searchTerm);
$this->setPageTitle(trans('entities.search_chapter_for_term', ['term' => $searchTerm]));
return view('search/entity-search-list', [
'entities' => $chapters,
'title' => 'Chapter Search Results',
'title' => trans('entities.search_results_chapter'),
'searchTerm' => $searchTerm
]);
}
......@@ -110,10 +106,10 @@ class SearchController extends Controller
$searchTerm = $request->get('term');
$paginationAppends = $request->only('term');
$books = $this->bookRepo->getBySearch($searchTerm, 20, $paginationAppends);
$this->setPageTitle('Book Search For ' . $searchTerm);
$this->setPageTitle(trans('entities.search_book_for_term', ['term' => $searchTerm]));
return view('search/entity-search-list', [
'entities' => $books,
'title' => 'Book Search Results',
'title' => trans('entities.search_results_book'),
'searchTerm' => $searchTerm
]);
}
......
<?php namespace BookStack\Http\Controllers;
use Illuminate\Http\Request;
use BookStack\Http\Requests;
use Illuminate\Http\Response;
use Setting;
class SettingController extends Controller
......@@ -39,7 +38,7 @@ class SettingController extends Controller
Setting::put($key, $value);
}
session()->flash('success', 'Settings Saved');
session()->flash('success', trans('settings.settings_save_success'));
return redirect('/settings');
}
......
......@@ -2,7 +2,6 @@
use BookStack\Repos\TagRepo;
use Illuminate\Http\Request;
use BookStack\Http\Requests;
class TagController extends Controller
{
......@@ -16,12 +15,14 @@ class TagController extends Controller
public function __construct(TagRepo $tagRepo)
{
$this->tagRepo = $tagRepo;
parent::__construct();
}
/**
* Get all the Tags for a particular entity
* @param $entityType
* @param $entityId
* @return \Illuminate\Http\JsonResponse
*/
public function getForEntity($entityType, $entityId)
{
......@@ -30,28 +31,9 @@ class TagController extends Controller
}
/**
* Update the tags for a particular entity.
* @param $entityType
* @param $entityId
* @param Request $request
* @return mixed
*/
public function updateForEntity($entityType, $entityId, Request $request)
{
$entity = $this->tagRepo->getEntity($entityType, $entityId, 'update');
if ($entity === null) return $this->jsonError("Entity not found", 404);
$inputTags = $request->input('tags');
$tags = $this->tagRepo->saveTagsToEntity($entity, $inputTags);
return response()->json([
'tags' => $tags,
'message' => 'Tags successfully updated'
]);
}
/**
* Get tag name suggestions from a given search term.
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function getNameSuggestions(Request $request)
{
......@@ -63,6 +45,7 @@ class TagController extends Controller
/**
* Get tag value suggestions from a given search term.
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function getValueSuggestions(Request $request)
{
......
......@@ -44,7 +44,7 @@ class UserController extends Controller
'sort' => $request->has('sort') ? $request->get('sort') : 'name',
];
$users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
$this->setPageTitle('Users');
$this->setPageTitle(trans('settings.users'));
$users->appends($listDetails);
return view('users/index', ['users' => $users, 'listDetails' => $listDetails]);
}
......@@ -83,7 +83,6 @@ class UserController extends Controller
}
$this->validate($request, $validationRules);
$user = $this->user->fill($request->all());
if ($authMethod === 'standard') {
......@@ -131,7 +130,7 @@ class UserController extends Controller
$authMethod = ($user->system_name) ? 'system' : config('auth.method');
$activeSocialDrivers = $socialAuthService->getActiveDrivers();
$this->setPageTitle('User Profile');
$this->setPageTitle(trans('settings.user_profile'));
$roles = $this->userRepo->getAllRoles();
return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
}
......@@ -154,8 +153,6 @@ class UserController extends Controller
'email' => 'min:2|email|unique:users,email,' . $id,
'password' => 'min:5|required_with:password_confirm',
'password-confirm' => 'same:password|required_with:password'
], [
'password-confirm.required_with' => 'Password confirmation required'
]);
$user = $this->user->findOrFail($id);
......@@ -179,7 +176,7 @@ class UserController extends Controller
}
$user->save();
session()->flash('success', 'User successfully updated');
session()->flash('success', trans('settings.users_edit_success'));
$redirectUrl = userCan('users-manage') ? '/settings/users' : '/settings/users/' . $user->id;
return redirect($redirectUrl);
......@@ -197,7 +194,7 @@ class UserController extends Controller
});
$user = $this->user->findOrFail($id);
$this->setPageTitle('Delete User ' . $user->name);
$this->setPageTitle(trans('settings.users_delete_named', ['userName' => $user->name]));
return view('users/delete', ['user' => $user]);
}
......@@ -216,17 +213,17 @@ class UserController extends Controller
$user = $this->userRepo->getById($id);
if ($this->userRepo->isOnlyAdmin($user)) {
session()->flash('error', 'You cannot delete the only admin');
session()->flash('error', trans('errors.users_cannot_delete_only_admin'));
return redirect($user->getEditUrl());
}
if ($user->system_name === 'public') {
session()->flash('error', 'You cannot delete the guest user');
session()->flash('error', trans('errors.users_cannot_delete_guest'));
return redirect($user->getEditUrl());
}
$this->userRepo->destroy($user);
session()->flash('success', 'User successfully removed');
session()->flash('success', trans('settings.users_delete_success'));
return redirect('/settings/users');
}
......
......@@ -43,8 +43,9 @@ class ResetPassword extends Notification
public function toMail()
{
return (new MailMessage)
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', baseUrl('password/reset/' . $this->token))
->line('If you did not request a password reset, no further action is required.');
->subject(trans('auth.email_reset_subject', ['appName' => setting('app-name')]))
->line(trans('auth.email_reset_text'))
->action(trans('auth.reset_password'), baseUrl('password/reset/' . $this->token))
->line(trans('auth.email_reset_not_requested'));
}
}
......
......@@ -109,7 +109,7 @@ class BookRepo extends EntityRepo
public function getBySlug($slug)
{
$book = $this->bookQuery()->where('slug', '=', $slug)->first();
if ($book === null) throw new NotFoundException('Book not found');
if ($book === null) throw new NotFoundException(trans('errors.book_not_found'));
return $book;
}
......
......@@ -69,7 +69,7 @@ class ChapterRepo extends EntityRepo
public function getBySlug($slug, $bookId)
{
$chapter = $this->chapterQuery()->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
if ($chapter === null) throw new NotFoundException('Chapter not found');
if ($chapter === null) throw new NotFoundException(trans('errors.chapter_not_found'));
return $chapter;
}
......
......@@ -66,7 +66,7 @@ class PageRepo extends EntityRepo
public function getBySlug($slug, $bookId)
{
$page = $this->pageQuery()->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
if ($page === null) throw new NotFoundException('Page not found');
if ($page === null) throw new NotFoundException(trans('errors.page_not_found'));
return $page;
}
......@@ -134,7 +134,7 @@ class PageRepo extends EntityRepo
$draftPage->draft = false;
$draftPage->save();
$this->saveRevision($draftPage, 'Initial Publish');
$this->saveRevision($draftPage, trans('entities.pages_initial_revision'));
return $draftPage;
}
......@@ -143,12 +143,12 @@ class PageRepo extends EntityRepo
* Get a new draft page instance.
* @param Book $book
* @param Chapter|bool $chapter
* @return static
* @return Page
*/
public function getDraftPage(Book $book, $chapter = false)
{
$page = $this->page->newInstance();
$page->name = 'New Page';
$page->name = trans('entities.pages_initial_name');
$page->created_by = user()->id;
$page->updated_by = user()->id;
$page->draft = true;
......@@ -487,11 +487,9 @@ class PageRepo extends EntityRepo
*/
public function getUserPageDraftMessage(PageRevision $draft)
{
$message = 'You are currently editing a draft that was last saved ' . $draft->updated_at->diffForHumans() . '.';
if ($draft->page->updated_at->timestamp > $draft->updated_at->timestamp) {
$message .= "\n This page has been updated by since that time. It is recommended that you discard this draft.";
}
return $message;
$message = trans('entities.pages_editing_draft_notification', ['timeDiff' => $draft->updated_at->diffForHumans()]);
if ($draft->page->updated_at->timestamp <= $draft->updated_at->timestamp) return $message;
return $message . "\n" . trans('entities.pages_draft_edited_notification');
}
/**
......@@ -519,10 +517,10 @@ class PageRepo extends EntityRepo
public function getPageEditingActiveMessage(Page $page, $minRange = null)
{
$pageDraftEdits = $this->activePageEditingQuery($page, $minRange)->get();
$userMessage = $pageDraftEdits->count() > 1 ? $pageDraftEdits->count() . ' users have' : $pageDraftEdits->first()->createdBy->name . ' has';
$timeMessage = $minRange === null ? 'since the page was last updated' : 'in the last ' . $minRange . ' minutes';
$message = '%s started editing this page %s. Take care not to overwrite each other\'s updates!';
return sprintf($message, $userMessage, $timeMessage);
$userMessage = $pageDraftEdits->count() > 1 ? trans('entities.pages_draft_edit_active.start_a', ['count' => $pageDraftEdits->count()]): trans('entities.pages_draft_edit_active.start_b', ['userName' => $pageDraftEdits->first()->createdBy->name]);
$timeMessage = $minRange === null ? trans('entities.pages_draft_edit_active.time_a') : trans('entities.pages_draft_edit_active.time_b', ['minCount'=>$minRange]);
return trans('entities.pages_draft_edit_active.message', ['start' => $userMessage, 'time' => $timeMessage]);
}
/**
......
......@@ -133,9 +133,9 @@ class PermissionsRepo
// Prevent deleting admin role or default registration role.
if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
throw new PermissionsException('This role is a system role and cannot be deleted');
throw new PermissionsException(trans('errors.role_system_cannot_be_deleted'));
} else if ($role->id == setting('registration-role')) {
throw new PermissionsException('This role cannot be deleted while set as the default registration role.');
throw new PermissionsException(trans('errors.role_registration_default_cannot_delete'));
}
if ($migrateRoleId) {
......
......@@ -121,7 +121,7 @@ class TagRepo
/**
* Create a new Tag instance from user input.
* @param $input
* @return static
* @return Tag
*/
protected function newInstanceFromInput($input)
{
......
......@@ -3,7 +3,6 @@
use BookStack\Role;
use BookStack\User;
use Exception;
use Setting;
class UserRepo
{
......
......@@ -193,7 +193,7 @@ class AttachmentService extends UploadService
try {
$storage->put($attachmentStoragePath, $attachmentData);
} catch (Exception $e) {
throw new FileUploadException('File path ' . $attachmentStoragePath . ' could not be uploaded to. Ensure it is writable to the server.');
throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentStoragePath]));
}
return $attachmentPath;
}
......
......@@ -33,7 +33,7 @@ class EmailConfirmationService
public function sendConfirmation(User $user)
{
if ($user->email_confirmed) {
throw new ConfirmationEmailException('Email has already been confirmed, Try logging in.', '/login');
throw new ConfirmationEmailException(trans('errors.email_already_confirmed'), '/login');
}
$this->deleteConfirmationsByUser($user);
......@@ -63,7 +63,7 @@ class EmailConfirmationService
* Gets an email confirmation by looking up the token,
* Ensures the token has not expired.
* @param string $token
* @return EmailConfirmation
* @return array|null|\stdClass
* @throws UserRegistrationException
*/
public function getEmailConfirmationFromToken($token)
......@@ -72,14 +72,14 @@ class EmailConfirmationService
// If not found show error
if ($emailConfirmation === null) {
throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register');
throw new UserRegistrationException(trans('errors.email_confirmation_invalid'), '/register');
}
// If more than a day old
if (Carbon::now()->subDay()->gt(new Carbon($emailConfirmation->created_at))) {
$user = $this->users->getById($emailConfirmation->user_id);
$this->sendConfirmation($user);
throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm');
throw new UserRegistrationException(trans('errors.email_confirmation_expired'), '/register/confirm');
}
$emailConfirmation->user = $this->users->getById($emailConfirmation->user_id);
......
......@@ -59,7 +59,7 @@ class ImageService extends UploadService
{
$imageName = $imageName ? $imageName : basename($url);
$imageData = file_get_contents($url);
if($imageData === false) throw new \Exception('Cannot get image from ' . $url);
if($imageData === false) throw new \Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
return $this->saveNew($imageName, $imageData, $type);
}
......@@ -93,7 +93,7 @@ class ImageService extends UploadService
$storage->put($fullPath, $imageData);
$storage->setVisibility($fullPath, 'public');
} catch (Exception $e) {
throw new ImageUploadException('Image Path ' . $fullPath . ' is not writable by the server.');
throw new ImageUploadException(trans('errors.path_not_writable', ['filePath' => $fullPath]));
}
if ($this->isLocal()) $fullPath = str_replace_first('/public', '', $fullPath);
......@@ -160,7 +160,7 @@ class ImageService extends UploadService
$thumb = $this->imageTool->make($storage->get($imagePath));
} catch (Exception $e) {
if ($e instanceof \ErrorException || $e instanceof NotSupportedException) {
throw new ImageUploadException('The server cannot create thumbnails. Please check you have the GD PHP extension installed.');
throw new ImageUploadException(trans('errors.cannot_create_thumbs'));
} else {
throw $e;
}
......
......@@ -94,7 +94,7 @@ class LdapService
$ldapBind = $this->ldap->bind($connection, $ldapDn, $ldapPass);
}
if (!$ldapBind) throw new LdapException('LDAP access failed using ' . ($isAnonymous ? ' anonymous bind.' : ' given dn & pass details'));
if (!$ldapBind) throw new LdapException(($isAnonymous ? trans('errors.ldap_fail_anonymous') : trans('errors.ldap_fail_authed')));
}
/**
......@@ -109,7 +109,7 @@ class LdapService
// Check LDAP extension in installed
if (!function_exists('ldap_connect') && config('app.env') !== 'testing') {
throw new LdapException('LDAP PHP extension not installed');
throw new LdapException(trans('errors.ldap_extension_not_installed'));
}
// Get port from server string if specified.
......@@ -117,7 +117,7 @@ class LdapService
$ldapConnection = $this->ldap->connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389);
if ($ldapConnection === false) {
throw new LdapException('Cannot connect to ldap server, Initial connection failed');
throw new LdapException(trans('errors.ldap_cannot_connect'));
}
// Set any required options
......
......@@ -70,12 +70,12 @@ class SocialAuthService
// Check social account has not already been used
if ($this->socialAccount->where('driver_id', '=', $socialUser->getId())->exists()) {
throw new UserRegistrationException('This ' . $socialDriver . ' account is already in use, Try logging in via the ' . $socialDriver . ' option.', '/login');
throw new UserRegistrationException(trans('errors.social_account_in_use', ['socialAccount'=>$socialDriver]), '/login');
}
if ($this->userRepo->getByEmail($socialUser->getEmail())) {
$email = $socialUser->getEmail();
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');
throw new UserRegistrationException(trans('errors.social_account_in_use', ['socialAccount'=>$socialDriver, 'email' => $email]), '/login');
}
return $socialUser;
......@@ -113,27 +113,26 @@ class SocialAuthService
if ($isLoggedIn && $socialAccount === null) {
$this->fillSocialAccount($socialDriver, $socialUser);
$currentUser->socialAccounts()->save($this->socialAccount);
session()->flash('success', title_case($socialDriver) . ' account was successfully attached to your profile.');
session()->flash('success', trans('settings.users_social_connected', ['socialAccount' => title_case($socialDriver)]));
return redirect($currentUser->getEditUrl());
}
// When a user is logged in and the social account exists and is already linked to the current user.
if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) {
session()->flash('error', 'This ' . title_case($socialDriver) . ' account is already attached to your profile.');
session()->flash('error', trans('errors.social_account_existing', ['socialAccount' => title_case($socialDriver)]));
return redirect($currentUser->getEditUrl());
}
// When a user is logged in, A social account exists but the users do not match.
// Change the user that the social account is assigned to.
if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) {
session()->flash('success', 'This ' . title_case($socialDriver) . ' account is already used by another user.');
session()->flash('error', trans('errors.social_account_already_used_existing', ['socialAccount' => title_case($socialDriver)]));
return redirect($currentUser->getEditUrl());
}
// Otherwise let the user know this social account is not used by anyone.
$message = 'This ' . $socialDriver . ' account is not linked to any users. Please attach it in your profile settings';
$message = trans('errors.social_account_not_used', ['socialAccount' => title_case($socialDriver)]);
if (setting('registration-enabled')) {
$message .= ' or, If you do not yet have an account, You can register an account using the ' . $socialDriver . ' option';
$message .= trans('errors.social_account_register_instructions', ['socialAccount' => title_case($socialDriver)]);
}
throw new SocialSignInException($message . '.', '/login');
......@@ -157,8 +156,8 @@ class SocialAuthService
{
$driver = trim(strtolower($socialDriver));
if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found');
if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured("Your {$driver} social settings are not configured correctly.");
if (!in_array($driver, $this->validSocialDrivers)) abort(404, trans('errors.social_driver_not_found'));
if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured(trans('errors.social_driver_not_configured', ['socialAccount' => title_case($socialDriver)]));
return $driver;
}
......@@ -215,7 +214,7 @@ class SocialAuthService
{
session();
user()->socialAccounts()->where('driver', '=', $socialDriver)->delete();
session()->flash('success', title_case($socialDriver) . ' account successfully detached');
session()->flash('success', trans('settings.users_social_disconnected', ['socialAccount' => title_case($socialDriver)]));
return redirect(user()->getEditUrl());
}
......
......@@ -6,6 +6,7 @@
return [
'app-name' => 'BookStack',
'app-logo' => '',
'app-name-header' => true,
'app-editor' => 'wysiwyg',
'app-color' => '#0288D1',
......
......@@ -22,6 +22,7 @@
<php>
<env name="APP_ENV" value="testing"/>
<env name="APP_DEBUG" value="false"/>
<env name="APP_LANG" value="en"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
......
"use strict";
// AngularJS - Create application and load components
var angular = require('angular');
var ngResource = require('angular-resource');
var ngAnimate = require('angular-animate');
var ngSanitize = require('angular-sanitize');
require('angular-ui-sortable');
import angular from "angular";
import "angular-resource";
import "angular-animate";
import "angular-sanitize";
import "angular-ui-sortable";
// Url retrieval function
window.baseUrl = function(path) {
......@@ -15,7 +15,13 @@ window.baseUrl = function(path) {
return basePath + '/' + path;
};
var ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']);
let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']);
// Translation setup
// Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
import Translations from "./translations"
let translator = new Translations(window.translations);
window.trans = translator.get.bind(translator);
// Global Event System
class EventManager {
......@@ -25,9 +31,9 @@ class EventManager {
emit(eventName, eventData) {
if (typeof this.listeners[eventName] === 'undefined') return this;
var eventsToStart = this.listeners[eventName];
let eventsToStart = this.listeners[eventName];
for (let i = 0; i < eventsToStart.length; i++) {
var event = eventsToStart[i];
let event = eventsToStart[i];
event(eventData);
}
return this;
......@@ -70,49 +76,47 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
});
// Global jQuery Elements
$(function () {
var notifications = $('.notification');
var successNotification = notifications.filter('.pos');
var errorNotification = notifications.filter('.neg');
var warningNotification = notifications.filter('.warning');
// Notification Events
window.Events.listen('success', function (text) {
let notifications = $('.notification');
let successNotification = notifications.filter('.pos');
let errorNotification = notifications.filter('.neg');
let warningNotification = notifications.filter('.warning');
// Notification Events
window.Events.listen('success', function (text) {
successNotification.hide();
successNotification.find('span').text(text);
setTimeout(() => {
successNotification.show();
}, 1);
});
window.Events.listen('warning', function (text) {
});
window.Events.listen('warning', function (text) {
warningNotification.find('span').text(text);
warningNotification.show();
});
window.Events.listen('error', function (text) {
});
window.Events.listen('error', function (text) {
errorNotification.find('span').text(text);
errorNotification.show();
});
});
// Notification hiding
notifications.click(function () {
// Notification hiding
notifications.click(function () {
$(this).fadeOut(100);
});
});
// Chapter page list toggles
$('.chapter-toggle').click(function (e) {
// Chapter page list toggles
$('.chapter-toggle').click(function (e) {
e.preventDefault();
$(this).toggleClass('open');
$(this).closest('.chapter').find('.inset-list').slideToggle(180);
});
});
// Back to top button
$('#back-to-top').click(function() {
// Back to top button
$('#back-to-top').click(function() {
$('#header').smoothScrollTo();
});
var scrollTopShowing = false;
var scrollTop = document.getElementById('back-to-top');
var scrollTopBreakpoint = 1200;
window.addEventListener('scroll', function() {
});
let scrollTopShowing = false;
let scrollTop = document.getElementById('back-to-top');
let scrollTopBreakpoint = 1200;
window.addEventListener('scroll', function() {
let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
scrollTop.style.display = 'block';
......@@ -127,36 +131,28 @@ $(function () {
scrollTop.style.display = 'none';
}, 500);
}
});
});
// Common jQuery actions
$('[data-action="expand-entity-list-details"]').click(function() {
// Common jQuery actions
$('[data-action="expand-entity-list-details"]').click(function() {
$('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
});
});
// Popup close
$('.popup-close').click(function() {
// Popup close
$('.popup-close').click(function() {
$(this).closest('.overlay').fadeOut(240);
});
$('.overlay').click(function(event) {
});
$('.overlay').click(function(event) {
if (!$(event.target).hasClass('overlay')) return;
$(this).fadeOut(240);
});
// Prevent markdown display link click redirect
$('.markdown-display').on('click', 'a', function(event) {
event.preventDefault();
window.open($(this).attr('href'));
});
});
// Detect IE for css
if(navigator.userAgent.indexOf('MSIE')!==-1
// Detect IE for css
if(navigator.userAgent.indexOf('MSIE')!==-1
|| navigator.appVersion.indexOf('Trident/') > 0
|| navigator.userAgent.indexOf('Safari') !== -1){
$('body').addClass('flexbox-support');
}
});
}
// Page specific items
require('./pages/page-show');
import "./pages/page-show";
......
......@@ -60,7 +60,8 @@ function registerEditorShortcuts(editor) {
editor.addShortcut('meta+shift+E', '', ['FormatBlock', false, 'code']);
}
var mceOptions = module.exports = {
export default function() {
let settings = {
selector: '#html-editor',
content_css: [
window.baseUrl('/css/styles.css'),
......@@ -147,21 +148,20 @@ var mceOptions = module.exports = {
// Run additional setup actions
// Used by the angular side of things
for (let i = 0; i < mceOptions.extraSetups.length; i++) {
mceOptions.extraSetups[i](editor);
for (let i = 0; i < settings.extraSetups.length; i++) {
settings.extraSetups[i](editor);
}
registerEditorShortcuts(editor);
(function () {
var wrap;
let wrap;
function hasTextContent(node) {
return node && !!( node.textContent || node.innerText );
}
editor.on('dragstart', function () {
var node = editor.selection.getNode();
let node = editor.selection.getNode();
if (node.nodeName !== 'IMG') return;
wrap = editor.dom.getParent(node, '.mceTemp');
......@@ -172,7 +172,7 @@ var mceOptions = module.exports = {
});
editor.on('drop', function (event) {
var dom = editor.dom,
let dom = editor.dom,
rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
// Don't allow anything to be dropped in a captioned image.
......@@ -190,7 +190,6 @@ var mceOptions = module.exports = {
wrap = null;
});
})();
// Custom Image picker button
editor.addButton('image-insert', {
......@@ -212,4 +211,6 @@ var mceOptions = module.exports = {
editorPaste(event, editor);
});
}
};
\ No newline at end of file
};
return settings;
}
\ No newline at end of file
......
"use strict";
// Configure ZeroClipboard
var zeroClipBoard = require('zeroclipboard');
zeroClipBoard.config({
swfPath: window.baseUrl('/ZeroClipboard.swf')
});
import zeroClipBoard from "zeroclipboard";
window.setupPageShow = module.exports = function (pageId) {
export default window.setupPageShow = function (pageId) {
// Set up pointer
var $pointer = $('#pointer').detach();
var $pointerInner = $pointer.children('div.pointer').first();
var isSelection = false;
let $pointer = $('#pointer').detach();
let $pointerInner = $pointer.children('div.pointer').first();
let isSelection = false;
// Select all contents on input click
$pointer.on('click', 'input', function (e) {
......@@ -19,6 +16,9 @@ window.setupPageShow = module.exports = function (pageId) {
});
// Set up copy-to-clipboard
zeroClipBoard.config({
swfPath: window.baseUrl('/ZeroClipboard.swf')
});
new zeroClipBoard($pointer.find('button').first()[0]);
// Hide pointer when clicking away
......@@ -31,11 +31,11 @@ window.setupPageShow = module.exports = function (pageId) {
// Show pointer when selecting a single block of tagged content
$('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) {
e.stopPropagation();
var selection = window.getSelection();
let selection = window.getSelection();
if (selection.toString().length === 0) return;
// Show pointer and set link
var $elem = $(this);
let $elem = $(this);
let link = window.baseUrl('/link/' + pageId + '#' + $elem.attr('id'));
if (link.indexOf('http') !== 0) link = window.location.protocol + "//" + window.location.host + link;
$pointer.find('input').val(link);
......@@ -44,9 +44,9 @@ window.setupPageShow = module.exports = function (pageId) {
$pointer.show();
// Set pointer to sit near mouse-up position
var pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2));
let pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2));
if (pointerLeftOffset < 0) pointerLeftOffset = 0;
var pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100;
let pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100;
$pointerInner.css('left', pointerLeftOffsetPercent + '%');
isSelection = true;
......@@ -57,7 +57,7 @@ window.setupPageShow = module.exports = function (pageId) {
// Go to, and highlight if necessary, the specified text.
function goToText(text) {
var idElem = $('.page-content #' + text).first();
let idElem = $('.page-content #' + text).first();
if (idElem.length !== 0) {
idElem.smoothScrollTo();
idElem.css('background-color', 'rgba(244, 249, 54, 0.25)');
......@@ -68,19 +68,19 @@ window.setupPageShow = module.exports = function (pageId) {
// Check the hash on load
if (window.location.hash) {
var text = window.location.hash.replace(/\%20/g, ' ').substr(1);
let text = window.location.hash.replace(/\%20/g, ' ').substr(1);
goToText(text);
}
// Make the book-tree sidebar stick in view on scroll
var $window = $(window);
var $bookTree = $(".book-tree");
var $bookTreeParent = $bookTree.parent();
let $window = $(window);
let $bookTree = $(".book-tree");
let $bookTreeParent = $bookTree.parent();
// Check the page is scrollable and the content is taller than the tree
var pageScrollable = ($(document).height() > $window.height()) && ($bookTree.height() < $('.page-content').height());
let pageScrollable = ($(document).height() > $window.height()) && ($bookTree.height() < $('.page-content').height());
// Get current tree's width and header height
var headerHeight = $("#header").height() + $(".toolbar").height();
var isFixed = $window.scrollTop() > headerHeight;
let headerHeight = $("#header").height() + $(".toolbar").height();
let isFixed = $window.scrollTop() > headerHeight;
// Function to fix the tree as a sidebar
function stickTree() {
$bookTree.width($bookTreeParent.width() + 15);
......@@ -95,7 +95,7 @@ window.setupPageShow = module.exports = function (pageId) {
}
// Checks if the tree stickiness state should change
function checkTreeStickiness(skipCheck) {
var shouldBeFixed = $window.scrollTop() > headerHeight;
let shouldBeFixed = $window.scrollTop() > headerHeight;
if (shouldBeFixed && (!isFixed || skipCheck)) {
stickTree();
} else if (!shouldBeFixed && (isFixed || skipCheck)) {
......
/**
* Translation Manager
* Handles the JavaScript side of translating strings
* in a way which fits with Laravel.
*/
class Translator {
/**
* Create an instance, Passing in the required translations
* @param translations
*/
constructor(translations) {
this.store = translations;
}
/**
* Get a translation, Same format as laravel's 'trans' helper
* @param key
* @param replacements
* @returns {*}
*/
get(key, replacements) {
let splitKey = key.split('.');
let value = splitKey.reduce((a, b) => {
return a != undefined ? a[b] : a;
}, this.store);
if (value === undefined) {
console.log(`Translation with key "${key}" does not exist`);
value = key;
}
if (replacements === undefined) return value;
let replaceMatches = value.match(/:([\S]+)/g);
if (replaceMatches === null) return value;
replaceMatches.forEach(match => {
let key = match.substring(1);
if (typeof replacements[key] === 'undefined') return;
value = value.replace(match, replacements[key]);
});
return value;
}
}
export default Translator
......@@ -466,3 +466,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
}
}
}
.image-picker .none {
display: none;
}
\ No newline at end of file
......
......@@ -268,8 +268,3 @@ input.outline {
.image-picker img {
background-color: #BBB;
}
\ No newline at end of file
div[toggle-switch] {
height: 18px;
width: 150px;
}
\ No newline at end of file
......
......@@ -322,6 +322,9 @@ ul.pagination {
font-size: 0.75em;
margin-top: $-xs;
}
.text-muted p.text-muted {
margin-top: 0;
}
.page.draft .text-page {
color: $color-page-draft;
}
......
......@@ -35,6 +35,12 @@ table.table {
tr:hover {
background-color: #EEE;
}
.text-right {
text-align: right;
}
.text-center {
text-align: center;
}
}
table.no-style {
......
......@@ -109,6 +109,9 @@ em, i, .italic {
small, p.small, span.small, .text-small {
font-size: 0.8em;
color: lighten($text-dark, 20%);
small, p.small, span.small, .text-small {
font-size: 1em;
}
}
sup, .superscript {
......
......@@ -16,7 +16,7 @@ return [
'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.',
'app_name_header' => 'Anwendungsname im Header anzeigen?',
'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?',
'app_secure_images' => 'Erh&oml;hte Sicherheit f&uuml;r Bilduploads aktivieren?',
'app_secure_images' => 'Erh&ouml;hte Sicherheit f&uuml;r Bilduploads aktivieren?',
'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.',
'app_editor' => 'Seiteneditor',
'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 [
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
/**
* Email Confirmation Text
* Login & Register
*/
'sign_up' => 'Sign up',
'log_in' => 'Log in',
'logout' => 'Logout',
'name' => 'Name',
'username' => 'Username',
'email' => 'Email',
'password' => 'Password',
'password_confirm' => 'Confirm Password',
'password_hint' => 'Must be over 5 characters',
'forgot_password' => 'Forgot Password?',
'remember_me' => 'Remember Me',
'ldap_email_hint' => 'Please enter an email to use for this account.',
'create_account' => 'Create Account',
'social_login' => 'Social Login',
'social_registration' => 'Social Registration',
'social_registration_text' => 'Register and sign in using another service.',
'register_thanks' => 'Thanks for registering!',
'register_confirm' => 'Please check your email and click the confirmation button to access :appName.',
'registrations_disabled' => 'Registrations are currently disabled',
'registration_email_domain_invalid' => 'That email domain does not have access to this application',
'register_success' => 'Thanks for signing up! You are now registered and signed in.',
/**
* Password Reset
*/
'reset_password' => 'Reset Password',
'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.',
'reset_password_send_button' => 'Send Reset Link',
'reset_password_sent_success' => 'A password reset link has been sent to :email.',
'reset_password_success' => 'Your password has been successfully reset.',
'email_reset_subject' => 'Reset your :appName password',
'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.',
/**
* Email Confirmation
*/
'email_confirm_subject' => 'Confirm your email on :appName',
'email_confirm_greeting' => 'Thanks for joining :appName!',
......@@ -23,4 +65,10 @@ return [
'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.',
'email_confirm_success' => 'Your email has been confirmed!',
'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.',
'email_not_confirmed' => 'Email Address Not Confirmed',
'email_not_confirmed_text' => 'Your email address has not yet been confirmed.',
'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.',
'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.',
'email_not_confirmed_resend_button' => 'Resend Confirmation Email',
];
\ No newline at end of file
......
<?php
return [
/**
* Buttons
*/
'cancel' => 'Cancel',
'confirm' => 'Confirm',
'back' => 'Back',
'save' => 'Save',
'continue' => 'Continue',
'select' => 'Select',
/**
* Form Labels
*/
'name' => 'Name',
'description' => 'Description',
'role' => 'Role',
/**
* Actions
*/
'actions' => 'Actions',
'view' => 'View',
'create' => 'Create',
'update' => 'Update',
'edit' => 'Edit',
'sort' => 'Sort',
'move' => 'Move',
'delete' => 'Delete',
'search' => 'Search',
'search_clear' => 'Clear Search',
'reset' => 'Reset',
'remove' => 'Remove',
/**
* Misc
*/
'deleted_user' => 'Deleted User',
'no_activity' => 'No activity to show',
'no_items' => 'No items available',
'back_to_top' => 'Back to top',
'toggle_details' => 'Toggle Details',
/**
* Header
*/
'view_profile' => 'View Profile',
'edit_profile' => 'Edit Profile',
/**
* Email Content
*/
'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
'email_rights' => 'All rights reserved',
];
\ No newline at end of file
<?php
return [
/**
* Image Manager
*/
'image_select' => 'Image Select',
'image_all' => 'All',
'image_all_title' => 'View all images',
'image_book_title' => 'View images uploaded to this book',
'image_page_title' => 'View images uploaded to this page',
'image_search_hint' => 'Search by image name',
'image_uploaded' => 'Uploaded :uploadedDate',
'image_load_more' => 'Load More',
'image_image_name' => 'Image Name',
'image_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.',
'image_select_image' => 'Select Image',
'image_dropzone' => 'Drop images or click here to upload',
'images_deleted' => 'Images Deleted',
'image_preview' => 'Image Preview',
'image_upload_success' => 'Image uploaded successfully',
'image_update_success' => 'Image details successfully updated',
'image_delete_success' => 'Image successfully deleted'
];
\ No newline at end of file
......@@ -6,7 +6,65 @@ return [
* Error text strings.
*/
// Pages
// Permissions
'permission' => 'You do not have permission to access the requested page.',
'permissionJson' => 'You do not have permission to perform the requested action.'
'permissionJson' => 'You do not have permission to perform the requested action.',
// Auth
'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
'social_no_action_defined' => 'No action defined',
'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
'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.',
'social_account_existing' => 'This :socialAccount is already attached to your profile.',
'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
'social_driver_not_found' => 'Social driver not found',
'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
// System
'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
'cannot_get_image_from_url' => 'Cannot get image from :url',
'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
'image_upload_error' => 'An error occurred uploading the image',
// Attachments
'attachment_page_mismatch' => 'Page mismatch during attachment update',
// Pages
'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
// Entities
'entity_not_found' => 'Entity not found',
'book_not_found' => 'Book not found',
'page_not_found' => 'Page not found',
'chapter_not_found' => 'Chapter not found',
'selected_book_not_found' => 'The selected book was not found',
'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
'guests_cannot_save_drafts' => 'Guests cannot save drafts',
// Users
'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
'users_cannot_delete_guest' => 'You cannot delete the guest user',
// Roles
'role_cannot_be_edited' => 'This role cannot be edited',
'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
// Error pages
'404_page_not_found' => 'Page Not Found',
'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
'return_home' => 'Return to home',
'error_occurred' => 'An Error Occurred',
'app_down' => ':appName is down right now',
'back_soon' => 'It will be back up soon.',
];
\ No newline at end of file
......
......@@ -10,6 +10,11 @@ return [
'settings' => 'Settings',
'settings_save' => 'Save Settings',
'settings_save_success' => 'Settings saved',
/**
* App settings
*/
'app_settings' => 'App Settings',
'app_name' => 'Application name',
......@@ -27,6 +32,10 @@ return [
'app_primary_color' => 'Application primary color',
'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
/**
* Registration settings
*/
'reg_settings' => 'Registration Settings',
'reg_allow' => 'Allow registration?',
'reg_default_role' => 'Default user role after registration',
......@@ -36,4 +45,96 @@ return [
'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.',
'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
/**
* Role settings
*/
'roles' => 'Roles',
'role_user_roles' => 'User Roles',
'role_create' => 'Create New Role',
'role_create_success' => 'Role successfully created',
'role_delete' => 'Delete Role',
'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
'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.',
'role_delete_no_migration' => "Don't migrate users",
'role_delete_sure' => 'Are you sure you want to delete this role?',
'role_delete_success' => 'Role successfully deleted',
'role_edit' => 'Edit Role',
'role_details' => 'Role Details',
'role_name' => 'Role Name',
'role_desc' => 'Short Description of Role',
'role_system' => 'System Permissions',
'role_manage_users' => 'Manage users',
'role_manage_roles' => 'Manage roles & role permissions',
'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
'role_manage_settings' => 'Manage app settings',
'role_asset' => 'Asset Permissions',
'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
'role_all' => 'All',
'role_own' => 'Own',
'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
'role_save' => 'Save Role',
'role_update_success' => 'Role successfully updated',
'role_users' => 'Users in this role',
'role_users_none' => 'No users are currently assigned to this role',
/**
* Users
*/
'users' => 'Users',
'user_profile' => 'User Profile',
'users_add_new' => 'Add New User',
'users_search' => 'Search Users',
'users_role' => 'User Roles',
'users_external_auth_id' => 'External Authentication ID',
'users_password_warning' => 'Only fill the below if you would like to change your password:',
'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
'users_delete' => 'Delete User',
'users_delete_named' => 'Delete ser :userName',
'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
'users_delete_confirm' => 'Are you sure you want to delete this user?',
'users_delete_success' => 'Users successfully removed',
'users_edit' => 'Edit User',
'users_edit_profile' => 'Edit Profile',
'users_edit_success' => 'User successfully updated',
'users_avatar' => 'User Avatar',
'users_avatar_desc' => 'This image should be approx 256px square.',
'users_social_accounts' => 'Social Accounts',
'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.',
'users_social_connect' => 'Connect Account',
'users_social_disconnect' => 'Disconnect Account',
'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
];
......
......@@ -87,8 +87,8 @@ return [
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
'password-confirm' => [
'required_with' => 'Password confirmation required',
],
],
......
<div class="form-group">
<label for="username">Username</label>
<label for="username">{{ trans('auth.username') }}</label>
@include('form/text', ['name' => 'username', 'tabindex' => 1])
</div>
@if(session('request-email', false) === true)
<div class="form-group">
<label for="email">Email</label>
<label for="email">{{ trans('auth.email') }}</label>
@include('form/text', ['name' => 'email', 'tabindex' => 1])
<span class="text-neg">
Please enter an email to use for this account.
{{ trans('auth.ldap_email_hint') }}
</span>
</div>
@endif
<div class="form-group">
<label for="password">Password</label>
<label for="password">{{ trans('auth.password') }}</label>
@include('form/password', ['name' => 'password', 'tabindex' => 2])
</div>
\ No newline at end of file
......
<div class="form-group">
<label for="email">Email</label>
<label for="email">{{ trans('auth.email') }}</label>
@include('form/text', ['name' => 'email', 'tabindex' => 1])
</div>
<div class="form-group">
<label for="password">Password</label>
<label for="password">{{ trans('auth.password') }}</label>
@include('form/password', ['name' => 'password', 'tabindex' => 2])
<span class="block small"><a href="{{ baseUrl('/password/email') }}">Forgot Password?</a></span>
<span class="block small"><a href="{{ baseUrl('/password/email') }}">{{ trans('auth.forgot_password') }}</a></span>
</div>
\ No newline at end of file
......
......@@ -2,7 +2,7 @@
@section('header-buttons')
@if(setting('registration-enabled', false))
<a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>Sign up</a>
<a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>{{ trans('auth.sign_up') }}</a>
@endif
@stop
......@@ -10,7 +10,7 @@
<div class="text-center">
<div class="center-box">
<h1>Log In</h1>
<h1>{{ title_case(trans('auth.log_in')) }}</h1>
<form action="{{ baseUrl("/login") }}" method="POST" id="login-form">
{!! csrf_field() !!}
......@@ -19,20 +19,20 @@
@include('auth/forms/login/' . $authMethod)
<div class="form-group">
<label for="remember" class="inline">Remember Me</label>
<label for="remember" class="inline">{{ trans('auth.remember_me') }}</label>
<input type="checkbox" id="remember" name="remember" class="toggle-switch-checkbox">
<label for="remember" class="toggle-switch"></label>
</div>
<div class="from-group">
<button class="button block pos" tabindex="3"><i class="zmdi zmdi-sign-in"></i> Sign In</button>
<button class="button block pos" tabindex="3"><i class="zmdi zmdi-sign-in"></i> {{ title_case(trans('auth.log_in')) }}</button>
</div>
</form>
@if(count($socialDrivers) > 0)
<hr class="margin-top">
<h3 class="text-muted">Social Login</h3>
<h3 class="text-muted">{{ trans('auth.social_login') }}</h3>
@if(isset($socialDrivers['google']))
<a href="{{ baseUrl("/login/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
@endif
......
@extends('public')
@section('header-buttons')
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
@if(setting('registration-enabled'))
<a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>Sign up</a>
<a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>{{ trans('auth.sign_up') }}</a>
@endif
@stop
......@@ -12,20 +12,20 @@
<div class="text-center">
<div class="center-box text-left">
<h1>Reset Password</h1>
<h1>{{ trans('auth.reset_password') }}</h1>
<p class="muted small">Enter your email below and you will be sent an email with a password reset link.</p>
<p class="muted small">{{ trans('auth.reset_password_send_instructions') }}</p>
<form action="{{ baseUrl("/password/email") }}" method="POST">
{!! csrf_field() !!}
<div class="form-group">
<label for="email">Email</label>
<label for="email">{{ trans('auth.email') }}</label>
@include('form/text', ['name' => 'email'])
</div>
<div class="from-group">
<button class="button block pos">Send Reset Link</button>
<button class="button block pos">{{ trans('auth.reset_password_send_button') }}</button>
</div>
</form>
</div>
......
@extends('public')
@section('header-buttons')
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
@if(setting('registration-enabled'))
<a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>Sign up</a>
<a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>{{ trans('auth.sign_up') }}</a>
@endif
@stop
......@@ -14,29 +14,29 @@
<div class="text-center">
<div class="center-box text-left">
<h1>Reset Password</h1>
<h1>{{ trans('auth.reset_password') }}</h1>
<form action="{{ baseUrl("/password/reset") }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group">
<label for="email">Email</label>
<label for="email">{{ trans('auth.email') }}</label>
@include('form/text', ['name' => 'email'])
</div>
<div class="form-group">
<label for="password">Password</label>
<label for="password">{{ trans('auth.password') }}</label>
@include('form/password', ['name' => 'password'])
</div>
<div class="form-group">
<label for="password_confirmation">Confirm Password</label>
<label for="password_confirmation">{{ trans('auth.password_confirm') }}</label>
@include('form/password', ['name' => 'password_confirmation'])
</div>
<div class="from-group">
<button class="button block pos">Reset Password</button>
<button class="button block pos">{{ trans('auth.reset_password') }}</button>
</div>
</form>
</div>
......
......@@ -2,7 +2,7 @@
@section('header-buttons')
@if(!$signedIn)
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
@endif
@stop
......@@ -10,10 +10,9 @@
<div class="text-center">
<div class="center-box">
<h2>Thanks for registering!</h2>
<p>Please check your email and click the confirmation button to access {{ setting('app-name', 'BookStack') }}.</p>
<h2>{{ trans('auth.register_thanks') }}</h2>
<p>{{ trans('auth.register_confirm', ['appName' => setting('app-name')]) }}</p>
</div>
</div>
@stop
......
@extends('public')
@section('header-buttons')
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
@stop
@section('content')
<div class="text-center">
<div class="center-box">
<h1>Sign Up</h1>
<h1>{{ title_case(trans('auth.sign_up')) }}</h1>
<form action="{{ baseUrl("/register") }}" method="POST">
{!! csrf_field() !!}
<div class="form-group">
<label for="email">Name</label>
<label for="email">{{ trans('auth.name') }}</label>
@include('form/text', ['name' => 'name'])
</div>
<div class="form-group">
<label for="email">Email</label>
<label for="email">{{ trans('auth.email') }}</label>
@include('form/text', ['name' => 'email'])
</div>
<div class="form-group">
<label for="password">Password</label>
@include('form/password', ['name' => 'password', 'placeholder' => 'Must be over 5 characters'])
<label for="password">{{ trans('auth.password') }}</label>
@include('form/password', ['name' => 'password', 'placeholder' => trans('auth.password_hint')])
</div>
<div class="from-group">
<button class="button block pos">Create Account</button>
<button class="button block pos">{{ trans('auth.create_account') }}</button>
</div>
</form>
@if(count($socialDrivers) > 0)
<hr class="margin-top">
<h3 class="text-muted">Social Registration</h3>
<p class="text-small">Register and sign in using another service.</p>
<h3 class="text-muted">{{ trans('auth.social_registration') }}</h3>
<p class="text-small">{{ trans('auth.social_registration_text') }}</p>
@if(isset($socialDrivers['google']))
<a href="{{ baseUrl("/register/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
@endif
......
......@@ -4,16 +4,16 @@
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h2>Email Address not confirmed</h2>
<p class="text-muted">Your email address has not yet been confirmed. <br>
Please click the link in the email that was sent shortly after you registered. <br>
If you cannot find the email you can re-send the confirmation email by submitting the form below.
<h2>{{ trans('auth.email_not_confirmed') }}</h2>
<p class="text-muted">{{ trans('auth.email_not_confirmed_text') }}<br>
{{ trans('auth.email_not_confirmed_click_link') }} <br>
{{ trans('auth.email_not_confirmed_resend') }}
</p>
<hr>
<form action="{{ baseUrl("/register/confirm/resend") }}" method="POST">
{!! csrf_field() !!}
<div class="form-group">
<label for="email">Email Address</label>
<label for="email">{{ trans('auth.email') }}</label>
@if(auth()->check())
@include('form/text', ['name' => 'email', 'model' => auth()->user()])
@else
......@@ -21,7 +21,7 @@
@endif
</div>
<div class="form-group">
<button type="submit" class="button pos">Resend Confirmation Email</button>
<button type="submit" class="button pos">{{ trans('auth.email_not_confirmed_resend_button') }}</button>
</div>
</form>
</div>
......
......@@ -17,6 +17,7 @@
<!-- Scripts -->
<script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script>
<script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script>
<script src="{{ baseUrl('/translations.js') }}"></script>
@yield('head')
......@@ -53,32 +54,16 @@
<div class="col-lg-4 col-sm-5">
<div class="float right">
<div class="links text-center">
<a href="{{ baseUrl('/books') }}"><i class="zmdi zmdi-book"></i>Books</a>
<a href="{{ baseUrl('/books') }}"><i class="zmdi zmdi-book"></i>{{ trans('entities.books') }}</a>
@if(isset($currentUser) && userCan('settings-manage'))
<a href="{{ baseUrl('/settings') }}"><i class="zmdi zmdi-settings"></i>Settings</a>
<a href="{{ baseUrl('/settings') }}"><i class="zmdi zmdi-settings"></i>{{ trans('settings.settings') }}</a>
@endif
@if(!isset($signedIn) || !$signedIn)
<a href="{{ baseUrl('/login') }}"><i class="zmdi zmdi-sign-in"></i>Sign In</a>
<a href="{{ baseUrl('/login') }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
@endif
</div>
@if(isset($signedIn) && $signedIn)
<div class="dropdown-container" dropdown>
<span class="user-name" dropdown-toggle>
<img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
<span class="name" ng-non-bindable>{{ $currentUser->getShortName(9) }}</span> <i class="zmdi zmdi-caret-down"></i>
</span>
<ul>
<li>
<a href="{{ baseUrl("/user/{$currentUser->id}") }}" class="text-primary"><i class="zmdi zmdi-account zmdi-hc-fw zmdi-hc-lg"></i>View Profile</a>
</li>
<li>
<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>
</li>
<li>
<a href="{{ baseUrl('/logout') }}" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-fw zmdi-hc-lg"></i>Logout</a>
</li>
</ul>
</div>
@include('partials._header-dropdown', ['currentUser' => $currentUser])
@endif
</div>
......@@ -93,7 +78,7 @@
<div id="back-to-top">
<div class="inner">
<i class="zmdi zmdi-chevron-up"></i> <span>Back to top</span>
<i class="zmdi zmdi-chevron-up"></i> <span>{{ trans('common.back_to_top') }}</span>
</div>
</div>
@yield('bottom')
......
<div class="breadcrumbs">
<a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
</div>
\ No newline at end of file
......@@ -3,7 +3,7 @@
@section('content')
<div class="container small" ng-non-bindable>
<h1>Create New Book</h1>
<h1>{{ trans('entities.books_create') }}</h1>
<form action="{{ baseUrl("/books") }}" method="POST">
@include('books/form')
</form>
......
......@@ -2,16 +2,26 @@
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
</div>
<div class="container small" ng-non-bindable>
<h1>Delete Book</h1>
<p>This will delete the book with the name '{{$book->name}}', All pages and chapters will be removed.</p>
<p class="text-neg">Are you sure you want to delete this book?</p>
<h1>{{ trans('entities.books_delete') }}</h1>
<p>{{ trans('entities.books_delete_explain', ['bookName' => $book->name]) }}</p>
<p class="text-neg">{{ trans('entities.books_delete_confirmation') }}</p>
<form action="{{$book->getUrl()}}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="DELETE">
<a href="{{$book->getUrl()}}" class="button">Cancel</a>
<button type="submit" class="button neg">Confirm</button>
<a href="{{$book->getUrl()}}" class="button">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</form>
</div>
......
......@@ -2,8 +2,18 @@
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
</div>
<div class="container small" ng-non-bindable>
<h1>Edit Book</h1>
<h1>{{ trans('entities.books_edit') }}</h1>
<form action="{{ $book->getUrl() }}" method="POST">
<input type="hidden" name="_method" value="PUT">
@include('books/form', ['model' => $book])
......
{{ csrf_field() }}
<div class="form-group title-input">
<label for="name">Book Name</label>
<label for="name">{{ trans('common.name') }}</label>
@include('form/text', ['name' => 'name'])
</div>
<div class="form-group description-input">
<label for="description">Description</label>
<label for="description">{{ trans('common.description') }}</label>
@include('form/textarea', ['name' => 'description'])
</div>
<div class="form-group">
<a href="{{ back()->getTargetUrl() }}" class="button muted">Cancel</a>
<button type="submit" class="button pos">Save Book</button>
<a href="{{ back()->getTargetUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.books_save') }}</button>
</div>
\ No newline at end of file
......
......@@ -9,7 +9,7 @@
<div class="col-xs-11 faded">
<div class="action-buttons">
@if($currentUser->can('book-create-all'))
<a href="{{ baseUrl("/books/create") }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>Add new book</a>
<a href="{{ baseUrl("/books/create") }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.books_create') }}</a>
@endif
</div>
</div>
......@@ -21,7 +21,7 @@
<div class="container" ng-non-bindable>
<div class="row">
<div class="col-sm-7">
<h1>Books</h1>
<h1>{{ trans('entities.books') }}</h1>
@if(count($books) > 0)
@foreach($books as $book)
@include('books/list-item', ['book' => $book])
......@@ -29,27 +29,27 @@
@endforeach
{!! $books->render() !!}
@else
<p class="text-muted">No books have been created.</p>
<p class="text-muted">{{ trans('entities.books_empty') }}</p>
@if(userCan('books-create-all'))
<a href="{{ baseUrl("/books/create") }}" class="text-pos"><i class="zmdi zmdi-edit"></i>Create one now</a>
<a href="{{ baseUrl("/books/create") }}" class="text-pos"><i class="zmdi zmdi-edit"></i>{{ trans('entities.create_one_now') }}</a>
@endif
@endif
</div>
<div class="col-sm-4 col-sm-offset-1">
<div id="recents">
@if($recents)
<div class="margin-top large">&nbsp;</div>
<h3>Recently Viewed</h3>
<div class="margin-top">&nbsp;</div>
<h3>{{ trans('entities.recently_viewed') }}</h3>
@include('partials/entity-list', ['entities' => $recents])
@endif
</div>
<div class="margin-top large">&nbsp;</div>
<div id="popular">
<h3>Popular Books</h3>
<h3>{{ trans('entities.books_popular') }}</h3>
@if(count($popular) > 0)
@include('partials/entity-list', ['entities' => $popular])
@else
<p class="text-muted">The most popular books will appear here.</p>
<p class="text-muted">{{ trans('entities.books_popular_empty') }}</p>
@endif
</div>
</div>
......
......@@ -6,9 +6,7 @@
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
</div>
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
......@@ -16,7 +14,7 @@
<div class="container" ng-non-bindable>
<h1>Book Permissions</h1>
<h1>{{ trans('entities.books_permissions') }}</h1>
@include('form/restriction-form', ['model' => $book])
</div>
......
......@@ -5,29 +5,32 @@
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="col-md-6 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
<div class="col-md-6">
<div class="action-buttons faded">
@if(userCan('page-create', $book))
<a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Page</a>
<a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
@endif
@if(userCan('chapter-create', $book))
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Chapter</a>
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.chapters_new') }}</a>
@endif
@if(userCan('book-update', $book))
<a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a>
<a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
@endif
@if(userCan('book-update', $book) || userCan('restrictions-manage', $book) || userCan('book-delete', $book))
<div dropdown class="dropdown-container">
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
<ul>
@if(userCan('book-update', $book))
<li><a href="{{ $book->getUrl('/sort') }}" class="text-primary"><i class="zmdi zmdi-sort"></i>Sort</a></li>
<li><a href="{{ $book->getUrl('/sort') }}" class="text-primary"><i class="zmdi zmdi-sort"></i>{{ trans('common.sort') }}</a></li>
@endif
@if(userCan('restrictions-manage', $book))
<li><a href="{{ $book->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
<li><a href="{{ $book->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.permissions') }}</a></li>
@endif
@if(userCan('book-delete', $book))
<li><a href="{{ $book->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
<li><a href="{{ $book->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
@endif
</ul>
</div>
......@@ -59,23 +62,19 @@
<hr>
@endforeach
@else
<p class="text-muted">No pages or chapters have been created for this book.</p>
<p class="text-muted">{{ trans('entities.books_empty_contents') }}</p>
<p>
<a href="{{ $book->getUrl('/page/create') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
&nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp;
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>Add a chapter</a>
<a href="{{ $book->getUrl('/page/create') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
&nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>{{ trans('entities.books_empty_add_chapter') }}</a>
</p>
<hr>
@endif
<p class="text-muted small">
Created {{$book->created_at->diffForHumans()}} @if($book->createdBy) by <a href="{{ $book->createdBy->getProfileUrl() }}">{{$book->createdBy->name}}</a> @endif
<br>
Last Updated {{$book->updated_at->diffForHumans()}} @if($book->updatedBy) by <a href="{{ $book->updatedBy->getProfileUrl() }}">{{$book->updatedBy->name}}</a> @endif
</p>
@include('partials.entity-meta', ['entity' => $book])
</div>
</div>
<div class="search-results" ng-cloak ng-show="searching">
<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>
<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>
<div ng-if="!searchResults">
@include('partials/loading-icon')
</div>
......@@ -90,21 +89,21 @@
@if($book->restricted)
<p class="text-muted">
@if(userCan('restrictions-manage', $book))
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>Book Permissions Active
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
@endif
</p>
@endif
<div class="search-box">
<form ng-submit="searchBook($event)">
<input ng-model="searchTerm" ng-change="checkSearchForm()" type="text" name="term" placeholder="Search This Book">
<input ng-model="searchTerm" ng-change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
<button type="submit"><i class="zmdi zmdi-search"></i></button>
<button ng-if="searching" ng-click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
</form>
</div>
<div class="activity anim fadeIn">
<h3>Recent Activity</h3>
<h3>{{ trans('entities.recent_activity') }}</h3>
@include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)])
</div>
</div>
......
......@@ -6,8 +6,18 @@
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
</div>
<div class="container" ng-non-bindable>
<h1>Sorting Pages & Chapters<span class="subheader">For {{ $book->name }}</span></h1>
<h1>{{ trans('entities.books_sort') }}</h1>
<div class="row">
<div class="col-md-8" id="sort-boxes">
......@@ -17,7 +27,7 @@
@if(count($books) > 1)
<div class="col-md-4">
<h3>Show Other Books</h3>
<h3>{{ trans('entities.books_sort_show_other') }}</h3>
<div id="additional-books">
@foreach($books as $otherBook)
@if($otherBook->id !== $book->id)
......@@ -37,8 +47,8 @@
<input type="hidden" name="_method" value="PUT">
<input type="hidden" id="sort-tree-input" name="sort-tree">
<div class="list">
<a href="{{ $book->getUrl() }}" class="button muted">Cancel</a>
<button class="button pos" type="submit">Save Order</button>
<a href="{{ $book->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button class="button pos" type="submit">{{ trans('entities.books_sort_save') }}</button>
</div>
</form>
......
<div class="breadcrumbs">
<a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
<span class="sep">&raquo;</span>
<a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a>
</div>
\ No newline at end of file
......@@ -3,7 +3,7 @@
@section('content')
<div class="container small" ng-non-bindable>
<h1>Create New Chapter</h1>
<h1>{{ trans('entities.chapters_create') }}</h1>
<form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
@include('chapters/form')
</form>
......
......@@ -2,17 +2,26 @@
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
</div>
</div>
</div>
<div class="container small" ng-non-bindable>
<h1>Delete Chapter</h1>
<p>This will delete the chapter with the name '{{$chapter->name}}', All pages will be removed
and added directly to the book.</p>
<p class="text-neg">Are you sure you want to delete this chapter?</p>
<h1>{{ trans('entities.chapters_delete') }}</h1>
<p>{{ trans('entities.chapters_delete_explain', ['chapterName' => $chapter->name]) }}</p>
<p class="text-neg">{{ trans('entities.chapters_delete_confirm') }}</p>
<form action="{{ $chapter->getUrl() }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="DELETE">
<a href="{{ $chapter->getUrl() }}" class="button primary">Cancel</a>
<button type="submit" class="button neg">Confirm</button>
<a href="{{ $chapter->getUrl() }}" class="button primary">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</form>
</div>
......
......@@ -3,7 +3,7 @@
@section('content')
<div class="container small" ng-non-bindable>
<h1>Edit Chapter</h1>
<h1>{{ trans('entities.chapters_edit') }}</h1>
<form action="{{ $chapter->getUrl() }}" method="POST">
<input type="hidden" name="_method" value="PUT">
@include('chapters/form', ['model' => $chapter])
......
......@@ -2,16 +2,16 @@
{!! csrf_field() !!}
<div class="form-group title-input">
<label for="name">Chapter Name</label>
<label for="name">{{ trans('common.name') }}</label>
@include('form/text', ['name' => 'name'])
</div>
<div class="form-group description-input">
<label for="description">Description</label>
<label for="description">{{ trans('common.description') }}</label>
@include('form/textarea', ['name' => 'description'])
</div>
<div class="form-group">
<a href="{{ back()->getTargetUrl() }}" class="button muted">Cancel</a>
<button type="submit" class="button pos">Save Chapter</button>
<a href="{{ back()->getTargetUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
</div>
......
......@@ -17,7 +17,7 @@
@endif
@if(!isset($hidePages) && count($chapter->pages) > 0)
<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>
<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>
<div class="inset-list">
@foreach($chapter->pages as $page)
<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 @@
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
<span class="sep">&raquo;</span>
<a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{ $chapter->getShortName() }}</a>
</div>
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
</div>
</div>
</div>
<div class="container">
<h1>Move Chapter <small class="subheader">{{$chapter->name}}</small></h1>
<h1>{{ trans('entities.chapters_move') }}</h1>
<form action="{{ $chapter->getUrl('/move') }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
@include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
<a href="{{ $chapter->getUrl() }}" class="button muted">Cancel</a>
<button type="submit" class="button pos">Move Chapter</button>
<a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button>
</form>
</div>
......
......@@ -6,18 +6,14 @@
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
<span class="sep">&raquo;</span>
<a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a>
</div>
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
</div>
</div>
</div>
<div class="container" ng-non-bindable>
<h1>Chapter Permissions</h1>
<h1>{{ trans('entities.chapters_permissions') }}</h1>
@include('form/restriction-form', ['model' => $chapter])
</div>
......
......@@ -6,30 +6,28 @@
<div class="container">
<div class="row">
<div class="col-sm-8 faded" ng-non-bindable>
<div class="breadcrumbs">
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
</div>
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
<div class="col-sm-4 faded">
<div class="action-buttons">
@if(userCan('page-create', $chapter))
<a href="{{ $chapter->getUrl('/create-page') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>New Page</a>
<a href="{{ $chapter->getUrl('/create-page') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
@endif
@if(userCan('chapter-update', $chapter))
<a href="{{ $chapter->getUrl('/edit') }}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a>
<a href="{{ $chapter->getUrl('/edit') }}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
@endif
@if(userCan('chapter-update', $chapter) || userCan('restrictions-manage', $chapter) || userCan('chapter-delete', $chapter))
<div dropdown class="dropdown-container">
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
<ul>
@if(userCan('chapter-update', $chapter))
<li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary"><i class="zmdi zmdi-folder"></i>Move</a></li>
<li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary"><i class="zmdi zmdi-folder"></i>{{ trans('common.move') }}</a></li>
@endif
@if(userCan('restrictions-manage', $chapter))
<li><a href="{{ $chapter->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
<li><a href="{{ $chapter->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.permissions') }}</a></li>
@endif
@if(userCan('chapter-delete', $chapter))
<li><a href="{{ $chapter->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
<li><a href="{{ $chapter->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
@endif
</ul>
</div>
......@@ -57,26 +55,22 @@
</div>
@else
<hr>
<p class="text-muted">No pages are currently in this chapter.</p>
<p class="text-muted">{{ trans('entities.chapters_empty') }}</p>
<p>
@if(userCan('page-create', $chapter))
<a href="{{ $chapter->getUrl('/create-page') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
<a href="{{ $chapter->getUrl('/create-page') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
@endif
@if(userCan('page-create', $chapter) && userCan('book-update', $book))
&nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
@endif
@if(userCan('book-update', $book))
<a href="{{ $book->getUrl('/sort') }}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a>
<a href="{{ $book->getUrl('/sort') }}" class="text-book"><i class="zmdi zmdi-book"></i>{{ trans('entities.books_empty_sort_current_book') }}</a>
@endif
</p>
<hr>
@endif
<p class="text-muted small">
Created {{ $chapter->created_at->diffForHumans() }} @if($chapter->createdBy) by <a href="{{ $chapter->createdBy->getProfileUrl() }}">{{ $chapter->createdBy->name}}</a> @endif
<br>
Last Updated {{ $chapter->updated_at->diffForHumans() }} @if($chapter->updatedBy) by <a href="{{ $chapter->updatedBy->getProfileUrl() }}">{{ $chapter->updatedBy->name}}</a> @endif
</p>
@include('partials.entity-meta', ['entity' => $chapter])
</div>
<div class="col-md-3 col-md-offset-1">
<div class="margin-top large"></div>
......@@ -84,19 +78,20 @@
<div class="text-muted">
@if($book->restricted)
<p class="text-muted">
@if(userCan('restrictions-manage', $book))
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>Book Permissions Active
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
@endif
<br>
</p>
@endif
@if($chapter->restricted)
@if(userCan('restrictions-manage', $chapter))
<a href="{{ $chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active</a>
<a href="{{ $chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}
@endif
@endif
</div>
......
......@@ -2,12 +2,12 @@
<div class="overlay" entity-link-selector>
<div class="popup-body small flex-child">
<div class="popup-header primary-background">
<div class="popup-title">Entity Select</div>
<div class="popup-title">{{ trans('entities.entity_select') }}</div>
<button type="button" class="corner-button neg button popup-close">x</button>
</div>
@include('partials/entity-selector', ['name' => 'entity-selector'])
@include('components.entity-selector', ['name' => 'entity-selector'])
<div class="popup-footer">
<button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">Select</button>
<button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button>
</div>
</div>
</div>
......
<div class="form-group">
<div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}">
<input type="hidden" entity-selector-input name="{{$name}}" value="">
<input type="text" placeholder="Search" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()">
<div class="text-center loading" ng-show="loading">@include('partials/loading-icon')</div>
<input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()">
<div class="text-center loading" ng-show="loading">@include('partials.loading-icon')</div>
<div ng-show="!loading" ng-bind-html="entityResults"></div>
</div>
</div>
\ No newline at end of file
......
......@@ -3,7 +3,7 @@
<div class="popup-body" ng-click="$event.stopPropagation()">
<div class="popup-header primary-background">
<div class="popup-title">Image Select</div>
<div class="popup-title">{{ trans('components.image_select') }}</div>
<button class="popup-close neg corner-button button">x</button>
</div>
......@@ -12,16 +12,16 @@
<div class="image-manager-content">
<div ng-if="imageType === 'gallery'" class="container">
<div class="image-manager-header row faded-small nav-tabs">
<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>
<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>
<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>
<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>
<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>
<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>
</div>
</div>
<div ng-show="view === 'all'" >
<form ng-submit="searchImages()" class="contained-search-box">
<input type="text" placeholder="Search by image name" ng-model="searchTerm">
<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>
<button title="Search" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
<input type="text" placeholder="{{ trans('components.image_search_hint') }}" ng-model="searchTerm">
<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>
<button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
</form>
</div>
<div class="image-manager-list">
......@@ -31,11 +31,11 @@
<img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
<div class="image-meta">
<span class="name" ng-bind="image.name"></span>
<span class="date">Uploaded @{{ getDate(image.created_at) }}</span>
<span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
</div>
</div>
</div>
<div class="load-more" ng-show="hasMore" ng-click="fetchData()">Load More</div>
<div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.image_load_more') }}</div>
</div>
</div>
......@@ -51,15 +51,14 @@
</a>
</div>
<div class="form-group">
<label for="name">Image Name</label>
<label for="name">{{ trans('components.image_image_name') }}</label>
<input type="text" id="name" name="name" ng-model="selectedImage.name">
</div>
</form>
<div ng-show="dependantPages">
<p class="text-neg text-small">
This image is used in the pages below, Click delete again to confirm you want to delete
this image.
{{ trans('components.image_delete_confirm') }}
</p>
<ul class="text-neg">
<li ng-repeat="page in dependantPages">
......@@ -73,13 +72,13 @@
<button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
</form>
<button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()">
<i class="zmdi zmdi-square-right"></i>Select Image
<i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
</button>
</div>
</div>
<drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
<drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
</div>
......
<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 '' }}">
<div>
<img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}">
</div>
<button class="button" type="button" data-action="show-image-manager">{{ trans('components.image_select_image') }}</button>
<br>
<button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button>
@if ($showRemove)
<span class="sep">|</span>
<button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button>
@endif
<input type="hidden" name="{{$name}}" id="{{$name}}" value="{{ isset($currentId) && ($currentId !== '' && $currentId !== false) ? $currentId : $currentImage}}">
</div>
<script>
(function(){
var picker = document.querySelector('[image-picker="{{$name}}"]');
picker.addEventListener('click', function(event) {
if (event.target.nodeName.toLowerCase() !== 'button') return;
var button = event.target;
var action = button.getAttribute('data-action');
var resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width');
var usingIds = picker.getAttribute('data-current-id') !== '';
var resizeCrop = picker.getAttribute('data-resize-crop') !== '';
var imageElem = picker.querySelector('img');
var input = picker.querySelector('input');
function setImage(image) {
if (image === 'none') {
imageElem.src = picker.getAttribute('data-default-image');
imageElem.classList.add('none');
input.value = 'none';
return;
}
imageElem.src = image.url;
input.value = usingIds ? image.id : image.url;
imageElem.classList.remove('none');
}
if (action === 'show-image-manager') {
window.ImageManager.showExternal((image) => {
if (!resize) {
setImage(image);
return;
}
var requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false');
$.get(window.baseUrl(requestString), resp => {
image.url = resp.url;
setImage(image);
});
});
} else if (action === 'reset-image') {
setImage({id: 0, url: picker.getAttribute('data-default-image')});
} else if (action === 'remove-image') {
setImage('none');
}
});
})();
</script>
\ No newline at end of file
<div toggle-switch="{{$name}}" class="toggle-switch @if($value) active @endif">
<input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/>
<div class="switch-handle"></div>
</div>
<script>
(function() {
var toggle = document.querySelector('[toggle-switch="{{$name}}"]');
var toggleInput = toggle.querySelector('input');
toggle.onclick = function(event) {
var checked = toggleInput.value !== 'true';
toggleInput.value = checked ? 'true' : 'false';
checked ? toggle.classList.add('active') : toggle.classList.remove('active');
};
})()
</script>
\ No newline at end of file
......@@ -4,9 +4,28 @@
<div class="container">
<h1 class="text-muted">{{ $message or 'Page Not Found' }}</h1>
<p>Sorry, The page you were looking for could not be found.</p>
<a href="{{ baseUrl('/') }}" class="button">Return To Home</a>
<h1>{{ $message or trans('errors.404_page_not_found') }}</h1>
<p>{{ trans('errors.sorry_page_not_found') }}</p>
<p><a href="{{ baseUrl('/') }}" class="button">{{ trans('errors.return_home') }}</a></p>
<hr>
<div class="row">
<div class="col-md-4">
<h3 class="text-muted">{{ trans('entities.pages_popular') }}</h3>
@include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Page::class]), 'style' => 'compact'])
</div>
<div class="col-md-4">
<h3 class="text-muted">{{ trans('entities.books_popular') }}</h3>
@include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Book::class]), 'style' => 'compact'])
</div>
<div class="col-md-4">
<h3 class="text-muted">{{ trans('entities.chapters_popular') }}</h3>
@include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Chapter::class]), 'style' => 'compact'])
</div>
</div>
</div>
@stop
\ No newline at end of file
......
......@@ -3,7 +3,7 @@
@section('content')
<div class="container">
<h1 class="text-muted">An Error Occurred</h1>
<h1 class="text-muted">{{ trans('errors.error_occurred') }}</h1>
<p>{{ $message }}</p>
</div>
......
......@@ -3,8 +3,8 @@
@section('content')
<div class="container">
<h1 class="text-muted">{{ setting('app-name') }} is down right now</h1>
<p>It will be back up soon.</p>
<h1 class="text-muted">{{ trans('errors.app_down', ['appName' => setting('app-name')]) }}</h1>
<p>{{ trans('errors.back_soon') }}</p>
</div>
@stop
\ No newline at end of file
......
<form action="{{$url}}" method="POST" class="inline">
{{ csrf_field() }}
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="button neg">{{ isset($text) ? $text : 'Delete' }}</button>
<button type="submit" class="button neg">{{ isset($text) ? $text : trans('common.delete') }}</button>
</form>
\ No newline at end of file
......
......@@ -2,31 +2,31 @@
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
<p>Once enabled, These permissions will take priority over any set role permissions.</p>
<p>{{ trans('entities.permissions_intro') }}</p>
<div class="form-group">
@include('form/checkbox', ['name' => 'restricted', 'label' => 'Enable custom permissions'])
@include('form/checkbox', ['name' => 'restricted', 'label' => trans('entities.permissions_enable')])
</div>
<table class="table">
<tr>
<th>Role</th>
<th @if($model->isA('page')) colspan="3" @else colspan="4" @endif>Actions</th>
<th>{{ trans('common.role') }}</th>
<th @if($model->isA('page')) colspan="3" @else colspan="4" @endif>{{ trans('common.actions') }}</th>
</tr>
@foreach($roles as $role)
<tr>
<td>{{ $role->display_name }}</td>
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'View', 'action' => 'view'])</td>
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.view'), 'action' => 'view'])</td>
@if(!$model->isA('page'))
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'Create', 'action' => 'create'])</td>
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.create'), 'action' => 'create'])</td>
@endif
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'Update', 'action' => 'update'])</td>
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => 'Delete', 'action' => 'delete'])</td>
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.update'), 'action' => 'update'])</td>
<td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.delete'), 'action' => 'delete'])</td>
</tr>
@endforeach
</table>
<a href="{{ $model->getUrl() }}" class="button muted">Cancel</a>
<button type="submit" class="button pos">Save Permissions</button>
<a href="{{ $model->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.permissions_save') }}</button>
</form>
\ No newline at end of file
......
......@@ -5,14 +5,9 @@
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-4 faded">
<div class="col-sm-6 faded">
<div class="action-buttons text-left">
<a data-action="expand-entity-list-details" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>Toggle Details</a>
</div>
</div>
<div class="col-sm-8 faded">
<div class="action-buttons">
<a data-action="expand-entity-list-details" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_details') }}</a>
</div>
</div>
</div>
......@@ -25,44 +20,44 @@
<div class="col-sm-4">
<div id="recent-drafts">
@if(count($draftPages) > 0)
<h4>My Recent Drafts</h4>
<h4>{{ trans('entities.my_recent_drafts') }}</h4>
@include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
@endif
</div>
@if($signedIn)
<h4>My Recently Viewed</h4>
<h4>{{ trans('entities.my_recently_viewed') }}</h4>
@else
<h4>Recent Books</h4>
<h4>{{ trans('entities.books_recent') }}</h4>
@endif
@include('partials/entity-list', [
'entities' => $recents,
'style' => 'compact',
'emptyText' => $signedIn ? 'You have not viewed any pages' : 'No books have been created'
'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
])
</div>
<div class="col-sm-4">
<h4><a class="no-color" href="{{ baseUrl("/pages/recently-created") }}">Recently Created Pages</a></h4>
<h4><a class="no-color" href="{{ baseUrl("/pages/recently-created") }}">{{ trans('entities.recently_created_pages') }}</a></h4>
<div id="recently-created-pages">
@include('partials/entity-list', [
'entities' => $recentlyCreatedPages,
'style' => 'compact',
'emptyText' => 'No pages have been recently created'
'emptyText' => trans('entities.no_pages_recently_created')
])
</div>
<h4><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">Recently Updated Pages</a></h4>
<h4><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h4>
<div id="recently-updated-pages">
@include('partials/entity-list', [
'entities' => $recentlyUpdatedPages,
'style' => 'compact',
'emptyText' => 'No pages have been recently updated'
'emptyText' => trans('entites.no_pages_recently_updated')
])
</div>
</div>
<div class="col-sm-4" id="recent-activity">
<h4>Recent Activity</h4>
<h4>{{ trans('entities.recent_activity') }}</h4>
@include('partials/activity-list', ['activity' => $activity])
</div>
......
<div class="breadcrumbs">
<a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
@if($page->hasChapter())
<span class="sep">&raquo;</span>
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
<i class="zmdi zmdi-collection-bookmark"></i>
{{ $page->chapter->getShortName() }}
</a>
@endif
<span class="sep">&raquo;</span>
<a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
</div>
\ No newline at end of file
......@@ -2,15 +2,25 @@
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
<div class="container small" ng-non-bindable>
<h1>Delete {{ $page->draft ? 'Draft' : '' }} Page</h1>
<p class="text-neg">Are you sure you want to delete this {{ $page->draft ? 'draft' : '' }} page?</p>
<h1>{{ $page->draft ? trans('entities.pages_delete_draft') : trans('entities.pages_delete') }}</h1>
<p class="text-neg">{{ $page->draft ? trans('entities.pages_delete_draft_confirm'): trans('entities.pages_delete_confirm') }}</p>
<form action="{{ $page->getUrl() }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="DELETE">
<a href="{{ $page->getUrl() }}" class="button primary">Cancel</a>
<button type="submit" class="button neg">Confirm</button>
<a href="{{ $page->getUrl() }}" class="button primary">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</form>
</div>
......
......@@ -20,7 +20,7 @@
</div>
@include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
@include('partials/entity-selector-popup')
@include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
@include('components.entity-selector-popup')
@stop
\ No newline at end of file
......
......@@ -15,15 +15,11 @@
<div class="col-md-8 col-md-offset-2">
<div class="page-content">
@include('pages/page-display')
@include('pages.page-display')
<hr>
<p class="text-muted small">
Created {{$page->created_at->toDayDateTimeString()}} @if($page->createdBy) by {{$page->createdBy->name}} @endif
<br>
Last Updated {{$page->updated_at->toDayDateTimeString()}} @if($page->updatedBy) by {{$page->updatedBy->name}} @endif
</p>
@include('partials.entity-meta', ['entity' => $page])
</div>
</div>
......
......@@ -9,8 +9,8 @@
<div class="row">
<div class="col-sm-4 faded">
<div class="action-buttons text-left">
<a href="{{ back()->getTargetUrl() }}" class="text-button text-primary"><i class="zmdi zmdi-arrow-left"></i>Back</a>
<a onclick="$('body>header').slideToggle();" class="text-button text-primary"><i class="zmdi zmdi-swap-vertical"></i>Toggle Header</a>
<a href="{{ back()->getTargetUrl() }}" class="text-button text-primary"><i class="zmdi zmdi-arrow-left"></i>{{ trans('common.back') }}</a>
<a onclick="$('body>header').slideToggle();" class="text-button text-primary"><i class="zmdi zmdi-swap-vertical"></i>{{ trans('entities.pages_edit_toggle_header') }}</a>
</div>
</div>
<div class="col-sm-4 faded text-center">
......@@ -20,13 +20,13 @@
<i class="zmdi zmdi-check-circle text-pos draft-notification" ng-class="{visible: draftUpdated}"></i>
<ul>
<li>
<a ng-click="forceDraftSave()" class="text-pos"><i class="zmdi zmdi-save"></i>Save Draft</a>
<a ng-click="forceDraftSave()" class="text-pos"><i class="zmdi zmdi-save"></i>{{ trans('entities.pages_edit_save_draft') }}</a>
</li>
<li ng-if="isNewPageDraft">
<a href="{{ $model->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete Draft</a>
<a href="{{ $model->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('entities.pages_edit_delete_draft') }}</a>
</li>
<li>
<a type="button" ng-if="isUpdateDraft" ng-click="discardDraft()" class="text-neg"><i class="zmdi zmdi-close-circle"></i>Discard Draft</a>
<a type="button" ng-if="isUpdateDraft" ng-click="discardDraft()" class="text-neg"><i class="zmdi zmdi-close-circle"></i>{{ trans('entities.pages_edit_discard_draft') }}</a>
</li>
</ul>
</div>
......@@ -34,16 +34,16 @@
<div class="col-sm-4 faded">
<div class="action-buttons" ng-cloak>
<div dropdown class="dropdown-container">
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-edit"></i> @{{(changeSummary | limitTo:16) + (changeSummary.length>16?'...':'') || 'Set Changelog'}}</a>
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-edit"></i> <span ng-bind="(changeSummary | limitTo:16) + (changeSummary.length>16?'...':'') || '{{ trans('entities.pages_edit_set_changelog') }}'"></span></a>
<ul class="wide">
<li class="padded">
<p class="text-muted">Enter a brief description of the changes you've made</p>
<input name="summary" id="summary-input" type="text" placeholder="Enter Changelog" ng-model="changeSummary" />
<p class="text-muted">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
<input name="summary" id="summary-input" type="text" placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" ng-model="changeSummary" />
</li>
</ul>
</div>
<button type="submit" id="save-button" class="text-button text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</button>
<button type="submit" id="save-button" class="text-button text-pos"><i class="zmdi zmdi-floppy"></i>{{ trans('entities.pages_save') }}</button>
</div>
</div>
</div>
......@@ -53,7 +53,7 @@
{{--Title input--}}
<div class="title-input page-title clearfix" ng-non-bindable>
<div class="input">
@include('form/text', ['name' => 'name', 'placeholder' => 'Page Title'])
@include('form/text', ['name' => 'name', 'placeholder' => trans('entities.pages_title')])
</div>
</div>
......@@ -78,11 +78,11 @@
<div class="markdown-editor-wrap">
<div class="editor-toolbar">
<span class="float left">Editor</span>
<span class="float left">{{ trans('entities.pages_md_editor') }}</span>
<div class="float right buttons">
<button class="text-button" type="button" data-action="insertImage"><i class="zmdi zmdi-image"></i>Insert Image</button>
<button class="text-button" type="button" data-action="insertImage"><i class="zmdi zmdi-image"></i>{{ trans('entities.pages_md_insert_image') }}</button>
&nbsp;|&nbsp;
<button class="text-button" type="button" data-action="insertEntityLink"><i class="zmdi zmdi-link"></i>Insert Entity Link</button>
<button class="text-button" type="button" data-action="insertEntityLink"><i class="zmdi zmdi-link"></i>{{ trans('entities.pages_md_insert_link') }}</button>
</div>
</div>
......@@ -95,7 +95,7 @@
<div class="markdown-editor-wrap">
<div class="editor-toolbar">
<div class="">Preview</div>
<div class="">{{ trans('entities.pages_md_preview') }}</div>
</div>
<div class="markdown-display">
<div class="page-content" ng-bind-html="displayContent"></div>
......
......@@ -3,19 +3,19 @@
@section('content')
<div class="container small" ng-non-bindable>
<h1>Create Page</h1>
<h1>{{ trans('entities.pages_new') }}</h1>
<form action="{{ $parent->getUrl('/page/create/guest') }}" method="POST">
{!! csrf_field() !!}
<div class="form-group title-input">
<label for="name">Page Name</label>
<label for="name">{{ trans('entities.pages_name') }}</label>
@include('form/text', ['name' => 'name'])
</div>
<div class="form-group">
<a href="{{ $parent->getUrl() }}" class="button muted">Cancel</a>
<button type="submit" class="button pos">Continue</button>
<a href="{{ $parent->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('common.continue') }}</button>
</div>
</form>
......
......@@ -12,8 +12,7 @@
@if(isset($style) && $style === 'detailed')
<div class="row meta text-muted text-small">
<div class="col-md-6">
Created {{$page->created_at->diffForHumans()}} @if($page->createdBy)by {{$page->createdBy->name}}@endif <br>
Last updated {{ $page->updated_at->diffForHumans() }} @if($page->updatedBy)by {{$page->updatedBy->name}} @endif
@include('partials.entity-meta', ['entity' => $page])
</div>
<div class="col-md-6">
<a class="text-book" href="{{ $page->book->getUrl() }}"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName(30) }}</a>
......@@ -21,7 +20,7 @@
@if($page->chapter)
<a class="text-chapter" href="{{ $page->chapter->getUrl() }}"><i class="zmdi zmdi-collection-bookmark"></i>{{ $page->chapter->getShortName(30) }}</a>
@else
<i class="zmdi zmdi-collection-bookmark"></i> Page is not in a chapter
<i class="zmdi zmdi-collection-bookmark"></i> {{ trans('entities.pages_not_in_chapter') }}
@endif
</div>
</div>
......
......@@ -6,34 +6,23 @@
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
@if($page->hasChapter())
<span class="sep">&raquo;</span>
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
<i class="zmdi zmdi-collection-bookmark"></i>
{{ $page->chapter->getShortName() }}
</a>
@endif
<span class="sep">&raquo;</span>
<a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file-text"></i>{{ $page->getShortName() }}</a>
</div>
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
<div class="container">
<h1>Move Page <small class="subheader">{{$page->name}}</small></h1>
<h1>{{ trans('entities.pages_move') }}</h1>
<form action="{{ $page->getUrl('/move') }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
@include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter'])
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter'])
<a href="{{ $page->getUrl() }}" class="button muted">Cancel</a>
<button type="submit" class="button pos">Move Page</button>
<a href="{{ $page->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button>
</form>
</div>
......
......@@ -36,6 +36,5 @@
max-width: none;
display: none;
}
</style>
@stop
\ No newline at end of file
......
......@@ -6,26 +6,15 @@
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
@if($page->hasChapter())
<span class="sep">&raquo;</span>
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
<i class="zmdi zmdi-collection-bookmark"></i>
{{ $page->chapter->getShortName() }}
</a>
@endif
<span class="sep">&raquo;</span>
<a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
</div>
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
<div class="container" ng-non-bindable>
<h1>Page Permissions</h1>
@include('form/restriction-form', ['model' => $page])
<h1>{{ trans('entities.pages_permissions') }}</h1>
@include('form.restriction-form', ['model' => $page])
</div>
@stop
......
......@@ -7,14 +7,12 @@
<div class="row">
<div class="col-md-9">
<div class="page-content anim fadeIn">
@include('pages/page-display')
@include('pages.page-display')
</div>
</div>
</div>
</div>
@include('partials/highlight')
@include('partials.highlight')
@stop
......
......@@ -6,37 +6,24 @@
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
@if($page->hasChapter())
<span class="sep">&raquo;</span>
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
<i class="zmdi zmdi-collection-bookmark"></i>
{{ $page->chapter->getShortName() }}
</a>
@endif
<span class="sep">&raquo;</span>
<a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
</div>
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
<div class="container" ng-non-bindable>
<h1>Page Revisions <span class="subheader">For "{{ $page->name }}"</span></h1>
<h1>{{ trans('entities.pages_revisions') }}</h1>
@if(count($page->revisions) > 0)
<table class="table">
<tr>
<th width="23%">Name</th>
<th colspan="2" width="8%">Created By</th>
<th width="15%">Revision Date</th>
<th width="25%">Changelog</th>
<th width="20%">Actions</th>
<th width="23%">{{ trans('entities.pages_name') }}</th>
<th colspan="2" width="8%">{{ trans('entities.pages_revisions_created_by') }}</th>
<th width="15%">{{ trans('entities.pages_revisions_date') }}</th>
<th width="25%">{{ trans('entities.pages_revisions_changelog') }}</th>
<th width="20%">{{ trans('common.actions') }}</th>
</tr>
@foreach($page->revisions as $index => $revision)
<tr>
......@@ -46,19 +33,19 @@
<img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{ $revision->createdBy->name }}">
@endif
</td>
<td> @if($revision->createdBy) {{ $revision->createdBy->name }} @else Deleted User @endif</td>
<td> @if($revision->createdBy) {{ $revision->createdBy->name }} @else {{ trans('common.deleted_user') }} @endif</td>
<td><small>{{ $revision->created_at->format('jS F, Y H:i:s') }} <br> ({{ $revision->created_at->diffForHumans() }})</small></td>
<td>{{ $revision->summary }}</td>
<td>
<a href="{{ $revision->getUrl('changes') }}" target="_blank">Changes</a>
<a href="{{ $revision->getUrl('changes') }}" target="_blank">{{ trans('entities.pages_revisions_changes') }}</a>
<span class="text-muted">&nbsp;|&nbsp;</span>
@if ($index === 0)
<a target="_blank" href="{{ $page->getUrl() }}"><i>Current Version</i></a>
<a target="_blank" href="{{ $page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
@else
<a href="{{ $revision->getUrl() }}" target="_blank">Preview</a>
<a href="{{ $revision->getUrl() }}" target="_blank">{{ trans('entities.pages_revisions_preview') }}</a>
<span class="text-muted">&nbsp;|&nbsp;</span>
<a href="{{ $revision->getUrl('restore') }}" target="_blank">Restore</a>
<a href="{{ $revision->getUrl('restore') }}" target="_blank">{{ trans('entities.pages_revisions_restore') }}</a>
@endif
</td>
</tr>
......@@ -66,7 +53,7 @@
</table>
@else
<p>This page has no revisions.</p>
<p>{{ trans('entities.pages_revisions_none') }}</p>
@endif
</div>
......
......@@ -6,43 +6,34 @@
<div class="container">
<div class="row">
<div class="col-sm-6 faded">
<div class="breadcrumbs">
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
@if($page->hasChapter())
<span class="sep">&raquo;</span>
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
<i class="zmdi zmdi-collection-bookmark"></i>
{{ $page->chapter->getShortName() }}
</a>
@endif
</div>
@include('pages._breadcrumbs', ['page' => $page])
</div>
<div class="col-sm-6 faded">
<div class="action-buttons">
<span dropdown class="dropdown-container">
<div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>Export</div>
<div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.pages_export') }}</div>
<ul class="wide">
<li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">Contained Web File <span class="text-muted float right">.html</span></a></li>
<li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">PDF File <span class="text-muted float right">.pdf</span></a></li>
<li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">Plain Text File <span class="text-muted float right">.txt</span></a></li>
<li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">{{ trans('entities.pages_export_html') }} <span class="text-muted float right">.html</span></a></li>
<li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.pages_export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
<li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.pages_export_text') }} <span class="text-muted float right">.txt</span></a></li>
</ul>
</span>
@if(userCan('page-update', $page))
<a href="{{ $page->getUrl('/edit') }}" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>Edit</a>
<a href="{{ $page->getUrl('/edit') }}" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
@endif
@if(userCan('page-update', $page) || userCan('restrictions-manage', $page) || userCan('page-delete', $page))
<div dropdown class="dropdown-container">
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
<ul>
@if(userCan('page-update', $page))
<li><a href="{{ $page->getUrl('/move') }}" class="text-primary" ><i class="zmdi zmdi-folder"></i>Move</a></li>
<li><a href="{{ $page->getUrl('/revisions') }}" class="text-primary"><i class="zmdi zmdi-replay"></i>Revisions</a></li>
<li><a href="{{ $page->getUrl('/move') }}" class="text-primary" ><i class="zmdi zmdi-folder"></i>{{ trans('common.move') }}</a></li>
<li><a href="{{ $page->getUrl('/revisions') }}" class="text-primary"><i class="zmdi zmdi-replay"></i>{{ trans('entities.revisions') }}</a></li>
@endif
@if(userCan('restrictions-manage', $page))
<li><a href="{{ $page->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
<li><a href="{{ $page->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.permissions') }}</a></li>
@endif
@if(userCan('page-delete', $page))
<li><a href="{{ $page->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
<li><a href="{{ $page->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
@endif
</ul>
</div>
......@@ -64,7 +55,7 @@
<div class="pointer anim">
<i class="zmdi zmdi-link"></i>
<input readonly="readonly" type="text" placeholder="url">
<button class="button icon" title="Copy Link" data-clipboard-text=""><i class="zmdi zmdi-copy"></i></button>
<button class="button icon" title="{{ trans('entities.pages_copy_link') }}" data-clipboard-text=""><i class="zmdi zmdi-copy"></i></button>
</div>
</div>
......@@ -72,11 +63,7 @@
<hr>
<p class="text-muted small">
Created {{ $page->created_at->diffForHumans() }} @if($page->createdBy) by <a href="{{ $page->createdBy->getProfileUrl() }}">{{$page->createdBy->name}}</a> @endif
<br>
Last Updated {{ $page->updated_at->diffForHumans() }} @if($page->updatedBy) by <a href="{{ $page->updatedBy->getProfileUrl() }}">{{$page->updatedBy->name}}</a> @endif
</p>
@include('partials.entity-meta', ['entity' => $page])
</div>
</div>
......@@ -88,27 +75,27 @@
@if($book->restricted)
@if(userCan('restrictions-manage', $book))
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>Book Permissions Active
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
@endif
<br>
@endif
@if($page->chapter && $page->chapter->restricted)
@if(userCan('restrictions-manage', $page->chapter))
<a href="{{ $page->chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active</a>
<a href="{{ $page->chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}
@endif
<br>
@endif
@if($page->restricted)
@if(userCan('restrictions-manage', $page))
<a href="{{ $page->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Page Permissions Active</a>
<a href="{{ $page->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.pages_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>Page Permissions Active
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.pages_permissions_active') }}
@endif
<br>
@endif
......