]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'master' into release
authorDan Brown <redacted>
Sun, 10 Sep 2017 16:05:05 +0000 (17:05 +0100)
committerDan Brown <redacted>
Sun, 10 Sep 2017 16:05:05 +0000 (17:05 +0100)
207 files changed:
.gitignore
.travis.yml
app/Comment.php [new file with mode: 0644]
app/Entity.php
app/Http/Controllers/Auth/RegisterController.php
app/Http/Controllers/BookController.php
app/Http/Controllers/CommentController.php [new file with mode: 0644]
app/Http/Controllers/HomeController.php
app/Http/Controllers/PageController.php
app/PageRevision.php
app/Repos/CommentRepo.php [new file with mode: 0644]
app/Repos/EntityRepo.php
app/Repos/TagRepo.php
app/Services/ExportService.php
app/Services/PermissionService.php
app/Services/SearchService.php
app/Services/ViewService.php
config/app.php [changed mode: 0644->0755]
database/factories/ModelFactory.php
database/migrations/2017_08_01_130541_create_comments_table.php [new file with mode: 0644]
database/seeds/DummyContentSeeder.php
gulpfile.js
package.json
public/fonts/roboto-mono-v4-latin-regular.woff [deleted file]
public/fonts/roboto-mono-v4-latin-regular.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-100.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-100.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-100italic.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-100italic.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-300.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-300.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-300italic.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-300italic.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-500.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-500.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-500italic.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-500italic.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-700.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-700.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-700italic.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-700italic.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-italic.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-italic.woff2 [deleted file]
public/fonts/roboto-v15-cyrillic_latin-regular.woff [deleted file]
public/fonts/roboto-v15-cyrillic_latin-regular.woff2 [deleted file]
public/logo.png
readme.md
resources/assets/js/code.js
resources/assets/js/components/back-top-top.js [new file with mode: 0644]
resources/assets/js/components/chapter-toggle.js [new file with mode: 0644]
resources/assets/js/components/dropdown.js [new file with mode: 0644]
resources/assets/js/components/entity-selector-popup.js [new file with mode: 0644]
resources/assets/js/components/entity-selector.js [new file with mode: 0644]
resources/assets/js/components/expand-toggle.js [new file with mode: 0644]
resources/assets/js/components/index.js [new file with mode: 0644]
resources/assets/js/components/notification.js [new file with mode: 0644]
resources/assets/js/components/overlay.js [new file with mode: 0644]
resources/assets/js/components/page-comments.js [new file with mode: 0644]
resources/assets/js/components/page-picker.js [new file with mode: 0644]
resources/assets/js/components/sidebar.js [new file with mode: 0644]
resources/assets/js/controllers.js
resources/assets/js/directives.js
resources/assets/js/dom-polyfills.js [new file with mode: 0644]
resources/assets/js/global.js
resources/assets/js/pages/page-form.js
resources/assets/js/pages/page-show.js
resources/assets/js/services.js [deleted file]
resources/assets/js/translations.js
resources/assets/js/vues/attachment-manager.js [new file with mode: 0644]
resources/assets/js/vues/components/autosuggest.js [new file with mode: 0644]
resources/assets/js/vues/components/dropzone.js [new file with mode: 0644]
resources/assets/js/vues/entity-dashboard.js [moved from resources/assets/js/vues/entity-search.js with 100% similarity]
resources/assets/js/vues/image-manager.js [new file with mode: 0644]
resources/assets/js/vues/tag-manager.js [new file with mode: 0644]
resources/assets/js/vues/vues.js
resources/assets/sass/_animations.scss
resources/assets/sass/_blocks.scss
resources/assets/sass/_buttons.scss
resources/assets/sass/_codemirror.scss
resources/assets/sass/_components.scss
resources/assets/sass/_fonts.scss [deleted file]
resources/assets/sass/_forms.scss
resources/assets/sass/_grid.scss
resources/assets/sass/_header.scss
resources/assets/sass/_html.scss
resources/assets/sass/_lists.scss
resources/assets/sass/_pages.scss
resources/assets/sass/_tables.scss
resources/assets/sass/_text.scss
resources/assets/sass/_tinymce.scss
resources/assets/sass/_variables.scss
resources/assets/sass/export-styles.scss
resources/assets/sass/styles.scss
resources/lang/de/activities.php
resources/lang/de/auth.php
resources/lang/de/common.php
resources/lang/de/components.php
resources/lang/de/entities.php
resources/lang/de/errors.php
resources/lang/de/pagination.php
resources/lang/de/passwords.php
resources/lang/de/settings.php
resources/lang/de/validation.php
resources/lang/en/activities.php
resources/lang/en/common.php
resources/lang/en/entities.php
resources/lang/en/errors.php
resources/lang/en/settings.php [changed mode: 0644->0755]
resources/lang/es/entities.php
resources/lang/es/errors.php
resources/lang/fr/entities.php
resources/lang/fr/errors.php
resources/lang/it/activities.php [new file with mode: 0755]
resources/lang/it/auth.php [new file with mode: 0755]
resources/lang/it/common.php [new file with mode: 0755]
resources/lang/it/components.php [new file with mode: 0755]
resources/lang/it/entities.php [new file with mode: 0755]
resources/lang/it/errors.php [new file with mode: 0755]
resources/lang/it/pagination.php [new file with mode: 0755]
resources/lang/it/passwords.php [new file with mode: 0755]
resources/lang/it/settings.php [new file with mode: 0755]
resources/lang/it/validation.php [new file with mode: 0755]
resources/lang/ja/components.php
resources/lang/nl/entities.php
resources/lang/nl/errors.php
resources/lang/pt_BR/entities.php
resources/lang/pt_BR/errors.php
resources/lang/sk/entities.php
resources/lang/sk/errors.php
resources/views/auth/login.blade.php
resources/views/auth/register-confirm.blade.php
resources/views/auth/register.blade.php
resources/views/auth/user-unconfirmed.blade.php
resources/views/base.blade.php
resources/views/books/create.blade.php
resources/views/books/delete.blade.php
resources/views/books/edit.blade.php
resources/views/books/form.blade.php
resources/views/books/index.blade.php
resources/views/books/list-item.blade.php
resources/views/books/restrictions.blade.php
resources/views/books/show.blade.php
resources/views/books/sort.blade.php
resources/views/chapters/create.blade.php
resources/views/chapters/delete.blade.php
resources/views/chapters/edit.blade.php
resources/views/chapters/form.blade.php
resources/views/chapters/list-item.blade.php
resources/views/chapters/move.blade.php
resources/views/chapters/restrictions.blade.php
resources/views/chapters/show.blade.php
resources/views/comments/comment.blade.php [new file with mode: 0644]
resources/views/comments/comments.blade.php [new file with mode: 0644]
resources/views/components/code-editor.blade.php
resources/views/components/entity-selector-popup.blade.php
resources/views/components/entity-selector.blade.php
resources/views/components/image-manager.blade.php
resources/views/components/image-picker.blade.php
resources/views/components/page-picker.blade.php [new file with mode: 0644]
resources/views/errors/404.blade.php
resources/views/errors/500.blade.php
resources/views/errors/503.blade.php
resources/views/form/restriction-form.blade.php
resources/views/home-custom.blade.php [new file with mode: 0644]
resources/views/home.blade.php
resources/views/pages/delete.blade.php
resources/views/pages/detailed-listing.blade.php
resources/views/pages/edit.blade.php
resources/views/pages/form-toolbox.blade.php
resources/views/pages/form.blade.php
resources/views/pages/list-item.blade.php
resources/views/pages/move.blade.php
resources/views/pages/page-display.blade.php
resources/views/pages/restrictions.blade.php
resources/views/pages/revision.blade.php
resources/views/pages/revisions.blade.php
resources/views/pages/show.blade.php
resources/views/pages/sidebar-tree-list.blade.php
resources/views/partials/activity-list.blade.php
resources/views/partials/book-tree.blade.php [new file with mode: 0644]
resources/views/partials/custom-styles.blade.php
resources/views/partials/entity-meta.blade.php
resources/views/partials/loading-icon.blade.php
resources/views/partials/notifications.blade.php
resources/views/public.blade.php
resources/views/search/all.blade.php
resources/views/settings/index.blade.php
resources/views/settings/navbar.blade.php
resources/views/settings/roles/create.blade.php
resources/views/settings/roles/delete.blade.php
resources/views/settings/roles/edit.blade.php
resources/views/settings/roles/form.blade.php
resources/views/settings/roles/index.blade.php
resources/views/sidebar-layout.blade.php [new file with mode: 0644]
resources/views/simple-layout.blade.php [new file with mode: 0644]
resources/views/users/create.blade.php
resources/views/users/delete.blade.php
resources/views/users/edit.blade.php
resources/views/users/index.blade.php
resources/views/users/profile.blade.php
routes/web.php
tests/Entity/CommentTest.php [new file with mode: 0644]
tests/Entity/EntityTest.php
tests/HomepageTest.php [new file with mode: 0644]
tests/Permissions/RolesTest.php
tests/TestCase.php
version

index d7281b29ce7564631d16e346ed2686cf7c6430ca..00602d29a4fa1ffe58536a29c9492dc053b77dc2 100644 (file)
@@ -8,16 +8,15 @@ Homestead.yaml
 /public/css/*.map
 /public/js/*.map
 /public/bower
+/public/build/
 /storage/images
 _ide_helper.php
 /storage/debugbar
 .phpstorm.meta.php
 yarn.lock
 /bin
+nbproject
 .buildpath
-
 .project
-
 .settings/org.eclipse.wst.common.project.facet.core.xml
-
 .settings/org.eclipse.php.core.prefs
index 839d3be3f951a83f0f0b3e35855c069ca6bce232..29f9fb7a55b60fd81172a3ba0819bdfcf0eefece 100644 (file)
@@ -2,7 +2,7 @@ dist: trusty
 sudo: false
 language: php
 php:
-  - 7.0
+  - 7.0.7
 
 cache:
   directories:
diff --git a/app/Comment.php b/app/Comment.php
new file mode 100644 (file)
index 0000000..2800ab2
--- /dev/null
@@ -0,0 +1,43 @@
+<?php namespace BookStack;
+
+class Comment extends Ownable
+{
+    protected $fillable = ['text', 'html', 'parent_id'];
+    protected $appends = ['created', 'updated'];
+
+    /**
+     * Get the entity that this comment belongs to
+     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+     */
+    public function entity()
+    {
+        return $this->morphTo('entity');
+    }
+
+    /**
+     * Check if a comment has been updated since creation.
+     * @return bool
+     */
+    public function isUpdated()
+    {
+        return $this->updated_at->timestamp > $this->created_at->timestamp;
+    }
+
+    /**
+     * Get created date as a relative diff.
+     * @return mixed
+     */
+    public function getCreatedAttribute()
+    {
+        return $this->created_at->diffForHumans();
+    }
+
+    /**
+     * Get updated date as a relative diff.
+     * @return mixed
+     */
+    public function getUpdatedAttribute()
+    {
+        return $this->updated_at->diffForHumans();
+    }
+}
index e5dd04bf28bdb01d30690c18e5b9a2b6986a7929..df8e4d38b241e9928ca951697b6da392744a39e2 100644 (file)
@@ -1,6 +1,8 @@
 <?php namespace BookStack;
 
 
+use Illuminate\Database\Eloquent\Relations\MorphMany;
+
 class Entity extends Ownable
 {
 
@@ -65,6 +67,17 @@ class Entity extends Ownable
         return $this->morphMany(Tag::class, 'entity')->orderBy('order', 'asc');
     }
 
+    /**
+     * Get the comments for an entity
+     * @param bool $orderByCreated
+     * @return MorphMany
+     */
+    public function comments($orderByCreated = true)
+    {
+        $query = $this->morphMany(Comment::class, 'entity');
+        return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
+    }
+
     /**
      * Get the related search terms.
      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
index 8b0ef309aeba8c61d156a4fd0692dbc94e2ba712..1ba8b97db8a7abaadcccffdadaf54eed94db4366 100644 (file)
@@ -8,6 +8,7 @@ use BookStack\Exceptions\UserRegistrationException;
 use BookStack\Repos\UserRepo;
 use BookStack\Services\EmailConfirmationService;
 use BookStack\Services\SocialAuthService;
+use BookStack\SocialAccount;
 use BookStack\User;
 use Exception;
 use Illuminate\Http\Request;
@@ -103,7 +104,7 @@ class RegisterController extends Controller
      * @param Request|\Illuminate\Http\Request $request
      * @return Response
      * @throws UserRegistrationException
-     * @throws \Illuminate\Foundation\Validation\ValidationException
+     * @throws \Illuminate\Validation\ValidationException
      */
     public function postRegister(Request $request)
     {
@@ -230,7 +231,6 @@ class RegisterController extends Controller
             return redirect('/register/confirm');
         }
 
-        $this->emailConfirmationService->sendConfirmation($user);
         session()->flash('success', trans('auth.email_confirm_resent'));
         return redirect('/register/confirm');
     }
@@ -255,16 +255,13 @@ class RegisterController extends Controller
      */
     public function socialCallback($socialDriver)
     {
-        if (session()->has('social-callback')) {
-            $action = session()->pull('social-callback');
-            if ($action == 'login') {
-                return $this->socialAuthService->handleLoginCallback($socialDriver);
-            } elseif ($action == 'register') {
-                return $this->socialRegisterCallback($socialDriver);
-            }
-        } else {
+        if (!session()->has('social-callback')) {
             throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
         }
+
+        $action = session()->pull('social-callback');
+        if ($action == 'login') return $this->socialAuthService->handleLoginCallback($socialDriver);
+        if ($action == 'register') return $this->socialRegisterCallback($socialDriver);
         return redirect()->back();
     }
 
index 8996ae64aec248d3dc61a2d0307a982d4b496e10..5342ece6bafbc60be02d6053b1b4cbd36c14ee9f 100644 (file)
@@ -36,11 +36,17 @@ class BookController extends Controller
      */
     public function index()
     {
-        $books = $this->entityRepo->getAllPaginated('book', 10);
+        $books = $this->entityRepo->getAllPaginated('book', 20);
         $recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
         $popular = $this->entityRepo->getPopular('book', 4, 0);
+        $new = $this->entityRepo->getRecentlyCreated('book', 4, 0);
         $this->setPageTitle('Books');
-        return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular]);
+        return view('books/index', [
+            'books' => $books,
+            'recents' => $recents,
+            'popular' => $popular,
+            'new' => $new
+        ]);
     }
 
     /**
@@ -84,7 +90,12 @@ class BookController extends Controller
         $bookChildren = $this->entityRepo->getBookChildren($book);
         Views::add($book);
         $this->setPageTitle($book->getShortName());
-        return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]);
+        return view('books/show', [
+            'book' => $book,
+            'current' => $book,
+            'bookChildren' => $bookChildren,
+            'activity' => Activity::entityActivity($book, 20, 0)
+        ]);
     }
 
     /**
diff --git a/app/Http/Controllers/CommentController.php b/app/Http/Controllers/CommentController.php
new file mode 100644 (file)
index 0000000..7bf0a2a
--- /dev/null
@@ -0,0 +1,93 @@
+<?php namespace BookStack\Http\Controllers;
+
+use Activity;
+use BookStack\Repos\CommentRepo;
+use BookStack\Repos\EntityRepo;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
+use Illuminate\Http\Request;
+
+class CommentController extends Controller
+{
+    protected $entityRepo;
+    protected $commentRepo;
+
+    /**
+     * CommentController constructor.
+     * @param EntityRepo $entityRepo
+     * @param CommentRepo $commentRepo
+     */
+    public function __construct(EntityRepo $entityRepo, CommentRepo $commentRepo)
+    {
+        $this->entityRepo = $entityRepo;
+        $this->commentRepo = $commentRepo;
+        parent::__construct();
+    }
+
+    /**
+     * Save a new comment for a Page
+     * @param Request $request
+     * @param integer $pageId
+     * @param null|integer $commentId
+     * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
+     */
+    public function savePageComment(Request $request, $pageId, $commentId = null)
+    {
+        $this->validate($request, [
+            'text' => 'required|string',
+            'html' => 'required|string',
+        ]);
+
+        try {
+            $page = $this->entityRepo->getById('page', $pageId, true);
+        } catch (ModelNotFoundException $e) {
+            return response('Not found', 404);
+        }
+
+        $this->checkOwnablePermission('page-view', $page);
+
+        // Prevent adding comments to draft pages
+        if ($page->draft) {
+            return $this->jsonError(trans('errors.cannot_add_comment_to_draft'), 400);
+        }
+
+        // Create a new comment.
+        $this->checkPermission('comment-create-all');
+        $comment = $this->commentRepo->create($page, $request->only(['html', 'text', 'parent_id']));
+        Activity::add($page, 'commented_on', $page->book->id);
+        return view('comments/comment', ['comment' => $comment]);
+    }
+
+    /**
+     * Update an existing comment.
+     * @param Request $request
+     * @param integer $commentId
+     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+     */
+    public function update(Request $request, $commentId)
+    {
+        $this->validate($request, [
+            'text' => 'required|string',
+            'html' => 'required|string',
+        ]);
+
+        $comment = $this->commentRepo->getById($commentId);
+        $this->checkOwnablePermission('page-view', $comment->entity);
+        $this->checkOwnablePermission('comment-update', $comment);
+
+        $comment = $this->commentRepo->update($comment, $request->only(['html', 'text']));
+        return view('comments/comment', ['comment' => $comment]);
+    }
+
+    /**
+     * Delete a comment from the system.
+     * @param integer $id
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function destroy($id)
+    {
+        $comment = $this->commentRepo->getById($id);
+        $this->checkOwnablePermission('comment-delete', $comment);
+        $this->commentRepo->delete($comment);
+        return response()->json(['message' => trans('entities.comment_deleted')]);
+    }
+}
index 7f60d7009f15e95e64746e0fa1fa5c8487d6b744..7d109b70a9bb9225e2d8edfccc678e191765acbf 100644 (file)
@@ -29,15 +29,25 @@ class HomeController extends Controller
         $activity = Activity::latest(10);
         $draftPages = $this->signedIn ? $this->entityRepo->getUserDraftPages(6) : [];
         $recentFactor = count($draftPages) > 0 ? 0.5 : 1;
-        $recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 10*$recentFactor);
-        $recentlyCreatedPages = $this->entityRepo->getRecentlyCreated('page', 5);
-        $recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 5);
-        return view('home', [
+        $recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 12*$recentFactor);
+        $recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 12);
+
+        // Custom homepage
+        $customHomepage = false;
+        $homepageSetting = setting('app-homepage');
+        if ($homepageSetting) {
+            $id = intval(explode(':', $homepageSetting)[0]);
+            $customHomepage = $this->entityRepo->getById('page', $id, false, true);
+            $this->entityRepo->renderPage($customHomepage, true);
+        }
+
+        $view = $customHomepage ? 'home-custom' : 'home';
+        return view($view, [
             'activity' => $activity,
             'recents' => $recents,
-            'recentlyCreatedPages' => $recentlyCreatedPages,
             'recentlyUpdatedPages' => $recentlyUpdatedPages,
-            'draftPages' => $draftPages
+            'draftPages' => $draftPages,
+            'customHomepage' => $customHomepage
         ]);
     }
 
index c97597bc4834822b5947eec2a3f0cc8964e3510c..c3090af83da072151838ffc308934ef79956d7f0 100644 (file)
@@ -161,13 +161,14 @@ class PageController extends Controller
         $pageContent = $this->entityRepo->renderPage($page);
         $sidebarTree = $this->entityRepo->getBookChildren($page->book);
         $pageNav = $this->entityRepo->getPageNav($pageContent);
-        
+        $page->load(['comments.createdBy']);
+
         Views::add($page);
         $this->setPageTitle($page->getShortName());
         return view('pages/show', [
             'page' => $page,'book' => $page->book,
             'current' => $page, 'sidebarTree' => $sidebarTree,
-            'pageNav' => $pageNav, 'pageContent' => $pageContent]);
+            'pageNav' => $pageNav]);
     }
 
     /**
@@ -376,10 +377,11 @@ class PageController extends Controller
 
         $page->fill($revision->toArray());
         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
-        
+
         return view('pages/revision', [
             'page' => $page,
             'book' => $page->book,
+            'revision' => $revision
         ]);
     }
 
@@ -409,6 +411,7 @@ class PageController extends Controller
             'page' => $page,
             'book' => $page->book,
             'diff' => $diff,
+            'revision' => $revision
         ]);
     }
 
index ff469f0ed3a78b3d3750c3ff6d07b6b2d787c226..0a9764729dded7bca21748e581605fba61887565 100644 (file)
@@ -47,4 +47,16 @@ class PageRevision extends Model
         return null;
     }
 
+    /**
+     * Allows checking of the exact class, Used to check entity type.
+     * Included here to align with entities in similar use cases.
+     * (Yup, Bit of an awkward hack)
+     * @param $type
+     * @return bool
+     */
+    public static function isA($type)
+    {
+        return $type === 'revision';
+    }
+
 }
diff --git a/app/Repos/CommentRepo.php b/app/Repos/CommentRepo.php
new file mode 100644 (file)
index 0000000..d8c57bd
--- /dev/null
@@ -0,0 +1,87 @@
+<?php namespace BookStack\Repos;
+
+use BookStack\Comment;
+use BookStack\Entity;
+
+/**
+ * Class CommentRepo
+ * @package BookStack\Repos
+ */
+class CommentRepo {
+
+    /**
+     * @var Comment $comment
+     */
+    protected $comment;
+
+    /**
+     * CommentRepo constructor.
+     * @param Comment $comment
+     */
+    public function __construct(Comment $comment)
+    {
+        $this->comment = $comment;
+    }
+
+    /**
+     * Get a comment by ID.
+     * @param $id
+     * @return Comment|\Illuminate\Database\Eloquent\Model
+     */
+    public function getById($id)
+    {
+        return $this->comment->newQuery()->findOrFail($id);
+    }
+
+    /**
+     * Create a new comment on an entity.
+     * @param Entity $entity
+     * @param array $data
+     * @return Comment
+     */
+    public function create (Entity $entity, $data = [])
+    {
+        $userId = user()->id;
+        $comment = $this->comment->newInstance($data);
+        $comment->created_by = $userId;
+        $comment->updated_by = $userId;
+        $comment->local_id = $this->getNextLocalId($entity);
+        $entity->comments()->save($comment);
+        return $comment;
+    }
+
+    /**
+     * Update an existing comment.
+     * @param Comment $comment
+     * @param array $input
+     * @return mixed
+     */
+    public function update($comment, $input)
+    {
+        $comment->updated_by = user()->id;
+        $comment->update($input);
+        return $comment;
+    }
+
+    /**
+     * Delete a comment from the system.
+     * @param Comment $comment
+     * @return mixed
+     */
+    public function delete($comment)
+    {
+        return $comment->delete();
+    }
+
+    /**
+     * Get the next local ID relative to the linked entity.
+     * @param Entity $entity
+     * @return int
+     */
+    protected function getNextLocalId(Entity $entity)
+    {
+        $comments = $entity->comments(false)->orderBy('local_id', 'desc')->first();
+        if ($comments === null) return 1;
+        return $comments->local_id + 1;
+    }
+}
\ No newline at end of file
index d87c40f9b12cca06c74fbe81b5133e84c6a7c517..9a6db3fc35f9504bd2e74f5eebaa7a675883feb3 100644 (file)
@@ -137,10 +137,15 @@ class EntityRepo
      * @param string $type
      * @param integer $id
      * @param bool $allowDrafts
+     * @param bool $ignorePermissions
      * @return Entity
      */
-    public function getById($type, $id, $allowDrafts = false)
+    public function getById($type, $id, $allowDrafts = false, $ignorePermissions = false)
     {
+        if ($ignorePermissions) {
+            $entity = $this->getEntity($type);
+            return $entity->newQuery()->find($id);
+        }
         return $this->entityQuery($type, $allowDrafts)->find($id);
     }
 
@@ -671,9 +676,10 @@ class EntityRepo
     /**
      * Render the page for viewing, Parsing and performing features such as page transclusion.
      * @param Page $page
+     * @param bool $ignorePermissions
      * @return mixed|string
      */
-    public function renderPage(Page $page)
+    public function renderPage(Page $page, $ignorePermissions = false)
     {
         $content = $page->html;
         $matches = [];
@@ -685,19 +691,19 @@ class EntityRepo
             $pageId = intval($splitInclude[0]);
             if (is_nan($pageId)) continue;
 
-            $page = $this->getById('page', $pageId);
-            if ($page === null) {
+            $matchedPage = $this->getById('page', $pageId, false, $ignorePermissions);
+            if ($matchedPage === null) {
                 $content = str_replace($matches[0][$index], '', $content);
                 continue;
             }
 
             if (count($splitInclude) === 1) {
-                $content = str_replace($matches[0][$index], $page->html, $content);
+                $content = str_replace($matches[0][$index], $matchedPage->html, $content);
                 continue;
             }
 
             $doc = new DOMDocument();
-            $doc->loadHTML(mb_convert_encoding('<body>'.$page->html.'</body>', 'HTML-ENTITIES', 'UTF-8'));
+            $doc->loadHTML(mb_convert_encoding('<body>'.$matchedPage->html.'</body>', 'HTML-ENTITIES', 'UTF-8'));
             $matchingElem = $doc->getElementById($splitInclude[1]);
             if ($matchingElem === null) {
                 $content = str_replace($matches[0][$index], '', $content);
@@ -710,6 +716,7 @@ class EntityRepo
             $content = str_replace($matches[0][$index], trim($innerContent), $content);
         }
 
+        $page->setAttribute('renderedHTML', $content);
         return $content;
     }
 
index c6350db1ae8a80b8ede8b499227bcb962eb02d1c..5edd6df3c175cfd511f385d5de1aa031e106712e 100644 (file)
@@ -33,6 +33,7 @@ class TagRepo
      * @param $entityType
      * @param $entityId
      * @param string $action
+     * @return \Illuminate\Database\Eloquent\Model|null|static
      */
     public function getEntity($entityType, $entityId, $action = 'view')
     {
index 78cef41a4d333270c0af29b7b5e7fba07ec03514..40402dbce461e73b3c0b81e7bb7b3a5b515c9805 100644 (file)
@@ -27,9 +27,9 @@ class ExportService
      */
     public function pageToContainedHtml(Page $page)
     {
+        $this->entityRepo->renderPage($page);
         $pageHtml = view('pages/export', [
-            'page' => $page,
-            'pageContent' => $this->entityRepo->renderPage($page)
+            'page' => $page
         ])->render();
         return $this->containHtml($pageHtml);
     }
@@ -74,9 +74,9 @@ class ExportService
      */
     public function pageToPdf(Page $page)
     {
+        $this->entityRepo->renderPage($page);
         $html = view('pages/pdf', [
-            'page' => $page,
-            'pageContent' => $this->entityRepo->renderPage($page)
+            'page' => $page
         ])->render();
         return $this->htmlToPdf($html);
     }
index c6c9813370369fa6f270cb7be81444fc37979685..93787a3e589ba9e6a1d82d2be5030af6ded82c13 100644 (file)
@@ -468,7 +468,7 @@ class PermissionService
         $action = end($explodedPermission);
         $this->currentAction = $action;
 
-        $nonJointPermissions = ['restrictions', 'image', 'attachment'];
+        $nonJointPermissions = ['restrictions', 'image', 'attachment', 'comment'];
 
         // Handle non entity specific jointPermissions
         if (in_array($explodedPermission[0], $nonJointPermissions)) {
index 3d1d45c3b77d47ab356285f0f45ffa9c58b90115..bb92a1d7c83cfcaba5f7eedd435865f44ac2a1a3 100644 (file)
@@ -92,7 +92,7 @@ class SearchService
         return [
             'total' => $total,
             'count' => count($results),
-            'results' => $results->sortByDesc('score')
+            'results' => $results->sortByDesc('score')->values()
         ];
     }
 
index 3285745ce4d8c5a3b1a9acc4adfa58ac77b50e29..770a9e39ffef3387ba10efc975b8749e662e5a5c 100644 (file)
@@ -62,7 +62,7 @@ class ViewService
             $query->whereIn('viewable_type', $filterModel);
         } else if ($filterModel) {
             $query->where('viewable_type', '=', get_class($filterModel));
-        };
+        }
 
         return $query->with('viewable')->skip($skipCount)->take($count)->get()->pluck('viewable');
     }
old mode 100644 (file)
new mode 100755 (executable)
index a390eaf..25e8910
@@ -58,7 +58,7 @@ return [
     */
 
     'locale' => env('APP_LANG', 'en'),
-    'locales' => ['en', 'de', 'es', 'fr', 'nl', 'pt_BR', 'sk', 'ja', 'pl'],
+    'locales' => ['en', 'de', 'es', 'fr', 'nl', 'pt_BR', 'sk', 'ja', 'pl', 'it'],
 
     /*
     |--------------------------------------------------------------------------
index ebf78d1fa4665251b4b0f570bc287f3f84e31093..c68f5c1e15ac8e4ce5ef86dc6e17be38a8a33928 100644 (file)
@@ -70,4 +70,14 @@ $factory->define(BookStack\Image::class, function ($faker) {
         'type' => 'gallery',
         'uploaded_to' => 0
     ];
+});
+
+$factory->define(BookStack\Comment::class, function($faker) {
+    $text = $faker->paragraph(1);
+    $html = '<p>' . $text. '</p>';
+    return [
+        'html' => $html,
+        'text' => $text,
+        'parent_id' => null
+    ];
 });
\ No newline at end of file
diff --git a/database/migrations/2017_08_01_130541_create_comments_table.php b/database/migrations/2017_08_01_130541_create_comments_table.php
new file mode 100644 (file)
index 0000000..1d69d1f
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class CreateCommentsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('comments', function (Blueprint $table) {
+            $table->increments('id')->unsigned();
+            $table->integer('entity_id')->unsigned();
+            $table->string('entity_type');
+            $table->longText('text')->nullable();
+            $table->longText('html')->nullable();
+            $table->integer('parent_id')->unsigned()->nullable();
+            $table->integer('local_id')->unsigned()->nullable();
+            $table->integer('created_by')->unsigned();
+            $table->integer('updated_by')->unsigned()->nullable();
+            $table->timestamps();
+
+            $table->index(['entity_id', 'entity_type']);
+            $table->index(['local_id']);
+
+            // Assign new comment permissions to admin role
+            $adminRoleId = DB::table('roles')->where('system_name', '=', 'admin')->first()->id;
+            // Create & attach new entity permissions
+            $ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
+            $entity = 'Comment';
+            foreach ($ops as $op) {
+                $permissionId = DB::table('role_permissions')->insertGetId([
+                    'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
+                    'display_name' => $op . ' ' . $entity . 's',
+                    'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
+                    'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
+                ]);
+                DB::table('permission_role')->insert([
+                    'role_id' => $adminRoleId,
+                    'permission_id' => $permissionId
+                ]);
+            }
+
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('comments');
+        // Delete comment role permissions
+        $ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
+        $entity = 'Comment';
+        foreach ($ops as $op) {
+            $permName = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
+            DB::table('role_permissions')->where('name', '=', $permName)->delete();
+        }
+    }
+}
index 3d92efab1674626df417be98d90de67c379e86d9..d18eb30db54d6b1166f9b96d3e44a8c5944064fc 100644 (file)
@@ -15,12 +15,11 @@ class DummyContentSeeder extends Seeder
         $role = \BookStack\Role::getRole('editor');
         $user->attachRole($role);
 
-
         factory(\BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id])
             ->each(function($book) use ($user) {
                 $chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id])
                     ->each(function($chapter) use ($user, $book){
-                       $pages = factory(\BookStack\Page::class, 5)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id]);
+                        $pages = factory(\BookStack\Page::class, 5)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id]);
                         $chapter->pages()->saveMany($pages);
                     });
                 $pages = factory(\BookStack\Page::class, 3)->make(['created_by' => $user->id, 'updated_by' => $user->id]);
@@ -33,7 +32,6 @@ class DummyContentSeeder extends Seeder
         $chapters = factory(\BookStack\Chapter::class, 50)->make(['created_by' => $user->id, 'updated_by' => $user->id]);
         $largeBook->pages()->saveMany($pages);
         $largeBook->chapters()->saveMany($chapters);
-
         app(\BookStack\Services\PermissionService::class)->buildJointPermissions();
         app(\BookStack\Services\SearchService::class)->indexAllEntities();
     }
index 08c8886bdd1e70b045ca531ea2354bcdee82cac9..c9f3f79561ca57ff7b06d987fcf2c9b234271a8b 100644 (file)
@@ -1,16 +1,22 @@
+'use strict';
+
 const argv = require('yargs').argv;
 const gulp = require('gulp'),
     plumber = require('gulp-plumber');
+
 const autoprefixer = require('gulp-autoprefixer');
-const uglify = require('gulp-uglify');
 const minifycss = require('gulp-clean-css');
 const sass = require('gulp-sass');
+const sourcemaps = require('gulp-sourcemaps');
+
 const browserify = require("browserify");
 const source = require('vinyl-source-stream');
 const buffer = require('vinyl-buffer');
 const babelify = require("babelify");
 const watchify = require("watchify");
 const envify = require("envify");
+const uglify = require('gulp-uglify');
+
 const gutil = require("gulp-util");
 const liveReload = require('gulp-livereload');
 
@@ -19,6 +25,7 @@ let isProduction = argv.production || process.env.NODE_ENV === 'production';
 
 gulp.task('styles', () => {
     let chain = gulp.src(['resources/assets/sass/**/*.scss'])
+        .pipe(sourcemaps.init())
         .pipe(plumber({
             errorHandler: function (error) {
                 console.log(error.message);
@@ -27,6 +34,7 @@ gulp.task('styles', () => {
         .pipe(sass())
         .pipe(autoprefixer('last 2 versions'));
     if (isProduction) chain = chain.pipe(minifycss());
+    chain = chain.pipe(sourcemaps.write());
     return chain.pipe(gulp.dest('public/css/')).pipe(liveReload());
 });
 
index 429572882c46fdb1f8fd81adfe680c0090abf9a4..ed8338abbcae0e595a1b014593378cdc99d010d4 100644 (file)
     "angular-sanitize": "^1.5.5",
     "angular-ui-sortable": "^0.17.0",
     "axios": "^0.16.1",
+    "babel-polyfill": "^6.23.0",
     "babel-preset-es2015": "^6.24.1",
-    "clipboard": "^1.5.16",
+    "clipboard": "^1.7.1",
     "codemirror": "^5.26.0",
     "dropzone": "^4.0.1",
+    "gulp-sourcemaps": "^2.6.1",
     "gulp-util": "^3.0.8",
     "markdown-it": "^8.3.1",
     "markdown-it-task-lists": "^2.0.0",
     "moment": "^2.12.0",
-    "vue": "^2.2.6"
+    "vue": "^2.2.6",
+    "vuedraggable": "^2.14.1"
   },
   "browser": {
     "vue": "vue/dist/vue.common.js"
diff --git a/public/fonts/roboto-mono-v4-latin-regular.woff b/public/fonts/roboto-mono-v4-latin-regular.woff
deleted file mode 100644 (file)
index 8cb9e6f..0000000
Binary files a/public/fonts/roboto-mono-v4-latin-regular.woff and /dev/null differ
diff --git a/public/fonts/roboto-mono-v4-latin-regular.woff2 b/public/fonts/roboto-mono-v4-latin-regular.woff2
deleted file mode 100644 (file)
index 1f65981..0000000
Binary files a/public/fonts/roboto-mono-v4-latin-regular.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-100.woff b/public/fonts/roboto-v15-cyrillic_latin-100.woff
deleted file mode 100644 (file)
index 4eb2be6..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-100.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-100.woff2 b/public/fonts/roboto-v15-cyrillic_latin-100.woff2
deleted file mode 100644 (file)
index 007b90e..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-100.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-100italic.woff b/public/fonts/roboto-v15-cyrillic_latin-100italic.woff
deleted file mode 100644 (file)
index fa7e51b..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-100italic.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-100italic.woff2 b/public/fonts/roboto-v15-cyrillic_latin-100italic.woff2
deleted file mode 100644 (file)
index f27a169..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-100italic.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-300.woff b/public/fonts/roboto-v15-cyrillic_latin-300.woff
deleted file mode 100644 (file)
index ace0529..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-300.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-300.woff2 b/public/fonts/roboto-v15-cyrillic_latin-300.woff2
deleted file mode 100644 (file)
index 0c093b9..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-300.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-300italic.woff b/public/fonts/roboto-v15-cyrillic_latin-300italic.woff
deleted file mode 100644 (file)
index 7984971..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-300italic.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-300italic.woff2 b/public/fonts/roboto-v15-cyrillic_latin-300italic.woff2
deleted file mode 100644 (file)
index 46ed6c7..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-300italic.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-500.woff b/public/fonts/roboto-v15-cyrillic_latin-500.woff
deleted file mode 100644 (file)
index 8ae98f2..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-500.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-500.woff2 b/public/fonts/roboto-v15-cyrillic_latin-500.woff2
deleted file mode 100644 (file)
index fba6784..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-500.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-500italic.woff b/public/fonts/roboto-v15-cyrillic_latin-500italic.woff
deleted file mode 100644 (file)
index 560968d..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-500italic.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-500italic.woff2 b/public/fonts/roboto-v15-cyrillic_latin-500italic.woff2
deleted file mode 100644 (file)
index cc41bf8..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-500italic.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-700.woff b/public/fonts/roboto-v15-cyrillic_latin-700.woff
deleted file mode 100644 (file)
index 7d19e33..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-700.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-700.woff2 b/public/fonts/roboto-v15-cyrillic_latin-700.woff2
deleted file mode 100644 (file)
index e2274a4..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-700.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-700italic.woff b/public/fonts/roboto-v15-cyrillic_latin-700italic.woff
deleted file mode 100644 (file)
index 1604c87..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-700italic.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-700italic.woff2 b/public/fonts/roboto-v15-cyrillic_latin-700italic.woff2
deleted file mode 100644 (file)
index f950ca2..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-700italic.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-italic.woff b/public/fonts/roboto-v15-cyrillic_latin-italic.woff
deleted file mode 100644 (file)
index d76d13d..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-italic.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-italic.woff2 b/public/fonts/roboto-v15-cyrillic_latin-italic.woff2
deleted file mode 100644 (file)
index a80f415..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-italic.woff2 and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-regular.woff b/public/fonts/roboto-v15-cyrillic_latin-regular.woff
deleted file mode 100644 (file)
index a2ada2f..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-regular.woff and /dev/null differ
diff --git a/public/fonts/roboto-v15-cyrillic_latin-regular.woff2 b/public/fonts/roboto-v15-cyrillic_latin-regular.woff2
deleted file mode 100644 (file)
index a3b35e6..0000000
Binary files a/public/fonts/roboto-v15-cyrillic_latin-regular.woff2 and /dev/null differ
index 1803feebf53414a98b6d670e4d6746a40b444ddb..585f8895b4f26c872ac9369bd4229811e725b252 100644 (file)
Binary files a/public/logo.png and b/public/logo.png differ
index 6067fbb945b954341095106432d71202413d0ead..1d4ed682e9e4b684a20542919a5e1e00389f2f12 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -1,7 +1,7 @@
 # BookStack
 
-[![GitHub release](https://img.shields.io/github/release/BookStackApp/BookStack.svg?maxAge=2592000)](https://github.com/BookStackApp/BookStack/releases/latest)
-[![license](https://img.shields.io/github/license/BookStackApp/BookStack.svg?maxAge=2592000)](https://github.com/BookStackApp/BookStack/blob/master/LICENSE)
+[![GitHub release](https://img.shields.io/github/release/BookStackApp/BookStack.svg)](https://github.com/BookStackApp/BookStack/releases/latest)
+[![license](https://img.shields.io/github/license/BookStackApp/BookStack.svg)](https://github.com/BookStackApp/BookStack/blob/master/LICENSE)
 [![Build Status](https://travis-ci.org/BookStackApp/BookStack.svg)](https://travis-ci.org/BookStackApp/BookStack)
 
 A platform for storing and organising information and documentation. General information and documentation for BookStack can be found at https://www.bookstackapp.com/.
@@ -13,6 +13,12 @@ A platform for storing and organising information and documentation. General inf
   * *Password: `password`*
 * [BookStack Blog](https://www.bookstackapp.com/blog)
 
+## Project Definition
+
+BookStack is an opinionated wiki system that provides a pleasant and simple out of the box experience. New users to an instance should find the experience intuitive and only basic word-processing skills should be required to get involved in creating content on BookStack. The platform should provide advanced power features to those that desire it but they should not interfere with the core simple user experience.
+
+BookStack is not designed as an extensible platform to be used for purposes that differ to the statement above. 
+
 ## Development & Testing
 
 All development on BookStack is currently done on the master branch. When it's time for a release the master branch is merged into release with built & minified CSS & JS then tagged at it's version. Here are the current development requirements:
@@ -79,7 +85,7 @@ These are the great open-source projects used to help build BookStack:
 * [jQuery Sortable](https://johnny.github.io/jquery-sortable/)
 * [Material Design Iconic Font](http://zavoloklom.github.io/material-design-iconic-font/icons.html)
 * [Dropzone.js](http://www.dropzonejs.com/)
-* [ZeroClipboard](http://zeroclipboard.org/)
+* [clipboard.js](https://clipboardjs.com/)
 * [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html)
 * [markdown-it](https://github.com/markdown-it/markdown-it) and [markdown-it-task-lists](https://github.com/revin/markdown-it-task-lists)
 * [Moment.js](http://momentjs.com/)
index 014c4eb779674fc1234bd9b07aec90618e4afc10..8a5a7a022d1b617d14f1106d794eaf8f2d8bfc8a 100644 (file)
@@ -53,13 +53,20 @@ const modeMap = {
     yml: 'yaml',
 };
 
-module.exports.highlight = function() {
-    let codeBlocks = document.querySelectorAll('.page-content pre');
+/**
+ * Highlight pre elements on a page
+ */
+function highlight() {
+    let codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre');
     for (let i = 0; i < codeBlocks.length; i++) {
         highlightElem(codeBlocks[i]);
     }
-};
+}
 
+/**
+ * Add code highlighting to a single element.
+ * @param {HTMLElement} elem
+ */
 function highlightElem(elem) {
     let innerCodeElem = elem.querySelector('code[class^=language-]');
     let mode = '';
@@ -68,7 +75,7 @@ function highlightElem(elem) {
         mode = getMode(langName);
     }
     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
-    let content = elem.textContent;
+    let content = elem.textContent.trim();
 
     CodeMirror(function(elt) {
         elem.parentNode.replaceChild(elt, elem);
@@ -76,7 +83,7 @@ function highlightElem(elem) {
         value: content,
         mode:  mode,
         lineNumbers: true,
-        theme: 'base16-light',
+        theme: getTheme(),
         readOnly: true
     });
 }
@@ -91,9 +98,21 @@ function getMode(suggestion) {
     return (typeof modeMap[suggestion] !== 'undefined') ? modeMap[suggestion] : '';
 }
 
-module.exports.highlightElem = highlightElem;
+/**
+ * Ge the theme to use for CodeMirror instances.
+ * @returns {*|string}
+ */
+function getTheme() {
+    return window.codeTheme || 'base16-light';
+}
 
-module.exports.wysiwygView = function(elem) {
+/**
+ * Create a CodeMirror instance for showing inside the WYSIWYG editor.
+ *  Manages a textarea element to hold code content.
+ * @param {HTMLElement} elem
+ * @returns {{wrap: Element, editor: *}}
+ */
+function wysiwygView(elem) {
     let doc = elem.ownerDocument;
     let codeElem = elem.querySelector('code');
 
@@ -122,16 +141,22 @@ module.exports.wysiwygView = function(elem) {
         value: content,
         mode:  getMode(lang),
         lineNumbers: true,
-        theme: 'base16-light',
+        theme: getTheme(),
         readOnly: true
     });
     setTimeout(() => {
         cm.refresh();
     }, 300);
     return {wrap: newWrap, editor: cm};
-};
+}
 
-module.exports.popupEditor = function(elem, modeSuggestion) {
+/**
+ * Create a CodeMirror instance to show in the WYSIWYG pop-up editor
+ * @param {HTMLElement} elem
+ * @param {String} modeSuggestion
+ * @returns {*}
+ */
+function popupEditor(elem, modeSuggestion) {
     let content = elem.textContent;
 
     return CodeMirror(function(elt) {
@@ -141,22 +166,38 @@ module.exports.popupEditor = function(elem, modeSuggestion) {
         value: content,
         mode:  getMode(modeSuggestion),
         lineNumbers: true,
-        theme: 'base16-light',
+        theme: getTheme(),
         lineWrapping: true
     });
-};
+}
 
-module.exports.setMode = function(cmInstance, modeSuggestion) {
+/**
+ * Set the mode of a codemirror instance.
+ * @param cmInstance
+ * @param modeSuggestion
+ */
+function setMode(cmInstance, modeSuggestion) {
       cmInstance.setOption('mode', getMode(modeSuggestion));
-};
-module.exports.setContent = function(cmInstance, codeContent) {
+}
+
+/**
+ * Set the content of a cm instance.
+ * @param cmInstance
+ * @param codeContent
+ */
+function setContent(cmInstance, codeContent) {
     cmInstance.setValue(codeContent);
     setTimeout(() => {
         cmInstance.refresh();
     }, 10);
-};
+}
 
-module.exports.markdownEditor = function(elem) {
+/**
+ * Get a CodeMirror instace to use for the markdown editor.
+ * @param {HTMLElement} elem
+ * @returns {*}
+ */
+function markdownEditor(elem) {
     let content = elem.textContent;
 
     return CodeMirror(function (elt) {
@@ -166,13 +207,27 @@ module.exports.markdownEditor = function(elem) {
         value: content,
         mode: "markdown",
         lineNumbers: true,
-        theme: 'base16-light',
+        theme: getTheme(),
         lineWrapping: true
     });
-};
+}
 
-module.exports.getMetaKey = function() {
+/**
+ * Get the 'meta' key dependant on the user's system.
+ * @returns {string}
+ */
+function getMetaKey() {
     let mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault;
     return mac ? "Cmd" : "Ctrl";
-};
+}
 
+module.exports = {
+    highlight: highlight,
+    highlightElem: highlightElem,
+    wysiwygView: wysiwygView,
+    popupEditor: popupEditor,
+    setMode: setMode,
+    setContent: setContent,
+    markdownEditor: markdownEditor,
+    getMetaKey: getMetaKey,
+};
\ No newline at end of file
diff --git a/resources/assets/js/components/back-top-top.js b/resources/assets/js/components/back-top-top.js
new file mode 100644 (file)
index 0000000..5fa9b34
--- /dev/null
@@ -0,0 +1,53 @@
+
+class BackToTop {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.targetElem = document.getElementById('header');
+        this.showing = false;
+        this.breakPoint = 1200;
+        this.elem.addEventListener('click', this.scrollToTop.bind(this));
+        window.addEventListener('scroll', this.onPageScroll.bind(this));
+    }
+
+    onPageScroll() {
+        let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
+        if (!this.showing && scrollTopPos > this.breakPoint) {
+            this.elem.style.display = 'block';
+            this.showing = true;
+            setTimeout(() => {
+                this.elem.style.opacity = 0.4;
+            }, 1);
+        } else if (this.showing && scrollTopPos < this.breakPoint) {
+            this.elem.style.opacity = 0;
+            this.showing = false;
+            setTimeout(() => {
+                this.elem.style.display = 'none';
+            }, 500);
+        }
+    }
+
+    scrollToTop() {
+        let targetTop = this.targetElem.getBoundingClientRect().top;
+        let scrollElem = document.documentElement.scrollTop ? document.documentElement : document.body;
+        let duration = 300;
+        let start = Date.now();
+        let scrollStart = this.targetElem.getBoundingClientRect().top;
+
+        function setPos() {
+            let percentComplete = (1-((Date.now() - start) / duration));
+            let target = Math.abs(percentComplete * scrollStart);
+            if (percentComplete > 0) {
+                scrollElem.scrollTop = target;
+                requestAnimationFrame(setPos.bind(this));
+            } else {
+                scrollElem.scrollTop = targetTop;
+            }
+        }
+
+        requestAnimationFrame(setPos.bind(this));
+    }
+
+}
+
+module.exports = BackToTop;
\ No newline at end of file
diff --git a/resources/assets/js/components/chapter-toggle.js b/resources/assets/js/components/chapter-toggle.js
new file mode 100644 (file)
index 0000000..ad373a6
--- /dev/null
@@ -0,0 +1,67 @@
+
+class ChapterToggle {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.isOpen = elem.classList.contains('open');
+        elem.addEventListener('click', this.click.bind(this));
+    }
+
+    open() {
+        let list = this.elem.parentNode.querySelector('.inset-list');
+
+        this.elem.classList.add('open');
+        list.style.display = 'block';
+        list.style.height = '';
+        let height = list.getBoundingClientRect().height;
+        list.style.height = '0px';
+        list.style.overflow = 'hidden';
+        list.style.transition = 'height ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            list.style.overflow = '';
+            list.style.height = '';
+            list.style.transition = '';
+            list.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            list.style.height = `${height}px`;
+            list.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    close() {
+        let list = this.elem.parentNode.querySelector('.inset-list');
+
+        this.elem.classList.remove('open');
+        list.style.display =  'block';
+        list.style.height = list.getBoundingClientRect().height + 'px';
+        list.style.overflow = 'hidden';
+        list.style.transition = 'height ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            list.style.overflow = '';
+            list.style.height = '';
+            list.style.transition = '';
+            list.style.display =  'none';
+            list.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            list.style.height = `0px`;
+            list.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    click(event) {
+        event.preventDefault();
+        this.isOpen ?  this.close() : this.open();
+        this.isOpen = !this.isOpen;
+    }
+
+}
+
+module.exports = ChapterToggle;
\ No newline at end of file
diff --git a/resources/assets/js/components/dropdown.js b/resources/assets/js/components/dropdown.js
new file mode 100644 (file)
index 0000000..0401efc
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * Dropdown
+ * Provides some simple logic to create simple dropdown menus.
+ */
+class DropDown {
+
+    constructor(elem) {
+        this.container = elem;
+        this.menu = elem.querySelector('ul');
+        this.toggle = elem.querySelector('[dropdown-toggle]');
+        this.setupListeners();
+    }
+
+    show() {
+        this.menu.style.display = 'block';
+        this.menu.classList.add('anim', 'menuIn');
+        this.container.addEventListener('mouseleave', this.hide.bind(this));
+
+        // Focus on first input if existing
+        let input = this.menu.querySelector('input');
+        if (input !== null) input.focus();
+    }
+
+    hide() {
+        this.menu.style.display = 'none';
+        this.menu.classList.remove('anim', 'menuIn');
+    }
+
+    setupListeners() {
+        // Hide menu on option click
+        this.container.addEventListener('click', event => {
+             let possibleChildren = Array.from(this.menu.querySelectorAll('a'));
+             if (possibleChildren.indexOf(event.target) !== -1) this.hide();
+        });
+        // Show dropdown on toggle click
+        this.toggle.addEventListener('click', this.show.bind(this));
+        // Hide menu on enter press
+        this.container.addEventListener('keypress', event => {
+                if (event.keyCode !== 13) return true;
+                event.preventDefault();
+                this.hide();
+                return false;
+        });
+    }
+
+}
+
+module.exports = DropDown;
\ No newline at end of file
diff --git a/resources/assets/js/components/entity-selector-popup.js b/resources/assets/js/components/entity-selector-popup.js
new file mode 100644 (file)
index 0000000..64c0c62
--- /dev/null
@@ -0,0 +1,47 @@
+
+class EntitySelectorPopup {
+
+    constructor(elem) {
+        this.elem = elem;
+        window.EntitySelectorPopup = this;
+
+        this.callback = null;
+        this.selection = null;
+
+        this.selectButton = elem.querySelector('.entity-link-selector-confirm');
+        this.selectButton.addEventListener('click', this.onSelectButtonClick.bind(this));
+
+        window.$events.listen('entity-select-change', this.onSelectionChange.bind(this));
+        window.$events.listen('entity-select-confirm', this.onSelectionConfirm.bind(this));
+    }
+
+    show(callback) {
+        this.callback = callback;
+        this.elem.components.overlay.show();
+    }
+
+    hide() {
+        this.elem.components.overlay.hide();
+    }
+
+    onSelectButtonClick() {
+        this.hide();
+        if (this.selection !== null && this.callback) this.callback(this.selection);
+    }
+
+    onSelectionConfirm(entity) {
+        this.hide();
+        if (this.callback && entity) this.callback(entity);
+    }
+
+    onSelectionChange(entity) {
+        this.selection = entity;
+        if (entity === null) {
+            this.selectButton.setAttribute('disabled', 'true');
+        } else {
+            this.selectButton.removeAttribute('disabled');
+        }
+    }
+}
+
+module.exports = EntitySelectorPopup;
\ No newline at end of file
diff --git a/resources/assets/js/components/entity-selector.js b/resources/assets/js/components/entity-selector.js
new file mode 100644 (file)
index 0000000..5335837
--- /dev/null
@@ -0,0 +1,118 @@
+
+class EntitySelector {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.search = '';
+        this.lastClick = 0;
+
+        let entityTypes = elem.hasAttribute('entity-types') ? elem.getAttribute('entity-types') : 'page,book,chapter';
+        this.searchUrl = window.baseUrl(`/ajax/search/entities?types=${encodeURIComponent(entityTypes)}`);
+
+        this.input = elem.querySelector('[entity-selector-input]');
+        this.searchInput = elem.querySelector('[entity-selector-search]');
+        this.loading = elem.querySelector('[entity-selector-loading]');
+        this.resultsContainer = elem.querySelector('[entity-selector-results]');
+
+        this.elem.addEventListener('click', this.onClick.bind(this));
+
+        let lastSearch = 0;
+        this.searchInput.addEventListener('input', event => {
+            lastSearch = Date.now();
+            this.showLoading();
+            setTimeout(() => {
+                if (Date.now() - lastSearch < 199) return;
+                this.searchEntities(this.searchInput.value);
+            }, 200);
+        });
+        this.searchInput.addEventListener('keydown', event => {
+            if (event.keyCode === 13) event.preventDefault();
+        });
+
+        this.showLoading();
+        this.initialLoad();
+    }
+
+    showLoading() {
+        this.loading.style.display = 'block';
+        this.resultsContainer.style.display = 'none';
+    }
+
+    hideLoading() {
+        this.loading.style.display = 'none';
+        this.resultsContainer.style.display = 'block';
+    }
+
+    initialLoad() {
+        window.$http.get(this.searchUrl).then(resp => {
+            this.resultsContainer.innerHTML = resp.data;
+            this.hideLoading();
+        })
+    }
+
+    searchEntities(searchTerm) {
+        this.input.value = '';
+        let url = this.searchUrl + `&term=${encodeURIComponent(searchTerm)}`;
+        window.$http.get(url).then(resp => {
+            this.resultsContainer.innerHTML = resp.data;
+            this.hideLoading();
+        });
+    }
+
+    isDoubleClick() {
+        let now = Date.now();
+        let answer = now - this.lastClick < 300;
+        this.lastClick = now;
+        return answer;
+    }
+
+    onClick(event) {
+        let t = event.target;
+        console.log('click', t);
+
+        if (t.matches('.entity-list-item  *')) {
+            event.preventDefault();
+            event.stopPropagation();
+            let item = t.closest('[data-entity-type]');
+            this.selectItem(item);
+        } else if (t.matches('[data-entity-type]')) {
+            this.selectItem(t)
+        }
+
+    }
+
+    selectItem(item) {
+        let isDblClick = this.isDoubleClick();
+        let type = item.getAttribute('data-entity-type');
+        let id = item.getAttribute('data-entity-id');
+        let isSelected = !item.classList.contains('selected') || isDblClick;
+
+        this.unselectAll();
+        this.input.value = isSelected ? `${type}:${id}` : '';
+
+        if (!isSelected) window.$events.emit('entity-select-change', null);
+        if (isSelected) {
+            item.classList.add('selected');
+            item.classList.add('primary-background');
+        }
+        if (!isDblClick && !isSelected) return;
+
+        let link = item.querySelector('.entity-list-item-link').getAttribute('href');
+        let name = item.querySelector('.entity-list-item-name').textContent;
+        let data = {id: Number(id), name: name, link: link};
+
+        if (isDblClick) window.$events.emit('entity-select-confirm', data);
+        if (isSelected) window.$events.emit('entity-select-change', data);
+    }
+
+    unselectAll() {
+        let selected = this.elem.querySelectorAll('.selected');
+        for (let i = 0, len = selected.length; i < len; i++) {
+            selected[i].classList.remove('selected');
+            selected[i].classList.remove('primary-background');
+        }
+    }
+
+}
+
+module.exports = EntitySelector;
\ No newline at end of file
diff --git a/resources/assets/js/components/expand-toggle.js b/resources/assets/js/components/expand-toggle.js
new file mode 100644 (file)
index 0000000..61d9f54
--- /dev/null
@@ -0,0 +1,65 @@
+
+class ExpandToggle {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.isOpen = false;
+        this.selector = elem.getAttribute('expand-toggle');
+        elem.addEventListener('click', this.click.bind(this));
+    }
+
+    open(elemToToggle) {
+        elemToToggle.style.display = 'block';
+        elemToToggle.style.height = '';
+        let height = elemToToggle.getBoundingClientRect().height;
+        elemToToggle.style.height = '0px';
+        elemToToggle.style.overflow = 'hidden';
+        elemToToggle.style.transition = 'height ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            elemToToggle.style.overflow = '';
+            elemToToggle.style.height = '';
+            elemToToggle.style.transition = '';
+            elemToToggle.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            elemToToggle.style.height = `${height}px`;
+            elemToToggle.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    close(elemToToggle) {
+        elemToToggle.style.display =  'block';
+        elemToToggle.style.height = elemToToggle.getBoundingClientRect().height + 'px';
+        elemToToggle.style.overflow = 'hidden';
+        elemToToggle.style.transition = 'all ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            elemToToggle.style.overflow = '';
+            elemToToggle.style.height = '';
+            elemToToggle.style.transition = '';
+            elemToToggle.style.display =  'none';
+            elemToToggle.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            elemToToggle.style.height = `0px`;
+            elemToToggle.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    click(event) {
+        event.preventDefault();
+        let matchingElems = document.querySelectorAll(this.selector);
+        for (let i = 0, len = matchingElems.length; i < len; i++) {
+            this.isOpen ?  this.close(matchingElems[i]) : this.open(matchingElems[i]);
+        }
+        this.isOpen = !this.isOpen;
+    }
+
+}
+
+module.exports = ExpandToggle;
\ No newline at end of file
diff --git a/resources/assets/js/components/index.js b/resources/assets/js/components/index.js
new file mode 100644 (file)
index 0000000..b0e8b84
--- /dev/null
@@ -0,0 +1,51 @@
+
+let componentMapping = {
+    'dropdown': require('./dropdown'),
+    'overlay': require('./overlay'),
+    'back-to-top': require('./back-top-top'),
+    'notification': require('./notification'),
+    'chapter-toggle': require('./chapter-toggle'),
+    'expand-toggle': require('./expand-toggle'),
+    'entity-selector-popup': require('./entity-selector-popup'),
+    'entity-selector': require('./entity-selector'),
+    'sidebar': require('./sidebar'),
+    'page-picker': require('./page-picker'),
+    'page-comments': require('./page-comments'),
+};
+
+window.components = {};
+
+let componentNames = Object.keys(componentMapping);
+initAll();
+
+/**
+ * Initialize components of the given name within the given element.
+ * @param {String} componentName
+ * @param {HTMLElement|Document} parentElement
+ */
+function initComponent(componentName, parentElement) {
+    let elems = parentElement.querySelectorAll(`[${componentName}]`);
+    if (elems.length === 0) return;
+
+    let component = componentMapping[componentName];
+    if (typeof window.components[componentName] === "undefined") window.components[componentName] = [];
+    for (let j = 0, jLen = elems.length; j < jLen; j++) {
+        let instance = new component(elems[j]);
+        if (typeof elems[j].components === 'undefined') elems[j].components = {};
+        elems[j].components[componentName] = instance;
+        window.components[componentName].push(instance);
+    }
+}
+
+/**
+ * Initialize all components found within the given element.
+ * @param parentElement
+ */
+function initAll(parentElement) {
+    if (typeof parentElement === 'undefined') parentElement = document;
+    for (let i = 0, len = componentNames.length; i < len; i++) {
+        initComponent(componentNames[i], parentElement);
+    }
+}
+
+window.components.init = initAll;
\ No newline at end of file
diff --git a/resources/assets/js/components/notification.js b/resources/assets/js/components/notification.js
new file mode 100644 (file)
index 0000000..daef5bd
--- /dev/null
@@ -0,0 +1,41 @@
+
+class Notification {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.type = elem.getAttribute('notification');
+        this.textElem = elem.querySelector('span');
+        this.autohide = this.elem.hasAttribute('data-autohide');
+        window.$events.listen(this.type, text => {
+            this.show(text);
+        });
+        elem.addEventListener('click', this.hide.bind(this));
+        if (elem.hasAttribute('data-show')) this.show(this.textElem.textContent);
+
+        this.hideCleanup = this.hideCleanup.bind(this);
+    }
+
+    show(textToShow = '') {
+        this.elem.removeEventListener('transitionend', this.hideCleanup);
+        this.textElem.textContent = textToShow;
+        this.elem.style.display = 'block';
+        setTimeout(() => {
+            this.elem.classList.add('showing');
+        }, 1);
+
+        if (this.autohide) setTimeout(this.hide.bind(this), 2000);
+    }
+
+    hide() {
+        this.elem.classList.remove('showing');
+        this.elem.addEventListener('transitionend', this.hideCleanup);
+    }
+
+    hideCleanup() {
+        this.elem.style.display = 'none';
+        this.elem.removeEventListener('transitionend', this.hideCleanup);
+    }
+
+}
+
+module.exports = Notification;
\ No newline at end of file
diff --git a/resources/assets/js/components/overlay.js b/resources/assets/js/components/overlay.js
new file mode 100644 (file)
index 0000000..6e7a598
--- /dev/null
@@ -0,0 +1,39 @@
+
+class Overlay {
+
+    constructor(elem) {
+        this.container = elem;
+        elem.addEventListener('click', event => {
+             if (event.target === elem) return this.hide();
+        });
+        let closeButtons = elem.querySelectorAll('.overlay-close');
+        for (let i=0; i < closeButtons.length; i++) {
+            closeButtons[i].addEventListener('click', this.hide.bind(this));
+        }
+    }
+
+    toggle(show = true) {
+        let start = Date.now();
+        let duration = 240;
+
+        function setOpacity() {
+            let elapsedTime = (Date.now() - start);
+            let targetOpacity = show ? (elapsedTime / duration) : 1-(elapsedTime / duration);
+            this.container.style.opacity = targetOpacity;
+            if (elapsedTime > duration) {
+                this.container.style.display = show ? 'flex' : 'none';
+                this.container.style.opacity = '';
+            } else {
+                requestAnimationFrame(setOpacity.bind(this));
+            }
+        }
+
+        requestAnimationFrame(setOpacity.bind(this));
+    }
+
+    hide() { this.toggle(false); }
+    show() { this.toggle(true); }
+
+}
+
+module.exports = Overlay;
\ No newline at end of file
diff --git a/resources/assets/js/components/page-comments.js b/resources/assets/js/components/page-comments.js
new file mode 100644 (file)
index 0000000..ae2a30f
--- /dev/null
@@ -0,0 +1,175 @@
+const MarkdownIt = require("markdown-it");
+const md = new MarkdownIt({ html: false });
+
+class PageComments {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.pageId = Number(elem.getAttribute('page-id'));
+        this.editingComment = null;
+        this.parentId = null;
+
+        this.container = elem.querySelector('[comment-container]');
+        this.formContainer = elem.querySelector('[comment-form-container]');
+
+        if (this.formContainer) {
+            this.form = this.formContainer.querySelector('form');
+            this.formInput = this.form.querySelector('textarea');
+            this.form.addEventListener('submit', this.saveComment.bind(this));
+        }
+
+        this.elem.addEventListener('click', this.handleAction.bind(this));
+        this.elem.addEventListener('submit', this.updateComment.bind(this));
+    }
+
+    handleAction(event) {
+        let actionElem = event.target.closest('[action]');
+        if (event.target.matches('a[href^="#"]')) {
+            let id = event.target.href.split('#')[1];
+            window.scrollAndHighlight(document.querySelector('#' + id));
+        }
+        if (actionElem === null) return;
+        event.preventDefault();
+
+        let action = actionElem.getAttribute('action');
+        if (action === 'edit') this.editComment(actionElem.closest('[comment]'));
+        if (action === 'closeUpdateForm') this.closeUpdateForm();
+        if (action === 'delete') this.deleteComment(actionElem.closest('[comment]'));
+        if (action === 'addComment') this.showForm();
+        if (action === 'hideForm') this.hideForm();
+        if (action === 'reply') this.setReply(actionElem.closest('[comment]'));
+        if (action === 'remove-reply-to') this.removeReplyTo();
+    }
+
+    closeUpdateForm() {
+        if (!this.editingComment) return;
+        this.editingComment.querySelector('[comment-content]').style.display = 'block';
+        this.editingComment.querySelector('[comment-edit-container]').style.display = 'none';
+    }
+
+    editComment(commentElem) {
+        this.hideForm();
+        if (this.editingComment) this.closeUpdateForm();
+        commentElem.querySelector('[comment-content]').style.display = 'none';
+        commentElem.querySelector('[comment-edit-container]').style.display = 'block';
+        let textArea = commentElem.querySelector('[comment-edit-container] textarea');
+        let lineCount = textArea.value.split('\n').length;
+        textArea.style.height = (lineCount * 20) + 'px';
+        this.editingComment = commentElem;
+    }
+
+    updateComment(event) {
+        let form = event.target;
+        event.preventDefault();
+        let text = form.querySelector('textarea').value;
+        let reqData = {
+            text: text,
+            html: md.render(text),
+            parent_id: this.parentId || null,
+        };
+        this.showLoading(form);
+        let commentId = this.editingComment.getAttribute('comment');
+        window.$http.put(window.baseUrl(`/ajax/comment/${commentId}`), reqData).then(resp => {
+            let newComment = document.createElement('div');
+            newComment.innerHTML = resp.data;
+            this.editingComment.innerHTML = newComment.children[0].innerHTML;
+            window.$events.emit('success', window.trans('entities.comment_updated_success'));
+            window.components.init(this.editingComment);
+            this.closeUpdateForm();
+            this.editingComment = null;
+            this.hideLoading(form);
+        });
+    }
+
+    deleteComment(commentElem) {
+        let id = commentElem.getAttribute('comment');
+        this.showLoading(commentElem.querySelector('[comment-content]'));
+        window.$http.delete(window.baseUrl(`/ajax/comment/${id}`)).then(resp => {
+            commentElem.parentNode.removeChild(commentElem);
+            window.$events.emit('success', window.trans('entities.comment_deleted_success'));
+            this.updateCount();
+        });
+    }
+
+    saveComment(event) {
+        event.preventDefault();
+        event.stopPropagation();
+        let text = this.formInput.value;
+        let reqData = {
+            text: text,
+            html: md.render(text),
+            parent_id: this.parentId || null,
+        };
+        this.showLoading(this.form);
+        window.$http.post(window.baseUrl(`/ajax/page/${this.pageId}/comment`), reqData).then(resp => {
+            let newComment = document.createElement('div');
+            newComment.innerHTML = resp.data;
+            let newElem = newComment.children[0];
+            this.container.appendChild(newElem);
+            window.components.init(newElem);
+            window.$events.emit('success', window.trans('entities.comment_created_success'));
+            this.resetForm();
+            this.updateCount();
+        });
+    }
+
+    updateCount() {
+        let count = this.container.children.length;
+        this.elem.querySelector('[comments-title]').textContent = window.trans_choice('entities.comment_count', count, {count});
+    }
+
+    resetForm() {
+        this.formInput.value = '';
+        this.formContainer.appendChild(this.form);
+        this.hideForm();
+        this.removeReplyTo();
+        this.hideLoading(this.form);
+    }
+
+    showForm() {
+        this.formContainer.style.display = 'block';
+        this.formContainer.parentNode.style.display = 'block';
+        this.elem.querySelector('[comment-add-button]').style.display = 'none';
+        this.formInput.focus();
+        window.scrollToElement(this.formInput);
+    }
+
+    hideForm() {
+        this.formContainer.style.display = 'none';
+        this.formContainer.parentNode.style.display = 'none';
+        this.elem.querySelector('[comment-add-button]').style.display = 'block';
+    }
+
+    setReply(commentElem) {
+        this.showForm();
+        this.parentId = Number(commentElem.getAttribute('local-id'));
+        this.elem.querySelector('[comment-form-reply-to]').style.display = 'block';
+        let replyLink = this.elem.querySelector('[comment-form-reply-to] a');
+        replyLink.textContent = `#${this.parentId}`;
+        replyLink.href = `#comment${this.parentId}`;
+    }
+
+    removeReplyTo() {
+        this.parentId = null;
+        this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
+    }
+
+    showLoading(formElem) {
+        let groups = formElem.querySelectorAll('.form-group');
+        for (let i = 0, len = groups.length; i < len; i++) {
+            groups[i].style.display = 'none';
+        }
+        formElem.querySelector('.form-group.loading').style.display = 'block';
+    }
+
+    hideLoading(formElem) {
+        let groups = formElem.querySelectorAll('.form-group');
+        for (let i = 0, len = groups.length; i < len; i++) {
+            groups[i].style.display = 'block';
+        }
+        formElem.querySelector('.form-group.loading').style.display = 'none';
+    }
+
+}
+
+module.exports = PageComments;
\ No newline at end of file
diff --git a/resources/assets/js/components/page-picker.js b/resources/assets/js/components/page-picker.js
new file mode 100644 (file)
index 0000000..e697d5f
--- /dev/null
@@ -0,0 +1,60 @@
+
+class PagePicker {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.input = elem.querySelector('input');
+        this.resetButton = elem.querySelector('[page-picker-reset]');
+        this.selectButton = elem.querySelector('[page-picker-select]');
+        this.display = elem.querySelector('[page-picker-display]');
+        this.defaultDisplay = elem.querySelector('[page-picker-default]');
+        this.buttonSep = elem.querySelector('span.sep');
+
+        this.value = this.input.value;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        // Select click
+        this.selectButton.addEventListener('click', event => {
+            window.EntitySelectorPopup.show(entity => {
+                this.setValue(entity.id, entity.name);
+            });
+        });
+
+        this.resetButton.addEventListener('click', event => {
+            this.setValue('', '');
+        });
+    }
+
+    setValue(value, name) {
+        this.value = value;
+        this.input.value = value;
+        this.controlView(name);
+    }
+
+    controlView(name) {
+        let hasValue = this.value && this.value !== 0;
+        toggleElem(this.resetButton, hasValue);
+        toggleElem(this.buttonSep, hasValue);
+        toggleElem(this.defaultDisplay, !hasValue);
+        toggleElem(this.display, hasValue);
+        if (hasValue) {
+            let id = this.getAssetIdFromVal();
+            this.display.textContent = `#${id}, ${name}`;
+            this.display.href = window.baseUrl(`/link/${id}`);
+        }
+    }
+
+    getAssetIdFromVal() {
+        return Number(this.value);
+    }
+
+}
+
+function toggleElem(elem, show) {
+    let display = (elem.tagName === 'BUTTON' || elem.tagName === 'SPAN') ? 'inline-block' : 'block';
+    elem.style.display = show ? display : 'none';
+}
+
+module.exports = PagePicker;
\ No newline at end of file
diff --git a/resources/assets/js/components/sidebar.js b/resources/assets/js/components/sidebar.js
new file mode 100644 (file)
index 0000000..fc80161
--- /dev/null
@@ -0,0 +1,16 @@
+
+class Sidebar {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.toggleElem = elem.querySelector('.sidebar-toggle');
+        this.toggleElem.addEventListener('click', this.toggle.bind(this));
+    }
+
+    toggle(show = true) {
+        this.elem.classList.toggle('open');
+    }
+
+}
+
+module.exports = Sidebar;
\ No newline at end of file
index 9337ea8899fd092b9f297c90ad9d1315875a4b9a..32ff76fa167bc9dad0a470a2429ed147ad4a3718 100644 (file)
@@ -8,256 +8,6 @@ moment.locale('en-gb');
 
 module.exports = function (ngApp, events) {
 
-    ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService',
-        function ($scope, $attrs, $http, $timeout, imageManagerService) {
-
-            $scope.images = [];
-            $scope.imageType = $attrs.imageType;
-            $scope.selectedImage = false;
-            $scope.dependantPages = false;
-            $scope.showing = false;
-            $scope.hasMore = false;
-            $scope.imageUpdateSuccess = false;
-            $scope.imageDeleteSuccess = false;
-            $scope.uploadedTo = $attrs.uploadedTo;
-            $scope.view = 'all';
-
-            $scope.searching = false;
-            $scope.searchTerm = '';
-
-            let page = 0;
-            let previousClickTime = 0;
-            let previousClickImage = 0;
-            let dataLoaded = false;
-            let callback = false;
-
-            let preSearchImages = [];
-            let preSearchHasMore = false;
-
-            /**
-             * Used by dropzone to get the endpoint to upload to.
-             * @returns {string}
-             */
-            $scope.getUploadUrl = function () {
-                return window.baseUrl('/images/' + $scope.imageType + '/upload');
-            };
-
-            /**
-             * Cancel the current search operation.
-             */
-            function cancelSearch() {
-                $scope.searching = false;
-                $scope.searchTerm = '';
-                $scope.images = preSearchImages;
-                $scope.hasMore = preSearchHasMore;
-            }
-            $scope.cancelSearch = cancelSearch;
-
-
-            /**
-             * Runs on image upload, Adds an image to local list of images
-             * and shows a success message to the user.
-             * @param file
-             * @param data
-             */
-            $scope.uploadSuccess = function (file, data) {
-                $scope.$apply(() => {
-                    $scope.images.unshift(data);
-                });
-                events.emit('success', trans('components.image_upload_success'));
-            };
-
-            /**
-             * Runs the callback and hides the image manager.
-             * @param returnData
-             */
-            function callbackAndHide(returnData) {
-                if (callback) callback(returnData);
-                $scope.hide();
-            }
-
-            /**
-             * Image select action. Checks if a double-click was fired.
-             * @param image
-             */
-            $scope.imageSelect = function (image) {
-                let dblClickTime = 300;
-                let currentTime = Date.now();
-                let timeDiff = currentTime - previousClickTime;
-
-                if (timeDiff < dblClickTime && image.id === previousClickImage) {
-                    // If double click
-                    callbackAndHide(image);
-                } else {
-                    // If single
-                    $scope.selectedImage = image;
-                    $scope.dependantPages = false;
-                }
-                previousClickTime = currentTime;
-                previousClickImage = image.id;
-            };
-
-            /**
-             * Action that runs when the 'Select image' button is clicked.
-             * Runs the callback and hides the image manager.
-             */
-            $scope.selectButtonClick = function () {
-                callbackAndHide($scope.selectedImage);
-            };
-
-            /**
-             * Show the image manager.
-             * Takes a callback to execute later on.
-             * @param doneCallback
-             */
-            function show(doneCallback) {
-                callback = doneCallback;
-                $scope.showing = true;
-                $('#image-manager').find('.overlay').css('display', 'flex').hide().fadeIn(240);
-                // Get initial images if they have not yet been loaded in.
-                if (!dataLoaded) {
-                    fetchData();
-                    dataLoaded = true;
-                }
-            }
-
-            // Connects up the image manger so it can be used externally
-            // such as from TinyMCE.
-            imageManagerService.show = show;
-            imageManagerService.showExternal = function (doneCallback) {
-                $scope.$apply(() => {
-                    show(doneCallback);
-                });
-            };
-            window.ImageManager = imageManagerService;
-
-            /**
-             * Hide the image manager
-             */
-            $scope.hide = function () {
-                $scope.showing = false;
-                $('#image-manager').find('.overlay').fadeOut(240);
-            };
-
-            let baseUrl = window.baseUrl('/images/' + $scope.imageType + '/all/');
-
-            /**
-             * Fetch the list image data from the server.
-             */
-            function fetchData() {
-                let url = baseUrl + page + '?';
-                let components = {};
-                if ($scope.uploadedTo) components['page_id'] = $scope.uploadedTo;
-                if ($scope.searching) components['term'] = $scope.searchTerm;
-
-
-                url += Object.keys(components).map((key) => {
-                    return key + '=' + encodeURIComponent(components[key]);
-                }).join('&');
-
-                $http.get(url).then((response) => {
-                    $scope.images = $scope.images.concat(response.data.images);
-                    $scope.hasMore = response.data.hasMore;
-                    page++;
-                });
-            }
-            $scope.fetchData = fetchData;
-
-            /**
-             * Start a search operation
-             */
-            $scope.searchImages = function() {
-
-                if ($scope.searchTerm === '') {
-                    cancelSearch();
-                    return;
-                }
-
-                if (!$scope.searching) {
-                    preSearchImages = $scope.images;
-                    preSearchHasMore = $scope.hasMore;
-                }
-
-                $scope.searching = true;
-                $scope.images = [];
-                $scope.hasMore = false;
-                page = 0;
-                baseUrl = window.baseUrl('/images/' + $scope.imageType + '/search/');
-                fetchData();
-            };
-
-            /**
-             * Set the current image listing view.
-             * @param viewName
-             */
-            $scope.setView = function(viewName) {
-                cancelSearch();
-                $scope.images = [];
-                $scope.hasMore = false;
-                page = 0;
-                $scope.view = viewName;
-                baseUrl = window.baseUrl('/images/' + $scope.imageType  + '/' + viewName + '/');
-                fetchData();
-            };
-
-            /**
-             * Save the details of an image.
-             * @param event
-             */
-            $scope.saveImageDetails = function (event) {
-                event.preventDefault();
-                let url = window.baseUrl('/images/update/' + $scope.selectedImage.id);
-                $http.put(url, this.selectedImage).then(response => {
-                    events.emit('success', trans('components.image_update_success'));
-                }, (response) => {
-                    if (response.status === 422) {
-                        let errors = response.data;
-                        let message = '';
-                        Object.keys(errors).forEach((key) => {
-                            message += errors[key].join('\n');
-                        });
-                        events.emit('error', message);
-                    } else if (response.status === 403) {
-                        events.emit('error', response.data.error);
-                    }
-                });
-            };
-
-            /**
-             * Delete an image from system and notify of success.
-             * Checks if it should force delete when an image
-             * has dependant pages.
-             * @param event
-             */
-            $scope.deleteImage = function (event) {
-                event.preventDefault();
-                let force = $scope.dependantPages !== false;
-                let url = window.baseUrl('/images/' + $scope.selectedImage.id);
-                if (force) url += '?force=true';
-                $http.delete(url).then((response) => {
-                    $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
-                    $scope.selectedImage = false;
-                    events.emit('success', trans('components.image_delete_success'));
-                }, (response) => {
-                    // Pages failure
-                    if (response.status === 400) {
-                        $scope.dependantPages = response.data;
-                    } else if (response.status === 403) {
-                        events.emit('error', response.data.error);
-                    }
-                });
-            };
-
-            /**
-             * Simple date creator used to properly format dates.
-             * @param stringDate
-             * @returns {Date}
-             */
-            $scope.getDate = function (stringDate) {
-                return new Date(stringDate);
-            };
-
-        }]);
 
     ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce',
         function ($scope, $http, $attrs, $interval, $timeout, $sce) {
@@ -394,285 +144,4 @@ module.exports = function (ngApp, events) {
         };
 
     }]);
-
-    ngApp.controller('PageTagController', ['$scope', '$http', '$attrs',
-        function ($scope, $http, $attrs) {
-
-            const pageId = Number($attrs.pageId);
-            $scope.tags = [];
-
-            $scope.sortOptions = {
-                handle: '.handle',
-                items: '> tr',
-                containment: "parent",
-                axis: "y"
-            };
-
-            /**
-             * Push an empty tag to the end of the scope tags.
-             */
-            function addEmptyTag() {
-                $scope.tags.push({
-                    name: '',
-                    value: ''
-                });
-            }
-            $scope.addEmptyTag = addEmptyTag;
-
-            /**
-             * Get all tags for the current book and add into scope.
-             */
-            function getTags() {
-                let url = window.baseUrl(`/ajax/tags/get/page/${pageId}`);
-                $http.get(url).then((responseData) => {
-                    $scope.tags = responseData.data;
-                    addEmptyTag();
-                });
-            }
-            getTags();
-
-            /**
-             * Set the order property on all tags.
-             */
-            function setTagOrder() {
-                for (let i = 0; i < $scope.tags.length; i++) {
-                    $scope.tags[i].order = i;
-                }
-            }
-
-            /**
-             * When an tag changes check if another empty editable
-             * field needs to be added onto the end.
-             * @param tag
-             */
-            $scope.tagChange = function(tag) {
-                let cPos = $scope.tags.indexOf(tag);
-                if (cPos !== $scope.tags.length-1) return;
-
-                if (tag.name !== '' || tag.value !== '') {
-                    addEmptyTag();
-                }
-            };
-
-            /**
-             * When an tag field loses focus check the tag to see if its
-             * empty and therefore could be removed from the list.
-             * @param tag
-             */
-            $scope.tagBlur = function(tag) {
-                let isLast = $scope.tags.length - 1 === $scope.tags.indexOf(tag);
-                if (tag.name === '' && tag.value === '' && !isLast) {
-                    let cPos = $scope.tags.indexOf(tag);
-                    $scope.tags.splice(cPos, 1);
-                }
-            };
-
-            /**
-             * Remove a tag from the current list.
-             * @param tag
-             */
-            $scope.removeTag = function(tag) {
-                let cIndex = $scope.tags.indexOf(tag);
-                $scope.tags.splice(cIndex, 1);
-            };
-
-        }]);
-
-
-    ngApp.controller('PageAttachmentController', ['$scope', '$http', '$attrs',
-        function ($scope, $http, $attrs) {
-
-            const pageId = $scope.uploadedTo = $attrs.pageId;
-            let currentOrder = '';
-            $scope.files = [];
-            $scope.editFile = false;
-            $scope.file = getCleanFile();
-            $scope.errors = {
-                link: {},
-                edit: {}
-            };
-
-            function getCleanFile() {
-                return {
-                    page_id: pageId
-                };
-            }
-
-            // Angular-UI-Sort options
-            $scope.sortOptions = {
-                handle: '.handle',
-                items: '> tr',
-                containment: "parent",
-                axis: "y",
-                stop: sortUpdate,
-            };
-
-            /**
-             * Event listener for sort changes.
-             * Updates the file ordering on the server.
-             * @param event
-             * @param ui
-             */
-            function sortUpdate(event, ui) {
-                let newOrder = $scope.files.map(file => {return file.id}).join(':');
-                if (newOrder === currentOrder) return;
-
-                currentOrder = newOrder;
-                $http.put(window.baseUrl(`/attachments/sort/page/${pageId}`), {files: $scope.files}).then(resp => {
-                    events.emit('success', resp.data.message);
-                }, checkError('sort'));
-            }
-
-            /**
-             * Used by dropzone to get the endpoint to upload to.
-             * @returns {string}
-             */
-            $scope.getUploadUrl = function (file) {
-                let suffix = (typeof file !== 'undefined') ? `/${file.id}` : '';
-                return window.baseUrl(`/attachments/upload${suffix}`);
-            };
-
-            /**
-             * Get files for the current page from the server.
-             */
-            function getFiles() {
-                let url = window.baseUrl(`/attachments/get/page/${pageId}`);
-                $http.get(url).then(resp => {
-                    $scope.files = resp.data;
-                    currentOrder = resp.data.map(file => {return file.id}).join(':');
-                }, checkError('get'));
-            }
-            getFiles();
-
-            /**
-             * Runs on file upload, Adds an file to local file list
-             * and shows a success message to the user.
-             * @param file
-             * @param data
-             */
-            $scope.uploadSuccess = function (file, data) {
-                $scope.$apply(() => {
-                    $scope.files.push(data);
-                });
-                events.emit('success', trans('entities.attachments_file_uploaded'));
-            };
-
-            /**
-             * Upload and overwrite an existing file.
-             * @param file
-             * @param data
-             */
-            $scope.uploadSuccessUpdate = function (file, data) {
-                $scope.$apply(() => {
-                    let search = filesIndexOf(data);
-                    if (search !== -1) $scope.files[search] = data;
-
-                    if ($scope.editFile) {
-                        $scope.editFile = angular.copy(data);
-                        data.link = '';
-                    }
-                });
-                events.emit('success', trans('entities.attachments_file_updated'));
-            };
-
-            /**
-             * Delete a file from the server and, on success, the local listing.
-             * @param file
-             */
-            $scope.deleteFile = function(file) {
-                if (!file.deleting) {
-                    file.deleting = true;
-                    return;
-                }
-                  $http.delete(window.baseUrl(`/attachments/${file.id}`)).then(resp => {
-                      events.emit('success', resp.data.message);
-                      $scope.files.splice($scope.files.indexOf(file), 1);
-                  }, checkError('delete'));
-            };
-
-            /**
-             * Attach a link to a page.
-             * @param file
-             */
-            $scope.attachLinkSubmit = function(file) {
-                file.uploaded_to = pageId;
-                $http.post(window.baseUrl('/attachments/link'), file).then(resp => {
-                    $scope.files.push(resp.data);
-                    events.emit('success', trans('entities.attachments_link_attached'));
-                    $scope.file = getCleanFile();
-                }, checkError('link'));
-            };
-
-            /**
-             * Start the edit mode for a file.
-             * @param file
-             */
-            $scope.startEdit = function(file) {
-                $scope.editFile = angular.copy(file);
-                $scope.editFile.link = (file.external) ? file.path : '';
-            };
-
-            /**
-             * Cancel edit mode
-             */
-            $scope.cancelEdit = function() {
-                $scope.editFile = false;
-            };
-
-            /**
-             * Update the name and link of a file.
-             * @param file
-             */
-            $scope.updateFile = function(file) {
-                $http.put(window.baseUrl(`/attachments/${file.id}`), file).then(resp => {
-                    let search = filesIndexOf(resp.data);
-                    if (search !== -1) $scope.files[search] = resp.data;
-
-                    if ($scope.editFile && !file.external) {
-                        $scope.editFile.link = '';
-                    }
-                    $scope.editFile = false;
-                    events.emit('success', trans('entities.attachments_updated_success'));
-                }, checkError('edit'));
-            };
-
-            /**
-             * Get the url of a file.
-             */
-            $scope.getFileUrl = function(file) {
-                return window.baseUrl('/attachments/' + file.id);
-            };
-
-            /**
-             * Search the local files via another file object.
-             * Used to search via object copies.
-             * @param file
-             * @returns int
-             */
-            function filesIndexOf(file) {
-                for (let i = 0; i < $scope.files.length; i++) {
-                    if ($scope.files[i].id == file.id) return i;
-                }
-                return -1;
-            }
-
-            /**
-             * Check for an error response in a ajax request.
-             * @param errorGroupName
-             */
-            function checkError(errorGroupName) {
-                $scope.errors[errorGroupName] = {};
-                return function(response) {
-                    if (typeof response.data !== 'undefined' && typeof response.data.error !== 'undefined') {
-                        events.emit('error', response.data.error);
-                    }
-                    if (typeof response.data !== 'undefined' && typeof response.data.validation !== 'undefined') {
-                        $scope.errors[errorGroupName] = response.data.validation;
-                        console.log($scope.errors[errorGroupName])
-                    }
-                }
-            }
-
-        }]);
-
 };
index 8d7d89cee2522be31848a697f65753c9275f1724..08b82357f59c2c648981d74e2d0014d209747b11 100644 (file)
 "use strict";
-const DropZone = require("dropzone");
 const MarkdownIt = require("markdown-it");
 const mdTasksLists = require('markdown-it-task-lists');
 const code = require('./code');
 
 module.exports = function (ngApp, events) {
 
-    /**
-     * Common tab controls using simple jQuery functions.
-     */
-    ngApp.directive('tabContainer', function() {
-        return {
-            restrict: 'A',
-            link: function (scope, element, attrs) {
-                const $content = element.find('[tab-content]');
-                const $buttons = element.find('[tab-button]');
-
-                if (attrs.tabContainer) {
-                    let initial = attrs.tabContainer;
-                    $buttons.filter(`[tab-button="${initial}"]`).addClass('selected');
-                    $content.hide().filter(`[tab-content="${initial}"]`).show();
-                } else {
-                    $content.hide().first().show();
-                    $buttons.first().addClass('selected');
-                }
-
-                $buttons.click(function() {
-                    let clickedTab = $(this);
-                    $buttons.removeClass('selected');
-                    $content.hide();
-                    let name = clickedTab.addClass('selected').attr('tab-button');
-                    $content.filter(`[tab-content="${name}"]`).show();
-                });
-            }
-        };
-    });
-
-    /**
-     * Sub form component to allow inner-form sections to act like their own forms.
-     */
-    ngApp.directive('subForm', function() {
-        return {
-            restrict: 'A',
-            link: function (scope, element, attrs) {
-                element.on('keypress', e => {
-                    if (e.keyCode === 13) {
-                        submitEvent(e);
-                    }
-                });
-
-                element.find('button[type="submit"]').click(submitEvent);
-
-                function submitEvent(e) {
-                    e.preventDefault();
-                    if (attrs.subForm) scope.$eval(attrs.subForm);
-                }
-            }
-        };
-    });
-
-    /**
-     * DropZone
-     * Used for uploading images
-     */
-    ngApp.directive('dropZone', [function () {
-        return {
-            restrict: 'E',
-            template: `
-            <div class="dropzone-container">
-                <div class="dz-message">{{message}}</div>
-            </div>
-            `,
-            scope: {
-                uploadUrl: '@',
-                eventSuccess: '=',
-                eventError: '=',
-                uploadedTo: '@',
-            },
-            link: function (scope, element, attrs) {
-                scope.message = attrs.message;
-                if (attrs.placeholder) element[0].querySelector('.dz-message').textContent = attrs.placeholder;
-                let dropZone = new DropZone(element[0].querySelector('.dropzone-container'), {
-                    url: scope.uploadUrl,
-                    init: function () {
-                        let dz = this;
-                        dz.on('sending', function (file, xhr, data) {
-                            let token = window.document.querySelector('meta[name=token]').getAttribute('content');
-                            data.append('_token', token);
-                            let uploadedTo = typeof scope.uploadedTo === 'undefined' ? 0 : scope.uploadedTo;
-                            data.append('uploaded_to', uploadedTo);
-                        });
-                        if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess);
-                        dz.on('success', function (file, data) {
-                            $(file.previewElement).fadeOut(400, function () {
-                                dz.removeFile(file);
-                            });
-                        });
-                        if (typeof scope.eventError !== 'undefined') dz.on('error', scope.eventError);
-                        dz.on('error', function (file, errorMessage, xhr) {
-                            console.log(errorMessage);
-                            console.log(xhr);
-                            function setMessage(message) {
-                                $(file.previewElement).find('[data-dz-errormessage]').text(message);
-                            }
-
-                            if (xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
-                            if (errorMessage.file) setMessage(errorMessage.file[0]);
-
-                        });
-                    }
-                });
-            }
-        };
-    }]);
-
-    /**
-     * Dropdown
-     * Provides some simple logic to create small dropdown menus
-     */
-    ngApp.directive('dropdown', [function () {
-        return {
-            restrict: 'A',
-            link: function (scope, element, attrs) {
-                const menu = element.find('ul');
-
-                function hide() {
-                    menu.hide();
-                    menu.removeClass('anim menuIn');
-                }
-
-                function show() {
-                    menu.show().addClass('anim menuIn');
-                    element.mouseleave(hide);
-
-                    // Focus on input if exist in dropdown and hide on enter press
-                    let inputs = menu.find('input');
-                    if (inputs.length > 0) inputs.first().focus();
-                }
-
-                // Hide menu on option click
-                element.on('click', '> ul a', hide);
-                // Show dropdown on toggle click.
-                element.find('[dropdown-toggle]').on('click', show);
-                // Hide menu on enter press in inputs
-                element.on('keypress', 'input', event => {
-                    if (event.keyCode !== 13) return true;
-                    event.preventDefault();
-                    hide();
-                    return false;
-                });
-            }
-        };
-    }]);
-
     /**
      * TinyMCE
      * An angular wrapper around the tinyMCE editor.
@@ -168,7 +20,7 @@ module.exports = function (ngApp, events) {
             link: function (scope, element, attrs) {
 
                 function tinyMceSetup(editor) {
-                    editor.on('ExecCommand change NodeChange ObjectResized', (e) => {
+                    editor.on('ExecCommand change input NodeChange ObjectResized', (e) => {
                         let content = editor.getContent();
                         $timeout(() => {
                             scope.mceModel = content;
@@ -177,7 +29,10 @@ module.exports = function (ngApp, events) {
                     });
 
                     editor.on('keydown', (event) => {
-                        scope.$emit('editor-keydown', event);
+                        if (event.keyCode === 83 && (navigator.platform.match("Mac") ? event.metaKey : event.ctrlKey)) {
+                            event.preventDefault();
+                            scope.$emit('save-draft', event);
+                        }
                     });
 
                     editor.on('init', (e) => {
@@ -247,7 +102,7 @@ module.exports = function (ngApp, events) {
                 extraKeys[`${metaKey}-7`] = function(cm) {wrapSelection('\n```\n', '\n```');};
                 extraKeys[`${metaKey}-8`] = function(cm) {wrapSelection('`', '`');};
                 extraKeys[`Shift-${metaKey}-E`] = function(cm) {wrapSelection('`', '`');};
-                extraKeys[`${metaKey}-9`] = function(cm) {wrapSelection('<p class="callout info">', '</div>');};
+                extraKeys[`${metaKey}-9`] = function(cm) {wrapSelection('<p class="callout info">', '</p>');};
                 cm.setOption('extraKeys', extraKeys);
 
                 // Update data on content change
@@ -341,12 +196,13 @@ module.exports = function (ngApp, events) {
                     }
 
                     cm.replaceRange(newLineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
-                    cm.setCursor({line: cursor.line, ch: cursor.ch + (newLineContent.length - lineLen)});
+                    cm.setCursor({line: cursor.line, ch: cursor.ch + start.length});
                 }
 
                 function wrapSelection(start, end) {
                     let selection = cm.getSelection();
                     if (selection === '') return wrapLine(start, end);
+
                     let newSelection = selection;
                     let frontDiff = 0;
                     let endDiff = 0;
@@ -400,7 +256,7 @@ module.exports = function (ngApp, events) {
                 // Show the popup link selector and insert a link when finished
                 function showLinkSelector() {
                     let cursorPos = cm.getCursor('from');
-                    window.showEntityLinkSelector(entity => {
+                    window.EntitySelectorPopup.show(entity => {
                         let selectedText = cm.getSelection() || entity.name;
                         let newText = `[${selectedText}](${entity.link})`;
                         cm.focus();
@@ -422,7 +278,7 @@ module.exports = function (ngApp, events) {
                 // Show the image manager and handle image insertion
                 function showImageManager() {
                     let cursorPos = cm.getCursor('from');
-                    window.ImageManager.showExternal(image => {
+                    window.ImageManager.show(image => {
                         let selectedText = cm.getSelection();
                         let newText = "![" + (selectedText || image.name) + "](" + image.thumbs.display + ")";
                         cm.focus();
@@ -534,333 +390,4 @@ module.exports = function (ngApp, events) {
             }
         }
     }]);
-
-    /**
-     * Tag Autosuggestions
-     * Listens to child inputs and provides autosuggestions depending on field type
-     * and input. Suggestions provided by server.
-     */
-    ngApp.directive('tagAutosuggestions', ['$http', function ($http) {
-        return {
-            restrict: 'A',
-            link: function (scope, elem, attrs) {
-
-                // Local storage for quick caching.
-                const localCache = {};
-
-                // Create suggestion element
-                const suggestionBox = document.createElement('ul');
-                suggestionBox.className = 'suggestion-box';
-                suggestionBox.style.position = 'absolute';
-                suggestionBox.style.display = 'none';
-                const $suggestionBox = $(suggestionBox);
-
-                // General state tracking
-                let isShowing = false;
-                let currentInput = false;
-                let active = 0;
-
-                // Listen to input events on autosuggest fields
-                elem.on('input focus', '[autosuggest]', function (event) {
-                    let $input = $(this);
-                    let val = $input.val();
-                    let url = $input.attr('autosuggest');
-                    let type = $input.attr('autosuggest-type');
-
-                    // Add name param to request if for a value
-                    if (type.toLowerCase() === 'value') {
-                        let $nameInput = $input.closest('tr').find('[autosuggest-type="name"]').first();
-                        let nameVal = $nameInput.val();
-                        if (nameVal !== '') {
-                            url += '?name=' + encodeURIComponent(nameVal);
-                        }
-                    }
-
-                    let suggestionPromise = getSuggestions(val.slice(0, 3), url);
-                    suggestionPromise.then(suggestions => {
-                        if (val.length === 0) {
-                            displaySuggestions($input, suggestions.slice(0, 6));
-                        } else  {
-                            suggestions = suggestions.filter(item => {
-                                return item.toLowerCase().indexOf(val.toLowerCase()) !== -1;
-                            }).slice(0, 4);
-                            displaySuggestions($input, suggestions);
-                        }
-                    });
-                });
-
-                // Hide autosuggestions when input loses focus.
-                // Slight delay to allow clicks.
-                let lastFocusTime = 0;
-                elem.on('blur', '[autosuggest]', function (event) {
-                    let startTime = Date.now();
-                    setTimeout(() => {
-                        if (lastFocusTime < startTime) {
-                            $suggestionBox.hide();
-                            isShowing = false;
-                        }
-                    }, 200)
-                });
-                elem.on('focus', '[autosuggest]', function (event) {
-                    lastFocusTime = Date.now();
-                });
-
-                elem.on('keydown', '[autosuggest]', function (event) {
-                    if (!isShowing) return;
-
-                    let suggestionElems = suggestionBox.childNodes;
-                    let suggestCount = suggestionElems.length;
-
-                    // Down arrow
-                    if (event.keyCode === 40) {
-                        let newActive = (active === suggestCount - 1) ? 0 : active + 1;
-                        changeActiveTo(newActive, suggestionElems);
-                    }
-                    // Up arrow
-                    else if (event.keyCode === 38) {
-                        let newActive = (active === 0) ? suggestCount - 1 : active - 1;
-                        changeActiveTo(newActive, suggestionElems);
-                    }
-                    // Enter or tab key
-                    else if ((event.keyCode === 13 || event.keyCode === 9) && !event.shiftKey) {
-                        currentInput[0].value = suggestionElems[active].textContent;
-                        currentInput.focus();
-                        $suggestionBox.hide();
-                        isShowing = false;
-                        if (event.keyCode === 13) {
-                            event.preventDefault();
-                            return false;
-                        }
-                    }
-                });
-
-                // Change the active suggestion to the given index
-                function changeActiveTo(index, suggestionElems) {
-                    suggestionElems[active].className = '';
-                    active = index;
-                    suggestionElems[active].className = 'active';
-                }
-
-                // Display suggestions on a field
-                let prevSuggestions = [];
-
-                function displaySuggestions($input, suggestions) {
-
-                    // Hide if no suggestions
-                    if (suggestions.length === 0) {
-                        $suggestionBox.hide();
-                        isShowing = false;
-                        prevSuggestions = suggestions;
-                        return;
-                    }
-
-                    // Otherwise show and attach to input
-                    if (!isShowing) {
-                        $suggestionBox.show();
-                        isShowing = true;
-                    }
-                    if ($input !== currentInput) {
-                        $suggestionBox.detach();
-                        $input.after($suggestionBox);
-                        currentInput = $input;
-                    }
-
-                    // Return if no change
-                    if (prevSuggestions.join() === suggestions.join()) {
-                        prevSuggestions = suggestions;
-                        return;
-                    }
-
-                    // Build suggestions
-                    $suggestionBox[0].innerHTML = '';
-                    for (let i = 0; i < suggestions.length; i++) {
-                        let suggestion = document.createElement('li');
-                        suggestion.textContent = suggestions[i];
-                        suggestion.onclick = suggestionClick;
-                        if (i === 0) {
-                            suggestion.className = 'active';
-                            active = 0;
-                        }
-                        $suggestionBox[0].appendChild(suggestion);
-                    }
-
-                    prevSuggestions = suggestions;
-                }
-
-                // Suggestion click event
-                function suggestionClick(event) {
-                    currentInput[0].value = this.textContent;
-                    currentInput.focus();
-                    $suggestionBox.hide();
-                    isShowing = false;
-                }
-
-                // Get suggestions & cache
-                function getSuggestions(input, url) {
-                    let hasQuery = url.indexOf('?') !== -1;
-                    let searchUrl = url + (hasQuery ? '&' : '?') + 'search=' + encodeURIComponent(input);
-
-                    // Get from local cache if exists
-                    if (typeof localCache[searchUrl] !== 'undefined') {
-                        return new Promise((resolve, reject) => {
-                            resolve(localCache[searchUrl]);
-                        });
-                    }
-
-                    return $http.get(searchUrl).then(response => {
-                        localCache[searchUrl] = response.data;
-                        return response.data;
-                    });
-                }
-
-            }
-        }
-    }]);
-
-    ngApp.directive('entityLinkSelector', [function($http) {
-        return {
-            restrict: 'A',
-            link: function(scope, element, attrs) {
-
-                const selectButton = element.find('.entity-link-selector-confirm');
-                let callback = false;
-                let entitySelection = null;
-
-                // Handle entity selection change, Stores the selected entity locally
-                function entitySelectionChange(entity) {
-                    entitySelection = entity;
-                    if (entity === null) {
-                        selectButton.attr('disabled', 'true');
-                    } else {
-                        selectButton.removeAttr('disabled');
-                    }
-                }
-                events.listen('entity-select-change', entitySelectionChange);
-
-                // Handle selection confirm button click
-                selectButton.click(event => {
-                    hide();
-                    if (entitySelection !== null) callback(entitySelection);
-                });
-
-                // Show selector interface
-                function show() {
-                    element.fadeIn(240);
-                }
-
-                // Hide selector interface
-                function hide() {
-                    element.fadeOut(240);
-                }
-
-                // Listen to confirmation of entity selections (doubleclick)
-                events.listen('entity-select-confirm', entity => {
-                    hide();
-                    callback(entity);
-                });
-
-                // Show entity selector, Accessible globally, and store the callback
-                window.showEntityLinkSelector = function(passedCallback) {
-                    show();
-                    callback = passedCallback;
-                };
-
-            }
-        };
-    }]);
-
-
-    ngApp.directive('entitySelector', ['$http', '$sce', function ($http, $sce) {
-        return {
-            restrict: 'A',
-            scope: true,
-            link: function (scope, element, attrs) {
-                scope.loading = true;
-                scope.entityResults = false;
-                scope.search = '';
-
-                // Add input for forms
-                const input = element.find('[entity-selector-input]').first();
-
-                // Detect double click events
-                let lastClick = 0;
-                function isDoubleClick() {
-                    let now = Date.now();
-                    let answer = now - lastClick < 300;
-                    lastClick = now;
-                    return answer;
-                }
-
-                // Listen to entity item clicks
-                element.on('click', '.entity-list a', function(event) {
-                    event.preventDefault();
-                    event.stopPropagation();
-                    let item = $(this).closest('[data-entity-type]');
-                    itemSelect(item, isDoubleClick());
-                });
-                element.on('click', '[data-entity-type]', function(event) {
-                    itemSelect($(this), isDoubleClick());
-                });
-
-                // Select entity action
-                function itemSelect(item, doubleClick) {
-                    let entityType = item.attr('data-entity-type');
-                    let entityId = item.attr('data-entity-id');
-                    let isSelected = !item.hasClass('selected') || doubleClick;
-                    element.find('.selected').removeClass('selected').removeClass('primary-background');
-                    if (isSelected) item.addClass('selected').addClass('primary-background');
-                    let newVal = isSelected ? `${entityType}:${entityId}` : '';
-                    input.val(newVal);
-
-                    if (!isSelected) {
-                        events.emit('entity-select-change', null);
-                    }
-
-                    if (!doubleClick && !isSelected) return;
-
-                    let link = item.find('.entity-list-item-link').attr('href');
-                    let name = item.find('.entity-list-item-name').text();
-
-                    if (doubleClick) {
-                        events.emit('entity-select-confirm', {
-                            id: Number(entityId),
-                            name: name,
-                            link: link
-                        });
-                    }
-
-                    if (isSelected) {
-                        events.emit('entity-select-change', {
-                            id: Number(entityId),
-                            name: name,
-                            link: link
-                        });
-                    }
-                }
-
-                // Get search url with correct types
-                function getSearchUrl() {
-                    let types = (attrs.entityTypes) ? encodeURIComponent(attrs.entityTypes) : encodeURIComponent('page,book,chapter');
-                    return window.baseUrl(`/ajax/search/entities?types=${types}`);
-                }
-
-                // Get initial contents
-                $http.get(getSearchUrl()).then(resp => {
-                    scope.entityResults = $sce.trustAsHtml(resp.data);
-                    scope.loading = false;
-                });
-
-                // Search when typing
-                scope.searchEntities = function() {
-                    scope.loading = true;
-                    input.val('');
-                    let url = getSearchUrl() + '&term=' + encodeURIComponent(scope.search);
-                    $http.get(url).then(resp => {
-                        scope.entityResults = $sce.trustAsHtml(resp.data);
-                        scope.loading = false;
-                    });
-                };
-            }
-        };
-    }]);
 };
diff --git a/resources/assets/js/dom-polyfills.js b/resources/assets/js/dom-polyfills.js
new file mode 100644 (file)
index 0000000..fcd89b7
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * Polyfills for DOM API's
+ */
+
+if (!Element.prototype.matches) {
+    Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
+}
+
+if (!Element.prototype.closest) {
+    Element.prototype.closest = function (s) {
+        var el = this;
+        var ancestor = this;
+        if (!document.documentElement.contains(el)) return null;
+        do {
+            if (ancestor.matches(s)) return ancestor;
+            ancestor = ancestor.parentElement;
+        } while (ancestor !== null);
+        return null;
+    };
+}
\ No newline at end of file
index 2ef062a5b2856187c01250ce619eade3e0a3ec94..7126479c1f2b4fda807cbd80619283a1400be18e 100644 (file)
@@ -1,4 +1,6 @@
 "use strict";
+require("babel-polyfill");
+require('./dom-polyfills');
 
 // Url retrieval function
 window.baseUrl = function(path) {
@@ -8,6 +10,33 @@ window.baseUrl = function(path) {
     return basePath + '/' + path;
 };
 
+// Global Event System
+class EventManager {
+    constructor() {
+        this.listeners = {};
+        this.stack = [];
+    }
+
+    emit(eventName, eventData) {
+        this.stack.push({name: eventName, data: eventData});
+        if (typeof this.listeners[eventName] === 'undefined') return this;
+        let eventsToStart = this.listeners[eventName];
+        for (let i = 0; i < eventsToStart.length; i++) {
+            let event = eventsToStart[i];
+            event(eventData);
+        }
+        return this;
+    }
+
+    listen(eventName, callback) {
+        if (typeof this.listeners[eventName] === 'undefined') this.listeners[eventName] = [];
+        this.listeners[eventName].push(callback);
+        return this;
+    }
+}
+
+window.$events = new EventManager();
+
 const Vue = require("vue");
 const axios = require("axios");
 
@@ -17,11 +46,17 @@ let axiosInstance = axios.create({
         'baseURL': window.baseUrl('')
     }
 });
+axiosInstance.interceptors.request.use(resp => {
+    return resp;
+}, err => {
+    if (typeof err.response === "undefined" || typeof err.response.data === "undefined") return Promise.reject(err);
+    if (typeof err.response.data.error !== "undefined") window.$events.emit('error', err.response.data.error);
+    if (typeof err.response.data.message !== "undefined") window.$events.emit('error', err.response.data.message);
+});
 window.$http = axiosInstance;
 
 Vue.prototype.$http = axiosInstance;
-
-require("./vues/vues");
+Vue.prototype.$events = window.$events;
 
 
 // AngularJS - Create application and load components
@@ -38,49 +73,57 @@ let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize'
 const Translations = require("./translations");
 let translator = new Translations(window.translations);
 window.trans = translator.get.bind(translator);
+window.trans_choice = translator.getPlural.bind(translator);
 
-// Global Event System
-class EventManager {
-    constructor() {
-        this.listeners = {};
-    }
-
-    emit(eventName, eventData) {
-        if (typeof this.listeners[eventName] === 'undefined') return this;
-        let eventsToStart = this.listeners[eventName];
-        for (let i = 0; i < eventsToStart.length; i++) {
-            let event = eventsToStart[i];
-            event(eventData);
-        }
-        return this;
-    }
-
-    listen(eventName, callback) {
-        if (typeof this.listeners[eventName] === 'undefined') this.listeners[eventName] = [];
-        this.listeners[eventName].push(callback);
-        return this;
-    }
-}
 
-window.Events = new EventManager();
-Vue.prototype.$events = window.Events;
+require("./vues/vues");
+require("./components");
 
 // Load in angular specific items
-const Services = require('./services');
 const Directives = require('./directives');
 const Controllers = require('./controllers');
-Services(ngApp, window.Events);
-Directives(ngApp, window.Events);
-Controllers(ngApp, window.Events);
+Directives(ngApp, window.$events);
+Controllers(ngApp, window.$events);
 
 //Global jQuery Config & Extensions
 
+/**
+ * Scroll the view to a specific element.
+ * @param {HTMLElement} element
+ */
+window.scrollToElement = function(element) {
+    if (!element) return;
+    let offset = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
+    let top = element.getBoundingClientRect().top + offset;
+    $('html, body').animate({
+        scrollTop: top - 60 // Adjust to change final scroll position top margin
+    }, 300);
+};
+
+/**
+ * Scroll and highlight an element.
+ * @param {HTMLElement} element
+ */
+window.scrollAndHighlight = function(element) {
+    if (!element) return;
+    window.scrollToElement(element);
+    let color = document.getElementById('custom-styles').getAttribute('data-color-light');
+    let initColor = window.getComputedStyle(element).getPropertyValue('background-color');
+    element.style.backgroundColor = color;
+    setTimeout(() => {
+        element.classList.add('selectFade');
+        element.style.backgroundColor = initColor;
+    }, 10);
+    setTimeout(() => {
+        element.classList.remove('selectFade');
+        element.style.backgroundColor = '';
+    }, 3000);
+};
+
 // Smooth scrolling
 jQuery.fn.smoothScrollTo = function () {
     if (this.length === 0) return;
-    $('html, body').animate({
-        scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
-    }, 300); // Adjust to change animations speed (ms)
+    window.scrollToElement(this[0]);
     return this;
 };
 
@@ -91,83 +134,11 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
     };
 });
 
-// Global jQuery Elements
-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) {
-    warningNotification.find('span').text(text);
-    warningNotification.show();
-});
-window.Events.listen('error', function (text) {
-    errorNotification.find('span').text(text);
-    errorNotification.show();
-});
-
-// Notification hiding
-notifications.click(function () {
-    $(this).fadeOut(100);
-});
-
-// 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() {
-     $('#header').smoothScrollTo();
-});
-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';
-        scrollTopShowing = true;
-        setTimeout(() => {
-            scrollTop.style.opacity = 0.4;
-        }, 1);
-    } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
-        scrollTop.style.opacity = 0;
-        scrollTopShowing = false;
-        setTimeout(() => {
-            scrollTop.style.display = 'none';
-        }, 500);
-    }
-});
-
-// 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() {
-    $(this).closest('.overlay').fadeOut(240);
-});
-$('.overlay').click(function(event) {
-    if (!$(event.target).hasClass('overlay')) return;
-    $(this).fadeOut(240);
-});
-
 // Detect IE for css
 if(navigator.userAgent.indexOf('MSIE')!==-1
     || navigator.appVersion.indexOf('Trident/') > 0
     || navigator.userAgent.indexOf('Safari') !== -1){
-    $('body').addClass('flexbox-support');
+    document.body.classList.add('flexbox-support');
 }
 
 // Page specific items
index 4f4c1fbe0fe6744afa82eda93739f89fbafd5d75..497dc0212521797f54eb410d0bc178cfa534b44d 100644 (file)
@@ -274,7 +274,7 @@ module.exports = function() {
         file_browser_callback: function (field_name, url, type, win) {
 
             if (type === 'file') {
-                window.showEntityLinkSelector(function(entity) {
+                window.EntitySelectorPopup.show(function(entity) {
                     let originalField = win.document.getElementById(field_name);
                     originalField.value = entity.link;
                     $(originalField).closest('.mce-form').find('input').eq(2).val(entity.name);
@@ -283,7 +283,7 @@ module.exports = function() {
 
             if (type === 'image') {
                 // Show image manager
-                window.ImageManager.showExternal(function (image) {
+                window.ImageManager.show(function (image) {
 
                     // Set popover link input to image url then fire change event
                     // to ensure the new value sticks
@@ -365,7 +365,7 @@ module.exports = function() {
                 icon: 'image',
                 tooltip: 'Insert an image',
                 onclick: function () {
-                    window.ImageManager.showExternal(function (image) {
+                    window.ImageManager.show(function (image) {
                         let html = `<a href="${image.url}" target="_blank">`;
                         html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
                         html += '</a>';
index 67d339d63a32c139831550c7be13bc9ee3fa9f13..14437cd68a326466b1179cf4639154e329386999 100644 (file)
@@ -1,5 +1,3 @@
-"use strict";
-// Configure ZeroClipboard
 const Clipboard = require("clipboard");
 const Code = require('../code');
 
@@ -83,15 +81,7 @@ let setupPageShow = window.setupPageShow = function (pageId) {
         let idElem = document.getElementById(text);
         $('.page-content [data-highlighted]').attr('data-highlighted', '').css('background-color', '');
         if (idElem !== null) {
-            let $idElem = $(idElem);
-            let color = $('#custom-styles').attr('data-color-light');
-            $idElem.css('background-color', color).attr('data-highlighted', 'true').smoothScrollTo();
-            setTimeout(() => {
-                $idElem.addClass('anim').addClass('selectFade').css('background-color', '');
-                setTimeout(() => {
-                   $idElem.removeClass('selectFade');
-                }, 3000);
-            }, 100);
+            window.scrollAndHighlight(idElem);
         } else {
             $('.page-content').find(':contains("' + text + '")').smoothScrollTo();
         }
@@ -108,25 +98,25 @@ let setupPageShow = window.setupPageShow = function (pageId) {
         goToText(event.target.getAttribute('href').substr(1));
     });
 
-    // Make the book-tree sidebar stick in view on scroll
+    // Make the sidebar stick in view on scroll
     let $window = $(window);
-    let $bookTree = $(".book-tree");
-    let $bookTreeParent = $bookTree.parent();
+    let $sidebar = $("#sidebar .scroll-body");
+    let $bookTreeParent = $sidebar.parent();
     // Check the page is scrollable and the content is taller than the tree
-    let pageScrollable = ($(document).height() > $window.height()) && ($bookTree.height() < $('.page-content').height());
+    let pageScrollable = ($(document).height() > $window.height()) && ($sidebar.height() < $('.page-content').height());
     // Get current tree's width and header height
     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);
-        $bookTree.addClass("fixed");
+        $sidebar.width($bookTreeParent.width() + 15);
+        $sidebar.addClass("fixed");
         isFixed = true;
     }
     // Function to un-fix the tree back into position
     function unstickTree() {
-        $bookTree.css('width', 'auto');
-        $bookTree.removeClass("fixed");
+        $sidebar.css('width', 'auto');
+        $sidebar.removeClass("fixed");
         isFixed = false;
     }
     // Checks if the tree stickiness state should change
@@ -160,7 +150,6 @@ let setupPageShow = window.setupPageShow = function (pageId) {
             unstickTree();
         }
     });
-
 };
 
 module.exports = setupPageShow;
\ No newline at end of file
diff --git a/resources/assets/js/services.js b/resources/assets/js/services.js
deleted file mode 100644 (file)
index cd2759c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-"use strict";
-
-module.exports = function(ngApp, events) {
-
-    ngApp.factory('imageManagerService', function() {
-        return {
-            show: false,
-            showExternal: false
-        };
-    });
-
-};
\ No newline at end of file
index ca6a7bd29a8f7224a31c8dae62f2c4f8a58f5266..70ebfc25541d98e0afe76af37221a36afab498e3 100644 (file)
@@ -20,9 +20,63 @@ class Translator {
      * @returns {*}
      */
     get(key, replacements) {
+        let text = this.getTransText(key);
+        return this.performReplacements(text, replacements);
+    }
+
+    /**
+     * Get pluralised text, Dependant on the given count.
+     * Same format at laravel's 'trans_choice' helper.
+     * @param key
+     * @param count
+     * @param replacements
+     * @returns {*}
+     */
+    getPlural(key, count, replacements) {
+        let text = this.getTransText(key);
+        let splitText = text.split('|');
+        let result = null;
+        let exactCountRegex = /^{([0-9]+)}/;
+        let rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
+
+        for (let i = 0, len = splitText.length; i < len; i++) {
+            let t = splitText[i];
+
+            // Parse exact matches
+            let exactMatches = t.match(exactCountRegex);
+            if (exactMatches !== null && Number(exactMatches[1]) === count) {
+                result = t.replace(exactCountRegex, '').trim();
+                break;
+            }
+
+            // Parse range matches
+            let rangeMatches = t.match(rangeRegex);
+            if (rangeMatches !== null) {
+                let rangeStart = Number(rangeMatches[1]);
+                if (rangeStart <= count && (rangeMatches[2] === '*' || Number(rangeMatches[2]) >= count)) {
+                    result = t.replace(rangeRegex, '').trim();
+                    break;
+                }
+            }
+        }
+
+        if (result === null && splitText.length > 1) {
+            result = (count === 1) ? splitText[0] : splitText[1];
+        }
+
+        if (result === null) result = splitText[0];
+        return this.performReplacements(result, replacements);
+    }
+
+    /**
+     * Fetched translation text from the store for the given key.
+     * @param key
+     * @returns {String|Object}
+     */
+    getTransText(key) {
         let splitKey = key.split('.');
         let value = splitKey.reduce((a, b) => {
-            return a != undefined ? a[b] : a;
+            return a !== undefined ? a[b] : a;
         }, this.store);
 
         if (value === undefined) {
@@ -30,16 +84,25 @@ class Translator {
             value = key;
         }
 
-        if (replacements === undefined) return value;
+        return value;
+    }
 
-        let replaceMatches = value.match(/:([\S]+)/g);
-        if (replaceMatches === null) return value;
+    /**
+     * Perform replacements on a string.
+     * @param {String} string
+     * @param {Object} replacements
+     * @returns {*}
+     */
+    performReplacements(string, replacements) {
+        if (!replacements) return string;
+        let replaceMatches = string.match(/:([\S]+)/g);
+        if (replaceMatches === null) return string;
         replaceMatches.forEach(match => {
             let key = match.substring(1);
             if (typeof replacements[key] === 'undefined') return;
-            value = value.replace(match, replacements[key]);
+            string = string.replace(match, replacements[key]);
         });
-        return value;
+        return string;
     }
 
 }
diff --git a/resources/assets/js/vues/attachment-manager.js b/resources/assets/js/vues/attachment-manager.js
new file mode 100644 (file)
index 0000000..635622b
--- /dev/null
@@ -0,0 +1,138 @@
+const draggable = require('vuedraggable');
+const dropzone = require('./components/dropzone');
+
+function mounted() {
+    this.pageId = this.$el.getAttribute('page-id');
+    this.file = this.newFile();
+
+    this.$http.get(window.baseUrl(`/attachments/get/page/${this.pageId}`)).then(resp => {
+        this.files = resp.data;
+    }).catch(err => {
+        this.checkValidationErrors('get', err);
+    });
+}
+
+let data = {
+    pageId: null,
+    files: [],
+    fileToEdit: null,
+    file: {},
+    tab: 'list',
+    editTab: 'file',
+    errors: {link: {}, edit: {}, delete: {}}
+};
+
+const components = {dropzone, draggable};
+
+let methods = {
+
+    newFile() {
+        return {page_id: this.pageId};
+    },
+
+    getFileUrl(file) {
+        return window.baseUrl(`/attachments/${file.id}`);
+    },
+
+    fileSortUpdate() {
+        this.$http.put(window.baseUrl(`/attachments/sort/page/${this.pageId}`), {files: this.files}).then(resp => {
+            this.$events.emit('success', resp.data.message);
+        }).catch(err => {
+            this.checkValidationErrors('sort', err);
+        });
+    },
+
+    startEdit(file) {
+        this.fileToEdit = Object.assign({}, file);
+        this.fileToEdit.link = file.external ? file.path : '';
+        this.editTab = file.external ? 'link' : 'file';
+    },
+
+    deleteFile(file) {
+        if (!file.deleting) return file.deleting = true;
+
+        this.$http.delete(window.baseUrl(`/attachments/${file.id}`)).then(resp => {
+            this.$events.emit('success', resp.data.message);
+            this.files.splice(this.files.indexOf(file), 1);
+        }).catch(err => {
+            this.checkValidationErrors('delete', err)
+        });
+    },
+
+    uploadSuccess(upload) {
+        this.files.push(upload.data);
+        this.$events.emit('success', trans('entities.attachments_file_uploaded'));
+    },
+
+    uploadSuccessUpdate(upload) {
+        let fileIndex = this.filesIndex(upload.data);
+        if (fileIndex === -1) {
+            this.files.push(upload.data)
+        } else {
+            this.files.splice(fileIndex, 1, upload.data);
+        }
+
+        if (this.fileToEdit && this.fileToEdit.id === upload.data.id) {
+            this.fileToEdit = Object.assign({}, upload.data);
+        }
+        this.$events.emit('success', trans('entities.attachments_file_updated'));
+    },
+
+    checkValidationErrors(groupName, err) {
+        console.error(err);
+        if (typeof err.response.data === "undefined" && typeof err.response.data.validation === "undefined") return;
+        this.errors[groupName] = err.response.data.validation;
+        console.log(this.errors[groupName]);
+    },
+
+    getUploadUrl(file) {
+        let url = window.baseUrl(`/attachments/upload`);
+        if (typeof file !== 'undefined') url += `/${file.id}`;
+        return url;
+    },
+
+    cancelEdit() {
+        this.fileToEdit = null;
+    },
+
+    attachNewLink(file) {
+        file.uploaded_to = this.pageId;
+        this.$http.post(window.baseUrl('/attachments/link'), file).then(resp => {
+            this.files.push(resp.data);
+            this.file = this.newFile();
+            this.$events.emit('success', trans('entities.attachments_link_attached'));
+        }).catch(err => {
+            this.checkValidationErrors('link', err);
+        });
+    },
+
+    updateFile(file) {
+        $http.put(window.baseUrl(`/attachments/${file.id}`), file).then(resp => {
+            let search = this.filesIndex(resp.data);
+            if (search === -1) {
+                this.files.push(resp.data);
+            } else {
+                this.files.splice(search, 1, resp.data);
+            }
+
+            if (this.fileToEdit && !file.external) this.fileToEdit.link = '';
+            this.fileToEdit = false;
+
+            this.$events.emit('success', trans('entities.attachments_updated_success'));
+        }).catch(err => {
+            this.checkValidationErrors('edit', err);
+        });
+    },
+
+    filesIndex(file) {
+        for (let i = 0, len = this.files.length; i < len; i++) {
+            if (this.files[i].id === file.id) return i;
+        }
+        return -1;
+    }
+
+};
+
+module.exports = {
+    data, methods, mounted, components,
+};
\ No newline at end of file
diff --git a/resources/assets/js/vues/components/autosuggest.js b/resources/assets/js/vues/components/autosuggest.js
new file mode 100644 (file)
index 0000000..4d6b97e
--- /dev/null
@@ -0,0 +1,130 @@
+
+const template = `
+    <div>
+        <input :value="value" :autosuggest-type="type" ref="input"
+            :placeholder="placeholder" :name="name"
+            @input="inputUpdate($event.target.value)" @focus="inputUpdate($event.target.value)"
+            @blur="inputBlur"
+            @keydown="inputKeydown"
+        />
+        <ul class="suggestion-box" v-if="showSuggestions">
+            <li v-for="(suggestion, i) in suggestions"
+                @click="selectSuggestion(suggestion)"
+                :class="{active: (i === active)}">{{suggestion}}</li>
+        </ul>
+    </div>
+    
+`;
+
+function data() {
+    return {
+        suggestions: [],
+        showSuggestions: false,
+        active: 0,
+    };
+}
+
+const ajaxCache = {};
+
+const props = ['url', 'type', 'value', 'placeholder', 'name'];
+
+function getNameInputVal(valInput) {
+    let parentRow = valInput.parentNode.parentNode;
+    let nameInput = parentRow.querySelector('[autosuggest-type="name"]');
+    return (nameInput === null) ? '' : nameInput.value;
+}
+
+const methods = {
+
+    inputUpdate(inputValue) {
+        this.$emit('input', inputValue);
+        let params = {};
+
+        if (this.type === 'value') {
+            let nameVal = getNameInputVal(this.$el);
+            if (nameVal !== "") params.name = nameVal;
+        }
+
+        this.getSuggestions(inputValue.slice(0, 3), params).then(suggestions => {
+            if (inputValue.length === 0) {
+                this.displaySuggestions(suggestions.slice(0, 6));
+                return;
+            }
+            // Filter to suggestions containing searched term
+            suggestions = suggestions.filter(item => {
+                return item.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1;
+            }).slice(0, 4);
+            this.displaySuggestions(suggestions);
+        });
+    },
+
+    inputBlur() {
+        setTimeout(() => {
+            this.$emit('blur');
+            this.showSuggestions = false;
+        }, 100);
+    },
+
+    inputKeydown(event) {
+        if (event.keyCode === 13) event.preventDefault();
+        if (!this.showSuggestions) return;
+
+        // Down arrow
+        if (event.keyCode === 40) {
+            this.active = (this.active === this.suggestions.length - 1) ? 0 : this.active+1;
+        }
+        // Up Arrow
+        else if (event.keyCode === 38) {
+            this.active = (this.active === 0) ? this.suggestions.length - 1 : this.active-1;
+        }
+        // Enter or tab keys
+        else if ((event.keyCode === 13 || event.keyCode === 9) && !event.shiftKey) {
+            this.selectSuggestion(this.suggestions[this.active]);
+        }
+        // Escape key
+        else if (event.keyCode === 27) {
+            this.showSuggestions = false;
+        }
+    },
+
+    displaySuggestions(suggestions) {
+        if (suggestions.length === 0) {
+            this.suggestions = [];
+            this.showSuggestions = false;
+            return;
+        }
+
+        this.suggestions = suggestions;
+        this.showSuggestions = true;
+        this.active = 0;
+    },
+
+    selectSuggestion(suggestion) {
+        this.$refs.input.value = suggestion;
+        this.$refs.input.focus();
+        this.$emit('input', suggestion);
+        this.showSuggestions = false;
+    },
+
+    /**
+     * Get suggestions from BookStack. Store and use local cache if already searched.
+     * @param {String} input
+     * @param {Object} params
+     */
+    getSuggestions(input, params) {
+        params.search = input;
+        let cacheKey = `${this.url}:${JSON.stringify(params)}`;
+
+        if (typeof ajaxCache[cacheKey] !== "undefined") return Promise.resolve(ajaxCache[cacheKey]);
+
+        return this.$http.get(this.url, {params}).then(resp => {
+            ajaxCache[cacheKey] = resp.data;
+            return resp.data;
+        });
+    }
+
+};
+
+const computed = [];
+
+module.exports = {template, data, props, methods, computed};
\ No newline at end of file
diff --git a/resources/assets/js/vues/components/dropzone.js b/resources/assets/js/vues/components/dropzone.js
new file mode 100644 (file)
index 0000000..0f31bd5
--- /dev/null
@@ -0,0 +1,60 @@
+const DropZone = require("dropzone");
+
+const template = `
+    <div class="dropzone-container">
+        <div class="dz-message">{{placeholder}}</div>
+    </div>
+`;
+
+const props = ['placeholder', 'uploadUrl', 'uploadedTo'];
+
+// TODO - Remove jQuery usage
+function mounted() {
+   let container = this.$el;
+   let _this = this;
+   new DropZone(container, {
+        url: function() {
+            return _this.uploadUrl;
+        },
+        init: function () {
+            let dz = this;
+
+            dz.on('sending', function (file, xhr, data) {
+                let token = window.document.querySelector('meta[name=token]').getAttribute('content');
+                data.append('_token', token);
+                let uploadedTo = typeof _this.uploadedTo === 'undefined' ? 0 : _this.uploadedTo;
+                data.append('uploaded_to', uploadedTo);
+            });
+
+            dz.on('success', function (file, data) {
+                _this.$emit('success', {file, data});
+                $(file.previewElement).fadeOut(400, function () {
+                    dz.removeFile(file);
+                });
+            });
+
+            dz.on('error', function (file, errorMessage, xhr) {
+                _this.$emit('error', {file, errorMessage, xhr});
+                console.log(errorMessage);
+                console.log(xhr);
+                function setMessage(message) {
+                    $(file.previewElement).find('[data-dz-errormessage]').text(message);
+                }
+
+                if (xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
+                if (errorMessage.file) setMessage(errorMessage.file[0]);
+            });
+        }
+   });
+}
+
+function data() {
+    return {}
+}
+
+module.exports = {
+    template,
+    props,
+    mounted,
+    data,
+};
\ No newline at end of file
diff --git a/resources/assets/js/vues/image-manager.js b/resources/assets/js/vues/image-manager.js
new file mode 100644 (file)
index 0000000..12ccc97
--- /dev/null
@@ -0,0 +1,178 @@
+const dropzone = require('./components/dropzone');
+
+let page = 0;
+let previousClickTime = 0;
+let previousClickImage = 0;
+let dataLoaded = false;
+let callback = false;
+let baseUrl = '';
+
+let preSearchImages = [];
+let preSearchHasMore = false;
+
+const data = {
+    images: [],
+
+    imageType: false,
+    uploadedTo: false,
+
+    selectedImage: false,
+    dependantPages: false,
+    showing: false,
+    view: 'all',
+    hasMore: false,
+    searching: false,
+    searchTerm: '',
+
+    imageUpdateSuccess: false,
+    imageDeleteSuccess: false,
+};
+
+const methods = {
+
+    show(providedCallback) {
+        callback = providedCallback;
+        this.showing = true;
+        this.$el.children[0].components.overlay.show();
+
+        // Get initial images if they have not yet been loaded in.
+        if (dataLoaded) return;
+        this.fetchData();
+        dataLoaded = true;
+    },
+
+    hide() {
+        this.showing = false;
+        this.$el.children[0].components.overlay.hide();
+    },
+
+    fetchData() {
+        let url = baseUrl + page;
+        let query = {};
+        if (this.uploadedTo !== false) query.page_id = this.uploadedTo;
+        if (this.searching) query.term = this.searchTerm;
+
+        this.$http.get(url, {params: query}).then(response => {
+            this.images = this.images.concat(response.data.images);
+            this.hasMore = response.data.hasMore;
+            page++;
+        });
+    },
+
+    setView(viewName) {
+        this.cancelSearch();
+        this.images = [];
+        this.hasMore = false;
+        page = 0;
+        this.view = viewName;
+        baseUrl = window.baseUrl(`/images/${this.imageType}/${viewName}/`);
+        this.fetchData();
+    },
+
+    searchImages() {
+        if (this.searchTerm === '') return this.cancelSearch();
+
+        // Cache current settings for later
+        if (!this.searching) {
+            preSearchImages = this.images;
+            preSearchHasMore = this.hasMore;
+        }
+
+        this.searching = true;
+        this.images = [];
+        this.hasMore = false;
+        page = 0;
+        baseUrl = window.baseUrl(`/images/${this.imageType}/search/`);
+        this.fetchData();
+    },
+
+    cancelSearch() {
+        this.searching = false;
+        this.searchTerm = '';
+        this.images = preSearchImages;
+        this.hasMore = preSearchHasMore;
+    },
+
+    imageSelect(image) {
+        let dblClickTime = 300;
+        let currentTime = Date.now();
+        let timeDiff = currentTime - previousClickTime;
+        let isDblClick = timeDiff < dblClickTime && image.id === previousClickImage;
+
+        if (isDblClick) {
+            this.callbackAndHide(image);
+        } else {
+            this.selectedImage = image;
+            this.dependantPages = false;
+        }
+
+        previousClickTime = currentTime;
+        previousClickImage = image.id;
+    },
+
+    callbackAndHide(imageResult) {
+        if (callback) callback(imageResult);
+        this.hide();
+    },
+
+    saveImageDetails() {
+        let url = window.baseUrl(`/images/update/${this.selectedImage.id}`);
+        this.$http.put(url, this.selectedImage).then(response => {
+            this.$events.emit('success', trans('components.image_update_success'));
+        }).catch(error => {
+            if (error.response.status === 422) {
+                let errors = error.response.data;
+                let message = '';
+                Object.keys(errors).forEach((key) => {
+                    message += errors[key].join('\n');
+                });
+                this.$events.emit('error', message);
+            }
+        });
+    },
+
+    deleteImage() {
+        let force = this.dependantPages !== false;
+        let url = window.baseUrl('/images/' + this.selectedImage.id);
+        if (force) url += '?force=true';
+        this.$http.delete(url).then(response => {
+            this.images.splice(this.images.indexOf(this.selectedImage), 1);
+            this.selectedImage = false;
+            this.$events.emit('success', trans('components.image_delete_success'));
+        }).catch(error=> {
+            if (error.response.status === 400) {
+                this.dependantPages = error.response.data;
+            }
+        });
+    },
+
+    getDate(stringDate) {
+        return new Date(stringDate);
+    },
+
+    uploadSuccess(event) {
+        this.images.unshift(event.data);
+        this.$events.emit('success', trans('components.image_upload_success'));
+    },
+};
+
+const computed = {
+    uploadUrl() {
+        return window.baseUrl(`/images/${this.imageType}/upload`);
+    }
+};
+
+function mounted() {
+    window.ImageManager = this;
+    this.imageType = this.$el.getAttribute('image-type');
+    this.uploadedTo = this.$el.getAttribute('uploaded-to');
+    baseUrl = window.baseUrl('/images/' + this.imageType + '/all/')
+}
+
+module.exports = {
+    mounted,
+    methods,
+    data,
+    computed,
+    components: {dropzone},
+};
\ No newline at end of file
diff --git a/resources/assets/js/vues/tag-manager.js b/resources/assets/js/vues/tag-manager.js
new file mode 100644 (file)
index 0000000..d97ceb9
--- /dev/null
@@ -0,0 +1,68 @@
+const draggable = require('vuedraggable');
+const autosuggest = require('./components/autosuggest');
+
+let data = {
+    pageId: false,
+    tags: [],
+};
+
+const components = {draggable, autosuggest};
+const directives = {};
+
+let computed = {};
+
+let methods = {
+
+    addEmptyTag() {
+        this.tags.push({name: '', value: '', key: Math.random().toString(36).substring(7)});
+    },
+
+    /**
+     * When an tag changes check if another empty editable field needs to be added onto the end.
+     * @param tag
+     */
+    tagChange(tag) {
+        let tagPos = this.tags.indexOf(tag);
+        if (tagPos === this.tags.length-1 && (tag.name !== '' || tag.value !== '')) this.addEmptyTag();
+    },
+
+    /**
+     * When an tag field loses focus check the tag to see if its
+     * empty and therefore could be removed from the list.
+     * @param tag
+     */
+    tagBlur(tag) {
+        let isLast = (this.tags.indexOf(tag) === this.tags.length-1);
+        if (tag.name !== '' || tag.value !== '' || isLast) return;
+        let cPos = this.tags.indexOf(tag);
+        this.tags.splice(cPos, 1);
+    },
+
+    removeTag(tag) {
+        let tagPos = this.tags.indexOf(tag);
+        if (tagPos === -1) return;
+        this.tags.splice(tagPos, 1);
+    },
+
+    getTagFieldName(index, key) {
+        return `tags[${index}][${key}]`;
+    },
+};
+
+function mounted() {
+    this.pageId = Number(this.$el.getAttribute('page-id'));
+
+    let url = window.baseUrl(`/ajax/tags/get/page/${this.pageId}`);
+    this.$http.get(url).then(response => {
+        let tags = response.data;
+        for (let i = 0, len = tags.length; i < len; i++) {
+            tags[i].key = Math.random().toString(36).substring(7);
+        }
+        this.tags = tags;
+        this.addEmptyTag();
+    });
+}
+
+module.exports = {
+    data, computed, methods, mounted, components, directives
+};
\ No newline at end of file
index 31d833bfb864a8308e87a062f0a353e2073f35fc..a70d32009a04a6747f68a4f4daf06d67b8ed31c2 100644 (file)
@@ -6,16 +6,19 @@ function exists(id) {
 
 let vueMapping = {
     'search-system': require('./search'),
-    'entity-dashboard': require('./entity-search'),
-    'code-editor': require('./code-editor')
+    'entity-dashboard': require('./entity-dashboard'),
+    'code-editor': require('./code-editor'),
+    'image-manager': require('./image-manager'),
+    'tag-manager': require('./tag-manager'),
+    'attachment-manager': require('./attachment-manager'),
 };
 
 window.vues = {};
 
-Object.keys(vueMapping).forEach(id => {
-    if (exists(id)) {
-        let config = vueMapping[id];
-        config.el = '#' + id;
-        window.vues[id] = new Vue(config);
-    }
-});
\ No newline at end of file
+let ids = Object.keys(vueMapping);
+for (let i = 0, len = ids.length; i < len; i++) {
+    if (!exists(ids[i])) continue;
+    let config = vueMapping[ids[i]];
+    config.el = '#' + ids[i];
+    window.vues[ids[i]] = new Vue(config);
+}
\ No newline at end of file
index 467399a660e90b0ccc12ade8422c7e09c5d7a4a3..c03553d15bf196d09a6702185c63853b14b11ba0 100644 (file)
   }
 }
 
-.anim.notification {
-  transform: translate3d(580px, 0, 0);
-  animation-name: notification;
-  animation-duration: 3s;
-  animation-timing-function: ease-in-out;
-  animation-fill-mode: forwards;
-  &.stopped {
-    animation-name: notificationStopped;
-  }
-}
-
-@keyframes notification {
-  0% {
-    transform: translate3d(580px, 0, 0);
-  }
-  10% {
-    transform: translate3d(0, 0, 0);
-  }
-  90% {
-    transform: translate3d(0, 0, 0);
-  }
-  100% {
-    transform: translate3d(580px, 0, 0);
-  }
-}
-@keyframes notificationStopped {
-  0% {
-    transform: translate3d(580px, 0, 0);
-  }
-  10% {
-    transform: translate3d(0, 0, 0);
-  }
-  100% {
-    transform: translate3d(0, 0, 0);
-  }
+.anim.menuIn {
+  transform-origin: 100% 0%;
+  animation-name: menuIn;
+  animation-duration: 120ms;
+  animation-delay: 0s;
+  animation-timing-function: cubic-bezier(.62, .28, .23, .99);
 }
 
 @keyframes menuIn {
   }
 }
 
-.anim.menuIn {
-  transform-origin: 100% 0%;
-  animation-name: menuIn;
-  animation-duration: 120ms;
-  animation-delay: 0s;
-  animation-timing-function: cubic-bezier(.62, .28, .23, .99);
-}
-
 @keyframes loadingBob {
   0% {
     transform: translate3d(0, 0, 0);
   animation-timing-function: cubic-bezier(.62, .28, .23, .99);
 }
 
-.anim.selectFade {
+.selectFade {
   transition: background-color ease-in-out 3000ms;
 }
\ No newline at end of file
index bd3f8ff4ea5d05b9ef5af72faa9394280e58240b..61be99fe6316b255451fc626944a389a85ecda8b 100644 (file)
 .callout {
   border-left: 3px solid #BBB;
   background-color: #EEE;
-  padding: $-s;
-  padding-left: $-xl;
+  padding: $-s $-s $-s $-xl;
   display: block;
   position: relative;
   &:before {
   &.warning:before {
     content: '\f1f1';
   }
+}
+
+.card {
+  margin: $-m;
+  background-color: #FFF;
+  box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.2);
+  h3 {
+    padding: $-m;
+    border-bottom: 1px solid #E8E8E8;
+    margin: 0;
+    font-size: $fs-s;
+    color: #888;
+    font-weight: 400;
+    text-transform: uppercase;
+  }
+  .body, p.empty-text {
+    padding: $-m;
+  }
+  a {
+    word-wrap: break-word;
+    word-break: break-word;
+  }
+}
+
+.card.drag-card {
+  border: 1px solid #DDD;
+  border-radius: 4px;
+  display: flex;
+  padding: 0;
+  padding-left: $-s + 28px;
+  margin: $-s 0;
+  position: relative;
+  .drag-card-action {
+    cursor: pointer;
+  }
+  .handle, .drag-card-action {
+    display: flex;
+    padding: 0;
+    align-items: center;
+    text-align: center;
+    width: 28px;
+    padding-left: $-xs;
+    padding-right: $-xs;
+    &:hover {
+      background-color: #EEE;
+    }
+    i {
+      flex: 1;
+      padding: 0;
+    }
+  }
+  > div .outline input {
+    margin: $-s 0;
+  }
+  > div.padded {
+    padding: $-s 0 !important;
+  }
+  .handle {
+    background-color: #EEE;
+    left: 0;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+  }
+  > div {
+    padding: 0 $-s;
+    max-width: 80%;
+  }
+}
+
+.well {
+  background-color: #F8F8F8;
+  padding: $-m;
+  border: 1px solid #DDD;
 }
\ No newline at end of file
index 6e03c9217cc29ea39bce87f44817dca4a22535e8..f9c6d9b9a0e3fd5c3199bcf15ba310279b740847 100644 (file)
@@ -2,9 +2,12 @@
 @mixin generate-button-colors($textColor, $backgroundColor) {
   background-color: $backgroundColor;
   color: $textColor;
+  text-transform: uppercase;
+  border: 1px solid $backgroundColor;
+  vertical-align: top;
   &:hover {
     background-color: lighten($backgroundColor, 8%);
-    box-shadow: $bs-med;
+    //box-shadow: $bs-med;
     text-decoration: none;
     color: $textColor;
   }
@@ -26,17 +29,16 @@ $button-border-radius: 2px;
   text-decoration: none;
   font-size: $fs-m;
   line-height: 1.4em;
-  padding: $-xs $-m;
+  padding: $-xs*1.3 $-m;
   margin: $-xs $-xs $-xs 0;
   display: inline-block;
   border: none;
-  font-weight: 500;
-  font-family: $text;
+  font-weight: 400;
   outline: 0;
   border-radius: $button-border-radius;
   cursor: pointer;
   transition: all ease-in-out 120ms;
-  box-shadow: 0 0.5px 1.5px 0 rgba(0, 0, 0, 0.21);
+  box-shadow: 0;
   @include generate-button-colors(#EEE, $primary);
 }
 
@@ -52,19 +54,54 @@ $button-border-radius: 2px;
     @include generate-button-colors(#EEE, $secondary);
   }
   &.muted {
-    @include generate-button-colors(#EEE, #888);
+    @include generate-button-colors(#EEE, #AAA);
   }
   &.muted-light {
     @include generate-button-colors(#666, #e4e4e4);
   }
 }
 
+.button.outline {
+  background-color: transparent;
+  color: #888;
+  border: 1px solid #DDD;
+  &:hover, &:focus, &:active {
+    box-shadow: none;
+    background-color: #EEE;
+  }
+  &.page {
+    border-color: $color-page;
+    color: $color-page;
+    &:hover, &:focus, &:active {
+      background-color: $color-page;
+      color: #FFF;
+    }
+  }
+  &.chapter {
+    border-color: $color-chapter;
+    color: $color-chapter;
+    &:hover, &:focus, &:active {
+      background-color: $color-chapter;
+      color: #FFF;
+    }
+  }
+  &.book {
+    border-color: $color-book;
+    color: $color-book;
+    &:hover, &:focus, &:active {
+      background-color: $color-book;
+      color: #FFF;
+    }
+  }
+}
+
 .text-button {
   @extend .link;
   background-color: transparent;
   padding: 0;
   margin: 0;
   border: none;
+  user-select: none;
   &:focus, &:active {
     outline: 0;
   }
index bd85218a5dd581285101d9fc593b7f14d8eede7f..14bcd29cf257f7d3465c2bbfef11ecc4d2f8172d 100644 (file)
@@ -2,7 +2,6 @@
 
 .CodeMirror {
   /* Set height, width, borders, and global font properties here */
-  font-family: monospace;
   height: 300px;
   color: black;
 }
@@ -235,7 +234,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
   border-width: 0;
   background: transparent;
-  font-family: inherit;
   font-size: inherit;
   margin: 0;
   white-space: pre;
@@ -368,9 +366,9 @@ span.CodeMirror-selectedtext { background: none; }
 .cm-s-base16-light span.cm-atom { color: #aa759f; }
 .cm-s-base16-light span.cm-number { color: #aa759f; }
 
-.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #90a959; }
+.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #678c30; }
 .cm-s-base16-light span.cm-keyword { color: #ac4142; }
-.cm-s-base16-light span.cm-string { color: #f4bf75; }
+.cm-s-base16-light span.cm-string { color: #e09c3c; }
 
 .cm-s-base16-light span.cm-variable { color: #90a959; }
 .cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; }
@@ -386,7 +384,10 @@ span.CodeMirror-selectedtext { background: none; }
 /**
  * Custom BookStack overrides
  */
-.cm-s-base16-light.CodeMirror {
+.CodeMirror, .CodeMirror pre {
+  font-size: 12px;
+}
+.CodeMirror {
   font-size: 12px;
   height: auto;
   margin-bottom: $-l;
@@ -394,7 +395,7 @@ span.CodeMirror-selectedtext { background: none; }
 }
 .cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 1px solid #DDD; }
 
-.flex-fill .CodeMirror {
+.code-fill .CodeMirror {
   position: absolute;
   top: 0;
   bottom: 0;
index 12babae7313efc3d9503cfb4e31af89ee8c1834a..54e109067098bce3608526227ee8918e0348b390 100644 (file)
@@ -1,4 +1,65 @@
-.overlay {
+// System wide notifications
+[notification] {
+  position: fixed;
+  top: 0;
+  right: 0;
+  margin: $-xl*2 $-xl;
+  padding: $-l $-xl;
+  background-color: #EEE;
+  border-radius: 3px;
+  box-shadow: $bs-med;
+  z-index: 999999;
+  display: block;
+  cursor: pointer;
+  max-width: 480px;
+  transition: transform ease-in-out 360ms;
+  transform: translate3d(580px, 0, 0);
+  i, span {
+    display: table-cell;
+  }
+  i {
+    font-size: 2em;
+    padding-right: $-l;
+  }
+  span {
+    vertical-align: middle;
+  }
+  &.pos {
+    background-color: $positive;
+    color: #EEE;
+  }
+  &.neg {
+    background-color: $negative;
+    color: #EEE;
+  }
+  &.warning {
+    background-color: $secondary;
+    color: #EEE;
+  }
+  &.showing {
+    transform: translate3d(0, 0, 0);
+  }
+}
+
+[chapter-toggle] {
+  cursor: pointer;
+  margin: 0;
+  transition: all ease-in-out 180ms;
+  user-select: none;
+  i.zmdi-caret-right {
+    transition: all ease-in-out 180ms;
+    transform: rotate(0deg);
+    transform-origin: 25% 50%;
+  }
+  &.open {
+    //margin-bottom: 0;
+  }
+  &.open i.zmdi-caret-right {
+    transform: rotate(90deg);
+  }
+}
+
+[overlay] {
   background-color: rgba(0, 0, 0, 0.333);
   position: fixed;
   z-index: 95536;
@@ -451,7 +512,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 }
 
 
-[tab-container] .nav-tabs {
+.tab-container .nav-tabs {
   text-align: left;
   border-bottom: 1px solid #DDD;
   margin-bottom: $-m;
@@ -479,4 +540,45 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
     margin-right: $-xs;
     text-decoration: underline;
   }
+}
+
+.comment-box {
+  border: 1px solid #DDD;
+  margin-bottom: $-s;
+  border-radius: 3px;
+  .content {
+    padding: $-s;
+    font-size: 0.666em;
+    p, ul {
+      font-size: $fs-m;
+      margin: .5em 0;
+    }
+  }
+  .reply-row {
+    padding: $-xs $-s;
+  }
+}
+
+.comment-box .header {
+  padding: $-xs $-s;
+  background-color: #f8f8f8;
+  border-bottom: 1px solid #DDD;
+  .meta {
+    img, a, span {
+      display: inline-block;
+      vertical-align: top;
+    }
+    a, span {
+      padding: $-xxs 0 $-xxs 0;
+      line-height: 1.6;
+    }
+    a { color: #666; }
+    span {
+      color: #888;
+      padding-left: $-xxs;
+    }
+  }
+  .text-muted {
+    color: #999;
+  }
 }
\ No newline at end of file
diff --git a/resources/assets/sass/_fonts.scss b/resources/assets/sass/_fonts.scss
deleted file mode 100644 (file)
index c8e8ea8..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-// Generated using https://google-webfonts-helper.herokuapp.com
-
-/* roboto-100 - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 100;
-  src: local('Roboto Thin'), local('Roboto-Thin'),
-  url('../fonts/roboto-v15-cyrillic_latin-100.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-100.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-100italic - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: italic;
-  font-weight: 100;
-  src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'),
-  url('../fonts/roboto-v15-cyrillic_latin-100italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-100italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-300 - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 300;
-  src: local('Roboto Light'), local('Roboto-Light'),
-  url('../fonts/roboto-v15-cyrillic_latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-300italic - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: italic;
-  font-weight: 300;
-  src: local('Roboto Light Italic'), local('Roboto-LightItalic'),
-  url('../fonts/roboto-v15-cyrillic_latin-300italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-300italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-regular - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'),
-  url('../fonts/roboto-v15-cyrillic_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-italic - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: italic;
-  font-weight: 400;
-  src: local('Roboto Italic'), local('Roboto-Italic'),
-  url('../fonts/roboto-v15-cyrillic_latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-500 - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'),
-  url('../fonts/roboto-v15-cyrillic_latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-500italic - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: italic;
-  font-weight: 500;
-  src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
-  url('../fonts/roboto-v15-cyrillic_latin-500italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-500italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-700 - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 700;
-  src: local('Roboto Bold'), local('Roboto-Bold'),
-  url('../fonts/roboto-v15-cyrillic_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-/* roboto-700italic - cyrillic_latin */
-@font-face {
-  font-family: 'Roboto';
-  font-style: italic;
-  font-weight: 700;
-  src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'),
-  url('../fonts/roboto-v15-cyrillic_latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-v15-cyrillic_latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
-
-/* roboto-mono-regular - latin */
-@font-face {
-  font-family: 'Roboto Mono';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto Mono'), local('RobotoMono-Regular'),
-  url('../fonts/roboto-mono-v4-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
-  url('../fonts/roboto-mono-v4-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
-}
\ No newline at end of file
index 392e9ec3e1d1d3450708cd8a2b4cd0ec6e28b39f..700c15336ff1d49677d8f20ebc9e8a4ccc17b01a 100644 (file)
@@ -2,15 +2,13 @@
 .input-base {
   background-color: #FFF;
   border-radius: 3px;
-  border: 1px solid #CCC;
+  border: 1px solid #D4D4D4;
   display: inline-block;
   font-size: $fs-s;
-  font-family: $text;
-  padding: $-xs;
-  color: #222;
+  padding: $-xs*1.5;
+  color: #666;
   width: 250px;
   max-width: 100%;
-  //-webkit-appearance:none;
   &.neg, &.invalid {
     border: 1px solid $negative;
   }
   }
 }
 
+.fake-input {
+  @extend .input-base;
+  overflow: auto;
+}
+
 #html-editor {
   display: none;
 }
@@ -33,7 +36,6 @@
   position: relative;
   z-index: 5;
   #markdown-editor-input {
-    font-family: 'Roboto Mono', monospace;
     font-style: normal;
     font-weight: 400;
     padding: $-xs $-m;
@@ -69,7 +71,6 @@
 .editor-toolbar {
   width: 100%;
   padding: $-xs $-m;
-  font-family: 'Roboto Mono', monospace;
   font-size: 11px;
   line-height: 1.6;
   border-bottom: 1px solid #DDD;
@@ -87,8 +88,9 @@ label {
   display: block;
   line-height: 1.4em;
   font-size: 0.94em;
-  font-weight: 500;
-  color: #666;
+  font-weight: 400;
+  color: #999;
+  text-transform: uppercase;
   padding-bottom: 2px;
   margin-bottom: 0.2em;
   &.inline {
@@ -189,28 +191,15 @@ input:checked + .toggle-switch {
 }
 
 .inline-input-style {
-  border: 2px dotted #BBB;
   display: block;
   width: 100%;
-  padding: $-xs $-s;
-}
-
-.title-input .input {
-  width: 100%;
-}
-
-.title-input label, .description-input label{
-  margin-top: $-m;
-  color: #666;
+  padding: $-s;
 }
 
 .title-input input[type="text"] {
-  @extend h1;
   @extend .inline-input-style;
   margin-top: 0;
-  padding-right: 0;
-  width: 100%;
-  color: #444;
+  font-size: 2em;
 }
 
 .title-input.page-title {
@@ -251,21 +240,20 @@ div[editor-type="markdown"] .title-input.page-title input[type="text"] {
     border: none;
     color: $primary;
     padding: 0;
-    margin: 0;
     cursor: pointer;
-    margin-left: $-s;
-  }
-  button[type="submit"] {
-    margin-left: -$-l;
+    position: absolute;
+    left: 8px;
+    top: 9.5px;
   }
   input {
-    padding-right: $-l;
+    display: block;
+    padding-left: $-l;
     width: 300px;
     max-width: 100%;
   }
 }
 
-input.outline {
+.outline > input {
   border: 0;
   border-bottom: 2px solid #DDD;
   border-radius: 0;
index de1ee83fb1caa914cc758ab12623584729286021..b9a6ea53f3e0d8cb96c1d7a478598aa64d5169d3 100644 (file)
@@ -20,19 +20,128 @@ body.flexbox {
   align-items: stretch;
   min-height: 0;
   position: relative;
-  .flex, &.flex {
-    min-height: 0;
-    flex: 1;
+  &.rows {
+    flex-direction: row;
+  }
+  &.columns {
+    flex-direction: column;
+  }
+}
+
+.flex {
+  min-height: 0;
+  flex: 1;
+}
+
+.flex.scroll {
+  //overflow-y: auto;
+  display: flex;
+  &.sidebar {
+    margin-right: -14px;
   }
 }
+.flex.scroll .scroll-body {
+  overflow-y: scroll;
+  flex: 1;
+}
 
 .flex-child > div {
   flex: 1;
 }
 
-//body.ie .flex-child > div {
-//  flex: 1 0 0px;
-//}
+.flex.sidebar {
+  flex: 1;
+  background-color: #F2F2F2;
+  max-width: 360px;
+  min-height: 90vh;
+}
+.flex.sidebar + .flex.content {
+  flex: 3;
+  background-color: #FFFFFF;
+  padding: 0 $-l;
+  border-left: 1px solid #DDD;
+  max-width: 100%;
+}
+.flex.sidebar .sidebar-toggle {
+  display: none;
+}
+
+@include smaller-than($xl) {
+  body.sidebar-layout {
+    padding-left: 30px;
+  }
+  .flex.sidebar {
+    position: fixed;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    z-index: 100;
+    padding-right: 30px;
+    width: 360px;
+    box-shadow: none;
+    transform: translate3d(-330px, 0, 0);
+    transition: transform ease-in-out 120ms;
+    display: flex;
+    flex-direction: column;
+  }
+  .flex.sidebar.open {
+    box-shadow: 1px 2px 2px 1px rgba(0,0,0,.10);
+    transform: translate3d(0, 0, 0);
+    .sidebar-toggle i {
+      transform: rotate(180deg);
+    }
+  }
+  .flex.sidebar .sidebar-toggle {
+    display: block;
+    position: absolute;
+    opacity: 0.9;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    width: 30px;
+    color: #666;
+    font-size: 20px;
+    vertical-align: middle;
+    text-align: center;
+    border: 1px solid #DDD;
+    border-top: 1px solid #BBB;
+    padding-top: $-m;
+    cursor: pointer;
+    i {
+      opacity: 0.5;
+      transition: all ease-in-out 120ms;
+      padding: 0;
+    }
+    &:hover i {
+      opacity: 1;
+    }
+  }
+  .sidebar .scroll-body {
+    flex: 1;
+    overflow-y: scroll;
+  }
+  #sidebar .scroll-body.fixed {
+    width: auto !important;
+  }
+}
+
+@include larger-than($xl) {
+  #sidebar .scroll-body.fixed {
+    z-index: 5;
+    position: fixed;
+    top: 0;
+    padding-right: $-m;
+    width: 30%;
+    left: 0;
+    height: 100%;
+    overflow-y: scroll;
+    -ms-overflow-style: none;
+    //background-color: $primary-faded;
+    border-left: 1px solid #DDD;
+    &::-webkit-scrollbar { width: 0 !important }
+  }
+}
+
 
 /** Rules for all columns */
 div[class^="col-"] img {
@@ -54,6 +163,10 @@ div[class^="col-"] img {
   &.small {
     max-width: 840px;
   }
+  &.nopad {
+    padding-left: 0;
+    padding-right: 0;
+  }
 }
 
 .row {
index ae8dd3ff51937b67f6bb62e15fb4b3ca7e260502..b9d9d68ef86151efad339b16d3dfa09621f0c540 100644 (file)
@@ -12,7 +12,6 @@ header {
     padding: $-m;
   }
   border-bottom: 1px solid #DDD;
-  //margin-bottom: $-l;
   .links {
     display: inline-block;
     vertical-align: top;
@@ -23,26 +22,27 @@ header {
   }
   .links a {
     display: inline-block;
-    padding: $-l;
+    padding: $-m $-l;
     color: #FFF;
     &:last-child {
       padding-right: 0;
     }
     @include smaller-than($screen-md) {
-      padding: $-l $-s;
+      padding: $-m $-s;
     }
   }
   .avatar, .user-name {
     display: inline-block;
   }
   .avatar {
-    //margin-top: (45px/2);
     width: 30px;
     height: 30px;
   }
   .user-name {
     vertical-align: top;
-    padding-top: $-l;
+    padding-top: $-m;
+    position: relative;
+    top: -3px;
     display: inline-block;
     cursor: pointer;
     > * {
@@ -66,53 +66,57 @@ header {
       }
     }
   }
-  @include smaller-than($screen-md) {
+  @include smaller-than($screen-sm) {
     text-align: center;
     .float.right {
       float: none;
     }
-  }
-  @include smaller-than($screen-sm) {
     .links a {
       padding: $-s;
     }
-    form.search-box {
-      margin-top: 0;
-    }
     .user-name {
       padding-top: $-s;
     }
   }
-  .dropdown-container {
-    font-size: 0.9em;
-  }
 }
 
-form.search-box {
-  margin-top: $-l *0.9;
+.header-search {
+  display: inline-block;
+}
+header .search-box {
   display: inline-block;
-  position: relative;
-  text-align: left;
+  margin-top: 10px;
   input {
-    background-color: transparent;
-    border-radius: 24px;
-    border: 2px solid #EEE;
+    background-color: rgba(0, 0, 0, 0.2);
+    border: 1px solid rgba(255, 255, 255, 0.3);
     color: #EEE;
-    padding-left: $-m;
-    padding-right: $-l;
-    outline: 0;
   }
   button {
-    vertical-align: top;
-    margin-left: -$-l;
-    color: #FFF;
-    top: 6px;
-    right: 4px;
-    display: inline-block;
-    position: absolute;
-    &:hover {
-      color: #FFF;
-    }
+    color: #EEE;
+  }
+  ::-webkit-input-placeholder { /* Chrome/Opera/Safari */
+    color: #DDD;
+  }
+  ::-moz-placeholder { /* Firefox 19+ */
+    color: #DDD;
+  }
+  :-ms-input-placeholder { /* IE 10+ */
+    color: #DDD;
+  }
+  :-moz-placeholder { /* Firefox 18- */
+    color: #DDD;
+  }
+  @include smaller-than($screen-lg) {
+    max-width: 250px;
+  }
+  @include smaller-than($l) {
+    max-width: 200px;
+  }
+}
+
+@include smaller-than($s) {
+  .header-search {
+    display: block;
   }
 }
 
@@ -128,12 +132,12 @@ form.search-box {
   font-size: 1.8em;
   color: #fff;
   font-weight: 400;
-  padding: $-l $-l $-l 0;
+  padding: 14px $-l 14px 0;
   vertical-align: top;
   line-height: 1;
 }
 .logo-image {
-  margin: $-m $-s $-m 0;
+  margin: $-xs $-s $-xs 0;
   vertical-align: top;
   height: 43px;
 }
@@ -167,6 +171,10 @@ form.search-box {
   background-color: $primary-faded;
 }
 
+.toolbar-container {
+  background-color: #FFF;
+}
+
 .breadcrumbs .text-button, .action-buttons .text-button {
   display: inline-block;
   padding: $-s;
@@ -227,4 +235,7 @@ form.search-box {
       border-bottom: 2px solid $primary;
     }
   }
+}
+.faded-small .nav-tabs a {
+  padding: $-s $-m;
 }
\ No newline at end of file
index c061f9d64253b6bf117ad018b37f22da5c5f7e47..65f05a71d5c69370707cca7b45b4510395ff8b05 100644 (file)
@@ -9,14 +9,19 @@ html {
   &.flexbox {
     overflow-y: hidden;
   }
+  &.shaded {
+    background-color: #F2F2F2;
+  }
 }
 
 body {
-  font-family: $text;
   font-size: $fs-m;
   line-height: 1.6;
   color: #616161;
   -webkit-font-smoothing: antialiased;
+  &.shaded {
+    background-color: #F2F2F2;
+  }
 }
 
 button {
index 05126892618449e35bf3f47adea7ea080c0cbde5..d30d4d4a22ab98745b5f7b1076db90297dc2fde0 100644 (file)
@@ -9,7 +9,6 @@
   .inset-list {
     display: none;
     overflow: hidden;
-    margin-bottom: $-l;
   }
   h5 {
     display: block;
@@ -22,6 +21,9 @@
       border-left-color: $color-page-draft;
     }
   }
+  .entity-list-item {
+    margin-bottom: $-m;
+  }
   hr {
     margin-top: 0;
   }
     margin-right: $-s;
   }
 }
-.chapter-toggle {
-  cursor: pointer;
-  margin: 0 0 $-l 0;
-  transition: all ease-in-out 180ms;
-  user-select: none;
-  i.zmdi-caret-right {
-    transition: all ease-in-out 180ms;
-    transform: rotate(0deg);
-    transform-origin: 25% 50%;
-  }
-  &.open {
-    margin-bottom: 0;
-  }
-  &.open i.zmdi-caret-right {
-    transform: rotate(90deg);
-  }
-}
 
 .sidebar-page-nav {
   $nav-indent: $-s;
 
 // Sidebar list
 .book-tree {
-  padding: $-xs 0 0 0;
-  position: relative;
-  right: 0;
-  top: 0;
   transition: ease-in-out 240ms;
   transition-property: right, border;
-  border-left: 0px solid #FFF;
-  background-color: #FFF;
-  max-width: 320px;
-  &.fixed {
-    background-color: #FFF;
-    z-index: 5;
-    position: fixed;
-    top: 0;
-    padding-left: $-l;
-    padding-right: $-l + 15;
-    width: 30%;
-    right: -15px;
-    height: 100%;
-    overflow-y: scroll;
-    -ms-overflow-style: none;
-    //background-color: $primary-faded;
-    border-left: 1px solid #DDD;
-    &::-webkit-scrollbar { width: 0 !important }
-  }
 }
 .book-tree h4 {
   padding: $-m $-s 0 $-s;
       background-color: rgba($color-chapter, 0.12);
     }
   }
-  .chapter-toggle {
+  [chapter-toggle] {
     padding-left: $-s;
   }
   .list-item-chapter {
   .left + .right {
     margin-left: 30px + $-s;
   }
+  &:last-of-type {
+    border-bottom: 0;
+  }
 }
 
 ul.pagination {
@@ -312,9 +277,6 @@ ul.pagination {
   h4 {
     margin: 0;
   }
-  p {
-    margin: $-xs 0 0 0;
-  }
   hr {
     margin: 0;
   }
@@ -331,15 +293,24 @@ ul.pagination {
   }
 }
 
+.card .entity-list-item, .card .activity-list-item {
+  padding-left: $-m;
+  padding-right: $-m;
+}
+
 .entity-list.compact {
   font-size: 0.6em;
   h4, a {
     line-height: 1.2;
   }
-  p {
+  .entity-item-snippet {
     display: none;
+  }
+  .entity-list-item p {
     font-size: $fs-m * 0.8;
     padding-top: $-xs;
+  }
+  p {
     margin: 0;
   }
   > p.empty-text {
@@ -381,6 +352,7 @@ ul.pagination {
   }
   li.padded {
     padding: $-xs $-m;
+    line-height: 1.2;
   }
   a {
     display: block;
index e5334c69c7d15d68fb2c43d5abf165460210143d..f73bbd546d12021ae1b3e88539dab00de0537d2e 100755 (executable)
@@ -1,12 +1,3 @@
-#page-show {
-  >.row .col-md-9 {
-    z-index: 2;
-  }
-  >.row .col-md-3 {
-    z-index: 1;
-  }
-}
-
 .page-editor {
   display: flex;
   flex-direction: column;
@@ -36,6 +27,8 @@
 
 .page-content {
   max-width: 840px;
+  margin: 0 auto;
+  margin-top: $-xxl;
   overflow-wrap: break-word;
   .align-left {
     text-align: left;
     width: 100%;
     min-width: 50px;
   }
-  .tags td {
+  .tags td, .tag-table > div > div > div {
     padding-right: $-s;
     padding-top: $-s;
     position: relative;
 }
 
 .tag-display {
-  width: 100%;
-  //opacity: 0.7;
   position: relative;
   table {
     width: 100%;
       background-color: #EEE;
     }
   }
+}
+
+.comment-editor .CodeMirror, .comment-editor .CodeMirror-scroll {
+  min-height: 175px;
 }
\ No newline at end of file
index 21553b8394d01f76392132b1fbeb3b2c269bbeed..38b044268b7c587311067eb718987d4d4fc24e60 100644 (file)
@@ -57,14 +57,4 @@ table.list-table {
     vertical-align: middle;
     padding: $-xs;
   }
-}
-
-table.file-table {
-  @extend .no-style;
-  td {
-    padding: $-xs;
-  }
-  .ui-sortable-helper {
-    display: table;
-  }
 }
\ No newline at end of file
index 2ef4bd16d80abe4e8034ae7d5e3996df22da3b3d..7191265265d2830f6f17aa6fb03421aa473976cb 100644 (file)
@@ -1,3 +1,14 @@
+/**
+ * Fonts
+ */
+
+body, button, input, select, label, textarea {
+  font-family: $text;
+}
+.Codemirror, pre, #markdown-editor-input, .editor-toolbar, .code-base {
+  font-family: $mono;
+}
+
 /*
  * Header Styles
  */
@@ -58,7 +69,6 @@ a, .link {
   cursor: pointer;
   text-decoration: none;
   transition: color ease-in-out 80ms;
-  font-family: $text;
   line-height: 1.6;
   &:hover {
     text-decoration: underline;
@@ -131,7 +141,6 @@ sub, .subscript {
 }
 
 pre {
-  font-family: monospace;
   font-size: 12px;
   background-color: #f5f5f5;
   border: 1px solid #DDD;
@@ -180,7 +189,6 @@ blockquote {
 
 .code-base {
     background-color: #F8F8F8;
-    font-family: monospace;
     font-size: 0.80em;
     border: 1px solid #DDD;
     border-radius: 3px;
@@ -370,12 +378,6 @@ span.sep {
   display: block;
 }
 
-.action-header {
-  h1 {
-    margin-top: $-m;
-  }
-}
-
 /**
   * Icons
   */
index 969bbc9689465a945252e0b0e2078ae514bc7e21..b8394b25b7e9642eef27eff7c74569b29d046809 100644 (file)
@@ -48,4 +48,7 @@
       }
     }
   }
+}
+.page-content.mce-content-body p {
+  line-height: 1.6;
 }
\ No newline at end of file
index 23bf2b219542077c6acfd9238f9d6649a8f94f59..d2b6acc9fa5074b25f8f0791cb15217372abca5c 100644 (file)
@@ -27,8 +27,12 @@ $-xs: 6px;
 $-xxs: 3px;
 
 // Fonts
-$heading:  'Roboto', 'DejaVu Sans', Helvetica,  Arial, sans-serif;
-$text: 'Roboto', 'DejaVu Sans', Helvetica,  Arial, sans-serif;
+$text: -apple-system, BlinkMacSystemFont,
+"Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell",
+"Fira Sans", "Droid Sans", "Helvetica Neue",
+sans-serif;
+$mono: "Lucida Console", "DejaVu Sans Mono", "Ubunto Mono", Monaco, monospace;
+$heading: $text;
 $fs-m: 15px;
 $fs-s: 14px;
 
@@ -55,4 +59,4 @@ $text-light: #EEE;
 // Shadows
 $bs-light: 0 0 4px 1px #CCC;
 $bs-med: 0 1px 3px 1px rgba(76, 76, 76, 0.26);
-$bs-hover: 0 2px 2px 1px rgba(0,0,0,.13);
+$bs-hover: 0 2px 2px 1px rgba(0,0,0,.13);
\ No newline at end of file
index 60450f3e2e5a1436dacc4554f41c4e363030d5d6..1f7caf1d96805f8192a978dca733a949f764e909 100644 (file)
@@ -1,4 +1,3 @@
-//@import "reset";
 @import "variables";
 @import "mixins";
 @import "html";
index afb9d531bd4a82ae8b17410083f97e1f6b0ab875..e8d87d520260dadee1693d7c7e6b5930c7f42d06 100644 (file)
@@ -1,6 +1,5 @@
 @import "reset";
 @import "variables";
-@import "fonts";
 @import "mixins";
 @import "html";
 @import "text";
 @import "lists";
 @import "pages";
 
-[v-cloak], [v-show] {
+[v-cloak] {
   display: none; opacity: 0;
   animation-name: none !important;
 }
 
-
 [ng\:cloak], [ng-cloak], .ng-cloak {
   display: none !important;
   user-select: none;
@@ -65,50 +63,11 @@ body.dragging, body.dragging * {
   }
 }
 
-// System wide notifications
-.notification {
-  position: fixed;
-  top: 0;
-  right: 0;
-  margin: $-xl*2 $-xl;
-  padding: $-l $-xl;
-  background-color: #EEE;
-  border-radius: 3px;
-  box-shadow: $bs-med;
-  z-index: 999999;
-  display: block;
-  cursor: pointer;
-  max-width: 480px;
-  i, span {
-    display: table-cell;
-  }
-  i {
-    font-size: 2em;
-    padding-right: $-l;
-  }
-  span {
-    vertical-align: middle;
-  }
-  &.pos {
-    background-color: $positive;
-    color: #EEE;
-  }
-  &.neg {
-    background-color: $negative;
-    color: #EEE;
-  }
-  &.warning {
-    background-color: $secondary;
-    color: #EEE;
-  }
-}
-
 // Loading icon
 $loadingSize: 10px;
 .loading-container {
   position: relative;
   display: block;
-  height: $loadingSize;
   margin: $-xl auto;
   > div {
     width: $loadingSize;
@@ -116,7 +75,8 @@ $loadingSize: 10px;
     border-radius: $loadingSize;
     display: inline-block;
     vertical-align: top;
-    transform: translate3d(0, 0, 0);
+    transform: translate3d(-10px, 0, 0);
+    margin-top: $-xs;
     animation-name: loadingBob;
     animation-duration: 1.4s;
     animation-iteration-count: infinite;
@@ -130,11 +90,17 @@ $loadingSize: 10px;
       background-color: $color-book;
       animation-delay: 0s;
   }
-  > div:last-child {
+  > div:last-of-type {
     left: $loadingSize+$-xs;
     background-color: $color-chapter;
     animation-delay: 0.6s;
   }
+  > span {
+    margin-left: $-s;
+    font-style: italic;
+    color: #888;
+    vertical-align: top;
+  }
 }
 
 
@@ -150,7 +116,7 @@ $loadingSize: 10px;
 
 // Back to top link
 $btt-size: 40px;
-#back-to-top {
+[back-to-top] {
   background-color: $primary;
   position: fixed;
   bottom: $-m;
@@ -256,22 +222,15 @@ $btt-size: 40px;
 }
 
 .center-box {
-  margin: $-xl auto 0 auto;
-  padding: $-m $-xxl $-xl $-xxl;
+  margin: $-xxl auto 0 auto;
   width: 420px;
   max-width: 100%;
   display: inline-block;
   text-align: left;
   vertical-align: top;
-  //border: 1px solid #DDD;
   input {
     width: 100%;
   }
-  &.login {
-    background-color: #EEE;
-    box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
-    border: 1px solid #DDD;
-  }
 }
 
 
index c2d20b3a6c6a5a6ef3a11ac2cb7e4d4e6c1a09a2..3318ea7520dd189193ec0ab061a83a835146584d 100644 (file)
@@ -8,33 +8,33 @@ return [
      */
 
     // Pages
-    'page_create'                 => 'Seite erstellt',
-    'page_create_notification'    => 'Seite erfolgreich erstellt',
-    'page_update'                 => 'Seite aktualisiert',
-    'page_update_notification'    => 'Seite erfolgreich aktualisiert',
-    'page_delete'                 => 'Seite gel&ouml;scht',
-    'page_delete_notification'    => 'Seite erfolgreich gel&ouml;scht',
-    'page_restore'                => 'Seite wiederhergstellt',
-    'page_restore_notification'   => 'Seite erfolgreich wiederhergstellt',
-    'page_move'                   => 'Seite verschoben',
+    'page_create'                 => 'hat Seite erstellt:',
+    'page_create_notification'    => 'hat Seite erfolgreich erstellt:',
+    'page_update'                 => 'hat Seite aktualisiert:',
+    'page_update_notification'    => 'hat Seite erfolgreich aktualisiert:',
+    'page_delete'                 => 'hat Seite gelöscht:',
+    'page_delete_notification'    => 'hat Seite erfolgreich gelöscht:',
+    'page_restore'                => 'hat Seite wiederhergstellt:',
+    'page_restore_notification'   => 'hat Seite erfolgreich wiederhergstellt:',
+    'page_move'                   => 'hat Seite verschoben:',
 
     // Chapters
-    'chapter_create'              => 'Kapitel erstellt',
-    'chapter_create_notification' => 'Kapitel erfolgreich erstellt',
-    'chapter_update'              => 'Kapitel aktualisiert',
-    'chapter_update_notification' => 'Kapitel erfolgreich aktualisiert',
-    'chapter_delete'              => 'Kapitel gel&ouml;scht',
-    'chapter_delete_notification' => 'Kapitel erfolgreich gel&ouml;scht',
-    'chapter_move'                => 'Kapitel verschoben',
+    'chapter_create'              => 'hat Kapitel erstellt:',
+    'chapter_create_notification' => 'hat Kapitel erfolgreich erstellt:',
+    'chapter_update'              => 'hat Kapitel aktualisiert:',
+    'chapter_update_notification' => 'hat Kapitel erfolgreich aktualisiert:',
+    'chapter_delete'              => 'hat Kapitel gelöscht',
+    'chapter_delete_notification' => 'hat Kapitel erfolgreich gelöscht:',
+    'chapter_move'                => 'hat Kapitel verschoben:',
 
     // Books
-    'book_create'                 => 'Buch erstellt',
-    'book_create_notification'    => 'Buch erfolgreich erstellt',
-    'book_update'                 => 'Buch aktualisiert',
-    'book_update_notification'    => 'Buch erfolgreich aktualisiert',
-    'book_delete'                 => 'Buch gel&ouml;scht',
-    'book_delete_notification'    => 'Buch erfolgreich gel&ouml;scht',
-    'book_sort'                   => 'Buch sortiert',
-    'book_sort_notification'      => 'Buch erfolgreich neu sortiert',
+    'book_create'                 => 'hat Buch erstellt:',
+    'book_create_notification'    => 'hat Buch erfolgreich erstellt:',
+    'book_update'                 => 'hat Buch aktualisiert:',
+    'book_update_notification'    => 'hat Buch erfolgreich aktualisiert:',
+    'book_delete'                 => 'hat Buch gelöscht:',
+    'book_delete_notification'    => 'hat Buch erfolgreich gelöscht:',
+    'book_sort'                   => 'hat Buch sortiert:',
+    'book_sort_notification'      => 'hat Buch erfolgreich neu sortiert:',
 
 ];
index f253cdfa144977cb2594477d085e81eb658bfca3..8f4afe654f99fa79aef4a69913ee8cb3146c7dad 100644 (file)
@@ -10,8 +10,8 @@ return [
     | these language lines according to your application's requirements.
     |
     */
-    'failed' => 'Dies sind keine g&uuml;ltigen Anmeldedaten.',
-    'throttle' => 'Zu viele Anmeldeversuche. Bitte versuchen sie es in :seconds Sekunden erneut.',
+    'failed' => 'Die eingegebenen Anmeldedaten sind ungültig.',
+    'throttle' => 'Zu viele Anmeldeversuche. Bitte versuchen Sie es in :seconds Sekunden erneut.',
 
     /**
      * Login & Register
@@ -29,16 +29,16 @@ return [
     'forgot_password' => 'Passwort vergessen?',
     'remember_me' => 'Angemeldet bleiben',
     'ldap_email_hint' => 'Bitte geben Sie eine E-Mail-Adresse ein, um diese mit dem Account zu nutzen.',
-    'create_account' => 'Account anlegen',
-    'social_login' => 'Social Login',
-    'social_registration' => 'Social Registrierung',
-    'social_registration_text' => 'Mit einem dieser Möglichkeiten registrieren oder anmelden.',
+    'create_account' => 'Account registrieren',
+    'social_login' => 'Mit Sozialem Netzwerk anmelden',
+    'social_registration' => 'Mit Sozialem Netzwerk registrieren',
+    'social_registration_text' => 'Mit einer dieser Dienste registrieren oder anmelden',
 
 
     'register_thanks' => 'Vielen Dank für Ihre Registrierung!',
-    'register_confirm' => 'Bitte prüfen Sie Ihren E-Mail Eingang und klicken auf den Verifizieren-Button, um :appName nutzen zu können.',
-    'registrations_disabled' => 'Die Registrierung ist momentan nicht möglich',
-    'registration_email_domain_invalid' => 'Diese E-Mail-Domain ist für die Benutzer der Applikation nicht freigeschaltet.',
+    'register_confirm' => 'Bitte prüfen Sie Ihren Posteingang und bestätigen Sie die Registrierung.',
+    'registrations_disabled' => 'Eine Registrierung ist momentan nicht möglich',
+    'registration_email_domain_invalid' => 'Sie können sich mit dieser E-Mail nicht registrieren.',
     'register_success' => 'Vielen Dank für Ihre Registrierung! Die Daten sind gespeichert und Sie sind angemeldet.',
 
 
@@ -46,30 +46,30 @@ return [
      * Password Reset
      */
     'reset_password' => 'Passwort vergessen',
-    'reset_password_send_instructions' => 'Bitte geben Sie unten Ihre E-Mail-Adresse ein und Sie erhalten eine E-Mail, um Ihr Passwort zurück zu setzen.',
+    'reset_password_send_instructions' => 'Bitte geben Sie Ihre E-Mail-Adresse ein. Danach erhalten Sie eine E-Mail mit einem Link zum Zurücksetzen Ihres Passwortes.',
     'reset_password_send_button' => 'Passwort zurücksetzen',
-    'reset_password_sent_success' => 'Eine E-Mail mit den Instruktionen, um Ihr Passwort zurückzusetzen wurde an :email gesendet.',
-    'reset_password_success' => 'Ihr Passwort wurde erfolgreich zurück gesetzt.',
+    'reset_password_sent_success' => 'Eine E-Mail mit dem Link zum Zurücksetzen Ihres Passwortes wurde an :email gesendet.',
+    'reset_password_success' => 'Ihr Passwort wurde erfolgreich zurückgesetzt.',
 
     'email_reset_subject' => 'Passwort zurücksetzen für :appName',
-    'email_reset_text' => 'Sie erhalten diese E-Mail, weil eine Passwort-Rücksetzung für Ihren Account beantragt wurde.',
-    'email_reset_not_requested' => 'Wenn Sie die Passwort-Rücksetzung nicht ausgelöst haben, ist kein weiteres Handeln notwendig.',
+    'email_reset_text' => 'Sie erhalten diese E-Mail, weil jemand versucht hat, Ihr Passwort zurückzusetzen.',
+    'email_reset_not_requested' => 'Wenn Sie das nicht waren, brauchen Sie nichts weiter zu tun.',
 
 
     /**
      * Email Confirmation
      */
-    'email_confirm_subject' => 'Best&auml;tigen sie ihre E-Mail Adresse bei :appName',
-    'email_confirm_greeting' => 'Danke, dass sie :appName beigetreten sind!',
-    'email_confirm_text' => 'Bitte best&auml;tigen sie ihre E-Mail Adresse, indem sie auf den Button klicken:',
-    'email_confirm_action' => 'E-Mail Adresse best&auml;tigen',
-    'email_confirm_send_error' => 'Best&auml;tigungs-E-Mail ben&ouml;tigt, aber das System konnte die E-Mail nicht versenden. Kontaktieren sie den Administrator, um sicherzustellen, dass das Sytsem korrekt eingerichtet ist.',
-    'email_confirm_success' => 'Ihre E-Mail Adresse wurde best&auml;tigt!',
-    'email_confirm_resent' => 'Best&auml;tigungs-E-Mail wurde erneut versendet, bitte &uuml;berpr&uuml;fen sie ihren Posteingang.',
+    'email_confirm_subject' => 'Bestätigen Sie Ihre E-Mail-Adresse für :appName',
+    'email_confirm_greeting' => 'Danke, dass Sie sich für :appName registriert haben!',
+    'email_confirm_text' => 'Bitte bestätigen Sie Ihre E-Mail-Adresse, indem Sie auf die Schaltfläche klicken:',
+    'email_confirm_action' => 'E-Mail-Adresse bestätigen',
+    'email_confirm_send_error' => 'Leider konnte die für die Registrierung notwendige E-Mail zur bestätigung Ihrer E-Mail-Adresse nicht versandt werden. Bitte kontaktieren Sie den Systemadministrator!',
+    'email_confirm_success' => 'Ihre E-Mail-Adresse wurde best&auml;tigt!',
+    'email_confirm_resent' => 'Bestätigungs-E-Mail wurde erneut versendet, bitte überprüfen Sie Ihren Posteingang.',
 
     'email_not_confirmed' => 'E-Mail-Adresse ist nicht bestätigt',
     'email_not_confirmed_text' => 'Ihre E-Mail-Adresse ist bisher nicht bestätigt.',
     'email_not_confirmed_click_link' => 'Bitte klicken Sie auf den Link in der E-Mail, die Sie nach der Registrierung erhalten haben.',
     'email_not_confirmed_resend' => 'Wenn Sie die E-Mail nicht erhalten haben, können Sie die Nachricht erneut anfordern. Füllen Sie hierzu bitte das folgende Formular aus:',
-    'email_not_confirmed_resend_button' => 'Bestätigungs E-Mail erneut senden',
+    'email_not_confirmed_resend_button' => 'Bestätigungs-E-Mail erneut senden',
 ];
index 7ad1743a0119b114a07c0de538b90e99a8ba0bbe..3c21a9d0802bab681efb927e572df83f6f9ae77c 100644 (file)
@@ -28,9 +28,9 @@ return [
     'edit' => 'Bearbeiten',
     'sort' => 'Sortieren',
     'move' => 'Verschieben',
-    'delete' => 'L&ouml;schen',
+    'delete' => 'Löschen',
     'search' => 'Suchen',
-    'search_clear' => 'Suche l&ouml;schen',
+    'search_clear' => 'Suche löschen',
     'reset' => 'Zurücksetzen',
     'remove' => 'Entfernen',
 
@@ -38,9 +38,9 @@ return [
     /**
      * Misc
      */
-    'deleted_user' => 'Gel&ouml;schte Benutzer',
-    'no_activity' => 'Keine Aktivit&auml;ten zum Anzeigen',
-    'no_items' => 'Keine Eintr&auml;ge gefunden.',
+    'deleted_user' => 'Gelöschte Benutzer',
+    'no_activity' => 'Keine Aktivitäten zum Anzeigen',
+    'no_items' => 'Keine Einträge gefunden.',
     'back_to_top' => 'nach oben',
     'toggle_details' => 'Details zeigen/verstecken',
 
@@ -53,6 +53,6 @@ return [
     /**
      * Email Content
      */
-    'email_action_help' => 'Sollte es beim Anklicken des ":actionText" Buttons Probleme geben, kopieren Sie folgende URL und fügen diese in Ihrem Webbrowser ein:',
+    'email_action_help' => 'Sollte es beim Anklicken der Schaltfläche ":action_text" Probleme geben, öffnen Sie folgende URL in Ihrem Browser:',
     'email_rights' => 'Alle Rechte vorbehalten',
-];
\ No newline at end of file
+];
index a8538c46555a8e4d858b92314b6062c69ab93a27..26bf3e626a4ef54159e43c23b98944aa99cf7a6e 100644 (file)
@@ -13,12 +13,12 @@ return [
     'image_uploaded' => 'Hochgeladen am :uploadedDate',
     'image_load_more' => 'Mehr',
     'image_image_name' => 'Bildname',
-    'image_delete_confirm' => 'Dieses Bild wird auf den folgenden Seiten benutzt. Bitte klicken Sie erneut auf löschen, wenn Sie dieses Bild tatsächlich entfernen möchten.',
+    'image_delete_confirm' => 'Dieses Bild wird auf den folgenden Seiten benutzt. Bitte klicken Sie erneut auf löschen, wenn Sie dieses Bild wirklich entfernen möchten.',
     'image_select_image' => 'Bild auswählen',
-    'image_dropzone' => 'Ziehen Sie Bilder hier hinein oder klicken Sie hier, um ein Bild auszuwählen',
+    'image_dropzone' => 'Ziehen Sie Bilder hierher oder klicken Sie, um ein Bild auszuwählen',
     'images_deleted' => 'Bilder gelöscht',
     'image_preview' => 'Bildvorschau',
     'image_upload_success' => 'Bild erfolgreich hochgeladen',
     'image_update_success' => 'Bilddetails erfolgreich aktualisiert',
     'image_delete_success' => 'Bild erfolgreich gelöscht'
-];
\ No newline at end of file
+];
index c9feb8497920b9e7c3ef656745c0335f5ef49073..b75c647bcb41612db90740871aff77bcebec0cf5 100644 (file)
@@ -4,38 +4,38 @@ return [
     /**
      * Shared
      */
-    'recently_created' => 'K&uuml;rzlich angelegt',
-    'recently_created_pages' => 'K&uuml;rzlich angelegte Seiten',
-    'recently_updated_pages' => 'K&uuml;rzlich aktualisierte Seiten',
-    'recently_created_chapters' => 'K&uuml;rzlich angelegte Kapitel',
-    'recently_created_books' => 'K&uuml;rzlich angelegte B&uuml;cher',
-    'recently_update' => 'K&uuml;rzlich aktualisiert',
-    'recently_viewed' => 'K&uuml;rzlich angesehen',
-    'recent_activity' => 'K&uuml;rzliche Aktivit&auml;t',
+    'recently_created' => 'Kürzlich angelegt',
+    'recently_created_pages' => 'Kürzlich angelegte Seiten',
+    'recently_updated_pages' => 'Kürzlich aktualisierte Seiten',
+    'recently_created_chapters' => 'Kürzlich angelegte Kapitel',
+    'recently_created_books' => 'Kürzlich angelegte Bücher',
+    'recently_update' => 'Kürzlich aktualisiert',
+    'recently_viewed' => 'Kürzlich angesehen',
+    'recent_activity' => 'Kürzliche Aktivität',
     'create_now' => 'Jetzt anlegen',
-    'revisions' => 'Revisionen',
-    'meta_created' => 'Angelegt am :timeLength',
-    'meta_created_name' => 'Angelegt am :timeLength durch :user',
-    'meta_updated' => 'Aktualisiert am :timeLength',
-    'meta_updated_name' => 'Aktualisiert am :timeLength durch :user',
-    'x_pages' => ':count Seiten',
-    'entity_select' => 'Eintrag ausw&auml;hlen',
+    'revisions' => 'Versionen',
+    'meta_revision' => 'Version #:revisionCount',
+    'meta_created' => 'Erstellt: :timeLength',
+    'meta_created_name' => 'Erstellt: :timeLength von :user',
+    'meta_updated' => 'Zuletzt aktualisiert: :timeLength',
+    'meta_updated_name' => 'Zuletzt aktualisiert: :timeLength von :user',
+    'entity_select' => 'Eintrag auswählen',
     'images' => 'Bilder',
-    'my_recent_drafts' => 'Meine k&uuml;rzlichen Entw&uuml;rfe',
-    'my_recently_viewed' => 'K&uuml;rzlich von mir angesehen',
+    'my_recent_drafts' => 'Meine kürzlichen Entwürfe',
+    'my_recently_viewed' => 'Kürzlich von mir angesehen',
     'no_pages_viewed' => 'Sie haben bisher keine Seiten angesehen.',
     'no_pages_recently_created' => 'Sie haben bisher keine Seiten angelegt.',
     'no_pages_recently_updated' => 'Sie haben bisher keine Seiten aktualisiert.',
     'export' => 'Exportieren',
     'export_html' => 'HTML-Datei',
     'export_pdf' => 'PDF-Datei',
-    'export_text' => 'Text-Datei',
+    'export_text' => 'Textdatei',
 
     /**
      * Permissions and restrictions
      */
     'permissions' => 'Berechtigungen',
-    'permissions_intro' => 'Wenn individuelle Berechtigungen aktiviert werden, &uuml;berschreiben diese Einstellungen durch Rollen zugewiesene Berechtigungen.',
+    'permissions_intro' => 'Wenn individuelle Berechtigungen aktiviert werden, überschreiben diese Einstellungen durch Rollen zugewiesene Berechtigungen.',
     'permissions_enable' => 'Individuelle Berechtigungen aktivieren',
     'permissions_save' => 'Berechtigungen speichern',
 
@@ -43,41 +43,61 @@ return [
      * Search
      */
     'search_results' => 'Suchergebnisse',
-    'search_clear' => 'Suche zur&uuml;cksetzen',
-    'search_no_pages' => 'Es wurden keine passenden Suchergebnisse gefunden',
-    'search_for_term' => 'Suche nach :term',
+    'search_total_results_found' => ':count Ergebnis gefunden|:count Ergebnisse gesamt',
+    'search_clear' => 'Filter löschen',
+    'search_no_pages' => 'Keine Seiten gefunden',
+    'search_for_term' => 'Nach :term suchen',
+    'search_more' => 'Mehr Ergebnisse',
+    'search_filters' => 'Filter',
+    'search_content_type' => 'Inhaltstyp',
+    'search_exact_matches' => 'Exakte Treffer',
+    'search_tags' => 'Nach Schlagwort suchen',
+    'search_viewed_by_me' => 'Schon von mir angesehen',
+    'search_not_viewed_by_me' => 'Noch nicht von mir angesehen',
+    'search_permissions_set' => 'Berechtigungen gesetzt',
+    'search_created_by_me' => 'Von mir erstellt',
+    'search_updated_by_me' => 'Von mir aktualisiert',
+    'search_updated_before' => 'Aktualisiert vor',
+    'search_updated_after' => 'Aktualisiert nach',
+    'search_created_before' => 'Erstellt vor',
+    'search_created_after' => 'Erstellt nach',
+    'search_set_date' => 'Datum auswählen',
+    'search_update' => 'Suche aktualisieren',
 
     /**
      * Books
      */
     'book' => 'Buch',
-    'books' => 'B&uuml;cher',
-    'books_empty' => 'Es wurden keine B&uuml;cher angelegt',
-    'books_popular' => 'Popul&auml;re B&uuml;cher',
-    'books_recent' => 'K&uuml;rzlich genutzte B&uuml;cher',
-    'books_popular_empty' => 'Die popul&auml;rsten B&uuml;cher werden hier angezeigt.',
-    'books_create' => 'Neues Buch anlegen',
-    'books_delete' => 'Buch l&ouml;schen',
-    'books_delete_named' => 'Buch :bookName l&ouml;schen',
-    'books_delete_explain' => 'Sie m&ouml;chten das Buch \':bookName\' l&ouml;schen und alle Seiten und Kapitel entfernen.',
-    'books_delete_confirmation' => 'Sind Sie sicher, dass Sie dieses Buch l&ouml;schen m&ouml;chten?',
+    'books' => 'Bücher',
+    'x_books' => ':count Buch|:count Bücher',
+    'books_empty' => 'Keine Bücher vorhanden',
+    'books_popular' => 'Beliebte Bücher',
+    'books_recent' => 'Kürzlich angesehene Bücher',
+    'books_new' => 'Neue Bücher',
+    'books_popular_empty' => 'Die beliebtesten Bücher werden hier angezeigt.',
+    'books_new_empty' => 'Die neusten Bücher werden hier angezeigt.',
+    'books_create' => 'Neues Buch erstellen',
+    'books_delete' => 'Buch löschen',
+    'books_delete_named' => 'Buch ":bookName" löschen',
+    'books_delete_explain' => 'Das Buch ":bookName" wird gelöscht und alle zugehörigen Kapitel und Seiten entfernt.',
+    'books_delete_confirmation' => 'Sind Sie sicher, dass Sie dieses Buch löschen möchten?',
     'books_edit' => 'Buch bearbeiten',
-    'books_edit_named' => 'Buch :bookName bearbeiten',
-    'books_form_book_name' => 'Buchname',
+    'books_edit_named' => 'Buch ":bookName" bearbeiten',
+    'books_form_book_name' => 'Name des Buches',
     'books_save' => 'Buch speichern',
     'books_permissions' => 'Buch-Berechtigungen',
     'books_permissions_updated' => 'Buch-Berechtigungen aktualisiert',
-    'books_empty_contents' => 'Es sind noch keine Seiten oder Kapitel f&uuml;r dieses Buch angelegt.',
+    'books_empty_contents' => 'Es sind noch keine Seiten oder Kapitel zu diesem Buch hinzugefügt worden.',
     'books_empty_create_page' => 'Neue Seite anlegen',
     'books_empty_or' => 'oder',
     'books_empty_sort_current_book' => 'Aktuelles Buch sortieren',
-    'books_empty_add_chapter' => 'Neues Kapitel hinzuf&uuml;gen',
+    'books_empty_add_chapter' => 'Neues Kapitel hinzufügen',
     'books_permissions_active' => 'Buch-Berechtigungen aktiv',
     'books_search_this' => 'Dieses Buch durchsuchen',
-    'books_navigation' => 'Buch-Navigation',
+    'books_navigation' => 'Buchnavigation',
     'books_sort' => 'Buchinhalte sortieren',
-    'books_sort_named' => 'Buch :bookName sortieren',
-    'books_sort_show_other' => 'Andere B&uuml;cher zeigen',
+    'books_sort_named' => 'Buch ":bookName" sortieren',
+    'books_sort_show_other' => 'Andere Bücher anzeigen',
     'books_sort_save' => 'Neue Reihenfolge speichern',
 
     /**
@@ -85,132 +105,156 @@ return [
      */
     'chapter' => 'Kapitel',
     'chapters' => 'Kapitel',
-    'chapters_popular' => 'Popul&auml;re Kapitel',
+    'x_chapters' => ':count Kapitel',
+    'chapters_popular' => 'Beliebte Kapitel',
     'chapters_new' => 'Neues Kapitel',
     'chapters_create' => 'Neues Kapitel anlegen',
     'chapters_delete' => 'Kapitel entfernen',
-    'chapters_delete_named' => 'Kapitel :chapterName entfernen',
-    'chapters_delete_explain' => 'Sie m&ouml;chten das Kapitel \':chapterName\' l&ouml;schen und alle Seiten dem direkten Eltern-Buch hinzugef&uuml;gen.',
-    'chapters_delete_confirm' => 'Sind Sie sicher, dass Sie dieses Kapitel l&ouml;schen m&ouml;chten?',
+    'chapters_delete_named' => 'Kapitel ":chapterName" entfernen',
+    'chapters_delete_explain' => 'Das Kapitel ":chapterName" wird gelöscht und alle zugehörigen Seiten dem übergeordneten Buch zugeordnet.',
+    'chapters_delete_confirm' => 'Sind Sie sicher, dass Sie dieses Kapitel löschen möchten?',
     'chapters_edit' => 'Kapitel bearbeiten',
-    'chapters_edit_named' => 'Kapitel :chapterName bearbeiten',
+    'chapters_edit_named' => 'Kapitel ":chapterName" bearbeiten',
     'chapters_save' => 'Kapitel speichern',
     'chapters_move' => 'Kapitel verschieben',
-    'chapters_move_named' => 'Kapitel :chapterName verschieben',
-    'chapter_move_success' => 'Kapitel in das Buch :bookName verschoben.',
+    'chapters_move_named' => 'Kapitel ":chapterName" verschieben',
+    'chapter_move_success' => 'Das Kapitel wurde in das Buch ":bookName" verschoben.',
     'chapters_permissions' => 'Kapitel-Berechtigungen',
-    'chapters_empty' => 'Aktuell sind keine Kapitel in diesem Buch angelegt.',
+    'chapters_empty' => 'Aktuell sind keine Kapitel diesem Buch hinzugefügt worden.',
     'chapters_permissions_active' => 'Kapitel-Berechtigungen aktiv',
     'chapters_permissions_success' => 'Kapitel-Berechtigungenen aktualisisert',
+    'chapters_search_this' => 'Dieses Kapitel durchsuchen',
 
     /**
      * Pages
      */
     'page' => 'Seite',
     'pages' => 'Seiten',
-    'pages_popular' => 'Popul&auml;re Seiten',
+    'x_pages' => ':count Seite|:count Seiten',
+    'pages_popular' => 'Beliebte Seiten',
     'pages_new' => 'Neue Seite',
-    'pages_attachments' => 'Anh&auml;nge',
+    'pages_attachments' => 'Anhänge',
     'pages_navigation' => 'Seitennavigation',
-    'pages_delete' => 'Seite l&ouml;schen',
-    'pages_delete_named' => 'Seite :pageName l&ouml;schen',
-    'pages_delete_draft_named' => 'Seitenentwurf von :pageName l&ouml;schen',
-    'pages_delete_draft' => 'Seitenentwurf l&ouml;schen',
-    'pages_delete_success' => 'Seite gel&ouml;scht',
-    'pages_delete_draft_success' => 'Seitenentwurf gel&ouml;scht',
-    'pages_delete_confirm' => 'Sind Sie sicher, dass Sie diese Seite l&ouml;schen m&ouml;chen?',
-    'pages_delete_draft_confirm' => 'Sind Sie sicher, dass Sie diesen Seitenentwurf l&ouml;schen m&ouml;chten?',
-    'pages_editing_named' => 'Seite :pageName bearbeiten',
-    'pages_edit_toggle_header' => 'Toggle header',
+    'pages_delete' => 'Seite löschen',
+    'pages_delete_named' => 'Seite ":pageName" löschen',
+    'pages_delete_draft_named' => 'Seitenentwurf von ":pageName" löschen',
+    'pages_delete_draft' => 'Seitenentwurf löschen',
+    'pages_delete_success' => 'Seite gelöscht',
+    'pages_delete_draft_success' => 'Seitenentwurf gelöscht',
+    'pages_delete_confirm' => 'Sind Sie sicher, dass Sie diese Seite löschen möchen?',
+    'pages_delete_draft_confirm' => 'Sind Sie sicher, dass Sie diesen Seitenentwurf löschen möchten?',
+    'pages_editing_named' => 'Seite ":pageName" bearbeiten',
+    'pages_edit_toggle_header' => 'Hauptmenü anzeigen/verstecken',
     'pages_edit_save_draft' => 'Entwurf speichern',
     'pages_edit_draft' => 'Seitenentwurf bearbeiten',
     'pages_editing_draft' => 'Seitenentwurf bearbeiten',
     'pages_editing_page' => 'Seite bearbeiten',
     'pages_edit_draft_save_at' => 'Entwurf gespeichert um ',
-    'pages_edit_delete_draft' => 'Entwurf l&ouml;schen',
+    'pages_edit_delete_draft' => 'Entwurf löschen',
     'pages_edit_discard_draft' => 'Entwurf verwerfen',
-    'pages_edit_set_changelog' => 'Ver&auml;nderungshinweis setzen',
-    'pages_edit_enter_changelog_desc' => 'Bitte geben Sie eine kurze Zusammenfassung Ihrer &Auml;nderungen ein',
-    'pages_edit_enter_changelog' => 'Ver&auml;nderungshinweis eingeben',
+    'pages_edit_set_changelog' => 'Änderungsprotokoll hinzufügen',
+    'pages_edit_enter_changelog_desc' => 'Bitte geben Sie eine kurze Zusammenfassung Ihrer Änderungen ein',
+    'pages_edit_enter_changelog' => 'Änderungsprotokoll eingeben',
     'pages_save' => 'Seite speichern',
     'pages_title' => 'Seitentitel',
     'pages_name' => 'Seitenname',
     'pages_md_editor' => 'Redakteur',
     'pages_md_preview' => 'Vorschau',
-    'pages_md_insert_image' => 'Bild einf&uuml;gen',
-    'pages_md_insert_link' => 'Link zu einem Objekt einf&uuml;gen',
+    'pages_md_insert_image' => 'Bild einfügen',
+    'pages_md_insert_link' => 'Link zu einem Objekt einfügen',
     'pages_not_in_chapter' => 'Seite ist in keinem Kapitel',
     'pages_move' => 'Seite verschieben',
     'pages_move_success' => 'Seite nach ":parentName" verschoben',
     'pages_permissions' => 'Seiten Berechtigungen',
     'pages_permissions_success' => 'Seiten Berechtigungen aktualisiert',
+    'pages_revision' => 'Version',
     'pages_revisions' => 'Seitenversionen',
-    'pages_revisions_named' => 'Seitenversionen von :pageName',
-    'pages_revision_named' => 'Seitenversion von :pageName',
-    'pages_revisions_created_by' => 'Angelegt von',
+    'pages_revisions_named' => 'Seitenversionen von ":pageName"',
+    'pages_revision_named' => 'Seitenversion von ":pageName"',
+    'pages_revisions_created_by' => 'Erstellt von',
     'pages_revisions_date' => 'Versionsdatum',
-    'pages_revisions_changelog' => 'Ver&auml;nderungshinweise',
-    'pages_revisions_changes' => 'Ver&auml;nderungen',
+    'pages_revisions_number' => '#',
+    'pages_revisions_changelog' => 'Änderungsprotokoll',
+    'pages_revisions_changes' => 'Änderungen',
     'pages_revisions_current' => 'Aktuelle Version',
     'pages_revisions_preview' => 'Vorschau',
-    'pages_revisions_restore' => 'Zur&uuml;ck sichern',
-    'pages_revisions_none' => 'Diese Seite hat keine &auml;lteren Versionen.',
+    'pages_revisions_restore' => 'Wiederherstellen',
+    'pages_revisions_none' => 'Diese Seite hat keine älteren Versionen.',
     'pages_copy_link' => 'Link kopieren',
     'pages_permissions_active' => 'Seiten-Berechtigungen aktiv',
-    'pages_initial_revision' => 'Erste Ver&ouml;ffentlichung',
+    'pages_initial_revision' => 'Erste Veröffentlichung',
     'pages_initial_name' => 'Neue Seite',
-    'pages_editing_draft_notification' => 'Sie bearbeiten momenten einen Entwurf, der zuletzt um :timeDiff gespeichert wurde.',
-    'pages_draft_edited_notification' => 'Diese Seite wurde seit diesem Zeitpunkt ver&auml;ndert. Wir empfehlen Ihnen, diesen Entwurf zu verwerfen.',
+    'pages_editing_draft_notification' => 'Sie bearbeiten momenten einen Entwurf, der zuletzt :timeDiff gespeichert wurde.',
+    'pages_draft_edited_notification' => 'Diese Seite wurde seit diesem Zeitpunkt verändert. Wir empfehlen Ihnen, diesen Entwurf zu verwerfen.',
     'pages_draft_edit_active' => [
-        'start_a' => ':count Benutzer haben die Bearbeitung dieser Seite begonnen.',
-        'start_b' => ':userName hat die Bearbeitung dieser Seite begonnen.',
+        'start_a' => ':count Benutzer bearbeiten derzeit diese Seite.',
+        'start_b' => ':userName bearbeitet jetzt diese Seite.',
         'time_a' => 'seit die Seiten zuletzt aktualisiert wurden.',
         'time_b' => 'in den letzten :minCount Minuten',
-        'message' => ':start :time. Achten Sie darauf keine Aktualisierungen von anderen Benutzern zu &uuml;berschreiben!',
+        'message' => ':start :time. Achten Sie darauf, keine Änderungen von anderen Benutzern zu überschreiben!',
     ],
     'pages_draft_discarded' => 'Entwurf verworfen. Der aktuelle Seiteninhalt wurde geladen.',
 
     /**
      * Editor sidebar
      */
-    'page_tags' => 'Seiten-Schlagw&ouml;rter',
+    'page_tags' => 'Seiten-Schlagwörter',
     'tag' => 'Schlagwort',
-    'tags' =>  'Schlagworte',
-    'tag_value' => 'Schlagwortinhalt (Optional)',
-    'tags_explain' => "F&uuml;gen Sie Schlagworte hinzu, um Ihren Inhalt zu kategorisieren. \n Sie k&ouml;nnen einen erkl&auml;renden Inhalt hinzuf&uuml;gen, um eine genauere Unterteilung vorzunehmen.",
-    'tags_add' => 'Weiteres Schlagwort hinzuf&uuml;gen',
-    'attachments' => 'Anh&auml;nge',
-    'attachments_explain' => 'Sie k&ouml;nnen auf Ihrer Seite Dateien hochladen oder Links anf&uuml;gen. Diese werden in der seitlich angezeigt.',
-    'attachments_explain_instant_save' => '&Auml;nderungen werden direkt gespeichert.',
-    'attachments_items' => 'Angef&uuml;gte Elemente',
+    'tags' =>  'Schlagwörter',
+    'tag_value' => 'Inhalt (Optional)',
+    'tags_explain' => "Fügen Sie Schlagwörter hinzu, um Ihren Inhalt zu kategorisieren.\nSie können einen erklärenden Inhalt hinzufügen, um eine genauere Unterteilung vorzunehmen.",
+    'tags_add' => 'Weiteres Schlagwort hinzufügen',
+    'attachments' => 'Anhänge',
+    'attachments_explain' => 'Sie können auf Ihrer Seite Dateien hochladen oder Links hinzufügen. Diese werden in der Seitenleiste angezeigt.',
+    'attachments_explain_instant_save' => 'Änderungen werden direkt gespeichert.',
+    'attachments_items' => 'Angefügte Elemente',
     'attachments_upload' => 'Datei hochladen',
-    'attachments_link' => 'Link anf&uuml;gen',
+    'attachments_link' => 'Link hinzufügen',
     'attachments_set_link' => 'Link setzen',
-    'attachments_delete_confirm' => 'Klicken Sie erneut auf l&ouml;schen, um diesen Anhang zu entfernen.',
-    'attachments_dropzone' => 'Ziehen Sie Dateien hier hinein oder klicken Sie hier, um eine Datei auszuw&auml;hlen',
+    'attachments_delete_confirm' => 'Klicken Sie erneut auf löschen, um diesen Anhang zu entfernen.',
+    'attachments_dropzone' => 'Ziehen Sie Dateien hierher oder klicken Sie, um eine Datei auszuwählen',
     'attachments_no_files' => 'Es wurden bisher keine Dateien hochgeladen.',
-    'attachments_explain_link' => 'Wenn Sie keine Datei hochladen m&ouml;chten, k&ouml;nnen Sie stattdessen einen Link anf&uuml;gen. Dieser Link kann auf eine andere Seite oder zu einer Datei in der Cloud weisen.',
+    'attachments_explain_link' => 'Wenn Sie keine Datei hochladen möchten, können Sie stattdessen einen Link hinzufügen. Dieser Link kann auf eine andere Seite oder eine Datei im Internet weisen.',
     'attachments_link_name' => 'Link-Name',
     'attachment_link' => 'Link zum Anhang',
     'attachments_link_url' => 'Link zu einer Datei',
     'attachments_link_url_hint' => 'URL einer Seite oder Datei',
-    'attach' => 'anf&uuml;gen',
+    'attach' => 'Hinzufügen',
     'attachments_edit_file' => 'Datei bearbeiten',
     'attachments_edit_file_name' => 'Dateiname',
-    'attachments_edit_drop_upload' => 'Ziehen Sie Dateien hier hinein, um diese hochzuladen und zu &uuml;berschreiben',
-    'attachments_order_updated' => 'Reihenfolge der Anh&auml;nge aktualisiert',
-    'attachments_updated_success' => 'Anhang-Details aktualisiert',
-    'attachments_deleted' => 'Anhang gel&ouml;scht',
-    'attachments_file_uploaded' => 'Datei erfolgrecich hochgeladen',
-    'attachments_file_updated' => 'Datei erfolgreich aktualisisert',
-    'attachments_link_attached' => 'Link erfolgreich der Seite hinzugef&uuml;gt',
+    'attachments_edit_drop_upload' => 'Ziehen Sie Dateien hierher, um diese hochzuladen und zu überschreiben',
+    'attachments_order_updated' => 'Reihenfolge der Anhänge aktualisiert',
+    'attachments_updated_success' => 'Anhangdetails aktualisiert',
+    'attachments_deleted' => 'Anhang gelöscht',
+    'attachments_file_uploaded' => 'Datei erfolgreich hochgeladen',
+    'attachments_file_updated' => 'Datei erfolgreich aktualisiert',
+    'attachments_link_attached' => 'Link erfolgreich der Seite hinzugefügt',
 
     /**
      * Profile View
      */
     'profile_user_for_x' => 'Benutzer seit :time',
-    'profile_created_content' => 'Angelegte Inhalte',
-    'profile_not_created_pages' => ':userName hat bisher keine Seiten angelegt.',
-    'profile_not_created_chapters' => ':userName hat bisher keine Kapitel angelegt.',
-    'profile_not_created_books' => ':userName hat bisher keine B&uuml;cher angelegt.',
-];
\ No newline at end of file
+    'profile_created_content' => 'Erstellte Inhalte',
+    'profile_not_created_pages' => ':userName hat noch keine Seiten erstellt.',
+    'profile_not_created_chapters' => ':userName hat noch keine Kapitel erstellt.',
+    'profile_not_created_books' => ':userName hat noch keine Bücher erstellt.',
+
+    /**
+     * Comments
+     */
+    'comment' => 'Kommentar',
+    'comments' => 'Kommentare',
+    'comment_placeholder' => 'Geben Sie hier Ihre Kommentare ein (Markdown unterstützt)',
+    'comment_count' => '{0} Keine Kommentare|{1} 1 Kommentar|[2,*] :count Kommentare',
+    'comment_save' => 'Kommentar speichern',
+    'comment_saving' => 'Kommentar wird gespeichert...',
+    'comment_deleting' => 'Kommentar wird gelöscht...',
+    'comment_new' => 'Neuer Kommentar',
+    'comment_created' => ':createDiff kommentiert',
+    'comment_updated' => ':updateDiff aktualisiert von :username',
+    'comment_deleted_success' => 'Kommentar gelöscht',
+    'comment_created_success' => 'Kommentar hinzugefügt',
+    'comment_updated_success' => 'Kommentar aktualisiert',
+    'comment_delete_confirm' => 'Möchten Sie diesen Kommentar wirklich löschen?',
+    'comment_in_reply_to' => 'Antwort auf :commentId',
+];
index e085d9915cb5361b539cd61743e1a5a6566cb1de..0b961f8ee838631a447ac13b2b932dc79c061fe7 100644 (file)
@@ -7,37 +7,37 @@ return [
      */
 
     // Pages
-    'permission' => 'Sie haben keine Berechtigung auf diese Seite zuzugreifen.',
-    'permissionJson' => 'Sie haben keine Berechtigung die angeforderte Aktion auszuf&uuml;hren.',
+    'permission' => 'Sie haben keine Berechtigung, auf diese Seite zuzugreifen.',
+    'permissionJson' => 'Sie haben keine Berechtigung, die angeforderte Aktion auszuführen.',
 
     // Auth
-    'error_user_exists_different_creds' => 'Ein Benutzer mit der E-Mail-Adresse :email ist bereits mit anderen Anmeldedaten angelegt.',
-    'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits best&auml;tigt. Bitte melden Sie sich an.',
-    'email_confirmation_invalid' => 'Der Best&auml;tigungs-Token ist nicht g&uuml;ltig oder wurde bereits verwendet. Bitte registrieren Sie sich erneut.',
-    'email_confirmation_expired' => 'Der Best&auml;tigungs-Token ist abgelaufen. Es wurde eine neue Best&auml;tigungs-E-Mail gesendet.',
-    'ldap_fail_anonymous' => 'Anonymer LDAP Zugriff ist fehlgeschlafgen',
-    'ldap_fail_authed' => 'LDAP Zugriff mit DN & Passwort ist fehlgeschlagen',
-    'ldap_extension_not_installed' => 'LDAP PHP Erweiterung ist nicht installiert.',
-    'ldap_cannot_connect' => 'Die Verbindung zu LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf.',
+    'error_user_exists_different_creds' => 'Ein Benutzer mit der E-Mail-Adresse :email ist bereits mit anderen Anmeldedaten registriert.',
+    'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits bestätigt. Bitte melden Sie sich an.',
+    'email_confirmation_invalid' => 'Der Bestätigungslink ist nicht gültig oder wurde bereits verwendet. Bitte registrieren Sie sich erneut.',
+    'email_confirmation_expired' => 'Der Bestätigungslink ist abgelaufen. Es wurde eine neue Bestätigungs-E-Mail gesendet.',
+    'ldap_fail_anonymous' => 'Anonymer LDAP-Zugriff ist fehlgeschlafgen',
+    'ldap_fail_authed' => 'LDAP-Zugriff mit DN und Passwort ist fehlgeschlagen',
+    'ldap_extension_not_installed' => 'LDAP-PHP-Erweiterung ist nicht installiert.',
+    'ldap_cannot_connect' => 'Die Verbindung zum LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf.',
     'social_no_action_defined' => 'Es ist keine Aktion definiert',
-    'social_account_in_use' => 'Dieses :socialAccount Konto wird bereits verwendet. Bitte melden Sie sich mit dem :socialAccount Konto an.',
-    'social_account_email_in_use' => 'Die E-Mail-Adresse :email ist bereits registriert. Wenn Sie bereits registriert sind, k&ouml;nnen Sie Ihr :socialAccount Konto in Ihren Profil-Einstellungen verkn&uuml;pfen.',
-    'social_account_existing' => 'Dieses :socialAccount Konto ist bereits mit Ihrem Profil verkn&uuml;pft.',
-    'social_account_already_used_existing' => 'Dieses :socialAccount Konto wird bereits durch einen anderen Benutzer verwendet.',
-    'social_account_not_used' => 'Dieses :socialAccount Konto ist bisher keinem Benutzer zugeordnet. Bitte verkn&uuml;pfen Sie deses in Ihrem Profil-Einstellungen.',
-    'social_account_register_instructions' => 'Wenn Sie bisher keinen Social-Media Konto besitzen k&ouml;nnen Sie ein solches Konto mit der :socialAccount Option anlegen.',
-    'social_driver_not_found' => 'Social-Media Konto Treiber nicht gefunden',
-    'social_driver_not_configured' => 'Ihr :socialAccount Konto ist nicht korrekt konfiguriert.',
+    'social_account_in_use' => 'Dieses :socialAccount-Konto wird bereits verwendet. Bitte melden Sie sich mit dem :socialAccount-Konto an.',
+    'social_account_email_in_use' => 'Die E-Mail-Adresse ":email" ist bereits registriert. Wenn Sie bereits registriert sind, können Sie Ihr :socialAccount-Konto in Ihren Profil-Einstellungen verknüpfen.',
+    'social_account_existing' => 'Dieses :socialAccount-Konto ist bereits mit Ihrem Profil verknüpft.',
+    'social_account_already_used_existing' => 'Dieses :socialAccount-Konto wird bereits von einem anderen Benutzer verwendet.',
+    'social_account_not_used' => 'Dieses :socialAccount-Konto ist bisher keinem Benutzer zugeordnet. Sie können es in Ihren Profil-Einstellung.',
+    'social_account_register_instructions' => 'Wenn Sie bisher keinen Social-Media Konto besitzen, können Sie ein solches Konto mit der :socialAccount Option anlegen.',
+    'social_driver_not_found' => 'Treiber für Social-Media-Konten nicht gefunden',
+    'social_driver_not_configured' => 'Ihr :socialAccount-Konto ist nicht korrekt konfiguriert.',
 
     // System
     'path_not_writable' => 'Die Datei kann nicht in den angegebenen Pfad :filePath hochgeladen werden. Stellen Sie sicher, dass dieser Ordner auf dem Server beschreibbar ist.',
     'cannot_get_image_from_url' => 'Bild konnte nicht von der URL :url geladen werden.',
-    'cannot_create_thumbs' => 'Der Server kann keine Vorschau-Bilder erzeugen. Bitte pr&uuml;fen Sie, ob Sie die GD PHP Erweiterung installiert haben.',
-    'server_upload_limit' => 'Der Server verbietet das Hochladen von Dateien mit dieser Dateigr&ouml;&szlig;e. Bitte versuchen Sie es mit einer kleineren Datei.',
+    'cannot_create_thumbs' => 'Der Server kann keine Vorschau-Bilder erzeugen. Bitte prüfen Sie, ob die GD PHP-Erweiterung installiert ist.',
+    'server_upload_limit' => 'Der Server verbietet das Hochladen von Dateien mit dieser Dateigröße. Bitte versuchen Sie es mit einer kleineren Datei.',
     'image_upload_error' => 'Beim Hochladen des Bildes trat ein Fehler auf.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Die Seite stimmt nach dem Hochladen des Anhangs nicht &uuml;berein.',
+    'attachment_page_mismatch' => 'Die Seite stimmte nach dem Hochladen des Anhangs nicht überein.',
 
     // Pages
     'page_draft_autosave_fail' => 'Fehler beim Speichern des Entwurfs. Stellen Sie sicher, dass Sie mit dem Internet verbunden sind, bevor Sie den Entwurf dieser Seite speichern.',
@@ -47,24 +47,31 @@ return [
     'book_not_found' => 'Buch nicht gefunden',
     'page_not_found' => 'Seite nicht gefunden',
     'chapter_not_found' => 'Kapitel nicht gefunden',
-    'selected_book_not_found' => 'Das gew&auml;hlte Buch wurde nicht gefunden.',
-    'selected_book_chapter_not_found' => 'Das gew&auml;hlte Buch oder Kapitel wurde nicht gefunden.',
-    'guests_cannot_save_drafts' => 'G&auml;ste k&ouml;nnen keine Entw&uuml;rfe speichern',
+    'selected_book_not_found' => 'Das gewählte Buch wurde nicht gefunden.',
+    'selected_book_chapter_not_found' => 'Das gewählte Buch oder Kapitel wurde nicht gefunden.',
+    'guests_cannot_save_drafts' => 'Gäste können keine Entwürfe speichern',
 
     // Users
-    'users_cannot_delete_only_admin' => 'Sie k&ouml;nnen den einzigen Administrator nicht l&ouml;schen.',
-    'users_cannot_delete_guest' => 'Sie k&ouml;nnen den Gast-Benutzer nicht l&ouml;schen',
+    'users_cannot_delete_only_admin' => 'Sie können den einzigen Administrator nicht löschen.',
+    'users_cannot_delete_guest' => 'Sie können den Gast-Benutzer nicht löschen',
 
     // Roles
     'role_cannot_be_edited' => 'Diese Rolle kann nicht bearbeitet werden.',
-    'role_system_cannot_be_deleted' => 'Dies ist eine Systemrolle und kann nicht gel&ouml;scht werden',
-    'role_registration_default_cannot_delete' => 'Diese Rolle kann nicht gel&ouml;scht werden solange sie als Standardrolle f&uuml;r neue Registrierungen gesetzt ist',
+    'role_system_cannot_be_deleted' => 'Dies ist eine Systemrolle und kann nicht gelöscht werden',
+    'role_registration_default_cannot_delete' => 'Diese Rolle kann nicht gelöscht werden, solange sie als Standardrolle für neue Registrierungen gesetzt ist',
+
+    // Comments
+    'comment_list' => 'Beim Abrufen der Kommentare ist ein Fehler aufgetreten.',
+    'cannot_add_comment_to_draft' => 'Du kannst keine Kommentare zu einem Entwurf hinzufügen.',
+    'comment_add' => 'Beim Hinzufügen des Kommentars ist ein Fehler aufgetreten.',
+    'comment_delete' => 'Beim Löschen des Kommentars ist ein Fehler aufgetreten.',
+    'empty_comment' => 'Kann keinen leeren Kommentar hinzufügen',
 
     // Error pages
     '404_page_not_found' => 'Seite nicht gefunden',
-    'sorry_page_not_found' => 'Entschuldigung. Die Seite, die Sie angefordert haben wurde nicht gefunden.',
-    'return_home' => 'Zur&uuml;ck zur Startseite',
+    'sorry_page_not_found' => 'Entschuldigung. Die Seite, die Sie angefordert haben, wurde nicht gefunden.',
+    'return_home' => 'Zurück zur Startseite',
     'error_occurred' => 'Es ist ein Fehler aufgetreten',
     'app_down' => ':appName befindet sich aktuell im Wartungsmodus.',
-    'back_soon' => 'Wir werden so schnell wie m&ouml;glich wieder online sein.',
+    'back_soon' => 'Wir werden so schnell wie möglich wieder online sein.'
 ];
index a3bf7c8c856ecf058da1665c19128ff4c344f74a..6ed0e30f0a27a8eee592f5ea1d976e2223da8343 100644 (file)
@@ -14,6 +14,6 @@ return [
     */
 
     'previous' => '&laquo; Vorherige',
-    'next'     => 'N&auml;chste &raquo;',
+    'next'     => 'Nächste &raquo;',
 
 ];
index c44b49baa26ee6e05486cb43347955fa1ae6c8cf..25ed05a04f41dc05ac0dc0999d105e5a205d324b 100644 (file)
@@ -13,10 +13,10 @@ return [
     |
     */
 
-    'password' => 'Pass&ouml;rter m&uuml;ssen mindestens sechs Zeichen enthalten und die Wiederholung muss identisch sein.',
-    'user' => "Wir k&ouml;nnen keinen Benutzer mit dieser E-Mail Adresse finden.",
-    'token' => 'Dieser Passwort-Reset-Token ist ung&uuml;ltig.',
-    'sent' => 'Wir haben Ihnen eine E-Mail mit einem Link zum Zur&uuml;cksetzen des Passworts zugesendet!',
-    'reset' => 'Ihr Passwort wurde zur&uuml;ckgesetzt!',
+    'password' => 'Passörter müssen mindestens sechs Zeichen enthalten und die Wiederholung muss übereinstimmen.',
+    'user' => "Es konnte kein Benutzer mit dieser E-Mail-Adresse gefunden werden.",
+    'token' => 'Dieser Link zum Zurücksetzen des Passwortes ist ungültig.',
+    'sent' => 'Wir haben Ihnen eine E-Mail mit einem Link zum Zurücksetzen des Passworts zugesendet!',
+    'reset' => 'Ihr Passwort wurde zurückgesetzt!',
 
 ];
index 668eecf337a54a6cc60ac6c793b3ef8568dd39f6..9435ec80853ecea267fdc04126acbe268f50181f 100644 (file)
@@ -20,17 +20,17 @@ return [
     'app_name' => 'Anwendungsname',
     'app_name_desc' => 'Dieser Name wird im Header und in E-Mails angezeigt.',
     'app_name_header' => 'Anwendungsname im Header anzeigen?',
-    'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?',
-    '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 Zugriff zu verhindern.',
+    'app_public_viewing' => 'Öffentliche Ansicht erlauben?',
+    'app_secure_images' => 'Erhöhte Sicherheit für hochgeladene Bilder aktivieren?',
+    'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu eratene, Zeichenketten zu Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnisindizes deaktiviert sind, um einen einfachen Zugriff 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.',
+    'app_editor_desc' => 'Wählen Sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.',
     'app_custom_html' => 'Benutzerdefinierter HTML <head> Inhalt',
-    'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugef&uuml;gt wird, wird am Ende der <head> Sektion jeder Seite eingef&uuml;gt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics Code hinzuzuf&uuml;gen.',
+    'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der <head> Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics-Code hinzuzufügen.',
     'app_logo' => 'Anwendungslogo',
-    'app_logo_desc' => 'Dieses Bild sollte 43px hoch sein. <br>Gr&ouml;&szlig;ere Bilder werden verkleinert.',
-    'app_primary_color' => 'Prim&auml;re Anwendungsfarbe',
-    'app_primary_color_desc' => 'Dies sollte ein HEX Wert sein. <br>Wenn Sie nicht eingeben, wird die Anwendung auf die Standardfarbe zur&uuml;ckgesetzt.',
+    'app_logo_desc' => "Dieses Bild sollte 43px hoch sein.\nGrößere Bilder werden verkleinert.",
+    'app_primary_color' => 'Primäre Anwendungsfarbe',
+    'app_primary_color_desc' => "Dies sollte ein HEX Wert sein.\nWenn Sie nicht eingeben, wird die Anwendung auf die Standardfarbe zurückgesetzt.",
 
     /**
      * Registration settings
@@ -39,11 +39,11 @@ return [
     'reg_settings' => 'Registrierungseinstellungen',
     'reg_allow' => 'Registrierung erlauben?',
     'reg_default_role' => 'Standard-Benutzerrolle nach Registrierung',
-    'reg_confirm_email' => 'Best&auml;tigung per E-Mail erforderlich?',
-    'reg_confirm_email_desc' => 'Falls die Einschr&auml;nkung f&uuml;r Domains genutzt wird, ist die Best&auml;tigung per E-Mail zwingend erforderlich und der untenstehende Wert wird ignoriert.',
-    'reg_confirm_restrict_domain' => 'Registrierung auf bestimmte Domains einschr&auml;nken',
-    'reg_confirm_restrict_domain_desc' => 'F&uuml;gen sie eine, durch Komma getrennte, Liste von E-Mail Domains hinzu, auf die die Registrierung eingeschr&auml;nkt werden soll. Benutzern wird eine E-Mail gesendet, um ihre E-Mail Adresse zu best&auml;tigen, bevor sie diese Anwendung nutzen k&ouml;nnen. <br> Hinweis: Benutzer k&ouml;nnen ihre E-Mail Adresse nach erfolgreicher Registrierung &auml;ndern.',
-    'reg_confirm_restrict_domain_placeholder' => 'Keine Einschr&auml;nkung gesetzt',
+    'reg_confirm_email' => 'Bestätigung per E-Mail erforderlich?',
+    'reg_confirm_email_desc' => 'Falls die Einschränkung für Domains genutzt wird, ist die Bestätigung per E-Mail zwingend erforderlich und der untenstehende Wert wird ignoriert.',
+    'reg_confirm_restrict_domain' => 'Registrierung auf bestimmte Domains einschränken',
+    'reg_confirm_restrict_domain_desc' => "Fügen sie eine durch Komma getrennte Liste von Domains hinzu, auf die die Registrierung eingeschränkt werden soll. Benutzern wird eine E-Mail gesendet, um ihre E-Mail Adresse zu bestätigen, bevor sie diese Anwendung nutzen können.\nHinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung ändern.",
+    'reg_confirm_restrict_domain_placeholder' => 'Keine Einschränkung gesetzt',
 
     /**
      * Role settings
@@ -53,31 +53,31 @@ return [
     'role_user_roles' => 'Benutzer-Rollen',
     'role_create' => 'Neue Rolle anlegen',
     'role_create_success' => 'Rolle erfolgreich angelegt',
-    'role_delete' => 'Rolle l&ouml;schen',
-    'role_delete_confirm' => 'Sie m&ouml;chten die Rolle \':roleName\' l&ouml;schen.',
-    'role_delete_users_assigned' => 'Diese Rolle ist :userCount Benutzern zugeordnet. Sie k&ouml;nnen unten eine neue Rolle ausw&auml;hlen, die Sie diesen Benutzern zuordnen m&ouml;chten.',
+    'role_delete' => 'Rolle löschen',
+    'role_delete_confirm' => 'Sie möchten die Rolle ":roleName" löschen.',
+    'role_delete_users_assigned' => 'Diese Rolle ist :userCount Benutzern zugeordnet. Sie können unten eine neue Rolle auswählen, die Sie diesen Benutzern zuordnen möchten.',
     'role_delete_no_migration' => "Den Benutzern keine andere Rolle zuordnen",
-    'role_delete_sure' => 'Sind Sie sicher, dass Sie diese Rolle l&ouml;schen m&ouml;chten?',
-    'role_delete_success' => 'Rolle erfolgreich gel&ouml;scht',
+    'role_delete_sure' => 'Sind Sie sicher, dass Sie diese Rolle löschen möchten?',
+    'role_delete_success' => 'Rolle erfolgreich gelöscht',
     'role_edit' => 'Rolle bearbeiten',
-    'role_details' => 'Rollen-Details',
+    'role_details' => 'Rollendetails',
     'role_name' => 'Rollenname',
     'role_desc' => 'Kurzbeschreibung der Rolle',
     'role_system' => 'System-Berechtigungen',
     'role_manage_users' => 'Benutzer verwalten',
-    'role_manage_roles' => 'Rollen & Rollen-Berechtigungen verwalten',
-    'role_manage_entity_permissions' => 'Alle Buch-, Kapitel und Seiten-Berechtigungen verwalten',
-    'role_manage_own_entity_permissions' => 'Nur Berechtigungen eigener B&uuml;cher, Kapitel und Seiten verwalten',
-    'role_manage_settings' => 'Globaleinstellungen verwalrten',
+    'role_manage_roles' => 'Rollen und Rollen-Berechtigungen verwalten',
+    'role_manage_entity_permissions' => 'Alle Buch-, Kapitel- und Seiten-Berechtigungen verwalten',
+    'role_manage_own_entity_permissions' => 'Nur Berechtigungen eigener Bücher, Kapitel und Seiten verwalten',
+    'role_manage_settings' => 'Globaleinstellungen verwalten',
     'role_asset' => 'Berechtigungen',
-    'role_asset_desc' => 'Diese Berechtigungen gelten f&uuml;r den Standard-Zugriff innerhalb des Systems. Berechtigungen f&uuml;r B&uuml;cher, Kapitel und Seiten &uuml;berschreiben diese Berechtigungenen.',
+    'role_asset_desc' => 'Diese Berechtigungen gelten für den Standard-Zugriff innerhalb des Systems. Berechtigungen für Bücher, Kapitel und Seiten überschreiben diese Berechtigungenen.',
     'role_all' => 'Alle',
     'role_own' => 'Eigene',
-    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_controlled_by_asset' => 'Berechtigungen werden vom Uploadziel bestimmt',
     'role_save' => 'Rolle speichern',
     'role_update_success' => 'Rolle erfolgreich gespeichert',
     'role_users' => 'Dieser Rolle zugeordnete Benutzer',
-    'role_users_none' => 'Bisher sind dieser Rolle keiner Benutzer zugeordnet,',
+    'role_users_none' => 'Bisher sind dieser Rolle keine Benutzer zugeordnet',
 
     /**
      * Users
@@ -85,27 +85,27 @@ return [
 
     'users' => 'Benutzer',
     'user_profile' => 'Benutzerprofil',
-    'users_add_new' => 'Benutzer hinzuf&uuml;gen',
+    'users_add_new' => 'Benutzer hinzufügen',
     'users_search' => 'Benutzer suchen',
     'users_role' => 'Benutzerrollen',
     'users_external_auth_id' => 'Externe Authentifizierungs-ID',
-    'users_password_warning' => 'F&uuml;llen Sie die folgenden Felder nur aus, wenn Sie Ihr Passwort &auml;ndern m&ouml;chten:',
-    'users_system_public' => 'Dieser Benutzer repr&auml;sentiert alle Gast-Benutzer, die diese Seite betrachten. Er kann nicht zum Anmelden benutzt werden, sondern wird automatisch zugeordnet.',
-    'users_delete' => 'Benutzer l&ouml;schen',
-    'users_delete_named' => 'Benutzer :userName l&ouml;schen',
-    'users_delete_warning' => 'Sie m&ouml;chten den Benutzer \':userName\' g&auml;nzlich aus dem System l&ouml;schen.',
-    'users_delete_confirm' => 'Sind Sie sicher, dass Sie diesen Benutzer l&ouml;schen m&ouml;chten?',
-    'users_delete_success' => 'Benutzer erfolgreich gel&ouml;scht.',
+    'users_password_warning' => 'Füllen Sie die folgenden Felder nur aus, wenn Sie Ihr Passwort ändern möchten:',
+    'users_system_public' => 'Dieser Benutzer repräsentiert alle unangemeldeten Benutzer, die diese Seite betrachten. Er kann nicht zum Anmelden benutzt werden, sondern wird automatisch zugeordnet.',
+    'users_delete' => 'Benutzer löschen',
+    'users_delete_named' => 'Benutzer ":userName" löschen',
+    'users_delete_warning' => 'Der Benutzer ":userName" wird aus dem System gelöscht.',
+    'users_delete_confirm' => 'Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?',
+    'users_delete_success' => 'Benutzer erfolgreich gelöscht.',
     'users_edit' => 'Benutzer bearbeiten',
     'users_edit_profile' => 'Profil bearbeiten',
     'users_edit_success' => 'Benutzer erfolgreich aktualisisert',
     'users_avatar' => 'Benutzer-Bild',
-    'users_avatar_desc' => 'Dieses Bild sollte einen Durchmesser von ca. 256px haben.',
+    'users_avatar_desc' => 'Das Bild sollte eine Auflösung von 256x256px haben.',
     'users_preferred_language' => 'Bevorzugte Sprache',
     'users_social_accounts' => 'Social-Media Konten',
-    'users_social_accounts_info' => 'Hier k&ouml;nnen Sie andere Social-Media Konten f&uuml;r eine schnellere und einfachere Anmeldung verkn&uuml;pfen. Wenn Sie ein Social-Media Konto hier l&ouml;sen, bleibt der Zugriff erhalteb. Entfernen Sie in diesem Falle die Berechtigung in Ihren Profil-Einstellungen des verkn&uuml;pften Social-Media Kontos.',
-    'users_social_connect' => 'Social-Media Konto verkn&uuml;pfen',
-    'users_social_disconnect' => 'Social-Media Kontoverkn&uuml;pfung l&ouml;sen',
-    'users_social_connected' => ':socialAccount Konto wurde erfolgreich mit dem Profil verkn&uuml;pft.',
-    'users_social_disconnected' => ':socialAccount Konto wurde erfolgreich vom Profil gel&ouml;st.',
+    'users_social_accounts_info' => 'Hier können Sie andere Social-Media-Konten für eine schnellere und einfachere Anmeldung verknüpfen. Wenn Sie ein Social-Media Konto lösen, bleibt der Zugriff erhalten. Entfernen Sie in diesem Falle die Berechtigung in Ihren Profil-Einstellungen des verknüpften Social-Media-Kontos.',
+    'users_social_connect' => 'Social-Media-Konto verknüpfen',
+    'users_social_disconnect' => 'Social-Media-Konto lösen',
+    'users_social_connected' => ':socialAccount-Konto wurde erfolgreich mit dem Profil verknüpft.',
+    'users_social_disconnected' => ':socialAccount-Konto wurde erfolgreich vom Profil gelöst.',
 ];
index 3a6a1bc159a8570d1be97d5f7353cf1b5d1ca5f1..5ac4b1b2735061345bc12a3df4168ba42589b137 100644 (file)
@@ -19,54 +19,54 @@ return [
     'alpha'                => ':attribute kann nur Buchstaben enthalten.',
     'alpha_dash'           => ':attribute kann nur Buchstaben, Zahlen und Bindestriche enthalten.',
     'alpha_num'            => ':attribute kann nur Buchstaben und Zahlen enthalten.',
-    'array'                => ':attribute muss eine Array sein.',
+    'array'                => ':attribute muss ein Array sein.',
     'before'               => ':attribute muss ein Datum vor :date sein.',
     'between'              => [
         'numeric' => ':attribute muss zwischen :min und :max liegen.',
-        'file'    => ':attribute muss zwischen :min und :max Kilobytes gro&szlig; sein.',
+        'file'    => ':attribute muss zwischen :min und :max Kilobytes groß sein.',
         'string'  => ':attribute muss zwischen :min und :max Zeichen lang sein.',
         'array'   => ':attribute muss zwischen :min und :max Elemente enthalten.',
     ],
     'boolean'              => ':attribute Feld muss wahr oder falsch sein.',
-    'confirmed'            => ':attribute Best&auml;tigung stimmt nicht &uuml;berein.',
+    'confirmed'            => ':attribute stimmt nicht überein.',
     'date'                 => ':attribute ist kein valides Datum.',
     'date_format'          => ':attribute entspricht nicht dem Format :format.',
-    'different'            => ':attribute und :other m&uuml;ssen unterschiedlich sein.',
+    'different'            => ':attribute und :other müssen unterschiedlich sein.',
     'digits'               => ':attribute muss :digits Stellen haben.',
     'digits_between'       => ':attribute muss zwischen :min und :max Stellen haben.',
-    'email'                => ':attribute muss eine valide E-Mail Adresse sein.',
-    'filled'               => ':attribute Feld ist erforderlich.',
-    'exists'               => 'Markiertes :attribute ist ung&uuml;ltig.',
+    'email'                => ':attribute muss eine valide E-Mail-Adresse sein.',
+    'filled'               => ':attribute ist erforderlich.',
+    'exists'               => ':attribute ist ungültig.',
     'image'                => ':attribute muss ein Bild sein.',
-    'in'                   => 'Markiertes :attribute ist ung&uuml;ltig.',
+    'in'                   => ':attribute ist ungültig.',
     'integer'              => ':attribute muss eine Zahl sein.',
     'ip'                   => ':attribute muss eine valide IP-Adresse sein.',
     'max'                  => [
-        'numeric' => ':attribute darf nicht gr&ouml;&szlig;er als :max sein.',
-        'file'    => ':attribute darf nicht gr&ouml;&szlig;er als :max Kilobyte sein.',
-        'string'  => ':attribute darf nicht l&auml;nger als :max Zeichen sein.',
+        'numeric' => ':attribute darf nicht größer als :max sein.',
+        'file'    => ':attribute darf nicht größer als :max Kilobyte sein.',
+        'string'  => ':attribute darf nicht länger als :max Zeichen sein.',
         'array'   => ':attribute darf nicht mehr als :max Elemente enthalten.',
     ],
     'mimes'                => ':attribute muss eine Datei vom Typ: :values sein.',
     'min'                  => [
-        'numeric' => ':attribute muss mindestens :min. sein',
-        'file'    => ':attribute muss mindestens :min Kilobyte gro&szlig; sein.',
+        'numeric' => ':attribute muss mindestens :min sein',
+        'file'    => ':attribute muss mindestens :min Kilobyte groß sein.',
         'string'  => ':attribute muss mindestens :min Zeichen lang sein.',
         'array'   => ':attribute muss mindesten :min Elemente enthalten.',
     ],
-    'not_in'               => 'Markiertes :attribute ist ung&uuml;ltig.',
+    'not_in'               => ':attribute ist ungültig.',
     'numeric'              => ':attribute muss eine Zahl sein.',
-    'regex'                => ':attribute Format ist ung&uuml;ltig.',
-    'required'             => ':attribute Feld ist erforderlich.',
-    'required_if'          => ':attribute Feld ist erforderlich, wenn :other :value ist.',
-    'required_with'        => ':attribute Feld ist erforderlich, wenn :values vorhanden ist.',
-    'required_with_all'    => ':attribute Feld ist erforderlich, wenn :values vorhanden sind.',
-    'required_without'     => ':attribute Feld ist erforderlich, wenn :values nicht vorhanden ist.',
-    'required_without_all' => ':attribute Feld ist erforderlich, wenn :values nicht vorhanden sind.',
-    'same'                 => ':attribute und :other muss &uuml;bereinstimmen.',
+    'regex'                => ':attribute ist in einem ungültigen Format.',
+    'required'             => ':attribute ist erforderlich.',
+    'required_if'          => ':attribute ist erforderlich, wenn :other :value ist.',
+    'required_with'        => ':attribute ist erforderlich, wenn :values vorhanden ist.',
+    'required_with_all'    => ':attribute ist erforderlich, wenn :values vorhanden sind.',
+    'required_without'     => ':attribute ist erforderlich, wenn :values nicht vorhanden ist.',
+    'required_without_all' => ':attribute ist erforderlich, wenn :values nicht vorhanden sind.',
+    'same'                 => ':attribute und :other müssen übereinstimmen.',
     'size'                 => [
         'numeric' => ':attribute muss :size sein.',
-        'file'    => ':attribute muss :size Kilobytes gro&szlig; sein.',
+        'file'    => ':attribute muss :size Kilobytes groß sein.',
         'string'  => ':attribute muss :size Zeichen lang sein.',
         'array'   => ':attribute muss :size Elemente enthalten.',
     ],
index 56af4ca07b3c9914c9158a9e6c33fa116d07c2b7..187fe1e53fb7c687daf54bf592afee33e1a40e5a 100644 (file)
@@ -37,4 +37,6 @@ return [
     'book_sort'                   => 'sorted book',
     'book_sort_notification'      => 'Book Successfully Re-sorted',
 
+    // Other
+    'commented_on'                => 'commented on',
 ];
index e1d74c95e0e0718f0be435c5f40fdbb3da9f8e40..269905a59c7f181430f47565b8828fdc16b49b50 100644 (file)
@@ -10,6 +10,7 @@ return [
     'save' => 'Save',
     'continue' => 'Continue',
     'select' => 'Select',
+    'more' => 'More',
 
     /**
      * Form Labels
@@ -28,6 +29,7 @@ return [
     'edit' => 'Edit',
     'sort' => 'Sort',
     'move' => 'Move',
+    'reply' => 'Reply',
     'delete' => 'Delete',
     'search' => 'Search',
     'search_clear' => 'Clear Search',
@@ -35,7 +37,6 @@ return [
     'remove' => 'Remove',
     'add' => 'Add',
 
-
     /**
      * Misc
      */
@@ -44,6 +45,7 @@ return [
     'no_items' => 'No items available',
     'back_to_top' => 'Back to top',
     'toggle_details' => 'Toggle Details',
+    'details' => 'Details',
 
     /**
      * Header
index 450f4ce4851bcfb49703fe8e97e039ec1cb0c0aa..4dc5ccc382c1a242ca4a2f13153eb8f09a0d7191 100644 (file)
@@ -19,7 +19,6 @@ return [
     'meta_created_name' => 'Created :timeLength by :user',
     'meta_updated' => 'Updated :timeLength',
     'meta_updated_name' => 'Updated :timeLength by :user',
-    'x_pages' => ':count Pages',
     'entity_select' => 'Entity Select',
     'images' => 'Images',
     'my_recent_drafts' => 'My Recent Drafts',
@@ -70,14 +69,17 @@ return [
      */
     'book' => 'Book',
     'books' => 'Books',
+    'x_books' => ':count Book|:count Books',
     'books_empty' => 'No books have been created',
     'books_popular' => 'Popular Books',
     'books_recent' => 'Recent Books',
+    'books_new' => 'New Books',
     'books_popular_empty' => 'The most popular books will appear here.',
+    'books_new_empty' => 'The most recently created books will appear here.',
     'books_create' => 'Create New Book',
     'books_delete' => 'Delete Book',
     'books_delete_named' => 'Delete Book :bookName',
-    'books_delete_explain' => 'This will delete the book with the name \':bookName\', All pages and chapters will be removed.',
+    'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.',
     'books_delete_confirmation' => 'Are you sure you want to delete this book?',
     'books_edit' => 'Edit Book',
     'books_edit_named' => 'Edit Book :bookName',
@@ -103,13 +105,13 @@ return [
      */
     'chapter' => 'Chapter',
     'chapters' => 'Chapters',
+    'x_chapters' => ':count Chapter|:count Chapters',
     'chapters_popular' => 'Popular Chapters',
     'chapters_new' => 'New Chapter',
     'chapters_create' => 'Create New Chapter',
     'chapters_delete' => 'Delete Chapter',
     'chapters_delete_named' => 'Delete Chapter :chapterName',
-    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\', All pages will be removed
-        and added directly to the parent book.',
+    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
     'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
     'chapters_edit' => 'Edit Chapter',
     'chapters_edit_named' => 'Edit Chapter :chapterName',
@@ -128,6 +130,7 @@ return [
      */
     'page' => 'Page',
     'pages' => 'Pages',
+    'x_pages' => ':count Page|:count Pages',
     'pages_popular' => 'Popular Pages',
     'pages_new' => 'New Page',
     'pages_attachments' => 'Attachments',
@@ -164,6 +167,7 @@ return [
     'pages_move_success' => 'Page moved to ":parentName"',
     'pages_permissions' => 'Page Permissions',
     'pages_permissions_success' => 'Page permissions updated',
+    'pages_revision' => 'Revision',
     'pages_revisions' => 'Page Revisions',
     'pages_revisions_named' => 'Page Revisions for :pageName',
     'pages_revision_named' => 'Page Revision for :pageName',
@@ -234,4 +238,23 @@ return [
     'profile_not_created_pages' => ':userName has not created any pages',
     'profile_not_created_chapters' => ':userName has not created any chapters',
     'profile_not_created_books' => ':userName has not created any books',
+
+    /**
+     * Comments
+     */
+    'comment' => 'Comment',
+    'comments' => 'Comments',
+    'comment_placeholder' => 'Leave a comment here',
+    'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments',
+    'comment_save' => 'Save Comment',
+    'comment_saving' => 'Saving comment...',
+    'comment_deleting' => 'Deleting comment...',
+    'comment_new' => 'New Comment',
+    'comment_created' => 'commented :createDiff',
+    'comment_updated' => 'Updated :updateDiff by :username',
+    'comment_deleted_success' => 'Comment deleted',
+    'comment_created_success' => 'Comment added',
+    'comment_updated_success' => 'Comment updated',
+    'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
+    'comment_in_reply_to' => 'In reply to :commentId',
 ];
\ No newline at end of file
index c4578a37a11d91c1eee8c03c3c1e4f3b08c99963..09158caac04d696965d12e199c752a7409f42607 100644 (file)
@@ -60,6 +60,13 @@ return [
     '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',
 
+    // Comments
+    'comment_list' => 'An error occurred while fetching the comments.',
+    'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.',
+    'comment_add' => 'An error occurred while adding / updating the comment.',
+    'comment_delete' => 'An error occurred while deleting the comment.',
+    'empty_comment' => 'Cannot add an empty comment.',
+
     // Error pages
     '404_page_not_found' => 'Page Not Found',
     'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
old mode 100644 (file)
new mode 100755 (executable)
index 3eec773..8b07cce
@@ -31,6 +31,9 @@ return [
     'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
     '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.',
+    'app_homepage' => 'Application Homepage',
+    'app_homepage_desc' => 'Select a page to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
+    'app_homepage_default' => 'Default homepage view chosen',
 
     /**
      * Registration settings
@@ -123,6 +126,7 @@ return [
         'sk' => 'Slovensky',
         'ja' => '日本語',
         'pl' => 'Polski',
+        'it' => 'Italian'
     ]
     ///////////////////////////////////
 ];
index d6b2810bc1c7eb3110fd9ed6875a86eb4fa0ddd8..ba4ca955ad95cead812755a2b529729cefbd45de 100644 (file)
@@ -214,4 +214,12 @@ return [
     'profile_not_created_pages' => ':userName no ha creado ninguna página',
     'profile_not_created_chapters' => ':userName no ha creado ningún capítulo',
     'profile_not_created_books' => ':userName no ha creado ningún libro',
+
+    /**
+     * Comments
+     */
+    'comment' => 'Comentario',
+    'comments' => 'Comentarios',
+    'comment_placeholder' => 'Introduzca sus comentarios aquí',
+    'comment_save' => 'Guardar comentario',
 ];
index 1e39a3cb86f46484c528a1c2172711fee8facab3..e488b6a1b909edc0a33f007ea175a952eb7504cb 100644 (file)
@@ -67,4 +67,11 @@ return [
     'error_occurred' => 'Ha ocurrido un error',
     'app_down' => 'La aplicación :appName se encuentra caída en este momento',
     'back_soon' => 'Volverá a estar operativa en corto tiempo.',
+
+    // Comments
+    'comment_list' => 'Se ha producido un error al buscar los comentarios.',
+    'cannot_add_comment_to_draft' => 'No puedes añadir comentarios a un borrador.',
+    'comment_add' => 'Se ha producido un error al añadir el comentario.',
+    'comment_delete' => 'Se ha producido un error al eliminar el comentario.',
+    'empty_comment' => 'No se puede agregar un comentario vacío.',
 ];
index 17b4ea91372b2a47f8025a1ef9baf2301b0eb711..d4d9d247575ae25eda3ed1ff2325268fe0f9e976 100644 (file)
@@ -213,4 +213,12 @@ return [
     'profile_not_created_pages' => ':userName n\'a pas créé de page',
     'profile_not_created_chapters' => ':userName n\'a pas créé de chapitre',
     'profile_not_created_books' => ':userName n\'a pas créé de livre',
+
+    /**
+     * Comments
+     */
+    'comment' => 'Commentaire',
+    'comments' => 'Commentaires',
+    'comment_placeholder' => 'Entrez vos commentaires ici',
+    'comment_save' => 'Enregistrer le commentaire',
 ];
index 4197b1708e4b91c66a115607b0ff21e49992e3a7..9e20147b60a474e48b844267a37c9c3283375f56 100644 (file)
@@ -67,4 +67,11 @@ return [
     'error_occurred' => 'Une erreur est survenue',
     'app_down' => ':appName n\'est pas en service pour le moment',
     'back_soon' => 'Nous serons bientôt de retour.',
+
+    // comments
+    'comment_list' => 'Une erreur s\'est produite lors de la récupération des commentaires.',
+    'cannot_add_comment_to_draft' => 'Vous ne pouvez pas ajouter de commentaires à un projet.',
+    'comment_add' => 'Une erreur s\'est produite lors de l\'ajout du commentaire.',
+    'comment_delete' => 'Une erreur s\'est produite lors de la suppression du commentaire.',
+    'empty_comment' => 'Impossible d\'ajouter un commentaire vide.',
 ];
diff --git a/resources/lang/it/activities.php b/resources/lang/it/activities.php
new file mode 100755 (executable)
index 0000000..3fa5ad1
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+return [
+
+    /**
+     * Activity text strings.
+     * Is used for all the text within activity logs & notifications.
+     */
+
+    // Pages
+    'page_create'                 => 'ha creato la pagina',
+    'page_create_notification'    => 'Pagina Creata Correttamente',
+    'page_update'                 => 'ha aggiornato la pagina',
+    'page_update_notification'    => 'Pagina Aggiornata Correttamente',
+    'page_delete'                 => 'ha eliminato la pagina',
+    'page_delete_notification'    => 'Pagina Eliminata Correttamente',
+    'page_restore'                => 'ha ripristinato la pagina',
+    'page_restore_notification'   => 'Pagina Ripristinata Correttamente',
+    'page_move'                   => 'ha mosso la pagina',
+
+    // Chapters
+    'chapter_create'              => 'ha creato il capitolo',
+    'chapter_create_notification' => 'Capitolo Creato Correttamente',
+    'chapter_update'              => 'ha aggiornato il capitolo',
+    'chapter_update_notification' => 'Capitolo Aggiornato Correttamente',
+    'chapter_delete'              => 'ha eliminato il capitolo',
+    'chapter_delete_notification' => 'Capitolo Eliminato Correttamente',
+    'chapter_move'                => 'ha mosso il capitolo',
+
+    // Books
+    'book_create'                 => 'ha creato il libro',
+    'book_create_notification'    => 'Libro Creato Correttamente',
+    'book_update'                 => 'ha aggiornato il libro',
+    'book_update_notification'    => 'Libro Aggiornato Correttamente',
+    'book_delete'                 => 'ha eliminato il libro',
+    'book_delete_notification'    => 'Libro Eliminato Correttamente',
+    'book_sort'                   => 'ha ordinato il libro',
+    'book_sort_notification'      => 'Libro Riordinato Correttamente',
+
+];
diff --git a/resources/lang/it/auth.php b/resources/lang/it/auth.php
new file mode 100755 (executable)
index 0000000..68fee41
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+return [
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used during authentication for various
+    | messages that we need to display to the user. You are free to modify
+    | these language lines according to your application's requirements.
+    |
+    */
+    'failed' => 'Credenziali errate.',
+    'throttle' => 'Troppi tentativi di login. Riprova in :seconds secondi.',
+
+    /**
+     * Login & Register
+     */
+    'sign_up' => 'Registrati',
+    'log_in' => 'Login',
+    'log_in_with' => 'Login con :socialDriver',
+    'sign_up_with' => 'Registrati con :socialDriver',
+    'logout' => 'Esci',
+
+    'name' => 'Nome',
+    'username' => 'Username',
+    'email' => 'Email',
+    'password' => 'Password',
+    'password_confirm' => 'Conferma Password',
+    'password_hint' => 'Deve essere più di 5 caratteri',
+    'forgot_password' => 'Password dimenticata?',
+    'remember_me' => 'Ricordami',
+    'ldap_email_hint' => 'Inserisci un email per usare quest\'account.',
+    'create_account' => 'Crea Account',
+    'social_login' => 'Login Social',
+    'social_registration' => 'Registrazione Social',
+    'social_registration_text' => 'Registrati usando un altro servizio.',
+
+    'register_thanks' => 'Grazie per esserti registrato!',
+    'register_confirm' => 'Controlla la tua mail e clicca il bottone di conferma per accedere a :appName.',
+    'registrations_disabled' => 'La registrazione è disabilitata',
+    'registration_email_domain_invalid' => 'Questo dominio della mail non ha accesso a questa applicazione',
+    'register_success' => 'Grazie per la registrazione! Sei registrato e loggato.',
+
+
+    /**
+     * Password Reset
+     */
+    'reset_password' => 'Reimposta Password',
+    'reset_password_send_instructions' => 'Inserisci il tuo indirizzo sotto e ti verrà inviata una mail contenente un link per resettare la tua password.',
+    'reset_password_send_button' => 'Invia Link Reset',
+    'reset_password_sent_success' => 'Un link di reset è stato mandato a :email.',
+    'reset_password_success' => 'La tua password è stata resettata correttamente.',
+
+    'email_reset_subject' => 'Reimposta la password di :appName',
+    'email_reset_text' => 'Stai ricevendo questa mail perché abbiamo ricevuto una richiesta di reset della password per il tuo account.',
+    'email_reset_not_requested' => 'Se non hai richiesto un reset della password, ignora questa mail.',
+
+
+    /**
+     * Email Confirmation
+     */
+    'email_confirm_subject' => 'Conferma email per :appName',
+    'email_confirm_greeting' => 'Grazie per esserti registrato a :appName!',
+    'email_confirm_text' => 'Conferma il tuo indirizzo email cliccando il pulsante sotto:',
+    'email_confirm_action' => 'Conferma Email',
+    'email_confirm_send_error' => 'La conferma della mail è richiesta ma non è stato possibile mandare la mail. Contatta l\'amministratore.',
+    'email_confirm_success' => 'La tua mail è stata confermata!',
+    'email_confirm_resent' => 'Mail di conferma reinviata, controlla la tua posta.',
+
+    'email_not_confirmed' => 'Indirizzo Email Non Confermato',
+    'email_not_confirmed_text' => 'Il tuo indirizzo email non è ancora stato confermato.',
+    'email_not_confirmed_click_link' => 'Clicca il link nella mail mandata subito dopo la tua registrazione.',
+    'email_not_confirmed_resend' => 'Se non riesci a trovare la mail puoi rimandarla cliccando il pulsante sotto.',
+    'email_not_confirmed_resend_button' => 'Reinvia Conferma',
+];
\ No newline at end of file
diff --git a/resources/lang/it/common.php b/resources/lang/it/common.php
new file mode 100755 (executable)
index 0000000..8c25bfb
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+return [
+
+    /**
+     * Buttons
+     */
+    'cancel' => 'Annulla',
+    'confirm' => 'Conferma',
+    'back' => 'Indietro',
+    'save' => 'Salva',
+    'continue' => 'Continua',
+    'select' => 'Seleziona',
+    'more' => 'More',
+
+    /**
+     * Form Labels
+     */
+    'name' => 'Nome',
+    'description' => 'Descrizione',
+    'role' => 'Ruolo',
+
+    /**
+     * Actions
+     */
+    'actions' => 'Azioni',
+    'view' => 'Visualizza',
+    'create' => 'Crea',
+    'update' => 'Aggiorna',
+    'edit' => 'Modifica',
+    'sort' => 'Ordina',
+    'move' => 'Muovi',
+    'delete' => 'Elimina',
+    'search' => 'Cerca',
+    'search_clear' => 'Pulisci Ricerca',
+    'reset' => 'Reset',
+    'remove' => 'Rimuovi',
+    'add' => 'Aggiungi',
+
+    /**
+     * Misc
+     */
+    'deleted_user' => 'Utente Eliminato',
+    'no_activity' => 'Nessuna attività da mostrare',
+    'no_items' => 'Nessun elemento disponibile',
+    'back_to_top' => 'Torna in alto',
+    'toggle_details' => 'Mostra Dettagli',
+    'details' => 'Dettagli',
+
+    /**
+     * Header
+     */
+    'view_profile' => 'Visualizza Profilo',
+    'edit_profile' => 'Modifica Profilo',
+
+    /**
+     * Email Content
+     */
+    'email_action_help' => 'Se hai problemi nel cliccare il pulsante ":actionText", copia e incolla lo URL sotto nel tuo browser:',
+    'email_rights' => 'Tutti i diritti riservati',
+];
\ No newline at end of file
diff --git a/resources/lang/it/components.php b/resources/lang/it/components.php
new file mode 100755 (executable)
index 0000000..081a4c0
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+return [
+
+    /**
+     * Image Manager
+     */
+    'image_select' => 'Selezione Immagine',
+    'image_all' => 'Tutte',
+    'image_all_title' => 'Visualizza tutte le immagini',
+    'image_book_title' => 'Visualizza immagini caricate in questo libro',
+    'image_page_title' => 'Visualizza immagini caricate in questa pagina',
+    'image_search_hint' => 'Cerca immagine per nome',
+    'image_uploaded' => 'Uploaded :uploadedDate',
+    'image_load_more' => 'Carica Altre',
+    'image_image_name' => 'Nome Immagine',
+    'image_delete_confirm' => 'Questa immagine è usata nelle pagine elencate, clicca elimina nuovamente per confermare.',
+    'image_select_image' => 'Seleziona Immagine',
+    'image_dropzone' => 'Rilascia immagini o clicca qui per caricarle',
+    'images_deleted' => 'Immagini Eliminate',
+    'image_preview' => 'Anteprima Immagine',
+    'image_upload_success' => 'Immagine caricata correttamente',
+    'image_update_success' => 'Dettagli immagine aggiornati correttamente',
+    'image_delete_success' => 'Immagine eliminata correttamente',
+
+    /**
+     * Code editor
+     */
+    'code_editor' => 'Modifica Codice',
+    'code_language' => 'Linguaggio Codice',
+    'code_content' => 'Contenuto Codice',
+    'code_save' => 'Salva Codice',
+];
\ No newline at end of file
diff --git a/resources/lang/it/entities.php b/resources/lang/it/entities.php
new file mode 100755 (executable)
index 0000000..0389dd5
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+return [
+
+    /**
+     * Shared
+     */
+    'recently_created' => 'Creati di recente',
+    'recently_created_pages' => 'Pagine create di recente',
+    'recently_updated_pages' => 'Pagine aggiornate di recente',
+    'recently_created_chapters' => 'Capitoli creati di recente',
+    'recently_created_books' => 'Libri creati di recente',
+    'recently_update' => 'Aggiornati di recente',
+    'recently_viewed' => 'Visti di recente',
+    'recent_activity' => 'Attività Recente',
+    'create_now' => 'Creane uno ora',
+    'revisions' => 'Versioni',
+    'meta_revision' => 'Versione #:revisionCount',
+    'meta_created' => 'Creato :timeLength',
+    'meta_created_name' => 'Creato :timeLength da :user',
+    'meta_updated' => 'Aggiornato :timeLength',
+    'meta_updated_name' => 'Aggiornato :timeLength da :user',
+    'x_pages' => ':count Pagine',
+    'entity_select' => 'Selezione Entità',
+    'images' => 'Immagini',
+    'my_recent_drafts' => 'Bozze Recenti',
+    'my_recently_viewed' => 'Visti di recente',
+    'no_pages_viewed' => 'Non hai visto nessuna pagina',
+    'no_pages_recently_created' => 'Nessuna pagina è stata creata di recente',
+    'no_pages_recently_updated' => 'Nessuna pagina è stata aggiornata di recente',
+    'export' => 'Esporta',
+    'export_html' => 'File Contenuto Web',
+    'export_pdf' => 'File PDF',
+    'export_text' => 'File di testo',
+
+    /**
+     * Permissions and restrictions
+     */
+    'permissions' => 'Permessi',
+    'permissions_intro' => 'Una volta abilitati, questi permessi avranno la priorità su tutti gli altri.',
+    'permissions_enable' => 'Abilita Permessi Custom',
+    'permissions_save' => 'Salva Permessi',
+
+    /**
+     * Search
+     */
+    'search_results' => 'Risultati Ricerca',
+    'search_total_results_found' => ':count risultato trovato|:count risultati trovati',
+    'search_clear' => 'Pulisci Ricerca',
+    'search_no_pages' => 'Nessuna pagina corrisponde alla ricerca',
+    'search_for_term' => 'Ricerca per :term',
+    'search_more' => 'Più Risultati',
+    'search_filters' => 'Filtri Ricerca',
+    'search_content_type' => 'Tipo di Contenuto',
+    'search_exact_matches' => 'Corrispondenza Esatta',
+    'search_tags' => 'Ricerche Tag',
+    'search_viewed_by_me' => 'Visti',
+    'search_not_viewed_by_me' => 'Non visti',
+    'search_permissions_set' => 'Permessi impostati',
+    'search_created_by_me' => 'Creati da me',
+    'search_updated_by_me' => 'Aggiornati da me',
+    'search_updated_before' => 'Aggiornati prima del',
+    'search_updated_after' => 'Aggiornati dopo il',
+    'search_created_before' => 'Creati prima del',
+    'search_created_after' => 'Creati dopo il',
+    'search_set_date' => 'Imposta Data',
+    'search_update' => 'Aggiorna Ricerca',
+
+    /**
+     * Books
+     */
+    'book' => 'Libro',
+    'books' => 'Libri',
+    'books_empty' => 'Nessun libro è stato creato',
+    'books_popular' => 'Libri Popolari',
+    'books_recent' => 'Libri Recenti',
+    'books_new' => 'Nuovi Libri',
+    'books_popular_empty' => 'I libri più popolari appariranno qui.',
+    'books_new_empty' => 'I libri creati più di recente appariranno qui.',
+    'books_create' => 'Crea Nuovo Libro',
+    'books_delete' => 'Elimina Libro',
+    'books_delete_named' => 'Elimina il libro :bookName',
+    'books_delete_explain' => 'Questo eliminerà il libro di nome \':bookName\'. Tutte le pagine e i capitoli saranno rimossi.',
+    'books_delete_confirmation' => 'Sei sicuro di voler eliminare questo libro?',
+    'books_edit' => 'Modifica Libro',
+    'books_edit_named' => 'Modifica il libro :bookName',
+    'books_form_book_name' => 'Nome Libro',
+    'books_save' => 'Salva Libro',
+    'books_permissions' => 'Permessi Libro',
+    'books_permissions_updated' => 'Permessi del libro aggiornati',
+    'books_empty_contents' => 'Non ci sono pagine o capitoli per questo libro.',
+    'books_empty_create_page' => 'Crea una nuova pagina',
+    'books_empty_or' => 'o',
+    'books_empty_sort_current_book' => 'Ordina il libro corrente',
+    'books_empty_add_chapter' => 'Aggiungi un capitolo',
+    'books_permissions_active' => 'Permessi libro attivi',
+    'books_search_this' => 'Cerca in questo libro',
+    'books_navigation' => 'Navigazione Libro',
+    'books_sort' => 'Ordina il contenuto del libro',
+    'books_sort_named' => 'Ordina il libro :bookName',
+    'books_sort_show_other' => 'Mostra Altri Libri',
+    'books_sort_save' => 'Salva il nuovo ordine',
+
+    /**
+     * Chapters
+     */
+    'chapter' => 'Capitolo',
+    'chapters' => 'Capitoli',
+    'chapters_popular' => 'Capitoli Popolari',
+    'chapters_new' => 'Nuovo Capitolo',
+    'chapters_create' => 'Crea un nuovo capitolo',
+    'chapters_delete' => 'Elimina Capitolo',
+    'chapters_delete_named' => 'Elimina il capitolo :chapterName',
+    'chapters_delete_explain' => 'Questo eliminerà il capitolo \':chapterName\'. Tutte le pagine verranno spostate nel libro.',
+    'chapters_delete_confirm' => 'Sei sicuro di voler eliminare questo capitolo?',
+    'chapters_edit' => 'Elimina Capitolo',
+    'chapters_edit_named' => 'Modifica il capitolo :chapterName',
+    'chapters_save' => 'Salva Capitolo',
+    'chapters_move' => 'Muovi Capitolo',
+    'chapters_move_named' => 'Muovi il capitolo :chapterName',
+    'chapter_move_success' => 'Capitolo mosso in :bookName',
+    'chapters_permissions' => 'Permessi Capitolo',
+    'chapters_empty' => 'Non ci sono pagine in questo capitolo.',
+    'chapters_permissions_active' => 'Permessi Capitolo Attivi',
+    'chapters_permissions_success' => 'Permessi Capitolo Aggiornati',
+    'chapters_search_this' => 'Cerca in questo capitolo',
+
+    /**
+     * Pages
+     */
+    'page' => 'Pagina',
+    'pages' => 'Pagine',
+    'pages_popular' => 'Pagine Popolari',
+    'pages_new' => 'Nuova Pagina',
+    'pages_attachments' => 'Allegati',
+    'pages_navigation' => 'Page Navigation',
+    'pages_delete' => 'Elimina Pagina',
+    'pages_delete_named' => 'Elimina la pagina :pageName',
+    'pages_delete_draft_named' => 'Delete Draft Page :pageName',
+    'pages_delete_draft' => 'Elimina Bozza Pagina',
+    'pages_delete_success' => 'Pagina eliminata',
+    'pages_delete_draft_success' => 'Bozza di una pagina eliminata',
+    'pages_delete_confirm' => 'Sei sicuro di voler eliminare questa pagina?',
+    'pages_delete_draft_confirm' => 'Sei sicuro di voler eliminare la bozza di questa pagina?',
+    'pages_editing_named' => 'Modifica :pageName',
+    'pages_edit_toggle_header' => 'Toggle header',
+    'pages_edit_save_draft' => 'Salva Bozza',
+    'pages_edit_draft' => 'Modifica Bozza della pagina',
+    'pages_editing_draft' => 'Modifica Bozza',
+    'pages_editing_page' => 'Modifica Pagina',
+    'pages_edit_draft_save_at' => 'Bozza salvata alle ',
+    'pages_edit_delete_draft' => 'Elimina Bozza',
+    'pages_edit_discard_draft' => 'Scarta Bozza',
+    'pages_edit_set_changelog' => 'Imposta Changelog',
+    'pages_edit_enter_changelog_desc' => 'Inserisci una breve descrizione dei cambiamenti che hai apportato',
+    'pages_edit_enter_changelog' => 'Inserisci Changelog',
+    'pages_save' => 'Salva Pagina',
+    'pages_title' => 'Titolo Pagina',
+    'pages_name' => 'Nome Pagina',
+    'pages_md_editor' => 'Editor',
+    'pages_md_preview' => 'Anteprima',
+    'pages_md_insert_image' => 'Inserisci Immagina',
+    'pages_md_insert_link' => 'Inserisci Link Entità',
+    'pages_not_in_chapter' => 'La pagina non è in un capitolo',
+    'pages_move' => 'Muovi Pagina',
+    'pages_move_success' => 'Pagina mossa in ":parentName"',
+    'pages_permissions' => 'Permessi Pagina',
+    'pages_permissions_success' => 'Page permissions updated',
+    'pages_revision' => 'Versione',
+    'pages_revisions' => 'Versioni Pagina',
+    'pages_revisions_named' => 'Versioni della pagina :pageName',
+    'pages_revision_named' => 'Versione della pagina :pageName',
+    'pages_revisions_created_by' => 'Creata Da',
+    'pages_revisions_date' => 'Data Versione',
+    'pages_revisions_number' => '#',
+    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_changes' => 'Cambiamenti',
+    'pages_revisions_current' => 'Versione Corrente',
+    'pages_revisions_preview' => 'Anteprima',
+    'pages_revisions_restore' => 'Ripristina',
+    'pages_revisions_none' => 'Questa pagina non ha versioni',
+    'pages_copy_link' => 'Copia Link',
+    'pages_permissions_active' => 'Permessi Pagina Attivi',
+    'pages_initial_revision' => 'Pubblicazione iniziale',
+    'pages_initial_name' => 'Nuova Pagina',
+    'pages_editing_draft_notification' => 'Stai modificando una bozza che è stata salvata il :timeDiff.',
+    'pages_draft_edited_notification' => 'Questa pagina è stata aggiornata. È consigliabile scartare questa bozza.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count hanno iniziato a modificare questa pagina',
+        'start_b' => ':userName ha iniziato a modificare questa pagina',
+        'time_a' => 'da quando le pagine sono state aggiornate',
+        'time_b' => 'negli ultimi :minCount minuti',
+        'message' => ':start :time. Assicurati di non sovrascrivere le modifiche degli altri!',
+    ],
+    'pages_draft_discarded' => "Bozza scartata, l'editor è stato aggiornato con il contenuto corrente della pagina",
+
+    /**
+     * Editor sidebar
+     */
+    'page_tags' => 'Tag Pagina',
+    'tag' => 'Tag',
+    'tags' =>  '',
+    'tag_value' => 'Valore (Opzionale)',
+    'tags_explain' => "Aggiungi tag per categorizzare meglio il contenuto. \n Puoi assegnare un valore ai tag per una migliore organizzazione.",
+    'tags_add' => 'Aggiungi un altro tag',
+    'attachments' => 'Allegati',
+    'attachments_explain' => 'Carica alcuni file o allega link per visualizzarli nella pagina. Questi sono visibili nella sidebar della pagina.',
+    'attachments_explain_instant_save' => 'I cambiamenti qui sono salvati istantaneamente.',
+    'attachments_items' => 'Oggetti Allegati',
+    'attachments_upload' => 'Carica File',
+    'attachments_link' => 'Allega Link',
+    'attachments_set_link' => 'Imposta Link',
+    'attachments_delete_confirm' => "Clicca elimina nuovamente per confermare l'eliminazione di questo allegato.",
+    'attachments_dropzone' => 'Rilascia file o clicca qui per allegare un file',
+    'attachments_no_files' => 'Nessun file è stato caricato',
+    'attachments_explain_link' => 'Puoi allegare un link se preferisci non caricare un file. Questo può essere un link a un\'altra pagina o a un file in un cloud.',
+    'attachments_link_name' => 'Nome Link',
+    'attachment_link' => 'Link allegato',
+    'attachments_link_url' => 'Link al file',
+    'attachments_link_url_hint' => 'Url del sito o del file',
+    'attach' => 'Allega',
+    'attachments_edit_file' => 'Modifica File',
+    'attachments_edit_file_name' => 'Nome File',
+    'attachments_edit_drop_upload' => 'Rilascia file o clicca qui per caricare e sovrascrivere',
+    'attachments_order_updated' => 'Ordine allegato aggiornato',
+    'attachments_updated_success' => 'Dettagli allegato aggiornati',
+    'attachments_deleted' => 'Allegato eliminato',
+    'attachments_file_uploaded' => 'File caricato correttamente',
+    'attachments_file_updated' => 'File aggiornato correttamente',
+    'attachments_link_attached' => 'Link allegato correttamente alla pagina',
+
+    /**
+     * Profile View
+     */
+    'profile_user_for_x' => 'Utente da :time',
+    'profile_created_content' => 'Contenuti Creati',
+    'profile_not_created_pages' => ':userName non ha creato pagine',
+    'profile_not_created_chapters' => ':userName non ha creato capitoli',
+    'profile_not_created_books' => ':userName non ha creato libri',
+
+    /**
+     * Comments
+     */
+    'comment' => 'Commento',
+    'comments' => 'Commenti',
+    'comment_count' => '1 Commento|:count Commenti',
+    'comment_save' => 'Salva Commento',
+    'comment_deleted_success' => 'Commento eliminato',
+    'comment_created_success' => 'Commento aggiunto',
+    'comment_updated_success' => 'Commento aggiornato',
+    'comment_delete_confirm' => 'Questo rimuoverà il contenuto del commento?',
+];
\ No newline at end of file
diff --git a/resources/lang/it/errors.php b/resources/lang/it/errors.php
new file mode 100755 (executable)
index 0000000..569b33c
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+return [
+
+    /**
+     * Error text strings.
+     */
+
+    // Permissions
+    'permission' => 'Non hai il permesso di accedere alla pagina richiesta.',
+    'permissionJson' => "Non hai il permesso di eseguire l'azione richiesta.",
+
+    // Auth
+    'error_user_exists_different_creds' => 'Un utente con la mail :email esiste già ma con credenziali differenti.',
+    'email_already_confirmed' => 'La mail è già stata confermata, esegui il login.',
+    'email_confirmation_invalid' => 'Questo token di conferma non è valido o già stato utilizzato, registrati nuovamente.',
+    'email_confirmation_expired' => 'Il token di conferma è scaduto, è stata inviata una nuova mail di conferma.',
+    'ldap_fail_anonymous' => 'Accesso LDAP fallito usando bind anonimo',
+    'ldap_fail_authed' => 'Accesso LDAP fallito usando il dn e la password inseriti',
+    'ldap_extension_not_installed' => 'L\'estensione PHP LDAP non è installata',
+    'ldap_cannot_connect' => 'Impossibile connettersi al server ldap, connessione iniziale fallita',
+    'social_no_action_defined' => 'Nessuna azione definita',
+    'social_account_in_use' => 'Questo account :socialAccount è già utilizzato, prova a loggarti usando l\'opzione :socialAccount.',
+    'social_account_email_in_use' => 'La mail :email è già in uso. Se hai già un account puoi connettere il tuo account :socialAccount dalle impostazioni del tuo profilo.',
+    'social_account_existing' => 'Questo account :socialAccount è già connesso al tuo profilo.',
+    'social_account_already_used_existing' => 'Questo accoutn :socialAccount è già utilizzato da un altro utente.',
+    'social_account_not_used' => 'Questo account :socialAccount non è collegato a nessun utente. Collegalo nelle impostazioni del profilo. ',
+    'social_account_register_instructions' => 'Se non hai ancora un account, puoi registrarti usando l\'opzione :socialAccount.',
+    'social_driver_not_found' => 'Driver social non trovato',
+    'social_driver_not_configured' => 'Le impostazioni di :socialAccount non sono configurate correttamente.',
+
+    // System
+    'path_not_writable' => 'La path :filePath non può essere scritta. Controlla che abbia i permessi corretti.',
+    'cannot_get_image_from_url' => 'Impossibile scaricare immagine da :url',
+    'cannot_create_thumbs' => 'Il server non può creare thumbnail. Controlla che l\'estensione GD sia installata.',
+    'server_upload_limit' => 'Il server non permette un upload di questa grandezza. Prova con un file più piccolo.',
+    'image_upload_error' => 'C\'è stato un errore caricando l\'immagine',
+
+    // Attachments
+    'attachment_page_mismatch' => 'Page mismatch during attachment update',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Impossibile salvare la bozza. Controlla di essere connesso ad internet prima di salvare questa pagina',
+
+    // Entities
+    'entity_not_found' => 'Entità non trovata',
+    'book_not_found' => 'Libro non trovato',
+    'page_not_found' => 'Pagina non trovata',
+    'chapter_not_found' => 'Capitolo non trovato',
+    'selected_book_not_found' => 'Il libro selezionato non è stato trovato',
+    'selected_book_chapter_not_found' => 'Il libro selezionato o il capitolo non sono stati trovati',
+    'guests_cannot_save_drafts' => 'Gli ospiti non possono salvare bozze',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Non puoi eliminare l\'unico adin',
+    'users_cannot_delete_guest' => 'Non puoi eliminare l\'utente ospite',
+
+    // Roles
+    'role_cannot_be_edited' => 'Questo ruolo non può essere modificato',
+    'role_system_cannot_be_deleted' => 'Questo ruolo è di sistema e non può essere eliminato',
+    'role_registration_default_cannot_delete' => 'Questo ruolo non può essere eliminato finchè è impostato come default alla registrazione',
+
+    // Comments
+    'comment_list' => 'C\'è stato un errore scaricando i commenti.',
+    'cannot_add_comment_to_draft' => 'Non puoi aggiungere commenti a una bozza.',
+    'comment_add' => 'C\'è stato un errore aggiungendo / aggiornando il commento.',
+    'comment_delete' => 'C\'è stato un errore eliminando il commento.',
+    'empty_comment' => 'Impossibile aggiungere un commento vuoto.',
+
+    // Error pages
+    '404_page_not_found' => 'Pagina Non Trovata',
+    'sorry_page_not_found' => 'La pagina che stavi cercando non è stata trovata.',
+    'return_home' => 'Ritorna alla home',
+    'error_occurred' => 'C\'è Stato un errore',
+    'app_down' => ':appName è offline',
+    'back_soon' => 'Ritornerà presto.',
+];
\ No newline at end of file
diff --git a/resources/lang/it/pagination.php b/resources/lang/it/pagination.php
new file mode 100755 (executable)
index 0000000..e3c5614
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Pagination Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used by the paginator library to build
+    | the simple pagination links. You are free to change them to anything
+    | you want to customize your views to better match your application.
+    |
+    */
+
+    'previous' => '&laquo; Precedente',
+    'next'     => 'Successivo &raquo;',
+
+];
diff --git a/resources/lang/it/passwords.php b/resources/lang/it/passwords.php
new file mode 100755 (executable)
index 0000000..a584aa3
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Reminder Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are the default lines which match reasons
+    | that are given by the password broker for a password update attempt
+    | has failed, such as for an invalid token or invalid new password.
+    |
+    */
+
+    'password' => 'La password deve avere almeno sei caratteri e corrispondere alla conferma.',
+    'user' => "Non possiamo trovare un utente per quella mail.",
+    'token' => 'Questo token per reimpostare la password non è valido.',
+    'sent' => 'Ti abbiamo inviato via mail il link per reimpostare la password!',
+    'reset' => 'La tua password è stata resettata!',
+
+];
diff --git a/resources/lang/it/settings.php b/resources/lang/it/settings.php
new file mode 100755 (executable)
index 0000000..d112cbc
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+return [
+
+    /**
+     * Settings text strings
+     * Contains all text strings used in the general settings sections of BookStack
+     * including users and roles.
+     */
+
+    'settings' => 'Impostazioni',
+    'settings_save' => 'Salva Impostazioni',
+    'settings_save_success' => 'Impostazioni salvate',
+
+    /**
+     * App settings
+     */
+
+    'app_settings' => 'Impostazioni App',
+    'app_name' => 'Nome applicazione',
+    'app_name_desc' => 'Questo nome è mostrato nell\'header e in tutte le mail.',
+    'app_name_header' => 'Mostrare il nome nell\'header',
+    'app_public_viewing' => 'Consentire la visione pubblica?',
+    'app_secure_images' => 'Abilitare una sicurezza maggiore per le immagini caricate?',
+    'app_secure_images_desc' => 'Per una ragione di prestazioni, tutte le immagini sono pubbliche. Questa opzione aaggiunge una stringa, difficile da indovinare, random negli url delle immagini. Assicurati che il listing delle cartelle non sia abilitato per prevenire un accesso semplice.',
+    'app_editor' => 'Editor pagine',
+    'app_editor_desc' => 'Seleziona quale editor verrà usato da tutti gli utenti per modificare le pagine.',
+    'app_custom_html' => 'Contenuto Head HTML Custom',
+    'app_custom_html_desc' => 'Qualsiasi contenuto aggiunto qui verrà inserito alla fine della sezione <head> di tutte le pagine. Questo è utile per sovrascrivere lo stile o aggiungere il codice per gli analytics.',
+    'app_logo' => 'Logo applicazione',
+    'app_logo_desc' => 'Questa immagine dovrebbe essere 43px in altezza. <br>Immagini più grandi verranno scalate.',
+    'app_primary_color' => 'Colore primario applicazione',
+    'app_primary_color_desc' => 'Deve essere un valore hex. <br>Lascia vuoto per reimpostare il colore di default.',
+    'app_homepage' => 'Homepage Applicazione',
+    'app_homepage_desc' => 'Seleziona una pagina da mostrare nella home anzichè quella di default. I permessi della pagina sono ignorati per quella selezionata.',
+    'app_homepage_default' => 'Homepage di default scelta',
+
+    /**
+     * Registration settings
+     */
+
+    'reg_settings' => 'Impostazioni Registrazione',
+    'reg_allow' => 'Consentire Registrazione?',
+    'reg_default_role' => 'Ruolo predefinito dopo la registrazione',
+    'reg_confirm_email' => 'Richiedere la conferma della mail?',
+    'reg_confirm_email_desc' => 'Se la restrizione per dominio è usata la conferma della mail sarà richiesta e la scelta ignorata.',
+    'reg_confirm_restrict_domain' => 'Restringi la registrazione al dominio',
+    'reg_confirm_restrict_domain_desc' => "Inserisci una lista separata da virgola di domini di email a cui vorresti restringere la registrazione. Agli utenti verrà inviata una mail per confermare il loro indirizzo prima che possano interagire con l'applicazione. <br> Nota che gli utenti saranno in grado di cambiare il loro indirizzo dopo aver completato la registrazione.",
+    'reg_confirm_restrict_domain_placeholder' => 'Nessuna restrizione impostata',
+
+    /**
+     * Role settings
+     */
+
+    'roles' => 'Ruoli',
+    'role_user_roles' => 'Ruoli Utente',
+    'role_create' => 'Crea Nuovo Ruolo',
+    'role_create_success' => 'Ruolo creato correttamente',
+    'role_delete' => 'Elimina Ruolo',
+    'role_delete_confirm' => 'Questo eliminerà il ruolo con il nome \':roleName\'.',
+    'role_delete_users_assigned' => 'Questo ruolo ha :userCount utenti assegnati. Se vuoi migrare gli utenti da questo ruolo selezionane uno nuovo sotto.',
+    'role_delete_no_migration' => "Non migrare gli utenti",
+    'role_delete_sure' => 'Sei sicuro di voler eliminare questo ruolo?',
+    'role_delete_success' => 'Ruolo eliminato correttamente',
+    'role_edit' => 'Modifica Ruolo',
+    'role_details' => 'Dettagli Ruolo',
+    'role_name' => 'Nome Ruolo',
+    'role_desc' => 'Breve Descrizione del Ruolo',
+    'role_system' => 'Permessi di Sistema',
+    'role_manage_users' => 'Gestire gli utenti',
+    'role_manage_roles' => 'Gestire ruoli e permessi di essi',
+    'role_manage_entity_permissions' => 'Gestire tutti i permessi di libri, capitoli e pagine',
+    'role_manage_own_entity_permissions' => 'Gestire i permessi sui propri libri, capitoli e pagine',
+    'role_manage_settings' => 'Gestire impostazioni app',
+    'role_asset' => 'Permessi Entità',
+    'role_asset_desc' => "Questi permessi controllano l'accesso di default alle entità. I permessi nei Libri, Capitoli e Pagine sovrascriveranno questi.",
+    'role_all' => 'Tutti',
+    'role_own' => 'Propri',
+    'role_controlled_by_asset' => "Controllato dall'entità in cui sono caricati",
+    'role_save' => 'Salva Ruolo',
+    'role_update_success' => 'Ruolo aggiornato correttamente',
+    'role_users' => 'Utenti in questo ruolo',
+    'role_users_none' => 'Nessun utente assegnato a questo ruolo',
+
+    /**
+     * Users
+     */
+
+    'users' => 'Utenti',
+    'user_profile' => 'Profilo Utente',
+    'users_add_new' => 'Aggiungi Nuovo Utente',
+    'users_search' => 'Cerca Utenti',
+    'users_role' => 'Ruoli Utente',
+    'users_external_auth_id' => 'ID Autenticazioni Esterna',
+    'users_password_warning' => 'Riempi solo se desideri cambiare la tua password:',
+    'users_system_public' => 'Questo utente rappresente qualsiasi ospite che visita il sito. Non può essere usato per effettuare il login ma è assegnato automaticamente.',
+    'users_delete' => 'Elimina Utente',
+    'users_delete_named' => "Elimina l'utente :userName",
+    'users_delete_warning' => 'Questo eliminerà completamente l\'utente \':userName\' dal sistema.',
+    'users_delete_confirm' => 'Sei sicuro di voler eliminare questo utente?',
+    'users_delete_success' => 'Utenti rimossi correttamente',
+    'users_edit' => 'Modifica Utente',
+    'users_edit_profile' => 'Modifica Profilo',
+    'users_edit_success' => 'Utente aggiornato correttamente',
+    'users_avatar' => 'Avatar Utente',
+    'users_avatar_desc' => "Quest'immagine dovrebbe essere approssimativamente 256px quadrata.",
+    'users_preferred_language' => 'Lingua Preferita',
+    'users_social_accounts' => 'Account Social',
+    'users_social_accounts_info' => 'Qui puoi connettere gli altri account per un accesso più veloce e semplice. Disconnettere un account qui non rimuoverà le altre sessioni. Revoca l\'accesso dal tuo profilo negli account social connessi.',
+    'users_social_connect' => 'Connetti Account',
+    'users_social_disconnect' => 'Disconnetti Account',
+    'users_social_connected' => 'L\'account :socialAccount è stato connesso correttamente al tuo profilo.',
+    'users_social_disconnected' => 'L\'account :socialAccount è stato disconnesso correttamente dal tuo profilo.',
+];
diff --git a/resources/lang/it/validation.php b/resources/lang/it/validation.php
new file mode 100755 (executable)
index 0000000..832480d
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines contain the default error messages used by
+    | the validator class. Some of these rules have multiple versions such
+    | as the size rules. Feel free to tweak each of these messages here.
+    |
+    */
+
+    'accepted'             => ':attribute deve essere accettato.',
+    'active_url'           => ':attribute non è uno URL valido.',
+    'after'                => ':attribute deve essere una data dopo il :date.',
+    'alpha'                => ':attribute deve contenere solo lettere.',
+    'alpha_dash'           => ':attribute deve contenere solo lettere, numeri e meno.',
+    'alpha_num'            => ':attribute deve contenere solo lettere e numeri.',
+    'array'                => ':attribute deve essere un array.',
+    'before'               => ':attribute deve essere una data prima del :date.',
+    'between'              => [
+        'numeric' => 'Il campo :attribute deve essere tra :min e :max.',
+        'file'    => 'Il campo :attribute deve essere tra :min e :max kilobytes.',
+        'string'  => 'Il campo :attribute deve essere tra :min e :max caratteri.',
+        'array'   => 'Il campo :attribute deve essere tra :min e :max oggetti.',
+    ],
+    'boolean'              => ':attribute deve contenere vero o falso.',
+    'confirmed'            => 'La conferma di :attribute non corrisponde.',
+    'date'                 => ':attribute non è una data valida.',
+    'date_format'          => 'Il campo :attribute non corrisponde al formato :format.',
+    'different'            => 'Il campo :attribute e :other devono essere differenti.',
+    'digits'               => 'Il campo :attribute deve essere di :digits numeri.',
+    'digits_between'       => 'Il campo :attribute deve essere tra i numeri :min e :max.',
+    'email'                => 'Il campo :attribute deve essere un indirizzo email valido.',
+    'filled'               => 'Il campo :attribute field is required.',
+    'exists'               => 'Il campo :attribute non è valido.',
+    'image'                => 'Il campo :attribute deve essere un\'immagine.',
+    'in'                   => 'Il campo :attribute selezionato non è valido.',
+    'integer'              => 'Il campo :attribute deve essere un intero.',
+    'ip'                   => 'Il campo :attribute deve essere un indirizzo IP valido.',
+    'max'                  => [
+        'numeric' => 'Il campo :attribute non deve essere maggiore di :max.',
+        'file'    => 'Il campo :attribute non deve essere maggiore di :max kilobytes.',
+        'string'  => 'Il campo :attribute non deve essere maggiore di :max caratteri.',
+        'array'   => 'Il campo :attribute non deve avere più di :max oggetti.',
+    ],
+    'mimes'                => 'Il campo :attribute deve essere: :values.',
+    'min'                  => [
+        'numeric' => 'Il campo :attribute deve essere almeno :min.',
+        'file'    => 'Il campo :attribute deve essere almeno :min kilobytes.',
+        'string'  => 'Il campo :attribute deve essere almeno :min caratteri.',
+        'array'   => 'Il campo :attribute deve contenere almeno :min elementi.',
+    ],
+    'not_in'               => 'Il :attribute selezionato non è valido.',
+    'numeric'              => ':attribute deve essere un numero.',
+    'regex'                => 'Il formato di :attribute non è valido.',
+    'required'             => 'Il campo :attribute è richiesto.',
+    'required_if'          => 'Il campo :attribute è richiesto quando :other è :value.',
+    'required_with'        => 'Il campo :attribute è richiesto quando :values è presente.',
+    'required_with_all'    => 'Il campo :attribute è richiesto quando :values sono presenti.',
+    'required_without'     => 'Il campo :attribute è richiesto quando :values non è presente.',
+    'required_without_all' => 'Il campo :attribute è richiesto quando nessuno dei :values sono presenti.',
+    'same'                 => ':attribute e :other devono corrispondere.',
+    'size'                 => [
+        'numeric' => 'Il campo :attribute deve essere :size.',
+        'file'    => 'Il campo :attribute deve essere :size kilobytes.',
+        'string'  => 'Il campo :attribute deve essere di :size caratteri.',
+        'array'   => 'Il campo :attribute deve contenere :size elementi.',
+    ],
+    'string'               => ':attribute deve essere una stringa.',
+    'timezone'             => ':attribute deve essere una zona valida.',
+    'unique'               => ':attribute è già preso.',
+    'url'                  => 'Il formato :attribute non è valido.',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify custom validation messages for attributes using the
+    | convention "attribute.rule" to name the lines. This makes it quick to
+    | specify a specific custom language line for a given attribute rule.
+    |
+    */
+
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Conferma della password richiesta',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Attributes
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used to swap attribute place-holders
+    | with something more reader friendly such as E-Mail Address instead
+    | of "email". This simply helps us make messages a little cleaner.
+    |
+    */
+
+    'attributes' => [],
+
+];
index 89ed33ef32e31fb675c7482ab1cbbf18b289b6f8..8e9eb2e475fe37ca75e65309206e2928e14578b5 100644 (file)
@@ -20,5 +20,13 @@ return [
     'image_preview' => '画像プレビュー',
     'image_upload_success' => '画像がアップロードされました',
     'image_update_success' => '画像が更新されました',
-    'image_delete_success' => '画像が削除されました'
+    'image_delete_success' => '画像が削除されました',
+
+    /**
+     * Code editor
+     */
+    'code_editor' => 'プログラムブロック編集',
+    'code_language' => 'プログラミング言語の選択',
+    'code_content' => 'プログラム内容',
+    'code_save' => 'プログラムを保存'
 ];
index d6975e130a6ed4b70f695b362ba99b6db769121d..a882294f1493d13810f7dba96b32a5dcd306e1eb 100644 (file)
@@ -214,4 +214,12 @@ return [
     'profile_not_created_pages' => ':userName heeft geen pagina\'s gemaakt',
     'profile_not_created_chapters' => ':userName heeft geen hoofdstukken gemaakt',
     'profile_not_created_books' => ':userName heeft geen boeken gemaakt',
+
+    /**
+     * Comments
+     */
+    'comment' => 'Commentaar',
+    'comments' => 'Commentaren',
+    'comment_placeholder' => 'Vul hier uw reacties in',
+    'comment_save' => 'Opslaan opslaan',
 ];
\ No newline at end of file
index f8b635bce808ba3729e9cb9ea0abbd160a05f59a..b8fab59fdca9b26a59e543cc2f1e613d62685e3b 100644 (file)
@@ -67,4 +67,11 @@ return [
     'error_occurred' => 'Er Ging Iets Fout',
     'app_down' => ':appName is nu niet beschikbaar',
     'back_soon' => 'Komt snel weer online.',
+
+    // Comments
+    'comment_list' => 'Er is een fout opgetreden tijdens het ophalen van de reacties.',
+    'cannot_add_comment_to_draft' => 'U kunt geen reacties toevoegen aan een ontwerp.',
+    'comment_add' => 'Er is een fout opgetreden tijdens het toevoegen van de reactie.',
+    'comment_delete' => 'Er is een fout opgetreden tijdens het verwijderen van de reactie.',
+    'empty_comment' => 'Kan geen lege reactie toevoegen.',
 ];
\ No newline at end of file
index 5a965fe624fe0926346c27e3cb2dec9dbbb4180a..bf0a8ac7202ca0eb11bf1c867ab3dfb160240dc2 100644 (file)
@@ -214,4 +214,12 @@ return [
     'profile_not_created_pages' => ':userName não criou páginas',
     'profile_not_created_chapters' => ':userName não criou capítulos',
     'profile_not_created_books' => ':userName não criou livros',
+
+    /**
+     * Comments
+     */
+    'comentário' => 'Comentário',
+    'comentários' => 'Comentários',
+    'comment_placeholder' => 'Digite seus comentários aqui',
+    'comment_save' => 'Salvar comentário',
 ];
\ No newline at end of file
index 91b85e3ef00f3211cfa9845506a8d4171167da89..16fc78ff55d27441a2be0c1b1a01196cc0d56904 100644 (file)
@@ -67,4 +67,11 @@ return [
     'error_occurred' => 'Um erro ocorreu',
     'app_down' => ':appName está fora do ar no momento',
     'back_soon' => 'Voltaremos em seguida.',
+
+    // comments
+    'comment_list' => 'Ocorreu um erro ao buscar os comentários.',
+    'cannot_add_comment_to_draft' => 'Você não pode adicionar comentários a um rascunho.',
+    'comment_add' => 'Ocorreu um erro ao adicionar o comentário.',
+    'comment_delete' => 'Ocorreu um erro ao excluir o comentário.',
+    'empty_comment' => 'Não é possível adicionar um comentário vazio.',
 ];
\ No newline at end of file
index e708647536e3a1c0c698dab549a661bfeaece0e7..25a1af140bad58d7539839fa2314f20c169ff071 100644 (file)
@@ -223,4 +223,12 @@ return [
     'profile_not_created_pages' => ':userName nevytvoril žiadne stránky',
     'profile_not_created_chapters' => ':userName nevytvoril žiadne kapitoly',
     'profile_not_created_books' => ':userName nevytvoril žiadne knihy',
+
+    /**
+     * Comments
+     */
+    'comment' => 'Komentár',
+    'comments' => 'Komentáre',
+    'comment_placeholder' => 'Tu zadajte svoje pripomienky',
+    'comment_save' => 'Uložiť komentár',
 ];
index e3420852ad500b5bed6e3d7f4ea2d670347967ad..d4c7b7a3a3a8f75ff07a307ae272ba5cfeb66691 100644 (file)
@@ -67,4 +67,11 @@ return [
     'error_occurred' => 'Nastala chyba',
     'app_down' => ':appName je momentálne nedostupná',
     'back_soon' => 'Čoskoro bude opäť dostupná.',
+
+    // comments
+    'comment_list' => 'Pri načítaní komentárov sa vyskytla chyba',
+    'cannot_add_comment_to_draft' => 'Do konceptu nemôžete pridávať komentáre.',
+    'comment_add' => 'Počas pridávania komentára sa vyskytla chyba',
+    'comment_delete' => 'Pri odstraňovaní komentára došlo k chybe',
+    'empty_comment' => 'Nelze pridať prázdny komentár.',
 ];
index 706747b8b65eecca75056c33f85a89bcb1a7ebf4..dda7336457b184a02ef5c4aeac489ef388b8c273 100644 (file)
@@ -9,36 +9,38 @@
 @section('content')
 
     <div class="text-center">
-        <div class="center-box">
-            <h1>{{ title_case(trans('auth.log_in')) }}</h1>
-
-            <form action="{{ baseUrl("/login") }}" method="POST" id="login-form">
-                {!! csrf_field() !!}
-
-
-                @include('auth/forms/login/' . $authMethod)
-
-                <div class="form-group">
-                    <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> {{ title_case(trans('auth.log_in')) }}</button>
-                </div>
-            </form>
-
-            @if(count($socialDrivers) > 0)
-                <hr class="margin-top">
-                @foreach($socialDrivers as $driver => $name)
-                    <a id="social-login-{{$driver}}" class="button block muted-light svg text-left" href="{{ baseUrl("/login/service/" . $driver) }}">
-                        @icon($driver)
-                        {{ trans('auth.log_in_with', ['socialDriver' => $name]) }}
-                    </a>
-                @endforeach
-            @endif
+        <div class="card center-box">
+            <h3><i class="zmdi zmdi-sign-in"></i> {{ title_case(trans('auth.log_in')) }}</h3>
+
+            <div class="body">
+                <form action="{{ baseUrl("/login") }}" method="POST" id="login-form">
+                    {!! csrf_field() !!}
+
+
+                    @include('auth/forms/login/' . $authMethod)
+
+                    <div class="form-group">
+                        <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> {{ title_case(trans('auth.log_in')) }}</button>
+                    </div>
+                </form>
+
+                @if(count($socialDrivers) > 0)
+                    <hr class="margin-top">
+                    @foreach($socialDrivers as $driver => $name)
+                        <a id="social-login-{{$driver}}" class="button block muted-light svg text-left" href="{{ baseUrl("/login/service/" . $driver) }}">
+                            @icon($driver)
+                            {{ trans('auth.log_in_with', ['socialDriver' => $name]) }}
+                        </a>
+                    @endforeach
+                @endif
+            </div>
         </div>
     </div>
 
index 364df92664182694c103ebea3757d46576491c13..5d945ef8163bce33b59371a5b75e9624b6646e13 100644 (file)
@@ -9,9 +9,11 @@
 @section('content')
 
     <div class="text-center">
-        <div class="center-box">
-            <h2>{{ trans('auth.register_thanks') }}</h2>
-            <p>{{ trans('auth.register_confirm', ['appName' => setting('app-name')]) }}</p>
+        <div class="card center-box">
+            <h3><i class="zmdi zmdi-accounts"></i> {{ trans('auth.register_thanks') }}</h3>
+            <div class="body">
+                <p>{{ trans('auth.register_confirm', ['appName' => setting('app-name')]) }}</p>
+            </div>
         </div>
     </div>
 
index d5db4afa8a4987d94e28198d8e26d6edbe7e3fa5..7b319d30cc9b5d264a7eadf6e44e2c6d816347a2 100644 (file)
@@ -7,41 +7,42 @@
 @section('content')
 
     <div class="text-center">
-        <div class="center-box">
-            <h1>{{ title_case(trans('auth.sign_up')) }}</h1>
-
-            <form action="{{ baseUrl("/register") }}" method="POST">
-                {!! csrf_field() !!}
-
-                <div class="form-group">
-                    <label for="email">{{ trans('auth.name') }}</label>
-                    @include('form/text', ['name' => 'name'])
-                </div>
-
-                <div class="form-group">
-                    <label for="email">{{ trans('auth.email') }}</label>
-                    @include('form/text', ['name' => 'email'])
-                </div>
-
-                <div class="form-group">
-                    <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">{{ trans('auth.create_account') }}</button>
-                </div>
-            </form>
-
-            @if(count($socialDrivers) > 0)
-                <hr class="margin-top">
-                @foreach($socialDrivers as $driver => $name)
-                    <a id="social-register-{{$driver}}" class="button block muted-light svg text-left" href="{{ baseUrl("/register/service/" . $driver) }}">
-                        @icon($driver)
-                        {{ trans('auth.sign_up_with', ['socialDriver' => $name]) }}
-                    </a>
-                @endforeach
-            @endif
+        <div class="card center-box">
+            <h3><i class="zmdi zmdi-account-add"></i> {{ title_case(trans('auth.sign_up')) }}</h3>
+            <div class="body">
+                <form action="{{ baseUrl("/register") }}" method="POST">
+                    {!! csrf_field() !!}
+
+                    <div class="form-group">
+                        <label for="email">{{ trans('auth.name') }}</label>
+                        @include('form/text', ['name' => 'name'])
+                    </div>
+
+                    <div class="form-group">
+                        <label for="email">{{ trans('auth.email') }}</label>
+                        @include('form/text', ['name' => 'email'])
+                    </div>
+
+                    <div class="form-group">
+                        <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">{{ trans('auth.create_account') }}</button>
+                    </div>
+                </form>
+
+                @if(count($socialDrivers) > 0)
+                    <hr class="margin-top">
+                    @foreach($socialDrivers as $driver => $name)
+                        <a id="social-register-{{$driver}}" class="button block muted-light svg text-left" href="{{ baseUrl("/register/service/" . $driver) }}">
+                            @icon($driver)
+                            {{ trans('auth.sign_up_with', ['socialDriver' => $name]) }}
+                        </a>
+                    @endforeach
+                @endif
+            </div>
         </div>
     </div>
 
index 13567b412817e1f20fbb6245fcdf6062b9e37f74..22c26d92b3e71ae570e8c6168ae2bebfe2c9a6b5 100644 (file)
@@ -2,29 +2,33 @@
 
 @section('content')
 
-    <div class="row">
-        <div class="col-md-6 col-md-offset-3">
-            <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">{{ trans('auth.email') }}</label>
-                    @if(auth()->check())
-                        @include('form/text', ['name' => 'email', 'model' => auth()->user()])
-                    @else
-                        @include('form/text', ['name' => 'email'])
-                    @endif
-                </div>
-                <div class="form-group">
-                    <button type="submit" class="button pos">{{ trans('auth.email_not_confirmed_resend_button') }}</button>
-                </div>
-            </form>
+    <div class="container small">
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-accounts"></i> {{ trans('auth.email_not_confirmed') }}</h3>
+            <div class="body">
+                <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">{{ trans('auth.email') }}</label>
+                        @if(auth()->check())
+                            @include('form/text', ['name' => 'email', 'model' => auth()->user()])
+                        @else
+                            @include('form/text', ['name' => 'email'])
+                        @endif
+                    </div>
+                    <div class="form-group">
+                        <button type="submit" class="button pos">{{ trans('auth.email_not_confirmed_resend_button') }}</button>
+                    </div>
+                </form>
+            </div>
         </div>
+
     </div>
 
 @stop
index 95a9d72b0dcff779bfea147298a97ca208a37129..edbd6a096526bcbe9a0284306c018da99959b5cd 100644 (file)
@@ -34,9 +34,9 @@
     @include('partials/notifications')
 
     <header id="header">
-        <div class="container">
+        <div class="container fluid">
             <div class="row">
-                <div class="col-lg-4 col-sm-4" ng-non-bindable>
+                <div class="col-sm-4" ng-non-bindable>
                     <a href="{{ baseUrl('/') }}" class="logo">
                         @if(setting('app-logo', '') !== 'none')
                             <img class="logo-image" src="{{ setting('app-logo', '') === '' ? baseUrl('/logo.png') : baseUrl(setting('app-logo', '')) }}" alt="Logo">
                         @endif
                     </a>
                 </div>
-                <div class="col-lg-4 col-sm-3 text-center">
-                    <form action="{{ baseUrl('/search') }}" method="GET" class="search-box">
-                        <input id="header-search-box-input" type="text" name="term" tabindex="2" value="{{ isset($searchTerm) ? $searchTerm : '' }}">
-                        <button id="header-search-box-button" type="submit" class="text-button"><i class="zmdi zmdi-search"></i></button>
-                    </form>
-                </div>
-                <div class="col-lg-4 col-sm-5">
+                <div class="col-sm-8">
                     <div class="float right">
+                        <div class="header-search">
+                            <form action="{{ baseUrl('/search') }}" method="GET" class="search-box">
+                                <button id="header-search-box-button" type="submit"><i class="zmdi zmdi-search"></i> </button>
+                                <input id="header-search-box-input" type="text" name="term" tabindex="2" placeholder="{{ trans('common.search') }}" value="{{ isset($searchTerm) ? $searchTerm : '' }}">
+                            </form>
+                        </div>
                         <div class="links text-center">
                             <a href="{{ baseUrl('/books') }}"><i class="zmdi zmdi-book"></i>{{ trans('entities.books') }}</a>
                             @if(signedInUser() && userCan('settings-manage'))
@@ -77,7 +77,7 @@
         @yield('content')
     </section>
 
-    <div id="back-to-top">
+    <div back-to-top>
         <div class="inner">
             <i class="zmdi zmdi-chevron-up"></i> <span>{{ trans('common.back_to_top') }}</span>
         </div>
index 2c629e699622053940849dc207121cb4a201b21d..385e6b9d7a4e868f4a5b42acf4585c287068c8cd 100644 (file)
@@ -1,12 +1,27 @@
-@extends('base')
+@extends('simple-layout')
 
-@section('content')
+@section('toolbar')
+    <div class="col-sm-8 faded">
+        <div class="breadcrumbs">
+            <a href="{{ baseUrl('/books') }}" class="text-button"><i class="zmdi zmdi-book"></i>{{ trans('entities.books') }}</a>
+            <span class="sep">&raquo;</span>
+            <a href="{{ baseUrl('/books/create') }}" class="text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.books_create') }}</a>
+        </div>
+    </div>
+@stop
 
-<div class="container small" ng-non-bindable>
-    <h1>{{ trans('entities.books_create') }}</h1>
-    <form action="{{ baseUrl("/books") }}" method="POST">
-        @include('books/form')
-    </form>
+@section('body')
+
+<div ng-non-bindable class="container small">
+    <p>&nbsp;</p>
+    <div class="card">
+        <h3><i class="zmdi zmdi-plus"></i> {{ trans('entities.books_create') }}</h3>
+        <div class="body">
+            <form action="{{ baseUrl("/books") }}" method="POST">
+                @include('books/form')
+            </form>
+        </div>
+    </div>
 </div>
 
 @stop
\ No newline at end of file
index 0b1e67d4acc7dc3dbd9c52dfcedf357a2c3d2444..9bcb1cfc68a63ae27db002f36f3ac108b51ee2fc 100644 (file)
@@ -1,28 +1,30 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('books._breadcrumbs', ['book' => $book])
     </div>
+@stop
+
+@section('body')
 
     <div class="container small" ng-non-bindable>
-        <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>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-delete"></i> {{ trans('entities.books_delete') }}</h3>
+            <div class="body">
+                <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 outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
+                </form>
+            </div>
+        </div>
 
-        <form action="{{$book->getUrl()}}" method="POST">
-            {!! csrf_field() !!}
-            <input type="hidden" name="_method" value="DELETE">
-            <a href="{{$book->getUrl()}}" class="button">{{ trans('common.cancel') }}</a>
-            <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
-        </form>
     </div>
 
 @stop
\ No newline at end of file
index 2419b68da807ab1a346293ee38ec694e67c1c25a..9d9c54fec6e521e909c6e5ce159c640c975dc3e5 100644 (file)
@@ -1,23 +1,24 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('books._breadcrumbs', ['book' => $book])
     </div>
+@stop
+
+@section('body')
 
     <div class="container small" ng-non-bindable>
-        <h1>{{ trans('entities.books_edit') }}</h1>
-        <form action="{{ $book->getUrl() }}" method="POST">
-            <input type="hidden" name="_method" value="PUT">
-            @include('books/form', ['model' => $book])
-        </form>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.books_edit') }}</h3>
+            <div class="body">
+                <form action="{{ $book->getUrl() }}" method="POST">
+                    <input type="hidden" name="_method" value="PUT">
+                    @include('books/form', ['model' => $book])
+                </form>
+            </div>
+        </div>
     </div>
 
 @stop
\ No newline at end of file
index b1484d1296f3cf84ef6f2e59bf11d6af8a5c3c2d..84a30e7e9982a40e7e0c5ecfe9c1bf752c9693b3 100644 (file)
@@ -10,7 +10,7 @@
     @include('form/textarea', ['name' => 'description'])
 </div>
 
-<div class="form-group">
-    <a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button muted">{{ trans('common.cancel') }}</a>
+<div class="form-group text-right">
+    <a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button outline">{{ trans('common.cancel') }}</a>
     <button type="submit" class="button pos">{{ trans('entities.books_save') }}</button>
 </div>
\ No newline at end of file
index c090a127e1dee9076020d9397ce46f7eaaf39804..c22b4591f01c43f3efc126cf30dc40a8acc4e1d7 100644 (file)
@@ -1,59 +1,59 @@
-@extends('base')
+@extends('sidebar-layout')
 
-@section('content')
+@section('toolbar')
+    <div class="col-xs-1"></div>
+    <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>{{ trans('entities.books_create') }}</a>
+            @endif
+        </div>
+    </div>
+@stop
 
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-xs-1"></div>
-                <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>{{ trans('entities.books_create') }}</a>
-                        @endif
-                    </div>
-                </div>
-            </div>
+@section('sidebar')
+    @if($recents)
+        <div id="recents" class="card">
+            <h3><i class="zmdi zmdi-eye"></i> {{ trans('entities.recently_viewed') }}</h3>
+            @include('partials/entity-list', ['entities' => $recents, 'style' => 'compact'])
         </div>
+    @endif
+
+    <div id="popular" class="card">
+        <h3><i class="zmdi zmdi-fire"></i> {{ trans('entities.books_popular') }}</h3>
+        @if(count($popular) > 0)
+            @include('partials/entity-list', ['entities' => $popular, 'style' => 'compact'])
+        @else
+            <div class="body text-muted">{{ trans('entities.books_popular_empty') }}</div>
+        @endif
     </div>
 
+    <div id="new" class="card">
+        <h3><i class="zmdi zmdi-star-circle"></i> {{ trans('entities.books_new') }}</h3>
+        @if(count($popular) > 0)
+            @include('partials/entity-list', ['entities' => $new, 'style' => 'compact'])
+        @else
+            <div class="body text-muted">{{ trans('entities.books_new_empty') }}</div>
+        @endif
+    </div>
+@stop
 
-    <div class="container" ng-non-bindable>
-        <div class="row">
-            <div class="col-sm-7">
-                <h1>{{ trans('entities.books') }}</h1>
-                @if(count($books) > 0)
-                    @foreach($books as $book)
-                        @include('books/list-item', ['book' => $book])
-                        <hr>
-                    @endforeach
-                    {!! $books->render() !!}
-                @else
-                    <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>{{ 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">&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>{{ trans('entities.books_popular') }}</h3>
-                    @if(count($popular) > 0)
-                        @include('partials/entity-list', ['entities' => $popular])
-                    @else
-                        <p class="text-muted">{{ trans('entities.books_popular_empty') }}</p>
-                    @endif
-                </div>
-            </div>
-        </div>
+@section('body')
+
+    <div class="container small" ng-non-bindable>
+        <h1>{{ trans('entities.books') }}</h1>
+        @if(count($books) > 0)
+            @foreach($books as $book)
+                @include('books/list-item', ['book' => $book])
+                <hr>
+            @endforeach
+            {!! $books->render() !!}
+        @else
+            <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>{{ trans('entities.create_one_now') }}</a>
+            @endif
+        @endif
     </div>
 
 @stop
\ No newline at end of file
index 605841f7f0ae31ada7c4c9f39d5b96757b7e9e0b..92d0f9e2d1881b58fd28ac5888835fca88af87bd 100644 (file)
@@ -1,8 +1,10 @@
 <div class="book entity-list-item"  data-entity-type="book" data-entity-id="{{$book->id}}">
     <h4 class="text-book"><a class="text-book entity-list-item-link" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i><span class="entity-list-item-name">{{$book->name}}</span></a></h4>
-    @if(isset($book->searchSnippet))
-        <p class="text-muted">{!! $book->searchSnippet !!}</p>
-    @else
-        <p class="text-muted">{{ $book->getExcerpt() }}</p>
-    @endif
+    <div class="entity-item-snippet">
+        @if(isset($book->searchSnippet))
+            <p class="text-muted">{!! $book->searchSnippet !!}</p>
+        @else
+            <p class="text-muted">{{ $book->getExcerpt() }}</p>
+        @endif
+    </div>
 </div>
\ No newline at end of file
index f558fdfce82c2952583cbfb90c9f943d79ed7fa5..5c7604c864fc33f2d106397e0314503f2b8c8d69 100644 (file)
@@ -1,21 +1,21 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('books._breadcrumbs', ['book' => $book])
     </div>
+@stop
 
+@section('body')
 
     <div class="container" ng-non-bindable>
-        <h1>{{ trans('entities.books_permissions') }}</h1>
-        @include('form/restriction-form', ['model' => $book])
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-lock-outline"></i> {{ trans('entities.books_permissions') }}</h3>
+            <div class="body">
+                @include('form/restriction-form', ['model' => $book])
+            </div>
+        </div>
     </div>
 
 @stop
index 9882f09a2085ab1855efddc13b6dade37d3c9901..956dbaf164fffba4b171a5cc25933ccaf4c4ee0a 100644 (file)
-@extends('base')
+@extends('sidebar-layout')
 
-@section('content')
-
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-sm-6 col-xs-1  faded">
-                    @include('books._breadcrumbs', ['book' => $book])
-                </div>
-                <div class="col-sm-6 col-xs-11">
-                    <div class="action-buttons faded">
-                        <span dropdown class="dropdown-container">
-                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
-                            <ul class="wide">
-                                <li><a href="{{ $book->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
-                                <li><a href="{{ $book->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
-                                <li><a href="{{ $book->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
-                            </ul>
-                        </span>
-                        @if(userCan('page-create', $book))
-                            <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>{{ trans('entities.chapters_new') }}</a>
-                        @endif
+@section('toolbar')
+    <div class="col-sm-6 col-xs-1  faded">
+        @include('books._breadcrumbs', ['book' => $book])
+    </div>
+    <div class="col-sm-6 col-xs-11">
+        <div class="action-buttons faded">
+            <span dropdown class="dropdown-container">
+                <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
+                <ul class="wide">
+                    <li><a href="{{ $book->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+                    <li><a href="{{ $book->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                    <li><a href="{{ $book->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                </ul>
+            </span>
+            @if(userCan('page-create', $book))
+                <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>{{ trans('entities.chapters_new') }}</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>{{ trans('common.more') }}</a>
+                    <ul>
                         @if(userCan('book-update', $book))
-                            <a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
+                            <li><a href="{{$book->getEditUrl()}}" class="text-primary"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</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>{{ trans('entities.permissions') }}</a></li>
                         @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>{{ 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>{{ 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>{{ trans('common.delete') }}</a></li>
-                                    @endif
-                                </ul>
-                            </div>
+                        @if(userCan('book-delete', $book))
+                            <li><a href="{{ $book->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
                         @endif
-                    </div>
+                    </ul>
                 </div>
-            </div>
+            @endif
         </div>
     </div>
+@stop
 
+@section('sidebar')
 
-    <div ng-non-bindable class="container" id="entity-dashboard" entity-id="{{ $book->id }}" entity-type="book">
-        <div class="row">
-            <div class="col-md-7">
+    <div class="card">
+        <div class="body">
+            <form v-on:submit.prevent="searchBook" class="search-box">
+                <input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
+                <button type="submit"><i class="zmdi zmdi-search"></i></button>
+                <button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
+            </form>
+        </div>
+    </div>
 
-                <h1>{{$book->name}}</h1>
-                <div class="book-content" v-if="!searching">
-                    <p class="text-muted" v-pre>{!! nl2br(e($book->description)) !!}</p>
+    @if($book->restricted)
+        <div class="card">
+            <h3><i class="zmdi zmdi-key"></i> {{ trans('entities.permissions') }}</h3>
+            <div class="body">
+                <p class="text-muted">
+                    @if(userCan('restrictions-manage', $book))
+                        <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>{{ trans('entities.books_permissions_active') }}
+                    @endif
+                </p>
+            </div>
+        </div>
+    @endif
 
-                    <div class="page-list" v-pre>
-                        <hr>
-                        @if(count($bookChildren) > 0)
-                            @foreach($bookChildren as $childElement)
-                                @if($childElement->isA('chapter'))
-                                    @include('chapters/list-item', ['chapter' => $childElement])
-                                @else
-                                    @include('pages/list-item', ['page' => $childElement])
-                                @endif
-                                <hr>
-                            @endforeach
-                        @else
-                            <p class="text-muted">{{ trans('entities.books_empty_contents') }}</p>
-                            <p>
-                                @if(userCan('page-create', $book))
-                                <a href="{{ $book->getUrl('/page/create') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
-                                @endif
-                                @if(userCan('page-create', $book) && userCan('chapter-create', $book))
-                                &nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
-                                @endif
-                                @if(userCan('chapter-create', $book))
-                                <a href="{{ $book->getUrl('/chapter/create') }}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>{{ trans('entities.books_empty_add_chapter') }}</a>
-                                @endif
-                            </p>
-                            <hr>
-                        @endif
-                        @include('partials.entity-meta', ['entity' => $book])
-                    </div>
-                </div>
-                <div class="search-results" v-cloak v-if="searching">
-                    <h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" v-on:click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
-                    <div v-if="!searchResults">
-                        @include('partials/loading-icon')
-                    </div>
-                    <div v-html="searchResults"></div>
-                </div>
+    @if(count($activity) > 0)
+        <div class="activity card">
+            <h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
+            @include('partials/activity-list', ['activity' => $activity])
+        </div>
+    @endif
 
+    <div class="card">
+        <h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
+        <div class="body">
+            @include('partials.entity-meta', ['entity' => $book])
+        </div>
+    </div>
+@stop
 
-            </div>
+@section('container-attrs')
+    id="entity-dashboard"
+    entity-id="{{ $book->id }}"
+    entity-type="book"
+@stop
 
-            <div class="col-md-4 col-md-offset-1">
-                <div class="margin-top large"></div>
+@section('body')
 
-                @if($book->restricted)
-                    <p class="text-muted">
-                        @if(userCan('restrictions-manage', $book))
-                            <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>{{ trans('entities.books_permissions_active') }}
+    <div ng-non-bindable class="container small">
+        <h1>{{$book->name}}</h1>
+        <div class="book-content" v-show="!searching">
+            <p class="text-muted" v-pre>{!! nl2br(e($book->description)) !!}</p>
+            @if(count($bookChildren) > 0)
+            <div class="page-list" v-pre>
+                <hr>
+                @foreach($bookChildren as $childElement)
+                    @if($childElement->isA('chapter'))
+                        @include('chapters/list-item', ['chapter' => $childElement])
+                    @else
+                        @include('pages/list-item', ['page' => $childElement])
+                    @endif
+                    <hr>
+                @endforeach
+            </div>
+            @else
+                <div class="well">
+                    <p class="text-muted italic">{{ trans('entities.books_empty_contents') }}</p>
+                        @if(userCan('page-create', $book))
+                            <a href="{{ $book->getUrl('/page/create') }}" class="button outline page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
+                        @endif
+                        @if(userCan('page-create', $book) && userCan('chapter-create', $book))
+                            &nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
+                        @endif
+                        @if(userCan('chapter-create', $book))
+                            <a href="{{ $book->getUrl('/chapter/create') }}" class="button outline chapter"><i class="zmdi zmdi-collection-bookmark"></i>{{ trans('entities.books_empty_add_chapter') }}</a>
                         @endif
-                    </p>
-                @endif
-
-                <div class="search-box">
-                    <form v-on:submit.prevent="searchBook">
-                        <input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
-                        <button type="submit"><i class="zmdi zmdi-search"></i></button>
-                        <button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
-                    </form>
                 </div>
+            @endif
 
-                <div class="activity">
-                    <h3>{{ trans('entities.recent_activity') }}</h3>
-                    @include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)])
-                </div>
+        </div>
+        <div class="search-results" v-cloak v-show="searching">
+            <h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" v-on:click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
+            <div v-if="!searchResults">
+                @include('partials/loading-icon')
             </div>
+            <div v-html="searchResults"></div>
         </div>
     </div>
 
index d96f502f167c9127b5ecdefaab4c8031c197dcb6..ebabb90ceae8496593e966858804a868777da544 100644 (file)
@@ -1,56 +1,59 @@
-@extends('base')
+@extends('simple-layout')
 
 @section('head')
     <script src="{{ baseUrl("/libs/jquery-sortable/jquery-sortable.min.js") }}"></script>
 @stop
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('books._breadcrumbs', ['book' => $book])
     </div>
+@stop
+
+@section('body')
 
     <div class="container" ng-non-bindable>
-        <h1>{{ trans('entities.books_sort') }}</h1>
-        <div class="row">
-            <div class="col-md-8" id="sort-boxes">
 
-                @include('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren])
+        <div class="row">
+            <div class="col-md-8">
+                <div class="card">
+                    <h3><i class="zmdi zmdi-sort"></i> {{ trans('entities.books_sort') }}</h3>
+                    <div class="body">
+                        <div id="sort-boxes">
+                            @include('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren])
+                        </div>
 
+                        <form action="{{ $book->getUrl('/sort') }}" method="POST">
+                            {!! csrf_field() !!}
+                            <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 outline">{{ trans('common.cancel') }}</a>
+                                <button class="button pos" type="submit">{{ trans('entities.books_sort_save') }}</button>
+                            </div>
+                        </form>
+                    </div>
+                </div>
             </div>
-
             @if(count($books) > 1)
-                <div class="col-md-4">
-                    <h3>{{ trans('entities.books_sort_show_other') }}</h3>
-                    <div id="additional-books">
-                    @foreach($books as $otherBook)
-                        @if($otherBook->id !== $book->id)
-                        <div>
-                            <a href="{{ $otherBook->getUrl('/sort-item') }}" class="text-book"><i class="zmdi zmdi-book"></i>{{ $otherBook->name }}</a>
-                        </div>
-                        @endif
-                    @endforeach
+            <div class="col-md-4">
+                <div class="card">
+                    <h3><i class="zmdi zmdi-book"></i> {{ trans('entities.books_sort_show_other') }}</h3>
+                    <div class="body" id="additional-books">
+                        @foreach($books as $otherBook)
+                            @if($otherBook->id !== $book->id)
+                                <div>
+                                    <a href="{{ $otherBook->getUrl('/sort-item') }}" class="text-book"><i class="zmdi zmdi-book"></i>{{ $otherBook->name }}</a>
+                                </div>
+                            @endif
+                        @endforeach
                     </div>
                 </div>
+            </div>
             @endif
-
         </div>
 
-        <form action="{{ $book->getUrl('/sort') }}" method="POST">
-            {!! csrf_field() !!}
-            <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">{{ trans('common.cancel') }}</a>
-                <button class="button pos" type="submit">{{ trans('entities.books_sort_save') }}</button>
-            </div>
-        </form>
+
 
     </div>
 
index afdbfa99d058c8fd5ee2cca2f3f9377552d7d712..8b2e2f66762818c1a63b124400cbd9c2a27027fd 100644 (file)
@@ -1,12 +1,26 @@
-@extends('base')
+@extends('simple-layout')
 
-@section('content')
+@section('toolbar')
+    <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="{{ baseUrl('/books/chapter/create') }}" class="text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.chapters_create') }}</a>
+        </div>
+    </div>
+@stop
+
+@section('body')
 
     <div class="container small" ng-non-bindable>
-        <h1>{{ trans('entities.chapters_create') }}</h1>
-        <form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
-            @include('chapters/form')
-        </form>
+        <div class="card">
+            <h3><i class="zmdi zmdi-plus"></i> {{ trans('entities.chapters_create') }}</h3>
+            <div class="body">
+                <form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
+                    @include('chapters/form')
+                </form>
+            </div>
+        </div>
     </div>
 
 @stop
\ No newline at end of file
index bacb8dca39df45ea72f83f8d702bfbde766f8958..3ea64044ab7972d6c88efafa2cee7d7d50287353 100644 (file)
@@ -1,28 +1,30 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('chapters._breadcrumbs', ['chapter' => $chapter])
     </div>
+@stop
+
+@section('body')
 
     <div class="container small" ng-non-bindable>
-        <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>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-delete"></i> {{ trans('entities.chapters_delete') }}</h3>
 
-        <form action="{{ $chapter->getUrl() }}" method="POST">
-            {!! csrf_field() !!}
-            <input type="hidden" name="_method" value="DELETE">
-            <a href="{{ $chapter->getUrl() }}" class="button primary">{{ trans('common.cancel') }}</a>
-            <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
-        </form>
+            <div class="body">
+                <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 outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
+                </form>
+            </div>
+        </div>
     </div>
 
 @stop
\ No newline at end of file
index 272543e67f9a6a6a8892c403fbf0af94fea9ddcc..b97be683165c699052d173a8d0268718db4c68e5 100644 (file)
@@ -1,13 +1,24 @@
-@extends('base')
+@extends('simple-layout')
 
-@section('content')
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('chapters._breadcrumbs', ['chapter' => $chapter])
+    </div>
+@stop
+
+@section('body')
 
     <div class="container small" ng-non-bindable>
-        <h1>{{ trans('entities.chapters_edit') }}</h1>
-        <form action="{{  $chapter->getUrl() }}" method="POST">
-            <input type="hidden" name="_method" value="PUT">
-            @include('chapters/form', ['model' => $chapter])
-        </form>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.chapters_edit') }}</h3>
+            <div class="body">
+                <form action="{{  $chapter->getUrl() }}" method="POST">
+                    <input type="hidden" name="_method" value="PUT">
+                    @include('chapters/form', ['model' => $chapter])
+                </form>
+            </div>
+        </div>
     </div>
 
 @stop
\ No newline at end of file
index 54722a58adc0c9094d0a8d1bd1ca3dfa55ec4a52..9e3b8e472685de56fe109a96cf95c10d67ad9aa8 100644 (file)
@@ -11,7 +11,7 @@
     @include('form/textarea', ['name' => 'description'])
 </div>
 
-<div class="form-group">
-    <a href="{{ back()->getTargetUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
+<div class="form-group text-right">
+    <a href="{{ back()->getTargetUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
     <button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
 </div>
index 1ae20b301c6e794b03267ee9e8038a66218e43ca..9c1e2d640a874372251b611081ebf431809949c5 100644 (file)
             <i class="zmdi zmdi-collection-bookmark"></i><span class="entity-list-item-name">{{ $chapter->name }}</span>
         </a>
     </h4>
-    @if(isset($chapter->searchSnippet))
-        <p class="text-muted">{!! $chapter->searchSnippet !!}</p>
-    @else
-        <p class="text-muted">{{ $chapter->getExcerpt() }}</p>
-    @endif
+
+    <div class="entity-item-snippet">
+        @if(isset($chapter->searchSnippet))
+            <p class="text-muted">{!! $chapter->searchSnippet !!}</p>
+        @else
+            <p class="text-muted">{{ $chapter->getExcerpt() }}</p>
+        @endif
+    </div>
+
 
     @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>{{ trans('entities.x_pages', ['count' => $chapter->pages->count()]) }}</span></p>
+        <p chapter-toggle class="text-muted"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans_choice('entities.x_pages', $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>
index 9e6ddb52138b1aa165ba8d231bca5b807ffebfbf..a656c4fae9e199787bb7b67c7a84043b968bd412 100644 (file)
@@ -1,29 +1,34 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('chapters._breadcrumbs', ['chapter' => $chapter])
     </div>
+@stop
+
+@section('body')
 
     <div class="container">
-        <h1>{{ trans('entities.chapters_move') }}</h1>
 
-        <form action="{{ $chapter->getUrl('/move') }}" method="POST">
-            {!! csrf_field() !!}
-            <input type="hidden" name="_method" value="PUT">
+        <div class="card">
+            <h3><i class="zmdi zmdi-folder"></i> {{ trans('entities.chapters_move') }}</h3>
+            <div class="body">
+                <form action="{{ $chapter->getUrl('/move') }}" method="POST">
+                    {!! csrf_field() !!}
+                    <input type="hidden" name="_method" value="PUT">
+
+                    @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
+
+                    <div class="form-group text-right">
+                        <a href="{{ $chapter->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                        <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+
 
-            @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
 
-            <a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
-            <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button>
-        </form>
     </div>
 
 @stop
index 7b908ee15d2144c927fb2b5a2673daf2a9ad5049..c2981c68a7fad6036d1637b7a4b620ac422267da 100644 (file)
@@ -1,20 +1,21 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('chapters._breadcrumbs', ['chapter' => $chapter])
     </div>
+@stop
+
+@section('body')
 
     <div class="container" ng-non-bindable>
-        <h1>{{ trans('entities.chapters_permissions') }}</h1>
-        @include('form/restriction-form', ['model' => $chapter])
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-lock-outline"></i> {{ trans('entities.chapters_permissions') }}</h3>
+            <div class="body">
+                @include('form/restriction-form', ['model' => $chapter])
+            </div>
+        </div>
     </div>
 
 @stop
index 9a319555595cda2ff105a6985c0f5f4cd4551b27..e5a044aa7cafacc0e7481cba202aa6a65834d1ae 100644 (file)
@@ -1,15 +1,11 @@
-@extends('base')
+@extends('sidebar-layout')
 
-@section('content')
-
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-sm-6 col-xs-3 faded" ng-non-bindable>
-                    @include('chapters._breadcrumbs', ['chapter' => $chapter])
-                </div>
-                <div class="col-sm-6 col-xs-9 faded">
-                    <div class="action-buttons">
+@section('toolbar')
+    <div class="col-sm-6 col-xs-3 faded" ng-non-bindable>
+        @include('chapters._breadcrumbs', ['chapter' => $chapter])
+    </div>
+    <div class="col-sm-6 col-xs-9 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>{{ trans('entities.export') }}</div>
                             <ul class="wide">
                                 <li><a href="{{ $chapter->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
                             </ul>
                         </span>
-                        @if(userCan('page-create', $chapter))
-                            <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('page-create', $chapter))
+                <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>{{ 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> {{ trans('common.more') }}</a>
+                    <ul>
                         @if(userCan('chapter-update', $chapter))
-                            <a href="{{ $chapter->getUrl('/edit') }}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
+                            <li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary"><i class="zmdi zmdi-folder"></i>{{ trans('common.move') }}</a></li>
                         @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>{{ 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>{{ 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>{{ trans('common.delete') }}</a></li>
-                                    @endif
-                                </ul>
-                            </div>
+                        @if(userCan('restrictions-manage', $chapter))
+                            <li><a href="{{ $chapter->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.permissions') }}</a></li>
                         @endif
-                    </div>
+                        @if(userCan('chapter-delete', $chapter))
+                            <li><a href="{{ $chapter->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
+                        @endif
+                    </ul>
                 </div>
-            </div>
+            @endif
         </div>
     </div>
+@stop
 
+@section('container-attrs')
+    id="entity-dashboard"
+    entity-id="{{ $chapter->id }}"
+    entity-type="chapter"
+@stop
 
-    <div class="container" id="entity-dashboard" ng-non-bindable entity-id="{{ $chapter->id }}" entity-type="chapter">
-        <div class="row">
-            <div class="col-md-7">
-                <h1>{{ $chapter->name }}</h1>
-                <div class="chapter-content" v-if="!searching">
-                    <p class="text-muted">{!! nl2br(e($chapter->description)) !!}</p>
-
-                    @if(count($pages) > 0)
-                        <div class="page-list">
-                            <hr>
-                            @foreach($pages as $page)
-                                @include('pages/list-item', ['page' => $page])
-                                <hr>
-                            @endforeach
-                        </div>
-                    @else
-                        <hr>
-                        <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>{{ trans('entities.books_empty_create_page') }}</a>
-                            @endif
-                            @if(userCan('page-create', $chapter) && userCan('book-update', $book))
-                                &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>{{ trans('entities.books_empty_sort_current_book') }}</a>
-                            @endif
-                        </p>
-                        <hr>
-                    @endif
-
-                    @include('partials.entity-meta', ['entity' => $chapter])
-                </div>
-
-                <div class="search-results" v-cloak v-if="searching">
-                    <h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" v-on:click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
-                    <div v-if="!searchResults">
-                        @include('partials/loading-icon')
-                    </div>
-                    <div v-html="searchResults"></div>
-                </div>
-            </div>
-            <div class="col-md-4 col-md-offset-1">
-                <div class="margin-top large"></div>
-                @if($book->restricted || $chapter->restricted)
-                    <div class="text-muted">
+@section('sidebar')
+    <div class="card">
+        <div class="body">
+            <form @submit.prevent="searchBook" class="search-box">
+                <input v-model="searchTerm" @change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.chapters_search_this') }}">
+                <button type="submit"><i class="zmdi zmdi-search"></i></button>
+                <button v-if="searching" v-cloak class="text-neg" @click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
+            </form>
+        </div>
+    </div>
 
-                        @if($book->restricted)
-                            <p class="text-muted">
-                                @if(userCan('restrictions-manage', $book))
-                                    <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>{{ trans('entities.books_permissions_active') }}
-                                @endif
-                            </p>
+    @if($book->restricted || $chapter->restricted)
+        <div class="card">
+            <h3><i class="zmdi zmdi-key"></i> {{ trans('entities.permissions') }}</h3>
+            <div class="body">
+                @if($book->restricted)
+                    <p class="text-muted">
+                        @if(userCan('restrictions-manage', $book))
+                            <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>{{ trans('entities.books_permissions_active') }}
                         @endif
+                    </p>
+                @endif
 
-                        @if($chapter->restricted)
-                            @if(userCan('restrictions-manage', $chapter))
-                                <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>{{ trans('entities.chapters_permissions_active') }}
-                            @endif
+                @if($chapter->restricted)
+                    <p class="text-muted">
+                        @if(userCan('restrictions-manage', $chapter))
+                            <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>{{ trans('entities.chapters_permissions_active') }}
                         @endif
-                    </div>
+                    </p>
                 @endif
-
-                <div class="search-box">
-                    <form v-on:submit.prevent="searchBook">
-                        <input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.chapters_search_this') }}">
-                        <button type="submit"><i class="zmdi zmdi-search"></i></button>
-                        <button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
-                    </form>
-                </div>
-
-                @include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree])
-
             </div>
         </div>
+    @endif
+
+    <div class="card">
+        <h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
+        <div class="body">
+            @include('partials.entity-meta', ['entity' => $chapter])
+        </div>
     </div>
 
+    @include('partials/book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
+@stop
+
+@section('body')
 
+    <div class="container small"  ng-non-bindable >
+        <h1>{{ $chapter->name }}</h1>
+        <div class="chapter-content" v-show="!searching">
+            <p class="text-muted">{!! nl2br(e($chapter->description)) !!}</p>
+
+            @if(count($pages) > 0)
+                <div class="page-list">
+                    <hr>
+                    @foreach($pages as $page)
+                        @include('pages/list-item', ['page' => $page])
+                        <hr>
+                    @endforeach
+                </div>
+            @else
+                <div class="well">
+                    <p class="text-muted italic">{{ trans('entities.chapters_empty') }}</p>
+                    <p>
+                        @if(userCan('page-create', $chapter))
+                            <a href="{{ $chapter->getUrl('/create-page') }}" class="button outline 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">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp; &nbsp;
+                        @endif
+                        @if(userCan('book-update', $book))
+                            <a href="{{ $book->getUrl('/sort') }}" class="button outline book"><i class="zmdi zmdi-book"></i>{{ trans('entities.books_empty_sort_current_book') }}</a>
+                        @endif
+                    </p>
+                </div>
+            @endif
+        </div>
 
+        <div class="search-results" v-cloak v-show="searching">
+            <h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" @click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
+            <div v-if="!searchResults">
+                @include('partials/loading-icon')
+            </div>
+            <div v-html="searchResults"></div>
+        </div>
+    </div>
 
 @stop
diff --git a/resources/views/comments/comment.blade.php b/resources/views/comments/comment.blade.php
new file mode 100644 (file)
index 0000000..ad53107
--- /dev/null
@@ -0,0 +1,76 @@
+<div class="comment-box" comment="{{ $comment->id }}" local-id="{{$comment->local_id}}" parent-id="{{$comment->parent_id}}" id="comment{{$comment->local_id}}">
+    <div class="header">
+
+        <div class="float right actions">
+            @if(userCan('comment-update', $comment))
+                <button type="button" class="text-button" action="edit" title="{{ trans('common.edit') }}"><i class="zmdi zmdi-edit"></i></button>
+            @endif
+            @if(userCan('comment-create-all'))
+                <button type="button" class="text-button" action="reply" title="{{ trans('common.reply') }}"><i class="zmdi zmdi-mail-reply-all"></i></button>
+            @endif
+            @if(userCan('comment-delete', $comment))
+
+                <div dropdown class="dropdown-container">
+                    <button type="button" dropdown-toggle class="text-button" title="{{ trans('common.delete') }}"><i class="zmdi zmdi-delete"></i></button>
+                    <ul>
+                        <li class="padded"><small class="text-muted">{{trans('entities.comment_delete_confirm')}}</small></li>
+                        <li><a action="delete" class="text-button neg" ><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
+                    </ul>
+                </div>
+            @endif
+        </div>
+
+        <div class="meta">
+            <a href="#comment{{$comment->local_id}}" class="text-muted">#{{$comment->local_id}}</a>
+            &nbsp;&nbsp;
+            @if ($comment->createdBy)
+                <img width="50" src="{{ $comment->createdBy->getAvatar(50) }}" class="avatar" alt="{{ $comment->createdBy->name }}">
+                &nbsp;
+                <a href="{{ $comment->createdBy->getProfileUrl() }}">{{ $comment->createdBy->name }}</a>
+            @else
+                <span>{{ trans('common.deleted_user') }}</span>
+            @endif
+            <span title="{{ $comment->created_at }}">
+            {{ trans('entities.comment_created', ['createDiff' => $comment->created]) }}
+        </span>
+            @if($comment->isUpdated())
+                <span title="{{ $comment->updated_at }}">
+                &bull;&nbsp;
+                    {{ trans('entities.comment_updated', ['updateDiff' => $comment->updated, 'username' => $comment->updatedBy? $comment->updatedBy->name : trans('common.deleted_user')]) }}
+            </span>
+            @endif
+        </div>
+
+    </div>
+
+    @if ($comment->parent_id)
+        <div class="reply-row primary-background-light text-muted">
+            {!! trans('entities.comment_in_reply_to', ['commentId' => '<a href="#comment'.$comment->parent_id.'">#'.$comment->parent_id.'</a>']) !!}
+        </div>
+    @endif
+
+    <div comment-content class="content">
+        <div class="form-group loading" style="display: none;">
+            @include('partials.loading-icon', ['text' => trans('entities.comment_deleting')])
+        </div>
+        {!! $comment->html  !!}
+    </div>
+
+    @if(userCan('comment-update', $comment))
+        <div comment-edit-container style="display: none;" class="content">
+            <form novalidate>
+                <div class="form-group">
+                    <textarea name="markdown" rows="3" v-model="comment.text" placeholder="{{ trans('entities.comment_placeholder') }}">{{ $comment->text }}</textarea>
+                </div>
+                <div class="form-group text-right">
+                    <button type="button" class="button outline" action="closeUpdateForm">{{ trans('common.cancel') }}</button>
+                    <button type="submit" class="button pos">{{ trans('entities.comment_save') }}</button>
+                </div>
+                <div class="form-group loading" style="display: none;">
+                    @include('partials.loading-icon', ['text' => trans('entities.comment_saving')])
+                </div>
+            </form>
+        </div>
+    @endif
+
+</div>
\ No newline at end of file
diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php
new file mode 100644 (file)
index 0000000..a5d6d3d
--- /dev/null
@@ -0,0 +1,40 @@
+<div page-comments page-id="{{ $page->id }}" ng-non-bindable class="comments-list">
+  <h3 comments-title>{{ trans_choice('entities.comment_count', count($page->comments), ['count' => count($page->comments)]) }}</h3>
+
+    <div class="comment-container" comment-container>
+        @foreach($page->comments as $comment)
+            @include('comments.comment', ['comment' => $comment])
+        @endforeach
+    </div>
+
+
+    @if(userCan('comment-create-all'))
+
+        <div class="comment-box" comment-box style="display:none;">
+            <div class="header"><i class="zmdi zmdi-comment"></i> {{ trans('entities.comment_new') }}</div>
+            <div comment-form-reply-to class="reply-row primary-background-light text-muted" style="display: none;">
+                <button class="text-button float right" action="remove-reply-to">{{ trans('common.remove') }}</button>
+                {!! trans('entities.comment_in_reply_to', ['commentId' => '<a href=""></a>']) !!}
+            </div>
+            <div class="content" comment-form-container>
+                <form novalidate>
+                    <div class="form-group">
+                        <textarea name="markdown" rows="3" v-model="comment.text" placeholder="{{ trans('entities.comment_placeholder') }}"></textarea>
+                    </div>
+                    <div class="form-group text-right">
+                        <button type="button" class="button outline" action="hideForm">{{ trans('common.cancel') }}</button>
+                        <button type="submit" class="button pos">{{ trans('entities.comment_save') }}</button>
+                    </div>
+                    <div class="form-group loading" style="display: none;">
+                        @include('partials.loading-icon', ['text' => trans('entities.comment_saving')])
+                    </div>
+                </form>
+            </div>
+        </div>
+
+        <div class="form-group" comment-add-button>
+            <button type="button" action="addComment" class="button outline">Add Comment</button>
+        </div>
+    @endif
+
+</div>
\ No newline at end of file
index 5a385ef49bae2ab463e393f5138023863862f167..5788bd7f7a6cad97eddd933c9cf2a2c4a296b392 100644 (file)
@@ -1,10 +1,10 @@
 <div id="code-editor">
-    <div class="overlay" ref="overlay" v-cloak @click="hide()">
+    <div overlay ref="overlay" v-cloak @click="hide()">
         <div class="popup-body" @click.stop>
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.code_editor') }}</div>
-                <button class="popup-close neg corner-button button" @click="hide()">x</button>
+                <button class="overlay-close neg corner-button button" @click="hide()">x</button>
             </div>
 
             <div class="padded">
index 1c4d1fadba9da58e41c8aa229ccfaab14e6f0c9b..ecd03c80f3fd32e66f5c82a33f782e90e1bbbaa0 100644 (file)
@@ -1,9 +1,9 @@
 <div id="entity-selector-wrap">
-    <div class="overlay" entity-link-selector>
+    <div overlay entity-selector-popup>
         <div class="popup-body small flex-child">
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('entities.entity_select') }}</div>
-                <button type="button" class="corner-button neg button popup-close">x</button>
+                <button type="button" class="corner-button neg button overlay-close">x</button>
             </div>
             @include('components.entity-selector', ['name' => 'entity-selector'])
             <div class="popup-footer">
index 8fb2187e6ff7395e8264c657813990fb79472baf..03e2066ed194b9f6ad597a53d497f8b0bb88745e 100644 (file)
@@ -1,8 +1,8 @@
 <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="{{ 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>
+        <input type="text" placeholder="{{ trans('common.search') }}" entity-selector-search>
+        <div class="text-center loading" entity-selector-loading>@include('partials.loading-icon')</div>
+        <div entity-selector-results></div>
     </div>
 </div>
\ No newline at end of file
index 39f3bcd3c1a27f2072d7357a9f7a69bf705741b0..a4612a4aca92eb128c0a96691f513a96e4cafa87 100644 (file)
@@ -1,91 +1,89 @@
-<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController" uploaded-to="{{ $uploaded_to or 0 }}">
-    <div class="overlay" ng-cloak ng-click="hide()">
-        <div class="popup-body" ng-click="$event.stopPropagation()">
+<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to or 0 }}">
+    <div overlay v-cloak>
+        <div class="popup-body" @click.stop="">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.image_select') }}</div>
-                <button class="popup-close neg corner-button button">x</button>
+                <button class="overlay-close neg corner-button button">x</button>
             </div>
 
             <div class="flex-fill image-manager-body">
 
                 <div class="image-manager-content">
-                    <div ng-if="imageType === 'gallery'" class="container">
+                    <div v-if="imageType === 'gallery'" class="container">
                         <div class="image-manager-header row faded-small nav-tabs">
-                            <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 class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" :class="{selected: (view=='all')}" @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') }}" :class="{selected: (view=='book')}" @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') }}" :class="{selected: (view=='page')}" @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="{{ 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>
+                    <div v-show="view === 'all'" >
+                        <form @submit="searchImages" class="contained-search-box">
+                            <input placeholder="{{ trans('components.image_search_hint') }}" v-model="searchTerm">
+                            <button :class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" @click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
+                            <button title="{{ trans('common.search') }}" class="text-button"><i class="zmdi zmdi-search"></i></button>
                         </form>
                     </div>
                     <div class="image-manager-list">
-                        <div ng-repeat="image in images">
-                            <div class="image anim fadeIn" ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}"
-                                 ng-class="{selected: (image==selectedImage)}" ng-click="imageSelect(image)">
-                                <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
+                        <div v-if="images.length > 0" v-for="(image, idx) in images">
+                            <div class="image anim fadeIn" :style="{animationDelay: (idx > 26) ? '160ms' : ((idx * 25) + 'ms')}"
+                                 :class="{selected: (image==selectedImage)}" @click="imageSelect(image)">
+                                <img :src="image.thumbs.gallery" :alt="image.title" :title="image.name">
                                 <div class="image-meta">
-                                    <span class="name" ng-bind="image.name"></span>
+                                    <span class="name" v-text="image.name"></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()">{{ trans('components.image_load_more') }}</div>
+                        <div class="load-more" v-show="hasMore" @click="fetchData">{{ trans('components.image_load_more') }}</div>
                     </div>
                 </div>
 
                 <div class="image-manager-sidebar">
                     <div class="inner">
 
-                        <div class="image-manager-details anim fadeIn" ng-show="selectedImage">
+                        <div class="image-manager-details anim fadeIn" v-if="selectedImage">
 
-                            <form ng-submit="saveImageDetails($event)">
+                            <form @submit.prevent="saveImageDetails">
                                 <div>
-                                    <a ng-href="@{{selectedImage.url}}" target="_blank" style="display: block;">
-                                        <img ng-src="@{{selectedImage.thumbs.gallery}}" ng-attr-alt="@{{selectedImage.title}}" ng-attr-title="@{{selectedImage.name}}">
+                                    <a :href="selectedImage.url" target="_blank" style="display: block;">
+                                        <img :src="selectedImage.thumbs.gallery" :alt="selectedImage.title"
+                                             :title="selectedImage.name">
                                     </a>
                                 </div>
                                 <div class="form-group">
                                     <label for="name">{{ trans('components.image_image_name') }}</label>
-                                    <input type="text" id="name" name="name" ng-model="selectedImage.name">
+                                    <input id="name" name="name" v-model="selectedImage.name">
                                 </div>
                             </form>
 
-                            <div ng-show="dependantPages">
+                            <div v-show="dependantPages">
                                 <p class="text-neg text-small">
                                     {{ trans('components.image_delete_confirm') }}
                                 </p>
                                 <ul class="text-neg">
-                                    <li ng-repeat="page in dependantPages">
-                                        <a ng-href="@{{ page.url }}" target="_blank" class="text-neg" ng-bind="page.name"></a>
+                                    <li v-for="page in dependantPages">
+                                        <a :href="page.url" target="_blank" class="text-neg" v-text="page.name"></a>
                                     </li>
                                 </ul>
                             </div>
 
                             <div class="clearfix">
-                                <form class="float left" ng-submit="deleteImage($event)">
+                                <form class="float left" @submit.prevent="deleteImage">
                                     <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()">
+                                <button class="button pos anim fadeIn float right" v-show="selectedImage" @click="callbackAndHide(selectedImage)">
                                     <i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
                                 </button>
                             </div>
 
                         </div>
 
-                        <drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
-
+                        <dropzone placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
 
                     </div>
                 </div>
 
-
-
             </div>
 
         </div>
index 47fb2b8b7a26f615ec70b3721d6216a9bd48a9a8..228e8d230c6663009ed3431758d1007ab4a4ec38 100644 (file)
@@ -43,7 +43,7 @@
              }
 
              if (action === 'show-image-manager') {
-                 window.ImageManager.showExternal((image) => {
+                 window.ImageManager.show((image) => {
                      if (!resize) {
                          setImage(image);
                          return;
diff --git a/resources/views/components/page-picker.blade.php b/resources/views/components/page-picker.blade.php
new file mode 100644 (file)
index 0000000..91d8eb6
--- /dev/null
@@ -0,0 +1,13 @@
+
+{{--Depends on entity selector popup--}}
+<div page-picker>
+    <div class="input-base">
+        <span @if($value) style="display: none" @endif page-picker-default class="text-muted italic">{{ $placeholder }}</span>
+        <a @if(!$value) style="display: none" @endif href="{{ baseUrl('/link/' . $value) }}" target="_blank" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Page::find($value)->name : '' }}</a>
+    </div>
+    <br>
+    <input type="hidden" value="{{$value}}" name="{{$name}}" id="{{$name}}">
+    <button @if(!$value) style="display: none" @endif type="button" page-picker-reset class="text-button">{{ trans('common.reset') }}</button>
+    <span @if(!$value) style="display: none" @endif class="sep">|</span>
+    <button type="button" page-picker-select class="text-button">{{ trans('common.select') }}</button>
+</div>
\ No newline at end of file
index a0e34e83ddd8d351528372242e757390b240c124..f6ef850afb2c2375c04109f6c6427ddc02b393d4 100644 (file)
@@ -1,30 +1,40 @@
-@extends('base')
+@extends('simple-layout')
 
 @section('content')
 
 
 <div class="container">
 
+    <p>&nbsp;</p>
 
-    <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>
+    <div class="card">
+        <h3><i class="zmdi zmdi-alert-octagon"></i> {{ $message or trans('errors.404_page_not_found') }}</h3>
+        <div class="body">
+            <h5>{{ trans('errors.sorry_page_not_found') }}</h5>
+            <p><a href="{{ baseUrl('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>
+        </div>
+    </div>
 
     @if (setting('app-public') || !user()->isDefault())
-        <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 class="card">
+                    <h3 class="text-muted"><i class="zmdi zmdi-file-text"></i> {{ trans('entities.pages_popular') }}</h3>
+                    @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Page::class]), 'style' => 'compact'])
+                </div>
             </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 class="card">
+                    <h3 class="text-muted"><i class="zmdi zmdi-book"></i> {{ trans('entities.books_popular') }}</h3>
+                    @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Book::class]), 'style' => 'compact'])
+                </div>
             </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 class="card">
+                    <h3 class="text-muted"><i class="zmdi zmdi-collection-bookmark"></i> {{ trans('entities.chapters_popular') }}</h3>
+                    @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Chapter::class]), 'style' => 'compact'])
+                </div>
             </div>
         </div>
     @endif
index 6dd96cdcc98257f254cbe6d1f224c6658744f24d..71fb78a350242909420e9e6abbfbc26912e92044 100644 (file)
@@ -3,8 +3,13 @@
 @section('content')
 
     <div class="container">
-        <h1 class="text-muted">{{ trans('errors.error_occurred') }}</h1>
-        <p>{{ $message }}</p>
+        <div class="card">
+            <h3 class="text-muted">{{ trans('errors.error_occurred') }}</h3>
+            <div class="body">
+                <h5>{{ $message }}</h5>
+                <p><a href="{{ baseUrl('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>
+            </div>
+        </div>
     </div>
 
 @stop
\ No newline at end of file
index 1ea39a7b8f1b3c59a9756d654ac22b97b43feadf..98670df8a141a46ba3acb34f9aa479adec3afd3a 100644 (file)
@@ -2,9 +2,13 @@
 
 @section('content')
 
-    <div class="container">
-        <h1 class="text-muted">{{ trans('errors.app_down', ['appName' => setting('app-name')]) }}</h1>
-        <p>{{ trans('errors.back_soon') }}</p>
+    <div class="container small">
+        <div class="card">
+            <div class="body">
+                <h4 class="text-muted"><i class="zmdi zmdi-alert-octagon"></i> {{ trans('errors.app_down', ['appName' => setting('app-name')]) }}</h4>
+                <p>{{ trans('errors.back_soon') }}</p>
+            </div>
+        </div>
     </div>
 
 @stop
\ No newline at end of file
index 7a1605197b7a6dd5896cd420fc8288e5b3daf686..c32fe005a087cb18384e8969e5300441b9e805a3 100644 (file)
@@ -27,6 +27,8 @@
         @endforeach
     </table>
 
-    <a href="{{ $model->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
-    <button type="submit" class="button pos">{{ trans('entities.permissions_save') }}</button>
+    <div class="text-right">
+        <a href="{{ $model->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+        <button type="submit" class="button pos">{{ trans('entities.permissions_save') }}</button>
+    </div>
 </form>
\ No newline at end of file
diff --git a/resources/views/home-custom.blade.php b/resources/views/home-custom.blade.php
new file mode 100644 (file)
index 0000000..3d4616c
--- /dev/null
@@ -0,0 +1,56 @@
+@extends('sidebar-layout')
+
+@section('toolbar')
+    <div class="col-sm-6 faded">
+        <div class="action-buttons text-left">
+            <a expand-toggle=".entity-list.compact .entity-item-snippet" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_details') }}</a>
+        </div>
+    </div>
+@stop
+
+@section('sidebar')
+    @if(count($draftPages) > 0)
+        <div id="recent-drafts" class="card">
+            <h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.my_recent_drafts') }}</h3>
+            @include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
+        </div>
+    @endif
+
+    <div class="card">
+        <h3><i class="zmdi zmdi-{{ $signedIn ? 'eye' : 'star-circle' }}"></i> {{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
+        @include('partials/entity-list', [
+            'entities' => $recents,
+            'style' => 'compact',
+            'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
+            ])
+    </div>
+
+    <div class="card">
+        <h3><i class="zmdi zmdi-file"></i> <a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
+        <div id="recently-updated-pages">
+            @include('partials/entity-list', [
+            'entities' => $recentlyUpdatedPages,
+            'style' => 'compact',
+            'emptyText' => trans('entities.no_pages_recently_updated')
+            ])
+        </div>
+    </div>
+
+    <div id="recent-activity" class="card">
+        <h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
+        @include('partials/activity-list', ['activity' => $activity])
+    </div>
+@stop
+
+@section('body')
+    <div class="page-content" ng-non-bindable>
+        @include('pages/page-display', ['page' => $customHomepage])
+    </div>
+@stop
+
+@section('scripts')
+    <script>
+        setupPageShow({{$customHomepage->id}});
+    </script>
+@stop
+
index 49cd2a75a75e14fa6af96a2e35861fe47a33c9bf..28be914144ac94c60eaca1a50ccc4b07eb46a84c 100644 (file)
@@ -1,64 +1,54 @@
-@extends('base')
+@extends('simple-layout')
 
-@section('content')
-
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <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>{{ trans('common.toggle_details') }}</a>
-                    </div>
-                </div>
-            </div>
+@section('toolbar')
+    <div class="col-sm-6 faded">
+        <div class="action-buttons text-left">
+            <a expand-toggle=".entity-list.compact .entity-item-snippet" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_details') }}</a>
         </div>
     </div>
+@stop
+
+@section('body')
 
     <div class="container" ng-non-bindable>
         <div class="row">
 
             <div class="col-sm-4">
-                <div id="recent-drafts">
-                    @if(count($draftPages) > 0)
-                        <h4>{{ trans('entities.my_recent_drafts') }}</h4>
+                @if(count($draftPages) > 0)
+                    <div id="recent-drafts" class="card">
+                        <h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.my_recent_drafts') }}</h3>
                         @include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
-                    @endif
-                </div>
-                @if($signedIn)
-                    <h4>{{ trans('entities.my_recently_viewed') }}</h4>
-                @else
-                    <h4>{{ trans('entities.books_recent') }}</h4>
+                    </div>
                 @endif
-                @include('partials/entity-list', [
-                'entities' => $recents,
-                'style' => 'compact',
-                '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") }}">{{ trans('entities.recently_created_pages') }}</a></h4>
-                <div id="recently-created-pages">
+                <div class="card">
+                    <h3><i class="zmdi zmdi-{{ $signedIn ? 'eye' : 'star-circle' }}"></i> {{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
                     @include('partials/entity-list', [
-                    'entities' => $recentlyCreatedPages,
-                    'style' => 'compact',
-                    'emptyText' => trans('entities.no_pages_recently_created')
-                    ])
+                        'entities' => $recents,
+                        'style' => 'compact',
+                        'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
+                        ])
                 </div>
+            </div>
 
-                <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' => trans('entities.no_pages_recently_updated')
-                    ])
+            <div class="col-sm-4">
+                <div class="card">
+                    <h3><i class="zmdi zmdi-file"></i> <a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
+                    <div id="recently-updated-pages">
+                        @include('partials/entity-list', [
+                        'entities' => $recentlyUpdatedPages,
+                        'style' => 'compact',
+                        'emptyText' => trans('entities.no_pages_recently_updated')
+                        ])
+                    </div>
                 </div>
             </div>
 
             <div class="col-sm-4" id="recent-activity">
-                <h4>{{ trans('entities.recent_activity') }}</h4>
-                @include('partials/activity-list', ['activity' => $activity])
+                <div class="card">
+                    <h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
+                    @include('partials/activity-list', ['activity' => $activity])
+                </div>
             </div>
 
         </div>
index f94a614fb2f32201ac9af475be6ccaa9b0f1cd61..47906a1f6fade617a2a868cdf85d8ee715082e26 100644 (file)
@@ -1,27 +1,30 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('pages._breadcrumbs', ['page' => $page])
     </div>
+@stop
+
+@section('body')
 
     <div class="container small" ng-non-bindable>
-        <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>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-delete"></i> {{ $page->draft ? trans('entities.pages_delete_draft') : trans('entities.pages_delete') }}</h3>
+            <div class="body">
+                <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">{{ trans('common.cancel') }}</a>
-            <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
-        </form>
+                <form action="{{ $page->getUrl() }}" method="POST">
+                    {!! csrf_field() !!}
+                    <input type="hidden" name="_method" value="DELETE">
+                    <div class="form-group">
+                        <a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                        <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
+                    </div>
+                </form>
+            </div>
+        </div>
     </div>
 
 @stop
\ No newline at end of file
index 85b77fae115c570298e42770fe26ca0f236941d1..b4991d79d2f035f6a91a2d267b2fdc86248a2e87 100644 (file)
@@ -1,18 +1,15 @@
-@extends('base')
-
-@section('content')
-
-    <div class="container">
-        <div class="row">
-
-            <div class="col-sm-7">
-                <h1>{{ $title }}</h1>
-                @include('partials/entity-list', ['entities' => $pages, 'style' => 'detailed'])
+@extends('simple-layout')
+
+@section('body')
+    <div class="container small">
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3>{{ $title }}</h3>
+            @include('partials/entity-list', ['entities' => $pages, 'style' => 'detailed'])
+            <div class="body text-center">
                 {!! $pages->links() !!}
             </div>
-
-            <div class="col-sm-4 col-sm-offset-1"></div>
-
         </div>
+
     </div>
 @stop
\ No newline at end of file
index 6de47aaf1a31987e540d62081b471d02a84483e7..79a4878fe63fed53169d76d61c6b92d011710be4 100644 (file)
@@ -16,8 +16,6 @@
             @include('pages/form', ['model' => $page])
             @include('pages/form-toolbox')
         </form>
-
-
     </div>
     
     @include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
index ecf7619b766c29e99ea3ffb503e978cd7fe6a9c2..929f7049c39ea465ff6747a73fc840b6e904df64 100644 (file)
         @endif
     </div>
 
-    <div toolbox-tab-content="tags" ng-controller="PageTagController" page-id="{{ $page->id or 0 }}">
+    <div toolbox-tab-content="tags" id="tag-manager" page-id="{{ $page->id or 0 }}">
         <h4>{{ trans('entities.page_tags') }}</h4>
         <div class="padded tags">
             <p class="muted small">{!! nl2br(e(trans('entities.tags_explain'))) !!}</p>
-            <table class="no-style" tag-autosuggestions style="width: 100%;">
-                <tbody ui-sortable="sortOptions" ng-model="tags" >
-                    <tr ng-repeat="tag in tags track by $index">
-                        <td width="20" ><i class="handle zmdi zmdi-menu"></i></td>
-                        <td><input autosuggest="{{ baseUrl('/ajax/tags/suggest/names') }}" autosuggest-type="name" class="outline" ng-attr-name="tags[@{{$index}}][name]" type="text" ng-model="tag.name" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="{{ trans('entities.tag') }}"></td>
-                        <td><input autosuggest="{{ baseUrl('/ajax/tags/suggest/values') }}" autosuggest-type="value" class="outline" ng-attr-name="tags[@{{$index}}][value]" type="text" ng-model="tag.value" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="{{ trans('entities.tag_value') }}"></td>
-                        <td width="10" ng-show="tags.length != 1" class="text-center text-neg" style="padding: 0;" ng-click="removeTag(tag)"><i class="zmdi zmdi-close"></i></td>
-                    </tr>
-                </tbody>
-            </table>
-            <table class="no-style" style="width: 100%;">
-                <tbody>
-                <tr class="unsortable">
-                    <td  width="34"></td>
-                    <td ng-click="addEmptyTag()">
-                        <button type="button" class="text-button">{{ trans('entities.tags_add') }}</button>
-                    </td>
-                    <td></td>
-                </tr>
-                </tbody>
-            </table>
+
+
+            <draggable :options="{handle: '.handle'}" :list="tags" element="div">
+                <div v-for="(tag, i) in tags" :key="tag.key" class="card drag-card">
+                    <div class="handle" ><i class="zmdi zmdi-menu"></i></div>
+                    <div>
+                        <autosuggest url="/ajax/tags/suggest/names" type="name" class="outline" :name="getTagFieldName(i, 'name')"
+                                     v-model="tag.name" @input="tagChange(tag)" @blur="tagBlur(tag)" placeholder="{{ trans('entities.tag') }}"/>
+                    </div>
+                    <div>
+                        <autosuggest url="/ajax/tags/suggest/values" type="value" class="outline" :name="getTagFieldName(i, 'value')"
+                                     v-model="tag.value" @change="tagChange(tag)" @blur="tagBlur(tag)" placeholder="{{ trans('entities.tag_value') }}"/>
+                    </div>
+                    <div v-show="tags.length !== 1" class="text-center drag-card-action text-neg" @click="removeTag(tag)"><i class="zmdi zmdi-close"></i></div>
+                </div>
+            </draggable>
+
+            <button @click="addEmptyTag" type="button" class="text-button">{{ trans('entities.tags_add') }}</button>
+
         </div>
     </div>
 
     @if(userCan('attachment-create-all'))
-        <div toolbox-tab-content="files" ng-controller="PageAttachmentController" page-id="{{ $page->id or 0 }}">
+        <div toolbox-tab-content="files" id="attachment-manager" page-id="{{ $page->id or 0 }}">
             <h4>{{ trans('entities.attachments') }}</h4>
             <div class="padded files">
 
-                <div id="file-list" ng-show="!editFile">
+                <div id="file-list" v-show="!fileToEdit">
                     <p class="muted small">{{ trans('entities.attachments_explain') }} <span class="secondary">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
 
-                    <div tab-container>
+                    <div class="tab-container">
                         <div class="nav-tabs">
-                            <div tab-button="list" class="tab-item">{{ trans('entities.attachments_items') }}</div>
-                            <div tab-button="file" class="tab-item">{{ trans('entities.attachments_upload') }}</div>
-                            <div tab-button="link" class="tab-item">{{ trans('entities.attachments_link') }}</div>
+                            <div @click="tab = 'list'" :class="{selected: tab === 'list'}" class="tab-item">{{ trans('entities.attachments_items') }}</div>
+                            <div @click="tab = 'file'" :class="{selected: tab === 'file'}" class="tab-item">{{ trans('entities.attachments_upload') }}</div>
+                            <div @click="tab = 'link'" :class="{selected: tab === 'link'}" class="tab-item">{{ trans('entities.attachments_link') }}</div>
                         </div>
-                        <div tab-content="list">
-                            <table class="file-table" style="width: 100%;">
-                                <tbody ui-sortable="sortOptions" ng-model="files" >
-                                <tr ng-repeat="file in files track by $index">
-                                    <td width="20" ><i class="handle zmdi zmdi-menu"></i></td>
-                                    <td>
-                                        <a ng-href="@{{getFileUrl(file)}}" target="_blank" ng-bind="file.name"></a>
-                                        <div ng-if="file.deleting">
+                        <div v-show="tab === 'list'">
+                            <draggable style="width: 100%;" :options="{handle: '.handle'}" @change="fileSortUpdate" :list="files" element="div">
+                                <div v-for="(file, index) in files" :key="file.id" class="card drag-card">
+                                    <div class="handle"><i class="zmdi zmdi-menu"></i></div>
+                                    <div class="padded">
+                                        <a :href="getFileUrl(file)" target="_blank" v-text="file.name"></a>
+                                        <div v-if="file.deleting">
                                             <span class="neg small">{{ trans('entities.attachments_delete_confirm') }}</span>
                                             <br>
-                                            <span class="text-primary small" ng-click="file.deleting=false;">{{ trans('common.cancel') }}</span>
+                                            <span class="text-primary small" @click="file.deleting = false;">{{ trans('common.cancel') }}</span>
                                         </div>
-                                    </td>
-                                    <td width="10" ng-click="startEdit(file)" class="text-center text-primary" style="padding: 0;"><i class="zmdi zmdi-edit"></i></td>
-                                    <td width="5"></td>
-                                    <td width="10" ng-click="deleteFile(file)" class="text-center text-neg" style="padding: 0;"><i class="zmdi zmdi-close"></i></td>
-                                </tr>
-                                </tbody>
-                            </table>
-                            <p class="small muted" ng-if="files.length == 0">
+                                    </div>
+                                    <div @click="startEdit(file)" class="drag-card-action text-center text-primary" style="padding: 0;"><i class="zmdi zmdi-edit"></i></div>
+                                    <div @click="deleteFile(file)" class="drag-card-action text-center text-neg" style="padding: 0;"><i class="zmdi zmdi-close"></i></div>
+                                </div>
+                            </draggable>
+                            <p class="small muted" v-if="files.length === 0">
                                 {{ trans('entities.attachments_no_files') }}
                             </p>
                         </div>
-                        <div tab-content="file">
-                            <drop-zone message="{{ trans('entities.attachments_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
+                        <div v-show="tab === 'file'">
+                            <dropzone placeholder="{{ trans('entities.attachments_dropzone') }}" :upload-url="getUploadUrl()" :uploaded-to="pageId" @success="uploadSuccess"></dropzone>
                         </div>
-                        <div tab-content="link" sub-form="attachLinkSubmit(file)">
+                        <div v-show="tab === 'link'" @keypress.enter.prevent="attachNewLink(file)">
                             <p class="muted small">{{ trans('entities.attachments_explain_link') }}</p>
                             <div class="form-group">
                                 <label for="attachment-via-link">{{ trans('entities.attachments_link_name') }}</label>
-                                <input type="text" placeholder="{{ trans('entities.attachments_link_name') }}" ng-model="file.name">
-                                <p class="small neg" ng-repeat="error in errors.link.name" ng-bind="error"></p>
+                                <input type="text" placeholder="{{ trans('entities.attachments_link_name') }}" v-model="file.name">
+                                <p class="small neg" v-for="error in errors.link.name" v-text="error"></p>
                             </div>
                             <div class="form-group">
                                 <label for="attachment-via-link">{{ trans('entities.attachments_link_url') }}</label>
-                                <input type="text" placeholder="{{ trans('entities.attachments_link_url_hint') }}" ng-model="file.link">
-                                <p class="small neg" ng-repeat="error in errors.link.link" ng-bind="error"></p>
+                                <input type="text"  placeholder="{{ trans('entities.attachments_link_url_hint') }}" v-model="file.link">
+                                <p class="small neg" v-for="error in errors.link.link" v-text="error"></p>
                             </div>
-                            <button type="submit" class="button pos">{{ trans('entities.attach') }}</button>
+                            <button @click.prevent="attachNewLink(file)" class="button pos">{{ trans('entities.attach') }}</button>
 
                         </div>
                     </div>
 
                 </div>
 
-                <div id="file-edit" ng-if="editFile" sub-form="updateFile(editFile)">
+                <div id="file-edit" v-if="fileToEdit" @keypress.enter.prevent="updateFile(fileToEdit)">
                     <h5>{{ trans('entities.attachments_edit_file') }}</h5>
 
                     <div class="form-group">
                         <label for="attachment-name-edit">{{ trans('entities.attachments_edit_file_name') }}</label>
-                        <input type="text" id="attachment-name-edit" placeholder="{{ trans('entities.attachments_edit_file_name') }}" ng-model="editFile.name">
-                        <p class="small neg" ng-repeat="error in errors.edit.name" ng-bind="error"></p>
+                        <input type="text" id="attachment-name-edit" placeholder="{{ trans('entities.attachments_edit_file_name') }}" v-model="fileToEdit.name">
+                        <p class="small neg" v-for="error in errors.edit.name" v-text="error"></p>
                     </div>
 
-                    <div tab-container="@{{ editFile.external ? 'link' : 'file' }}">
+                    <div class="tab-container">
                         <div class="nav-tabs">
-                            <div tab-button="file" class="tab-item">{{ trans('entities.attachments_upload') }}</div>
-                            <div tab-button="link" class="tab-item">{{ trans('entities.attachments_set_link') }}</div>
+                            <div @click="editTab = 'file'" :class="{selected: editTab === 'file'}" class="tab-item">{{ trans('entities.attachments_upload') }}</div>
+                            <div @click="editTab = 'link'" :class="{selected: editTab === 'link'}" class="tab-item">{{ trans('entities.attachments_set_link') }}</div>
                         </div>
-                        <div tab-content="file">
-                            <drop-zone upload-url="@{{getUploadUrl(editFile)}}" uploaded-to="@{{uploadedTo}}" placeholder="{{ trans('entities.attachments_edit_drop_upload') }}" event-success="uploadSuccessUpdate"></drop-zone>
+                        <div v-if="editTab === 'file'">
+                            <dropzone :upload-url="getUploadUrl(fileToEdit)" :uploaded-to="pageId" placeholder="{{ trans('entities.attachments_edit_drop_upload') }}" @success="uploadSuccessUpdate"></dropzone>
                             <br>
                         </div>
-                        <div tab-content="link">
+                        <div v-if="editTab === 'link'">
                             <div class="form-group">
                                 <label for="attachment-link-edit">{{ trans('entities.attachments_link_url') }}</label>
-                                <input type="text" id="attachment-link-edit" placeholder="{{ trans('entities.attachment_link') }}" ng-model="editFile.link">
-                                <p class="small neg" ng-repeat="error in errors.edit.link" ng-bind="error"></p>
+                                <input type="text" id="attachment-link-edit" placeholder="{{ trans('entities.attachment_link') }}" v-model="fileToEdit.link">
+                                <p class="small neg" v-for="error in errors.edit.link" v-text="error"></p>
                             </div>
                         </div>
                     </div>
 
-                    <button type="button" class="button" ng-click="cancelEdit()">{{ trans('common.back') }}</button>
-                    <button type="submit" class="button pos">{{ trans('common.save') }}</button>
+                    <button type="button" class="button outline" @click="cancelEdit">{{ trans('common.back') }}</button>
+                    <button @click.enter.prevent="updateFile(fileToEdit)" class="button pos">{{ trans('common.save') }}</button>
                 </div>
 
             </div>
index eb5ebb0bdde4b807928f3de5741270f54d48697f..ffc1c3fa61f56ec01067cc6ac4ae0fe9594f3985 100644 (file)
@@ -5,7 +5,7 @@
 
     {{--Header Bar--}}
     <div class="faded-small toolbar">
-        <div class="container">
+        <div class="container fluid">
             <div class="row">
                 <div class="col-sm-4 faded">
                     <div class="action-buttons text-left">
@@ -74,7 +74,7 @@
 
         {{--Markdown Editor--}}
         @if(setting('app-editor') === 'markdown')
-            <div id="markdown-editor" markdown-editor class="flex-fill flex">
+            <div id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
 
                 <div class="markdown-editor-wrap">
                     <div class="editor-toolbar">
index f440a52f6afa18d8e3f26ac0e67599af259db232..593c2fc3c56398314abc5a229512593d117d6423 100644 (file)
         <a href="{{ $page->getUrl() }}" class="text-page entity-list-item-link"><i class="zmdi zmdi-file-text"></i><span class="entity-list-item-name">{{ $page->name }}</span></a>
     </h4>
 
-    @if(isset($page->searchSnippet))
-        <p class="text-muted">{!! $page->searchSnippet !!}</p>
-    @else
-        <p class="text-muted">{{ $page->getExcerpt() }}</p>
-    @endif
+    <div class="entity-item-snippet">
+        @if(isset($page->searchSnippet))
+            <p class="text-muted">{!! $page->searchSnippet !!}</p>
+        @else
+            <p class="text-muted">{{ $page->getExcerpt() }}</p>
+        @endif
+    </div>
 
     @if(isset($style) && $style === 'detailed')
         <div class="row meta text-muted text-small">
index a9b6d69d7b4454b012b24856afe322a78abd4182..8f934a30ef84acb9d6c9f7771887975804f13cbd 100644 (file)
@@ -1,29 +1,31 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('pages._breadcrumbs', ['page' => $page])
     </div>
+@stop
 
-    <div class="container">
-        <h1>{{ trans('entities.pages_move') }}</h1>
+@section('body')
 
-        <form action="{{ $page->getUrl('/move') }}" method="POST">
-            {!! csrf_field() !!}
-            <input type="hidden" name="_method" value="PUT">
+    <div class="container">
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-folder"></i> {{ trans('entities.pages_move') }}</h3>
+            <div class="body">
+                <form action="{{ $page->getUrl('/move') }}" method="POST">
+                    {!! csrf_field() !!}
+                    <input type="hidden" name="_method" value="PUT">
 
-            @include('components.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">{{ trans('common.cancel') }}</a>
-            <button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button>
-        </form>
+                    <div class="form-group text-right">
+                        <a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                        <button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button>
+                    </div>
+                </form>
+            </div>
+        </div>
     </div>
 
 @stop
index cb7cae445bcafbe2b38b134bdc98c00607d23df7..2f70c747d490d37f25883b61d187c52c98eac0fc 100644 (file)
@@ -7,6 +7,6 @@
     @if (isset($diff) && $diff)
         {!! $diff !!}
     @else
-        {!! isset($pageContent) ? $pageContent : $page->html !!}
+        {!! isset($page->renderedHTML) ? $page->renderedHTML : $page->html !!}
     @endif
 </div>
\ No newline at end of file
index cfef2ed21a8f2cd994829a8f179f4ab579a2e281..8f0e5353db2f98e686fc10b0d4e896ddc0eeaba1 100644 (file)
@@ -1,20 +1,19 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('pages._breadcrumbs', ['page' => $page])
     </div>
+@stop
 
+@section('body')
     <div class="container" ng-non-bindable>
-        <h1>{{ trans('entities.pages_permissions') }}</h1>
-        @include('form.restriction-form', ['model' => $page])
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-lock-outline"></i> {{ trans('entities.pages_permissions') }}</h3>
+            <div class="body">
+                @include('form.restriction-form', ['model' => $page])
+            </div>
+        </div>
     </div>
-
 @stop
index a4b5fb4b503f2eab93136d932d35754f8b09071d..ca74e4afa29ee75c3daa52c9452e4cdc8527f8e9 100644 (file)
@@ -1,12 +1,20 @@
-@extends('base')
+@extends('sidebar-layout')
 
-@section('content')
+@section('sidebar')
+    <div class="card">
+        <h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
+        <div class="body">
+            @include('partials.entity-meta', ['entity' => $revision])
+        </div>
+    </div>
+@stop
 
+@section('body')
 
     <div class="container" ng-non-bindable>
         <div class="row">
             <div class="col-md-9">
-                <div class="page-content anim fadeIn">
+                <div class="page-content">
                     @include('pages.page-display')
                 </div>
             </div>
index bdc8460efa416b7bd6287ed49180abe351189199..ed3aa4832d380e79aee560ead1f1b08b01b5d5da 100644 (file)
@@ -1,62 +1,62 @@
-@extends('base')
+@extends('simple-layout')
 
-@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>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        @include('pages._breadcrumbs', ['page' => $page])
     </div>
+@stop
 
+@section('body')
     <div class="container" ng-non-bindable>
-        <h1>{{ trans('entities.pages_revisions') }}</h1>
-
-        @if(count($page->revisions) > 0)
-
-            <table class="table">
-                <tr>
-                    <th width="3%">{{ trans('entities.pages_revisions_number') }}</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>
-                        <td>{{ $revision->revision_number == 0 ? '' : $revision->revision_number }}</td>
-                        <td>{{ $revision->name }}</td>
-                        <td style="line-height: 0;">
-                            @if($revision->createdBy)
-                                <img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{ $revision->createdBy->name }}">
-                            @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">{{ trans('entities.pages_revisions_changes') }}</a>
-                            <span class="text-muted">&nbsp;|&nbsp;</span>
-
-                            @if ($index === 0)
-                                <a target="_blank" href="{{ $page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
-                            @else
-                                <a href="{{ $revision->getUrl() }}" target="_blank">{{ trans('entities.pages_revisions_preview') }}</a>
-                                <span class="text-muted">&nbsp;|&nbsp;</span>
-                                <a href="{{ $revision->getUrl('restore') }}">{{ trans('entities.pages_revisions_restore') }}</a>
-                            @endif
-                        </td>
-                    </tr>
-                @endforeach
-            </table>
-
-        @else
-            <p>{{ trans('entities.pages_revisions_none') }}</p>
-        @endif
+        <p>&nbsp;</p>
+
+        <div class="card">
+            <h3><i class="zmdi zmdi-replay"></i> {{ trans('entities.pages_revisions') }}</h3>
+            <div class="body">
+                @if(count($page->revisions) > 0)
+
+                    <table class="table">
+                        <tr>
+                            <th width="3%">{{ trans('entities.pages_revisions_number') }}</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>
+                                <td>{{ $revision->revision_number == 0 ? '' : $revision->revision_number }}</td>
+                                <td>{{ $revision->name }}</td>
+                                <td style="line-height: 0;">
+                                    @if($revision->createdBy)
+                                        <img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{ $revision->createdBy->name }}">
+                                    @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">{{ trans('entities.pages_revisions_changes') }}</a>
+                                    <span class="text-muted">&nbsp;|&nbsp;</span>
+
+                                    @if ($index === 0)
+                                        <a target="_blank" href="{{ $page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
+                                    @else
+                                        <a href="{{ $revision->getUrl() }}" target="_blank">{{ trans('entities.pages_revisions_preview') }}</a>
+                                        <span class="text-muted">&nbsp;|&nbsp;</span>
+                                        <a href="{{ $revision->getUrl('restore') }}">{{ trans('entities.pages_revisions_restore') }}</a>
+                                    @endif
+                                </td>
+                            </tr>
+                        @endforeach
+                    </table>
+
+                @else
+                    <p>{{ trans('entities.pages_revisions_none') }}</p>
+                @endif
+            </div>
+        </div>
 
     </div>
 
index 221ed44769393182553f5cebe59cca6dbc4a7e49..d7edd2fffcceb5c6ff7ec695e5f4edc02abc13b1 100644 (file)
-@extends('base')
+@extends('sidebar-layout')
 
-@section('content')
-
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-sm-8 col-xs-5 faded">
-                    @include('pages._breadcrumbs', ['page' => $page])
-                </div>
-                <div class="col-sm-4 col-xs-7 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>{{ trans('entities.export') }}</div>
-                            <ul class="wide">
-                                <li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
-                                <li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
-                                <li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
-                            </ul>
-                        </span>
+@section('toolbar')
+    <div class="col-sm-8 col-xs-5 faded">
+        @include('pages._breadcrumbs', ['page' => $page])
+    </div>
+    <div class="col-sm-4 col-xs-7 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>{{ trans('entities.export') }}</div>
+                <ul class="wide">
+                    <li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+                    <li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                    <li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.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>{{ 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> {{ trans('common.more') }}</a>
+                    <ul>
                         @if(userCan('page-update', $page))
-                            <a href="{{ $page->getUrl('/edit') }}" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
+                            <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('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>{{ 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>{{ 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>{{ trans('common.delete') }}</a></li>
-                                    @endif
-                                </ul>
-                            </div>
+                        @if(userCan('restrictions-manage', $page))
+                            <li><a href="{{ $page->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.permissions') }}</a></li>
                         @endif
-
-                    </div>
+                        @if(userCan('page-delete', $page))
+                            <li><a href="{{ $page->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>{{ trans('common.delete') }}</a></li>
+                        @endif
+                    </ul>
                 </div>
-            </div>
+            @endif
+
         </div>
     </div>
+@stop
 
-
-    <div class="container" id="page-show" ng-non-bindable>
-        <div class="row">
-            <div class="col-md-9 print-full-width">
-                <div class="page-content">
-
-                    <div class="pointer-container" id="pointer">
-                        <div class="pointer anim">
-                            <span class="icon text-primary"><i class="zmdi zmdi-link"></i></span>
-                            <input readonly="readonly" type="text" id="pointer-url" placeholder="url">
-                            <button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}"><i class="zmdi zmdi-copy"></i></button>
-                        </div>
-                    </div>
-
-                    @include('pages/page-display')
-
-                    <hr>
-
-                    @include('partials.entity-meta', ['entity' => $page])
-
-                </div>
-            </div>
-
-            <div class="col-md-3 print-hidden">
-                <div class="margin-top large"></div>
-                @if($book->restricted || ($page->chapter && $page->chapter->restricted) || $page->restricted)
-                    <div class="text-muted">
-
-                        @if($book->restricted)
-                            @if(userCan('restrictions-manage', $book))
-                                <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>{{ trans('entities.books_permissions_active') }}
-                            @endif
-                            <br>
+@section('sidebar')
+    @if($book->restricted || ($page->chapter && $page->chapter->restricted) || $page->restricted)
+        <div class="card">
+            <h3><i class="zmdi zmdi-key"></i> {{ trans('entities.permissions') }}</h3>
+            <div class="body">
+                <div class="text-muted">
+
+                    @if($book->restricted)
+                        @if(userCan('restrictions-manage', $book))
+                            <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>{{ trans('entities.books_permissions_active') }}
                         @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>{{ trans('entities.chapters_permissions_active') }}</a>
-                            @else
-                                <i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}
-                            @endif
-                            <br>
+                        <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>{{ trans('entities.chapters_permissions_active') }}</a>
+                        @else
+                            <i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}
                         @endif
-
-                        @if($page->restricted)
-                            @if(userCan('restrictions-manage', $page))
-                                <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>{{ trans('entities.pages_permissions_active') }}
-                            @endif
-                            <br>
+                        <br>
+                    @endif
+
+                    @if($page->restricted)
+                        @if(userCan('restrictions-manage', $page))
+                            <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>{{ trans('entities.pages_permissions_active') }}
                         @endif
+                        <br>
+                    @endif
+                </div>
+            </div>
+        </div>
+    @endif
+
+    @if($page->tags->count() > 0)
+        <div class="card tag-display">
+            <h3><i class="zmdi zmdi-tag"></i> {{ trans('entities.page_tags') }}</h3>
+            <div class="body">
+                <table>
+                    <tbody>
+                    @foreach($page->tags as $tag)
+                        <tr class="tag">
+                            <td @if(!$tag->value) colspan="2" @endif><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%5D') }}">{{ $tag->name }}</a></td>
+                            @if($tag->value) <td class="tag-value"><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></td> @endif
+                        </tr>
+                    @endforeach
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    @endif
+
+    @if ($page->attachments->count() > 0)
+        <div class="card">
+            <h3><i class="zmdi zmdi-attachment-alt"></i> {{ trans('entities.pages_attachments') }}</h3>
+            <div class="body">
+                @foreach($page->attachments as $attachment)
+                    <div class="attachment">
+                        <a href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif><i class="zmdi zmdi-{{ $attachment->external ? 'open-in-new' : 'file' }}"></i>{{ $attachment->name }}</a>
                     </div>
-                @endif
+                @endforeach
+            </div>
+        </div>
+    @endif
+
+    @if (isset($pageNav) && count($pageNav))
+        <div class="card">
+            <h3><i class="zmdi zmdi-compass"></i> {{ trans('entities.pages_navigation') }}</h3>
+            <div class="body">
+                <div class="sidebar-page-nav menu">
+                    @foreach($pageNav as $navItem)
+                        <li class="page-nav-item h{{ $navItem['level'] }}">
+                            <a href="{{ $navItem['link'] }}">{{ $navItem['text'] }}</a>
+                        </li>
+                    @endforeach
+                </div>
+            </div>
+        </div>
+    @endif
 
+    @include('partials/book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
 
+    <div class="card">
+        <h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
+        <div class="body">
+            @include('partials.entity-meta', ['entity' => $page])
+        </div>
+    </div>
+@stop
 
-                @include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree, 'pageNav' => $pageNav])
-            </div>
+@section('body')
+    <div class="page-content" ng-non-bindable>
 
+        <div class="pointer-container" id="pointer">
+            <div class="pointer anim" >
+                <span class="icon text-primary"><i class="zmdi zmdi-link"></i></span>
+                <input readonly="readonly" type="text" id="pointer-url" placeholder="url">
+                <button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}"><i class="zmdi zmdi-copy"></i></button>
+            </div>
         </div>
+
+        @include('pages/page-display')
+
     </div>
 
+    <div class="container small nopad">
+        @include('comments/comments', ['page' => $page])
+    </div>
 @stop
 
 @section('scripts')
index 0a10987d693bf5e24bae4efd0b2c5b006f18649e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,74 +0,0 @@
-
-<div class="book-tree" ng-non-bindable>
-
-    @if(isset($page) && $page->tags->count() > 0)
-        <div class="tag-display">
-            <h6 class="text-muted">{{ trans('entities.page_tags') }}</h6>
-            <table>
-                <tbody>
-                @foreach($page->tags as $tag)
-                    <tr class="tag">
-                        <td @if(!$tag->value) colspan="2" @endif><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%5D') }}">{{ $tag->name }}</a></td>
-                        @if($tag->value) <td class="tag-value"><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></td> @endif
-                    </tr>
-                @endforeach
-                </tbody>
-            </table>
-        </div>
-    @endif
-
-    @if (isset($page) && $page->attachments->count() > 0)
-        <h6 class="text-muted">{{ trans('entities.pages_attachments') }}</h6>
-        @foreach($page->attachments as $attachment)
-            <div class="attachment">
-                <a href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif><i class="zmdi zmdi-{{ $attachment->external ? 'open-in-new' : 'file' }}"></i>{{ $attachment->name }}</a>
-            </div>
-        @endforeach
-    @endif
-
-    @if (isset($pageNav) && count($pageNav))
-        <h6 class="text-muted">{{ trans('entities.pages_navigation') }}</h6>
-        <div class="sidebar-page-nav menu">
-            @foreach($pageNav as $navItem)
-                <li class="page-nav-item h{{ $navItem['level'] }}">
-                    <a href="{{ $navItem['link'] }}">{{ $navItem['text'] }}</a>
-                </li>
-            @endforeach
-        </div>
-    @endif
-
-    <h6 class="text-muted">{{ trans('entities.books_navigation') }}</h6>
-    <ul class="sidebar-page-list menu">
-
-        @if (userCan('view', $book))
-            <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
-        @endif
-
-        @foreach($sidebarTree as $bookChild)
-            <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
-                <a href="{{ $bookChild->getUrl() }}" class="{{ $bookChild->getClassName() }} {{ $current->matches($bookChild)? 'selected' : '' }}">
-                    @if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }}
-                </a>
-
-                @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
-                    <p class="text-muted chapter-toggle @if($bookChild->matchesOrContains($current)) open @endif">
-                        <i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans('entities.x_pages', ['count' => $bookChild->pages->count()]) }}</span>
-                    </p>
-                    <ul class="menu sub-menu inset-list @if($bookChild->matchesOrContains($current)) open @endif">
-                        @foreach($bookChild->pages as $childPage)
-                            <li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}">
-                                <a href="{{ $childPage->getUrl() }}" class="page {{ $current->matches($childPage)? 'selected' : '' }}">
-                                    <i class="zmdi zmdi-file-text"></i> {{ $childPage->name }}
-                                </a>
-                            </li>
-                        @endforeach
-                    </ul>
-                @endif
-
-
-            </li>
-        @endforeach
-
-
-    </ul>
-</div>
index 0dc38091e6161e940ed6ec5bf3273489661bf0f8..45322a1e53169ecd65d1089679839f3fb183ae49 100644 (file)
@@ -8,5 +8,5 @@
         @endforeach
     </div>
 @else
-    <p class="text-muted">{{ trans('common.no_activity') }}</p>
+    <p class="text-muted empty-text">{{ trans('common.no_activity') }}</p>
 @endif
\ No newline at end of file
diff --git a/resources/views/partials/book-tree.blade.php b/resources/views/partials/book-tree.blade.php
new file mode 100644 (file)
index 0000000..867d977
--- /dev/null
@@ -0,0 +1,36 @@
+<div class="card book-tree" ng-non-bindable>
+    <h3><i class="zmdi zmdi-book"></i> {{ trans('entities.books_navigation') }}</h3>
+    <div class="body">
+        <ul class="sidebar-page-list menu">
+
+            @if (userCan('view', $book))
+                <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
+            @endif
+
+            @foreach($sidebarTree as $bookChild)
+                <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
+                    <a href="{{ $bookChild->getUrl() }}" class="{{ $bookChild->getClassName() }} {{ $current->matches($bookChild)? 'selected' : '' }}">
+                        @if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }}
+                    </a>
+
+                    @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+                        <p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
+                            <i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans_choice('entities.x_pages', $bookChild->pages->count()) }}</span>
+                        </p>
+                        <ul class="menu sub-menu inset-list @if($bookChild->matchesOrContains($current)) open @endif">
+                            @foreach($bookChild->pages as $childPage)
+                                <li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}">
+                                    <a href="{{ $childPage->getUrl() }}" class="page {{ $current->matches($childPage)? 'selected' : '' }}">
+                                        <i class="zmdi zmdi-file-text"></i> {{ $childPage->name }}
+                                    </a>
+                                </li>
+                            @endforeach
+                        </ul>
+                    @endif
+
+
+                </li>
+            @endforeach
+        </ul>
+    </div>
+</div>
\ No newline at end of file
index 62bcc881fc3206f005f3bdad614cdf87e4235aa1..c13051df4f3a363c8bbcfbf140bad183cffbca99 100644 (file)
@@ -1,5 +1,5 @@
 <style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}">
-    header, #back-to-top, .primary-background {
+    header, [back-to-top], .primary-background {
         background-color: {{ setting('app-color') }} !important;
     }
     .faded-small, .primary-background-light {
index 0442ce50c5574bbadc6e8fb594d79bfa6e875b98..49671ba509051a3c383a235b96a42b77b1aba34d 100644 (file)
@@ -1,4 +1,9 @@
 <p class="text-muted small">
+    @if($entity->isA('revision'))
+        {{ trans('entities.pages_revision') }}
+        {{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
+        <br>
+    @endif
     @if ($entity->isA('page')) {{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br> @endif
     @if ($entity->createdBy)
         {!! trans('entities.meta_created_name', [
@@ -14,7 +19,7 @@
                 'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
                 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
             ]) !!}
-    @else
+    @elseif (!$entity->isA('revision'))
         <span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
     @endif
 </p>
\ No newline at end of file
index 481d9b349d4148ee648124241a05f0adb8042684..25cfee69ef1fee493c66deb82488dd12c2f808e7 100644 (file)
@@ -2,4 +2,7 @@
     <div></div>
     <div></div>
     <div></div>
+    @if(isset($text))
+        <span>{{$text}}</span>
+    @endif
 </div>
\ No newline at end of file
index c079080dbb1aedef4364cc5f8aa9a533cee047b7..215aee3ed4e213447d22fcd1a2b2ccc15b548382 100644 (file)
@@ -1,12 +1,12 @@
 
-<div class="notification anim pos" @if(!session()->has('success')) style="display:none;" @endif>
+<div notification="success" data-autohide class="pos" @if(session()->has('success')) data-show @endif>
     <i class="zmdi zmdi-check-circle"></i> <span>{!! nl2br(htmlentities(session()->get('success'))) !!}</span>
 </div>
 
-<div class="notification anim warning stopped" @if(!session()->has('warning')) style="display:none;" @endif>
+<div notification="warning" class="warning" @if(session()->has('warning')) data-show @endif>
     <i class="zmdi zmdi-info"></i> <span>{!! nl2br(htmlentities(session()->get('warning'))) !!}</span>
 </div>
 
-<div class="notification anim neg stopped" @if(!session()->has('error')) style="display:none;" @endif>
+<div notification="error" class="neg" @if(session()->has('error')) data-show @endif>
     <i class="zmdi zmdi-alert-circle"></i> <span>{!! nl2br(htmlentities(session()->get('error'))) !!}</span>
 </div>
index ea2069b866162abdc345528295ead45234eda09c..87d4f1c88b22adeb0ae68604bba16be9c6c0c582 100644 (file)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html>
+<html class="shaded">
 <head>
     <title>{{ setting('app-name') }}</title>
 
         {!! setting('app-custom-head') !!}
     @endif
 </head>
-<body class="@yield('body-class')" ng-app="bookStack">
+<body class="@yield('body-class')">
 
 @include('partials.notifications')
 
 <header id="header">
-    <div class="container">
+    <div class="container fluid">
         <div class="row">
             <div class="col-sm-6">
 
index 1029b65fabea41c320b447ad63e05dba57e79cb7..2c967d0a95add78ab7a3e62f85ab794a3c9eade0 100644 (file)
-@extends('base')
+@extends('sidebar-layout')
 
-@section('content')
-
-    <input type="hidden" name="searchTerm" value="{{$searchTerm}}">
-
-<div id="search-system">
-
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-sm-12 faded">
-                    <div class="breadcrumbs">
-                        <a href="{{ baseUrl("/search?term=" . urlencode($searchTerm)) }}" class="text-button"><i class="zmdi zmdi-search"></i>{{ trans('entities.search_for_term', ['term' => $searchTerm]) }}</a>
-                    </div>
-                </div>
-            </div>
+@section('toolbar')
+    <div class="col-sm-12 faded">
+        <div class="breadcrumbs">
+            <a href="{{ baseUrl("/search?term=" . urlencode($searchTerm)) }}" class="text-button"><i class="zmdi zmdi-search"></i>{{ trans('entities.search_for_term', ['term' => $searchTerm]) }}</a>
         </div>
     </div>
+@stop
+
+@section('container-attrs')
+    id="search-system"
+    ng-non-bindable=""
+@stop
+
+@section('sidebar')
+    <div class="card">
+        <h3>{{ trans('entities.search_filters') }}</h3>
+
+        <div class="body">
+            <form v-on:submit="updateSearch" v-cloak class="v-cloak anim fadeIn">
+                <h6 class="text-muted">{{ trans('entities.search_content_type') }}</h6>
+                <div class="form-group">
+                    <label class="inline checkbox text-page"><input type="checkbox" v-on:change="typeChange" v-model="search.type.page" value="page">{{ trans('entities.page') }}</label>
+                    <label class="inline checkbox text-chapter"><input type="checkbox" v-on:change="typeChange" v-model="search.type.chapter" value="chapter">{{ trans('entities.chapter') }}</label>
+                    <label class="inline checkbox text-book"><input type="checkbox" v-on:change="typeChange" v-model="search.type.book" value="book">{{ trans('entities.book') }}</label>
+                </div>
 
-    <div class="container" ng-non-bindable id="searchSystem">
-
-        <div class="row">
-
-            <div class="col-md-6">
-                <h1>{{ trans('entities.search_results') }}</h1>
-                <h6 class="text-muted">{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}</h6>
-                @include('partials/entity-list', ['entities' => $entities])
-                @if ($hasNextPage)
-                    <a href="{{ $nextPageLink }}" class="button">{{ trans('entities.search_more') }}</a>
-                @endif
-            </div>
-
-            <div class="col-md-5 col-md-offset-1">
-               <h3>{{ trans('entities.search_filters') }}</h3>
-
-                <form v-on:submit="updateSearch" v-cloak class="v-cloak anim fadeIn">
-                    <h6 class="text-muted">{{ trans('entities.search_content_type') }}</h6>
-                    <div class="form-group">
-                        <label class="inline checkbox text-page"><input type="checkbox" v-on:change="typeChange" v-model="search.type.page" value="page">{{ trans('entities.page') }}</label>
-                        <label class="inline checkbox text-chapter"><input type="checkbox" v-on:change="typeChange" v-model="search.type.chapter" value="chapter">{{ trans('entities.chapter') }}</label>
-                        <label class="inline checkbox text-book"><input type="checkbox" v-on:change="typeChange" v-model="search.type.book" value="book">{{ trans('entities.book') }}</label>
-                    </div>
-
-                    <h6 class="text-muted">{{ trans('entities.search_exact_matches') }}</h6>
-                    <table cellpadding="0" cellspacing="0" border="0" class="no-style">
-                        <tr v-for="(term, i) in search.exactTerms">
-                            <td style="padding: 0 12px 6px 0;">
-                                <input class="exact-input outline" v-on:input="exactChange" type="text" v-model="search.exactTerms[i]"></td>
-                            <td>
-                                <button type="button" class="text-neg text-button" v-on:click="removeExact(i)">
-                                    <i class="zmdi zmdi-close"></i>
-                                </button>
-                            </td>
-                        </tr>
-                        <tr>
-                            <td colspan="2">
-                                <button type="button" class="text-button" v-on:click="addExact">
-                                    <i class="zmdi zmdi-plus-circle-o"></i>{{ trans('common.add') }}
-                                </button>
-                            </td>
-                        </tr>
-                    </table>
-
-                    <h6 class="text-muted">{{ trans('entities.search_tags') }}</h6>
-                    <table cellpadding="0" cellspacing="0" border="0" class="no-style">
-                        <tr v-for="(term, i) in search.tagTerms">
-                            <td style="padding: 0 12px 6px 0;">
-                                <input class="tag-input outline" v-on:input="tagChange" type="text" v-model="search.tagTerms[i]"></td>
-                            <td>
-                                <button type="button" class="text-neg text-button" v-on:click="removeTag(i)">
-                                    <i class="zmdi zmdi-close"></i>
-                                </button>
-                            </td>
-                        </tr>
-                        <tr>
-                            <td colspan="2">
-                                <button type="button" class="text-button" v-on:click="addTag">
-                                    <i class="zmdi zmdi-plus-circle-o"></i>{{ trans('common.add') }}
-                                </button>
-                            </td>
-                        </tr>
-                    </table>
-
-                    <h6 class="text-muted">Options</h6>
-                    <label class="checkbox">
-                        <input type="checkbox" v-on:change="optionChange('viewed_by_me')"
-                               v-model="search.option.viewed_by_me" value="page">
-                        {{ trans('entities.search_viewed_by_me') }}
-                    </label>
-                    <label class="checkbox">
-                        <input type="checkbox" v-on:change="optionChange('not_viewed_by_me')"
-                               v-model="search.option.not_viewed_by_me" value="page">
-                        {{ trans('entities.search_not_viewed_by_me') }}
-                    </label>
-                    <label class="checkbox">
-                        <input type="checkbox" v-on:change="optionChange('is_restricted')"
-                               v-model="search.option.is_restricted" value="page">
-                        {{ trans('entities.search_permissions_set') }}
-                    </label>
-                    <label class="checkbox">
-                        <input type="checkbox" v-on:change="optionChange('created_by:me')"
-                               v-model="search.option['created_by:me']" value="page">
-                        {{ trans('entities.search_created_by_me') }}
-                    </label>
-                    <label class="checkbox">
-                        <input type="checkbox" v-on:change="optionChange('updated_by:me')"
-                               v-model="search.option['updated_by:me']" value="page">
-                        {{ trans('entities.search_updated_by_me') }}
-                    </label>
-
-                    <h6 class="text-muted">Date Options</h6>
-                    <table cellpadding="0" cellspacing="0" border="0" class="no-style form-table">
-                        <tr>
-                            <td width="200">{{ trans('entities.search_updated_after') }}</td>
-                            <td width="80">
-                                <button type="button" class="text-button" v-if="!search.dates.updated_after"
-                                        v-on:click="enableDate('updated_after')">{{ trans('entities.search_set_date') }}</button>
-
-                            </td>
-                        </tr>
-                        <tr v-if="search.dates.updated_after">
-                            <td>
-                                <input v-if="search.dates.updated_after" class="tag-input"
-                                       v-on:input="dateChange('updated_after')" type="date" v-model="search.dates.updated_after"
-                                       pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                            </td>
-                            <td>
-                                <button v-if="search.dates.updated_after" type="button" class="text-neg text-button"
-                                        v-on:click="dateRemove('updated_after')">
-                                    <i class="zmdi zmdi-close"></i>
-                                </button>
-                            </td>
-                        </tr>
-                        <tr>
-                            <td>{{ trans('entities.search_updated_before') }}</td>
-                            <td>
-                                <button type="button" class="text-button" v-if="!search.dates.updated_before"
-                                        v-on:click="enableDate('updated_before')">{{ trans('entities.search_set_date') }}</button>
-
-                            </td>
-                        </tr>
-                        <tr v-if="search.dates.updated_before">
-                            <td>
-                                <input v-if="search.dates.updated_before" class="tag-input"
-                                       v-on:input="dateChange('updated_before')" type="date" v-model="search.dates.updated_before"
-                                       pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                            </td>
-                            <td>
-                                <button v-if="search.dates.updated_before" type="button" class="text-neg text-button"
-                                        v-on:click="dateRemove('updated_before')">
-                                    <i class="zmdi zmdi-close"></i>
-                                </button>
-                            </td>
-                        </tr>
-                        <tr>
-                            <td>{{ trans('entities.search_created_after') }}</td>
-                            <td>
-                                <button type="button" class="text-button" v-if="!search.dates.created_after"
-                                        v-on:click="enableDate('created_after')">{{ trans('entities.search_set_date') }}</button>
-
-                            </td>
-                        </tr>
-                        <tr v-if="search.dates.created_after">
-                            <td>
-                                <input v-if="search.dates.created_after" class="tag-input"
-                                       v-on:input="dateChange('created_after')" type="date" v-model="search.dates.created_after"
-                                       pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                            </td>
-                            <td>
-                                <button v-if="search.dates.created_after" type="button" class="text-neg text-button"
-                                        v-on:click="dateRemove('created_after')">
-                                    <i class="zmdi zmdi-close"></i>
-                                </button>
-                            </td>
-                        </tr>
-                        <tr>
-                            <td>{{ trans('entities.search_created_before') }}</td>
-                            <td>
-                                <button type="button" class="text-button" v-if="!search.dates.created_before"
-                                        v-on:click="enableDate('created_before')">{{ trans('entities.search_set_date') }}</button>
-
-                            </td>
-                        </tr>
-                        <tr v-if="search.dates.created_before">
-                            <td>
-                                <input v-if="search.dates.created_before" class="tag-input"
-                                       v-on:input="dateChange('created_before')" type="date" v-model="search.dates.created_before"
-                                       pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
-                            </td>
-                            <td>
-                                <button v-if="search.dates.created_before" type="button" class="text-neg text-button"
-                                        v-on:click="dateRemove('created_before')">
-                                    <i class="zmdi zmdi-close"></i>
-                                </button>
-                            </td>
-                        </tr>
-                    </table>
-
-
-                    <button type="submit" class="button primary">{{ trans('entities.search_update') }}</button>
-                </form>
-
-
-            </div>
-
+                <h6 class="text-muted">{{ trans('entities.search_exact_matches') }}</h6>
+                <table cellpadding="0" cellspacing="0" border="0" class="no-style">
+                    <tr v-for="(term, i) in search.exactTerms">
+                        <td style="padding: 0 12px 6px 0;">
+                            <input class="exact-input outline" v-on:input="exactChange" type="text" v-model="search.exactTerms[i]"></td>
+                        <td>
+                            <button type="button" class="text-neg text-button" v-on:click="removeExact(i)">
+                                <i class="zmdi zmdi-close"></i>
+                            </button>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td colspan="2">
+                            <button type="button" class="text-button" v-on:click="addExact">
+                                <i class="zmdi zmdi-plus-circle-o"></i>{{ trans('common.add') }}
+                            </button>
+                        </td>
+                    </tr>
+                </table>
+
+                <h6 class="text-muted">{{ trans('entities.search_tags') }}</h6>
+                <table cellpadding="0" cellspacing="0" border="0" class="no-style">
+                    <tr v-for="(term, i) in search.tagTerms">
+                        <td style="padding: 0 12px 6px 0;">
+                            <input class="tag-input outline" v-on:input="tagChange" type="text" v-model="search.tagTerms[i]"></td>
+                        <td>
+                            <button type="button" class="text-neg text-button" v-on:click="removeTag(i)">
+                                <i class="zmdi zmdi-close"></i>
+                            </button>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td colspan="2">
+                            <button type="button" class="text-button" v-on:click="addTag">
+                                <i class="zmdi zmdi-plus-circle-o"></i>{{ trans('common.add') }}
+                            </button>
+                        </td>
+                    </tr>
+                </table>
+
+                <h6 class="text-muted">Options</h6>
+                <label class="checkbox">
+                    <input type="checkbox" v-on:change="optionChange('viewed_by_me')"
+                           v-model="search.option.viewed_by_me" value="page">
+                    {{ trans('entities.search_viewed_by_me') }}
+                </label>
+                <label class="checkbox">
+                    <input type="checkbox" v-on:change="optionChange('not_viewed_by_me')"
+                           v-model="search.option.not_viewed_by_me" value="page">
+                    {{ trans('entities.search_not_viewed_by_me') }}
+                </label>
+                <label class="checkbox">
+                    <input type="checkbox" v-on:change="optionChange('is_restricted')"
+                           v-model="search.option.is_restricted" value="page">
+                    {{ trans('entities.search_permissions_set') }}
+                </label>
+                <label class="checkbox">
+                    <input type="checkbox" v-on:change="optionChange('created_by:me')"
+                           v-model="search.option['created_by:me']" value="page">
+                    {{ trans('entities.search_created_by_me') }}
+                </label>
+                <label class="checkbox">
+                    <input type="checkbox" v-on:change="optionChange('updated_by:me')"
+                           v-model="search.option['updated_by:me']" value="page">
+                    {{ trans('entities.search_updated_by_me') }}
+                </label>
+
+                <h6 class="text-muted">Date Options</h6>
+                <table cellpadding="0" cellspacing="0" border="0" class="no-style form-table">
+                    <tr>
+                        <td width="200">{{ trans('entities.search_updated_after') }}</td>
+                        <td width="80">
+                            <button type="button" class="text-button" v-if="!search.dates.updated_after"
+                                    v-on:click="enableDate('updated_after')">{{ trans('entities.search_set_date') }}</button>
+
+                        </td>
+                    </tr>
+                    <tr v-if="search.dates.updated_after">
+                        <td>
+                            <input v-if="search.dates.updated_after" class="tag-input"
+                                   v-on:input="dateChange('updated_after')" type="date" v-model="search.dates.updated_after"
+                                   pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
+                        </td>
+                        <td>
+                            <button v-if="search.dates.updated_after" type="button" class="text-neg text-button"
+                                    v-on:click="dateRemove('updated_after')">
+                                <i class="zmdi zmdi-close"></i>
+                            </button>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td>{{ trans('entities.search_updated_before') }}</td>
+                        <td>
+                            <button type="button" class="text-button" v-if="!search.dates.updated_before"
+                                    v-on:click="enableDate('updated_before')">{{ trans('entities.search_set_date') }}</button>
+
+                        </td>
+                    </tr>
+                    <tr v-if="search.dates.updated_before">
+                        <td>
+                            <input v-if="search.dates.updated_before" class="tag-input"
+                                   v-on:input="dateChange('updated_before')" type="date" v-model="search.dates.updated_before"
+                                   pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
+                        </td>
+                        <td>
+                            <button v-if="search.dates.updated_before" type="button" class="text-neg text-button"
+                                    v-on:click="dateRemove('updated_before')">
+                                <i class="zmdi zmdi-close"></i>
+                            </button>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td>{{ trans('entities.search_created_after') }}</td>
+                        <td>
+                            <button type="button" class="text-button" v-if="!search.dates.created_after"
+                                    v-on:click="enableDate('created_after')">{{ trans('entities.search_set_date') }}</button>
+
+                        </td>
+                    </tr>
+                    <tr v-if="search.dates.created_after">
+                        <td>
+                            <input v-if="search.dates.created_after" class="tag-input"
+                                   v-on:input="dateChange('created_after')" type="date" v-model="search.dates.created_after"
+                                   pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
+                        </td>
+                        <td>
+                            <button v-if="search.dates.created_after" type="button" class="text-neg text-button"
+                                    v-on:click="dateRemove('created_after')">
+                                <i class="zmdi zmdi-close"></i>
+                            </button>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td>{{ trans('entities.search_created_before') }}</td>
+                        <td>
+                            <button type="button" class="text-button" v-if="!search.dates.created_before"
+                                    v-on:click="enableDate('created_before')">{{ trans('entities.search_set_date') }}</button>
+
+                        </td>
+                    </tr>
+                    <tr v-if="search.dates.created_before">
+                        <td>
+                            <input v-if="search.dates.created_before" class="tag-input"
+                                   v-on:input="dateChange('created_before')" type="date" v-model="search.dates.created_before"
+                                   pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
+                        </td>
+                        <td>
+                            <button v-if="search.dates.created_before" type="button" class="text-neg text-button"
+                                    v-on:click="dateRemove('created_before')">
+                                <i class="zmdi zmdi-close"></i>
+                            </button>
+                        </td>
+                    </tr>
+                </table>
+
+
+                <button type="submit" class="button primary">{{ trans('entities.search_update') }}</button>
+            </form>
         </div>
 
-
     </div>
-</div>
+@stop
+
+@section('body')
 
+    <div class="container small">
+        <input type="hidden" name="searchTerm" value="{{$searchTerm}}">
+
+        <h1>{{ trans('entities.search_results') }}</h1>
+        <h6 class="text-muted">{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}</h6>
+        @include('partials/entity-list', ['entities' => $entities])
+        @if ($hasNextPage)
+            <a href="{{ $nextPageLink }}" class="button">{{ trans('entities.search_more') }}</a>
+        @endif
+    </div>
 @stop
\ No newline at end of file
index c736bc24eb9520fce4089c51f2c5610d5c6c761f..09e480b4381701b477ac98f689866ce04b0f8c09 100644 (file)
-@extends('base')
-
-@section('content')
+@extends('simple-layout')
 
+@section('toolbar')
     @include('settings/navbar', ['selected' => 'settings'])
+@stop
 
-<div class="container small settings-container">
-
-    <h1>{{ trans('settings.settings') }}</h1>
-
-    <form action="{{ baseUrl("/settings") }}" method="POST">
-        {!! csrf_field() !!}
-
-        <h3>{{ trans('settings.app_settings') }}</h3>
-
-        <div class="row">
+@section('body')
+<div class="container small">
+
+    <div class="text-right text-muted container">
+        <br>
+        BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
+    </div>
+
+    <div class="card">
+        <h3><i class="zmdi zmdi-settings-square"></i> {{ trans('settings.app_settings') }}</h3>
+        <div class="body">
+            <form action="{{ baseUrl("/settings") }}" method="POST">
+            {!! csrf_field() !!}
+                <div class="row">
+
+                    <div class="col-md-6">
+                        <div class="form-group">
+                            <label for="setting-app-name">{{ trans('settings.app_name') }}</label>
+                            <p class="small">{{ trans('settings.app_name_desc') }}</p>
+                            <input type="text" value="{{ setting('app-name', 'BookStack') }}" name="setting-app-name" id="setting-app-name">
+                        </div>
+                        <div class="form-group">
+                            <label>{{ trans('settings.app_name_header') }}</label>
+                            @include('components.toggle-switch', ['name' => 'setting-app-name-header', 'value' => setting('app-name-header')])
+                        </div>
+                        <div class="form-group">
+                            <label for="setting-app-public">{{ trans('settings.app_public_viewing') }}</label>
+                            @include('components.toggle-switch', ['name' => 'setting-app-public', 'value' => setting('app-public')])
+                        </div>
+                        <div class="form-group">
+                            <label>{{ trans('settings.app_secure_images') }}</label>
+                            <p class="small">{{ trans('settings.app_secure_images_desc') }}</p>
+                            @include('components.toggle-switch', ['name' => 'setting-app-secure-images', 'value' => setting('app-secure-images')])
+                        </div>
+                        <div class="form-group">
+                            <label for="setting-app-editor">{{ trans('settings.app_editor') }}</label>
+                            <p class="small">{{ trans('settings.app_editor_desc') }}</p>
+                            <select name="setting-app-editor" id="setting-app-editor">
+                                <option @if(setting('app-editor') === 'wysiwyg') selected @endif value="wysiwyg">WYSIWYG</option>
+                                <option @if(setting('app-editor') === 'markdown') selected @endif value="markdown">Markdown</option>
+                            </select>
+                        </div>
+                    </div>
+
+                    <div class="col-md-6">
+                        <div class="form-group" id="logo-control">
+                            <label for="setting-app-logo">{{ trans('settings.app_logo') }}</label>
+                            <p class="small">{!! trans('settings.app_logo_desc') !!}</p>
+
+                            @include('components.image-picker', [
+                                'resizeHeight' => '43',
+                                'resizeWidth' => '200',
+                                'showRemove' => true,
+                                'defaultImage' => baseUrl('/logo.png'),
+                                'currentImage' => setting('app-logo'),
+                                'name' => 'setting-app-logo',
+                                'imageClass' => 'logo-image',
+                                'currentId' => false
+                            ])
+
+                        </div>
+                        <div class="form-group" id="color-control">
+                            <label for="setting-app-color">{{ trans('settings.app_primary_color') }}</label>
+                            <p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
+                            <input type="text" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#0288D1">
+                            <input type="hidden" value="{{ setting('app-color-light') }}" name="setting-app-color-light" id="setting-app-color-light">
+                        </div>
+                        <div class="form-group" id="homepage-control">
+                            <label for="setting-app-homepage">{{ trans('settings.app_homepage') }}</label>
+                            <p class="small">{{ trans('settings.app_homepage_desc') }}</p>
+                            @include('components.page-picker', ['name' => 'setting-app-homepage', 'placeholder' => trans('settings.app_homepage_default'), 'value' => setting('app-homepage')])
+                        </div>
+                    </div>
 
-            <div class="col-md-6">
-                <div class="form-group">
-                    <label for="setting-app-name">{{ trans('settings.app_name') }}</label>
-                    <p class="small">{{ trans('settings.app_name_desc') }}</p>
-                    <input type="text" value="{{ setting('app-name', 'BookStack') }}" name="setting-app-name" id="setting-app-name">
-                </div>
-                <div class="form-group">
-                    <label>{{ trans('settings.app_name_header') }}</label>
-                    @include('components.toggle-switch', ['name' => 'setting-app-name-header', 'value' => setting('app-name-header')])
-                </div>
-                <div class="form-group">
-                    <label for="setting-app-public">{{ trans('settings.app_public_viewing') }}</label>
-                    @include('components.toggle-switch', ['name' => 'setting-app-public', 'value' => setting('app-public')])
-                </div>
-                <div class="form-group">
-                    <label>{{ trans('settings.app_secure_images') }}</label>
-                    <p class="small">{{ trans('settings.app_secure_images_desc') }}</p>
-                    @include('components.toggle-switch', ['name' => 'setting-app-secure-images', 'value' => setting('app-secure-images')])
                 </div>
+
                 <div class="form-group">
-                    <label for="setting-app-editor">{{ trans('settings.app_editor') }}</label>
-                    <p class="small">{{ trans('settings.app_editor_desc') }}</p>
-                    <select name="setting-app-editor" id="setting-app-editor">
-                        <option @if(setting('app-editor') === 'wysiwyg') selected @endif value="wysiwyg">WYSIWYG</option>
-                        <option @if(setting('app-editor') === 'markdown') selected @endif value="markdown">Markdown</option>
-                    </select>
+                    <label for="setting-app-custom-head">{{ trans('settings.app_custom_html') }}</label>
+                    <p class="small">{{ trans('settings.app_custom_html_desc') }}</p>
+                    <textarea name="setting-app-custom-head" id="setting-app-custom-head">{{ setting('app-custom-head', '') }}</textarea>
                 </div>
-            </div>
-
-            <div class="col-md-6">
-                <div class="form-group" id="logo-control">
-                    <label for="setting-app-logo">{{ trans('settings.app_logo') }}</label>
-                    <p class="small">{!! trans('settings.app_logo_desc') !!}</p>
-
-                    @include('components.image-picker', [
-                        'resizeHeight' => '43',
-                        'resizeWidth' => '200',
-                        'showRemove' => true,
-                        'defaultImage' => baseUrl('/logo.png'),
-                        'currentImage' => setting('app-logo'),
-                        'name' => 'setting-app-logo',
-                        'imageClass' => 'logo-image',
-                        'currentId' => false
-                    ])
 
+                <div class="form-group text-right">
+                    <button type="submit" class="button pos">{{ trans('settings.settings_save') }}</button>
                 </div>
-                <div class="form-group" id="color-control">
-                    <label for="setting-app-color">{{ trans('settings.app_primary_color') }}</label>
-                    <p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
-                    <input  type="text" value="{{ setting('app-color', '') }}" name="setting-app-color" id="setting-app-color" placeholder="#0288D1">
-                    <input  type="hidden" value="{{ setting('app-color-light', '') }}" name="setting-app-color-light" id="setting-app-color-light" placeholder="rgba(21, 101, 192, 0.15)">
-                </div>
-            </div>
-
+            </form>
         </div>
-
-        <div class="form-group">
-            <label for="setting-app-custom-head">{{ trans('settings.app_custom_html') }}</label>
-            <p class="small">{{ trans('settings.app_custom_html_desc') }}</p>
-            <textarea name="setting-app-custom-head" id="setting-app-custom-head">{{ setting('app-custom-head', '') }}</textarea>
-        </div>
-
-        <hr class="margin-top">
-
-        <h3>{{ trans('settings.reg_settings') }}</h3>
-
-        <div class="row">
-            <div class="col-md-6">
-                <div class="form-group">
-                    <label for="setting-registration-enabled">{{ trans('settings.reg_allow') }}</label>
-                    @include('components.toggle-switch', ['name' => 'setting-registration-enabled', 'value' => setting('registration-enabled')])
-                </div>
-                <div class="form-group">
-                    <label for="setting-registration-role">{{ trans('settings.reg_default_role') }}</label>
-                    <select id="setting-registration-role" name="setting-registration-role" @if($errors->has('setting-registration-role')) class="neg" @endif>
-                        @foreach(\BookStack\Role::all() as $role)
-                            <option value="{{$role->id}}" data-role-name="{{ $role->name }}"
-                                    @if(setting('registration-role', \BookStack\Role::first()->id) == $role->id) selected @endif
+    </div>
+
+    <p>&nbsp;</p>
+
+    <div class="card">
+        <h3><i class="zmdi zmdi-accounts-add"></i> {{ trans('settings.reg_settings') }}</h3>
+        <div class="body">
+            <form action="{{ baseUrl("/settings") }}" method="POST">
+                {!! csrf_field() !!}
+
+                <div class="row">
+                    <div class="col-md-6">
+                        <div class="form-group">
+                            <label for="setting-registration-enabled">{{ trans('settings.reg_allow') }}</label>
+                            @include('components.toggle-switch', ['name' => 'setting-registration-enabled', 'value' => setting('registration-enabled')])
+                        </div>
+                        <div class="form-group">
+                            <label for="setting-registration-role">{{ trans('settings.reg_default_role') }}</label>
+                            <select id="setting-registration-role" name="setting-registration-role" @if($errors->has('setting-registration-role')) class="neg" @endif>
+                                @foreach(\BookStack\Role::all() as $role)
+                                    <option value="{{$role->id}}" data-role-name="{{ $role->name }}"
+                                            @if(setting('registration-role', \BookStack\Role::first()->id) == $role->id) selected @endif
                                     >
-                                {{ $role->display_name }}
-                            </option>
-                        @endforeach
-                    </select>
+                                        {{ $role->display_name }}
+                                    </option>
+                                @endforeach
+                            </select>
+                        </div>
+                        <div class="form-group">
+                            <label for="setting-registration-confirmation">{{ trans('settings.reg_confirm_email') }}</label>
+                            <p class="small">{{ trans('settings.reg_confirm_email_desc') }}</p>
+                            @include('components.toggle-switch', ['name' => 'setting-registration-confirmation', 'value' => setting('registration-confirmation')])
+                        </div>
+                    </div>
+                    <div class="col-md-6">
+                        <div class="form-group">
+                            <label for="setting-registration-restrict">{{ trans('settings.reg_confirm_restrict_domain') }}</label>
+                            <p class="small">{!! trans('settings.reg_confirm_restrict_domain_desc') !!}</p>
+                            <input type="text" id="setting-registration-restrict" name="setting-registration-restrict" placeholder="{{ trans('settings.reg_confirm_restrict_domain_placeholder') }}" value="{{ setting('registration-restrict', '') }}">
+                        </div>
+                    </div>
                 </div>
-                <div class="form-group">
-                    <label for="setting-registration-confirmation">{{ trans('settings.reg_confirm_email') }}</label>
-                    <p class="small">{{ trans('settings.reg_confirm_email_desc') }}</p>
-                    @include('components.toggle-switch', ['name' => 'setting-registration-confirmation', 'value' => setting('registration-confirmation')])
-                </div>
-            </div>
-            <div class="col-md-6">
-                <div class="form-group">
-                    <label for="setting-registration-restrict">{{ trans('settings.reg_confirm_restrict_domain') }}</label>
-                    <p class="small">{!! trans('settings.reg_confirm_restrict_domain_desc') !!}</p>
-                    <input type="text" id="setting-registration-restrict" name="setting-registration-restrict" placeholder="{{ trans('settings.reg_confirm_restrict_domain_placeholder') }}" value="{{ setting('registration-restrict', '') }}">
+
+                <div class="form-group text-right">
+                    <button type="submit" class="button pos">{{ trans('settings.settings_save') }}</button>
                 </div>
-            </div>
+            </form>
         </div>
+    </div>
 
-        <hr class="margin-top">
-
-        <div class="form-group">
-            <span class="float right muted">
-                BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
-            </span>
-            <button type="submit" class="button pos">{{ trans('settings.settings_save') }}</button>
-        </div>
-    </form>
 
 </div>
 
 @include('components.image-manager', ['imageType' => 'system'])
+@include('components.entity-selector-popup', ['entityTypes' => 'page'])
 
 @stop
 
                 var hexVal = '#' + this.color.colors.HEX;
                 var rgb = this.color.colors.RND.rgb;
                 var rgbLightVal = 'rgba('+ [rgb.r, rgb.g, rgb.b, '0.15'].join(',') +')';
+
                 // Set textbox color to hex color code.
                 var isEmpty = $.trim($elm.val()).length === 0;
                 if (!isEmpty) $elm.val(hexVal);
index 15000b1cf81578dd8f16fa7bebb6f455fed925b6..32ac4f4260ce6b7ff5b7ae4044f3078a2555100a 100644 (file)
@@ -1,18 +1,12 @@
 
-<div class="faded-small toolbar">
-    <div class="container">
-        <div class="row">
-            <div class="col-md-12 setting-nav nav-tabs">
-                @if($currentUser->can('settings-manage'))
-                    <a href="{{ baseUrl('/settings') }}" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>{{ trans('settings.settings') }}</a>
-                @endif
-                @if($currentUser->can('users-manage'))
-                    <a href="{{ baseUrl('/settings/users') }}" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>{{ trans('settings.users') }}</a>
-                @endif
-                @if($currentUser->can('user-roles-manage'))
-                    <a href="{{ baseUrl('/settings/roles') }}" @if($selected == 'roles') class="selected text-button" @endif><i class="zmdi zmdi-lock-open"></i>{{ trans('settings.roles') }}</a>
-                @endif
-            </div>
-        </div>
-    </div>
+<div class="col-md-12 setting-nav nav-tabs">
+    @if($currentUser->can('settings-manage'))
+        <a href="{{ baseUrl('/settings') }}" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>{{ trans('settings.settings') }}</a>
+    @endif
+    @if($currentUser->can('users-manage'))
+        <a href="{{ baseUrl('/settings/users') }}" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>{{ trans('settings.users') }}</a>
+    @endif
+    @if($currentUser->can('user-roles-manage'))
+        <a href="{{ baseUrl('/settings/roles') }}" @if($selected == 'roles') class="selected text-button" @endif><i class="zmdi zmdi-lock-open"></i>{{ trans('settings.roles') }}</a>
+    @endif
 </div>
\ No newline at end of file
index bae42540ce21423c6efb74b3f38076ab3914285d..e61922da609096327383052c17ec5e8ac5db08b0 100644 (file)
@@ -1,15 +1,17 @@
-@extends('base')
-
-@section('content')
+@extends('simple-layout')
 
+@section('toolbar')
     @include('settings/navbar', ['selected' => 'roles'])
+@stop
 
-    <div class="container">
-        <h1>{{ trans('settings.role_create') }}</h1>
+@section('body')
 
-        <form action="{{ baseUrl("/settings/roles/new") }}" method="POST">
-            @include('settings/roles/form')
-        </form>
-    </div>
+    <form action="{{ baseUrl("/settings/roles/new") }}" method="POST">
+        <div class="container">
+            <div class="row">
+                @include('settings/roles/form', ['title' => trans('settings.role_create'), 'icon' => 'plus'])
+            </div>
+        </div>
+    </form>
 
 @stop
index 0b8e1626fdb854ba14161ee77bf3cac893b9f1ed..8002cd4bd38ceb5b2ed781fd07f3e2360f0075db 100644 (file)
@@ -1,28 +1,36 @@
-@extends('base')
-
-@section('content')
+@extends('simple-layout')
 
+@section('toolbar')
     @include('settings/navbar', ['selected' => 'roles'])
+@stop
 
+@section('body')
     <div class="container small" ng-non-bindable>
-        <h1>{{ trans('settings.role_delete') }}</h1>
-        <p>{{ trans('settings.role_delete_confirm', ['roleName' => $role->display_name]) }}</p>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-delete"></i> {{ trans('settings.role_delete') }}</h3>
+            <div class="body">
+                <p>{{ trans('settings.role_delete_confirm', ['roleName' => $role->display_name]) }}</p>
 
-        <form action="{{ baseUrl("/settings/roles/delete/{$role->id}") }}" method="POST">
-            {!! csrf_field() !!}
-            <input type="hidden" name="_method" value="DELETE">
+                <form action="{{ baseUrl("/settings/roles/delete/{$role->id}") }}" method="POST">
+                    {!! csrf_field() !!}
+                    <input type="hidden" name="_method" value="DELETE">
 
-            @if($role->users->count() > 0)
-            <div class="form-group">
-                    <p>{{ trans('settings.role_delete_users_assigned', ['userCount' => $role->users->count()]) }}</p>
-                    @include('form/role-select', ['options' => $roles, 'name' => 'migration_role_id'])
+                    @if($role->users->count() > 0)
+                        <div class="form-group">
+                            <p>{{ trans('settings.role_delete_users_assigned', ['userCount' => $role->users->count()]) }}</p>
+                            @include('form/role-select', ['options' => $roles, 'name' => 'migration_role_id'])
+                        </div>
+                    @endif
+
+                    <p class="text-neg">{{ trans('settings.role_delete_sure') }}</p>
+                    <div class="form-group">
+                        <a href="{{ baseUrl("/settings/roles/{$role->id}") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                        <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
+                    </div>
+                </form>
             </div>
-            @endif
+        </div>
 
-            <p class="text-neg">{{ trans('settings.role_delete_sure') }}</p>
-            <a href="{{ baseUrl("/settings/roles/{$role->id}") }}" class="button muted">{{ trans('common.cancel') }}</a>
-            <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
-        </form>
     </div>
-
 @stop
index b46b646635f18417f390dd0bf83b30cd2f9da163..01ba53383595282cc8ceba368c912db01106e462 100644 (file)
@@ -1,24 +1,17 @@
-@extends('base')
-
-@section('content')
+@extends('simple-layout')
 
+@section('toolbar')
     @include('settings/navbar', ['selected' => 'roles'])
+@stop
 
-    <div class="container">
-        <div class="row">
-            <div class="col-sm-6">
-                <h1>{{ trans('settings.role_edit') }}</h1>
-            </div>
-            <div class="col-sm-6">
-                <p></p>
-                <a href="{{ baseUrl("/settings/roles/delete/{$role->id}") }}" class="button neg float right">{{ trans('settings.role_delete') }}</a>
+@section('body')
+
+    <form action="{{ baseUrl("/settings/roles/{$role->id}") }}" method="POST">
+        <input type="hidden" name="_method" value="PUT">
+        <div class="container">
+            <div class="row">
+                @include('settings/roles/form', ['model' => $role, 'title' => trans('settings.role_edit'), 'icon' => 'edit'])
             </div>
         </div>
-
-        <form action="{{ baseUrl("/settings/roles/{$role->id}") }}" method="POST">
-            <input type="hidden" name="_method" value="PUT">
-            @include('settings/roles/form', ['model' => $role])
-        </form>
-    </div>
-
+    </form>
 @stop
index 71b8f551fc97b22a083a82639421cae9fa53966a..b63a99259d5b3884d60bfb8436b446007df76d7c 100644 (file)
 {!! csrf_field() !!}
 
-<div class="row">
-
-    <div class="col-md-9">
-        <div class="row">
-            <div class="col-md-5">
-                <h3>{{ trans('settings.role_details') }}</h3>
-                <div class="form-group">
-                    <label for="name">{{ trans('settings.role_name') }}</label>
-                    @include('form/text', ['name' => 'display_name'])
-                </div>
-                <div class="form-group">
-                    <label for="name">{{ trans('settings.role_desc') }}</label>
-                    @include('form/text', ['name' => 'description'])
+<div class="col-md-9">
+    <div class="card">
+        <h3><i class="zmdi zmdi-{{$icon}}"></i> {{$title}}</h3>
+        <div class="body">
+            <div class="row">
+                <div class="col-md-5">
+                    <h5>{{ trans('settings.role_details') }}</h5>
+                    <div class="form-group">
+                        <label for="name">{{ trans('settings.role_name') }}</label>
+                        @include('form/text', ['name' => 'display_name'])
+                    </div>
+                    <div class="form-group">
+                        <label for="name">{{ trans('settings.role_desc') }}</label>
+                        @include('form/text', ['name' => 'description'])
+                    </div>
+                    <h5>{{ trans('settings.role_system') }}</h5>
+                    <label>@include('settings/roles/checkbox', ['permission' => 'users-manage']) {{ trans('settings.role_manage_users') }}</label>
+                    <label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) {{ trans('settings.role_manage_roles') }}</label>
+                    <label>@include('settings/roles/checkbox', ['permission' => 'restrictions-manage-all']) {{ trans('settings.role_manage_entity_permissions') }}</label>
+                    <label>@include('settings/roles/checkbox', ['permission' => 'restrictions-manage-own']) {{ trans('settings.role_manage_own_entity_permissions') }}</label>
+                    <label>@include('settings/roles/checkbox', ['permission' => 'settings-manage']) {{ trans('settings.role_manage_settings') }}</label>
                 </div>
-                <h3>{{ trans('settings.role_system') }}</h3>
-                <label>@include('settings/roles/checkbox', ['permission' => 'users-manage']) {{ trans('settings.role_manage_users') }}</label>
-                <label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) {{ trans('settings.role_manage_roles') }}</label>
-                <label>@include('settings/roles/checkbox', ['permission' => 'restrictions-manage-all']) {{ trans('settings.role_manage_entity_permissions') }}</label>
-                <label>@include('settings/roles/checkbox', ['permission' => 'restrictions-manage-own']) {{ trans('settings.role_manage_own_entity_permissions') }}</label>
-                <label>@include('settings/roles/checkbox', ['permission' => 'settings-manage']) {{ trans('settings.role_manage_settings') }}</label>
-            </div>
 
-            <div class="col-md-6">
+                <div class="col-md-6">
 
-                <h3>{{ trans('settings.role_asset') }}</h3>
-                <p>{{ trans('settings.role_asset_desc') }}</p>
+                    <h5>{{ trans('settings.role_asset') }}</h5>
+                    <p>{{ trans('settings.role_asset_desc') }}</p>
 
-                <table class="table">
-                    <tr>
-                        <th width="20%"></th>
-                        <th width="20%">{{ trans('common.create') }}</th>
-                        <th width="20%">{{ trans('common.view') }}</th>
-                        <th width="20%">{{ trans('common.edit') }}</th>
-                        <th width="20%">{{ trans('common.delete') }}</th>
-                    </tr>
-                    <tr>
-                        <td>{{ trans('entities.books') }}</td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'book-create-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'book-view-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'book-view-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'book-update-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'book-update-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'book-delete-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'book-delete-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                    </tr>
-                    <tr>
-                        <td>{{ trans('entities.chapters') }}</td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-create-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-create-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-view-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-view-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-update-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-update-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-delete-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'chapter-delete-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                    </tr>
-                    <tr>
-                        <td>{{ trans('entities.pages') }}</td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-create-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-create-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-view-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-view-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-update-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-update-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-delete-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'page-delete-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                    </tr>
-                    <tr>
-                        <td>{{ trans('entities.images') }}</td>
-                        <td>@include('settings/roles/checkbox', ['permission' => 'image-create-all'])</td>
-                        <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'image-update-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'image-update-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'image-delete-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'image-delete-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                    </tr>
-                    <tr>
-                        <td>{{ trans('entities.attachments') }}</td>
-                        <td>@include('settings/roles/checkbox', ['permission' => 'attachment-create-all'])</td>
-                        <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'attachment-update-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'attachment-update-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                        <td>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-own']) {{ trans('settings.role_own') }}</label>
-                            <label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-all']) {{ trans('settings.role_all') }}</label>
-                        </td>
-                    </tr>
-                </table>
+                    <table class="table">
+                        <tr>
+                            <th width="20%"></th>
+                            <th width="20%">{{ trans('common.create') }}</th>
+                            <th width="20%">{{ trans('common.view') }}</th>
+                            <th width="20%">{{ trans('common.edit') }}</th>
+                            <th width="20%">{{ trans('common.delete') }}</th>
+                        </tr>
+                        <tr>
+                            <td>{{ trans('entities.books') }}</td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'book-create-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'book-view-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'book-view-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'book-update-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'book-update-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'book-delete-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'book-delete-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>{{ trans('entities.chapters') }}</td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-create-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-create-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-view-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-view-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-update-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-update-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-delete-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'chapter-delete-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>{{ trans('entities.pages') }}</td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-create-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-create-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-view-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-view-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-update-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-update-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-delete-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'page-delete-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>{{ trans('entities.images') }}</td>
+                            <td>@include('settings/roles/checkbox', ['permission' => 'image-create-all'])</td>
+                            <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'image-update-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'image-update-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'image-delete-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'image-delete-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>{{ trans('entities.attachments') }}</td>
+                            <td>@include('settings/roles/checkbox', ['permission' => 'attachment-create-all'])</td>
+                            <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'attachment-update-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'attachment-update-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'attachment-delete-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>{{ trans('entities.comments') }}</td>
+                            <td>@include('settings/roles/checkbox', ['permission' => 'comment-create-all'])</td>
+                            <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'comment-update-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'comment-update-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                            <td>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'comment-delete-own']) {{ trans('settings.role_own') }}</label>
+                                <label>@include('settings/roles/checkbox', ['permission' => 'comment-delete-all']) {{ trans('settings.role_all') }}</label>
+                            </td>
+                        </tr>
+                    </table>
+                </div>
+            </div>
+            <div class="form-group text-right">
+                <a href="{{ baseUrl("/settings/roles") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                @if (isset($role) && $role->id)
+                    <a href="{{ baseUrl("/settings/roles/delete/{$role->id}") }}" class="button neg">{{ trans('settings.role_delete') }}</a>
+                @endif
+                <button type="submit" class="button pos">{{ trans('settings.role_save') }}</button>
             </div>
         </div>
-        <a href="{{ baseUrl("/settings/roles") }}" class="button muted">{{ trans('common.cancel') }}</a>
-        <button type="submit" class="button pos">{{ trans('settings.role_save') }}</button>
     </div>
-    <div class="col-md-3">
-        <h3>{{ trans('settings.role_users') }}</h3>
-
-        @if(isset($role) && count($role->users) > 0)
-        <table class="list-table">
-            @foreach($role->users as $user)
-                <tr>
-                    <td style="line-height: 0;"><img class="avatar small" src="{{ $user->getAvatar(40) }}" alt="{{ $user->name }}"></td>
-                    <td>
-                        @if(userCan('users-manage') || $currentUser->id == $user->id)
-                            <a href="{{ baseUrl("/settings/users/{$user->id}") }}">
-                                @endif
-                                {{ $user->name }}
+</div>
+<div class="col-md-3">
+    <div class="card">
+        <h3><i class="zmdi zmdi-accounts"></i> {{ trans('settings.role_users') }}</h3>
+        <div class="body">
+            @if(isset($role) && count($role->users) > 0)
+                <table class="list-table">
+                    @foreach($role->users as $user)
+                        <tr>
+                            <td style="line-height: 0;"><img class="avatar small" src="{{ $user->getAvatar(40) }}" alt="{{ $user->name }}"></td>
+                            <td>
                                 @if(userCan('users-manage') || $currentUser->id == $user->id)
-                            </a>
-                        @endif
-                    </td>
-                </tr>
-            @endforeach
-        </table>
-        @else
-            <p class="text-muted">
-                {{ trans('settings.role_users_none') }}
-            </p>
-        @endif
-
+                                    <a href="{{ baseUrl("/settings/users/{$user->id}") }}">
+                                        @endif
+                                        {{ $user->name }}
+                                        @if(userCan('users-manage') || $currentUser->id == $user->id)
+                                    </a>
+                                @endif
+                            </td>
+                        </tr>
+                    @endforeach
+                </table>
+            @else
+                <p class="text-muted">
+                    {{ trans('settings.role_users_none') }}
+                </p>
+            @endif
+        </div>
     </div>
-
-
-
 </div>
\ No newline at end of file
index c24254946d6fc34f6b974439219c8acb741d32d6..6cde245394afbdd33201d407c04d551f0cfe6da3 100644 (file)
@@ -1,35 +1,36 @@
-@extends('base')
-
-@section('content')
+@extends('simple-layout')
 
+@section('toolbar')
     @include('settings/navbar', ['selected' => 'roles'])
+@stop
+
+@section('body')
 
     <div class="container small">
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-lock-open"></i> {{ trans('settings.role_user_roles') }}</h3>
+            <div class="body">
+                <table class="table">
+                    <tr>
+                        <th>{{ trans('settings.role_name') }}</th>
+                        <th></th>
+                        <th class="text-center">{{ trans('settings.users') }}</th>
+                    </tr>
+                    @foreach($roles as $role)
+                        <tr>
+                            <td><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td>
+                            <td>{{ $role->description }}</td>
+                            <td class="text-center">{{ $role->users->count() }}</td>
+                        </tr>
+                    @endforeach
+                </table>
 
-        <div class="row action-header">
-            <div class="col-sm-8">
-                <h1>{{ trans('settings.role_user_roles') }}</h1>
-            </div>
-            <div class="col-sm-4">
-                <p></p>
-                <a href="{{ baseUrl("/settings/roles/new") }}" class="button float right pos"><i class="zmdi zmdi-lock-open"></i>{{ trans('settings.role_create') }}</a>
+                <div class="form-group">
+                    <a href="{{ baseUrl("/settings/roles/new") }}" class="button pos">{{ trans('settings.role_create') }}</a>
+                </div>
             </div>
         </div>
-
-        <table class="table">
-            <tr>
-                <th>{{ trans('settings.role_name') }}</th>
-                <th></th>
-                <th class="text-center">{{ trans('settings.users') }}</th>
-            </tr>
-            @foreach($roles as $role)
-                <tr>
-                    <td><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td>
-                    <td>{{ $role->description }}</td>
-                    <td class="text-center">{{ $role->users->count() }}</td>
-                </tr>
-            @endforeach
-        </table>
     </div>
 
 @stop
diff --git a/resources/views/sidebar-layout.blade.php b/resources/views/sidebar-layout.blade.php
new file mode 100644 (file)
index 0000000..592dbc4
--- /dev/null
@@ -0,0 +1,34 @@
+@extends('base')
+
+@section('body-class', 'sidebar-layout')
+
+@section('content')
+
+    <div class="toolbar-container">
+        <div class="faded-small toolbar">
+            <div class="container fluid">
+                <div class="row">
+                    @yield('toolbar')
+                </div>
+            </div>
+        </div>
+    </div>
+
+
+    <div class="flex-fill flex" @yield('container-attrs') >
+
+        <div sidebar class="sidebar flex print-hidden" id="sidebar">
+            <div class="sidebar-toggle primary-background-light"><i class="zmdi zmdi-caret-right-circle"></i>
+            </div>
+            <div class="scroll-body">
+                @yield('sidebar')
+            </div>
+        </div>
+
+        <div class="content flex">
+            @yield('body')
+        </div>
+    </div>
+
+
+@stop
diff --git a/resources/views/simple-layout.blade.php b/resources/views/simple-layout.blade.php
new file mode 100644 (file)
index 0000000..eeb4129
--- /dev/null
@@ -0,0 +1,27 @@
+@extends('base')
+
+@section('body-class', 'shaded')
+
+@section('content')
+
+    <div class="toolbar-container">
+        <div class="faded-small toolbar">
+            <div class="container fluid">
+                <div class="row">
+                    @yield('toolbar')
+                </div>
+            </div>
+        </div>
+    </div>
+
+
+    <div class="flex-fill flex">
+        <div class="content flex">
+            <div class="scroll-body">
+                @yield('body')
+            </div>
+        </div>
+    </div>
+
+
+@stop
index e0019e618f460a5c478578b1d55d17d1ffa19413..90ceac9b8d4f27042d84f9352dcd919b61db8f0d 100644 (file)
@@ -1,31 +1,26 @@
-@extends('base')
+@extends('simple-layout')
 
+@section('toolbar')
+    @include('settings/navbar', ['selected' => 'users'])
+@stop
 
-@section('content')
+@section('body')
 
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-sm-12 faded">
-                    <div class="breadcrumbs">
-                        <a href="{{ baseUrl('/settings/users') }}" class="text-button"><i class="zmdi zmdi-accounts"></i>{{ trans('settings.users') }}</a>
+    <div class="container small" ng-non-bindable>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-accounts-add"></i> {{ trans('settings.users_add_new') }}</h3>
+            <div class="body">
+                <form action="{{ baseUrl("/settings/users/create") }}" method="post">
+                    {!! csrf_field() !!}
+                    @include('users/forms/' . $authMethod)
+                    <div class="form-group text-right">
+                        <a href="{{  baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                        <button class="button pos" type="submit">{{ trans('common.save') }}</button>
                     </div>
-                </div>
+                </form>
             </div>
         </div>
     </div>
 
-    <div class="container small" ng-non-bindable>
-        <h1>{{ trans('settings.users_add_new') }}</h1>
-
-        <form action="{{ baseUrl("/settings/users/create") }}" method="post">
-            {!! csrf_field() !!}
-            @include('users/forms/' . $authMethod)
-            <div class="form-group">
-                <a href="{{  baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button muted">{{ trans('common.cancel') }}</a>
-                <button class="button pos" type="submit">{{ trans('common.save') }}</button>
-            </div>
-        </form>
-    </div>
-
 @stop
index 135231a32bf2325650643f8cc8a86db5e9d44ec9..a7d4e2476fc7ca61ad5266a9704478c64379fc89 100644 (file)
@@ -1,32 +1,27 @@
-@extends('base')
+@extends('simple-layout')
 
-@section('content')
+@section('toolbar')
+    @include('settings/navbar', ['selected' => 'users'])
+@stop
 
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-sm-12 faded">
-                    <div class="breadcrumbs">
-                        <a href="{{ baseUrl("/settings/users") }}" class="text-button"><i class="zmdi zmdi-accounts"></i>Users</a>
-                        <span class="sep">&raquo;</span>
-                        <a href="{{ baseUrl("/settings/users/{$user->id}") }}" class="text-button"><i class="zmdi zmdi-account"></i>{{ $user->name }}</a>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
+@section('body')
 
     <div class="container small" ng-non-bindable>
-        <h1>{{ trans('settings.users_delete') }}</h1>
-        <p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
-        <p class="text-neg">{{ trans('settings.users_delete_confirm') }}</p>
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-delete"></i> {{ trans('settings.users_delete') }}</h3>
+            <div class="body">
+                <p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
+                <p class="text-neg">{{ trans('settings.users_delete_confirm') }}</p>
 
-        <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="POST">
-            {!! csrf_field() !!}
-            <input type="hidden" name="_method" value="DELETE">
-            <a href="{{ baseUrl("/settings/users/{$user->id}") }}" class="button muted">{{ trans('common.cancel') }}</a>
-            <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
-        </form>
+                <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="POST">
+                    {!! csrf_field() !!}
+                    <input type="hidden" name="_method" value="DELETE">
+                    <a href="{{ baseUrl("/settings/users/{$user->id}") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
+                </form>
+            </div>
+        </div>
     </div>
 
 @stop
index ff3475194bdfacd7705d74cf17e8d81061a8048b..f860d12fd96a27dbb110f1a6705b9966f99a6e98 100644 (file)
@@ -1,80 +1,84 @@
-@extends('base')
-
-
-@section('content')
+@extends('simple-layout')
 
+@section('toolbar')
     @include('settings/navbar', ['selected' => 'users'])
+@stop
+
+@section('body')
 
     <div class="container small">
-        <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="post">
-            <div class="row">
-                <div class="col-sm-8">
-                    <h1>{{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
-                </div>
-                <div class="col-sm-4">
-                    <p></p>
-                    @if($authMethod !== 'system')
-                        <a href="{{ baseUrl("/settings/users/{$user->id}/delete") }}" class="neg button float right">{{ trans('settings.users_delete') }}</a>
-                    @endif
-                </div>
-            </div>
-            <div class="row">
-                <div class="col-sm-6" ng-non-bindable>
-                    {!! csrf_field() !!}
-                    <input type="hidden" name="_method" value="put">
-                    @include('users.forms.' . $authMethod, ['model' => $user])
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi-edit zmdi"></i> {{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h3>
+            <div class="body">
+                <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="post">
+                    <div class="row">
+                        <div class="col-sm-6" ng-non-bindable>
+                            {!! csrf_field() !!}
+                            <input type="hidden" name="_method" value="put">
+                            @include('users.forms.' . $authMethod, ['model' => $user])
 
-                </div>
-                <div class="col-sm-6">
-                    <div class="form-group" id="logo-control">
-                        <label for="user-avatar">{{ trans('settings.users_avatar') }}</label>
-                        <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
+                        </div>
+                        <div class="col-sm-6">
+                            <div class="form-group" id="logo-control">
+                                <label for="user-avatar">{{ trans('settings.users_avatar') }}</label>
+                                <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
 
-                        @include('components.image-picker', [
-                              'resizeHeight' => '512',
-                              'resizeWidth' => '512',
-                              'showRemove' => false,
-                              'defaultImage' => baseUrl('/user_avatar.png'),
-                              'currentImage' => $user->getAvatar(80),
-                              'currentId' => $user->image_id,
-                              'name' => 'image_id',
-                              'imageClass' => 'avatar large'
-                          ])
+                                @include('components.image-picker', [
+                                      'resizeHeight' => '512',
+                                      'resizeWidth' => '512',
+                                      'showRemove' => false,
+                                      'defaultImage' => baseUrl('/user_avatar.png'),
+                                      'currentImage' => $user->getAvatar(80),
+                                      'currentId' => $user->image_id,
+                                      'name' => 'image_id',
+                                      'imageClass' => 'avatar large'
+                                  ])
+                            </div>
+                            <div class="form-group">
+                                <label for="user-language">{{ trans('settings.users_preferred_language') }}</label>
+                                <select name="setting[language]" id="user-language">
+                                    @foreach(trans('settings.language_select') as $lang => $label)
+                                        <option @if(setting()->getUser($user, 'language') === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
+                                    @endforeach
+                                </select>
+                            </div>
+                        </div>
                     </div>
-                    <div class="form-group">
-                        <label for="user-language">{{ trans('settings.users_preferred_language') }}</label>
-                        <select name="setting[language]" id="user-language">
-                            @foreach(trans('settings.language_select') as $lang => $label)
-                                <option @if(setting()->getUser($user, 'language') === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
-                            @endforeach
-                        </select>
+                    <div class="form-group text-right">
+                        <a href="{{  baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                        @if($authMethod !== 'system')
+                            <a href="{{ baseUrl("/settings/users/{$user->id}/delete") }}" class="neg button">{{ trans('settings.users_delete') }}</a>
+                        @endif
+                        <button class="button pos" type="submit">{{ trans('common.save') }}</button>
                     </div>
-                </div>
+                </form>
             </div>
-            <div class="form-group">
-                <a href="{{  baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button muted">{{ trans('common.cancel') }}</a>
-                <button class="button pos" type="submit">{{ trans('common.save') }}</button>
-            </div>
-        </form>
-
-        <hr class="margin-top large">
+        </div>
 
         @if($currentUser->id === $user->id && count($activeSocialDrivers) > 0)
-            <h3>{{ trans('settings.users_social_accounts') }}</h3>
-            <p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
-            <div class="row">
-                @foreach($activeSocialDrivers as $driver => $enabled)
-                    <div class="col-sm-3 col-xs-6 text-center">
-                        <div>@icon($driver, ['width' => 56])</div>
-                        <div>
-                            @if($user->hasSocialAccount($driver))
-                                <a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button neg">{{ trans('settings.users_social_disconnect') }}</a>
-                            @else
-                                <a href="{{ baseUrl("/login/service/{$driver}") }}" class="button pos">{{ trans('settings.users_social_connect') }}</a>
-                            @endif
+            <div class="card">
+                <h3><i class="zmdi zmdi-sign-in"></i> {{ trans('settings.users_social_accounts') }}</h3>
+                <div class="body">
+                    <p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
+                    <div class="container">
+                        <div class="row">
+                            @foreach($activeSocialDrivers as $driver => $enabled)
+                                <div class="col-sm-4 col-xs-6 text-center">
+                                    <div>@icon($driver, ['width' => 56])</div>
+                                    <div>
+                                        @if($user->hasSocialAccount($driver))
+                                            <a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button neg">{{ trans('settings.users_social_disconnect') }}</a>
+                                        @else
+                                            <a href="{{ baseUrl("/login/service/{$driver}") }}" class="button pos">{{ trans('settings.users_social_connect') }}</a>
+                                        @endif
+                                    </div>
+                                    <div>&nbsp;</div>
+                                </div>
+                            @endforeach
                         </div>
                     </div>
-                @endforeach
+                </div>
             </div>
         @endif
 
index 3ea5a03f7fa377b9582ead6be0fd11b077669170..26da56858b7bb65bf1212afdf0a981af6b5f11da 100644 (file)
@@ -1,80 +1,77 @@
-@extends('base')
-
-
-@section('content')
+@extends('simple-layout')
 
+@section('toolbar')
     @include('settings/navbar', ['selected' => 'users'])
+@stop
 
-
+@section('body')
     <div class="container small" ng-non-bindable>
-        <div class="row action-header">
-            <div class="col-sm-8">
-                <h1>{{ trans('settings.users') }}</h1>
-            </div>
-            <div class="col-sm-4">
-                <p></p>
-                @if(userCan('users-manage'))
-                    <a href="{{ baseUrl("/settings/users/create") }}" class="pos button float right"><i class="zmdi zmdi-account-add"></i>{{ trans('settings.users_add_new') }}</a>
-                @endif
-            </div>
-        </div>
-
-        <div class="row">
-            <div class="col-sm-8">
-                <div class="compact">
-                    {{ $users->links() }}
+        <p>&nbsp;</p>
+        <div class="card">
+            <h3><i class="zmdi zmdi-accounts"></i> {{ trans('settings.users') }}</h3>
+            <div class="body">
+                <div class="container">
+                    <div class="row">
+                        <div class="col-sm-4">
+                            <form method="get" action="{{ baseUrl("/settings/users") }}">
+                                @foreach(collect($listDetails)->except('search') as $name => $val)
+                                    <input type="hidden" name="{{ $name }}" value="{{ $val }}">
+                                @endforeach
+                                <input type="text" name="search" placeholder="{{ trans('settings.users_search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
+                            </form>
+                        </div>
+                        <div class="col-sm-8 text-right">
+                            @if(userCan('users-manage'))
+                                <a href="{{ baseUrl("/settings/users/create") }}" style="margin-top: 0;" class="pos button">{{ trans('settings.users_add_new') }}</a>
+                            @endif
+                        </div>
+                    </div>
                 </div>
-            </div>
-            <div class="col-sm-4">
-                <form method="get" class="float right" action="{{ baseUrl("/settings/users") }}">
-                    @foreach(collect($listDetails)->except('search') as $name => $val)
-                        <input type="hidden" name="{{ $name }}" value="{{ $val }}">
-                    @endforeach
-                    <input type="text" name="search" placeholder="{{ trans('settings.users_search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
-                </form>
-            </div>
-        </div>
 
-        <table class="table">
-            <tr>
-                <th></th>
-                <th><a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'name']) }}">{{ trans('auth.name') }}</a></th>
-                <th><a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">{{ trans('auth.email') }}</a></th>
-                <th>{{ trans('settings.role_user_roles') }}</th>
-            </tr>
-            @foreach($users as $user)
-                <tr>
-                    <td style="line-height: 0;"><img class="avatar med" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}"></td>
-                    <td>
-                        @if(userCan('users-manage') || $currentUser->id == $user->id)
-                            <a href="{{ baseUrl("/settings/users/{$user->id}") }}">
-                                @endif
-                                {{ $user->name }}
+                <table class="table">
+                    <tr>
+                        <th></th>
+                        <th><a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'name']) }}">{{ trans('auth.name') }}</a></th>
+                        <th><a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">{{ trans('auth.email') }}</a></th>
+                        <th>{{ trans('settings.role_user_roles') }}</th>
+                    </tr>
+                    @foreach($users as $user)
+                        <tr>
+                            <td style="line-height: 0;"><img class="avatar med" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}"></td>
+                            <td>
                                 @if(userCan('users-manage') || $currentUser->id == $user->id)
-                            </a>
-                        @endif
-                    </td>
-                    <td>
-                        @if(userCan('users-manage') || $currentUser->id == $user->id)
-                            <a href="{{ baseUrl("/settings/users/{$user->id}") }}">
+                                    <a href="{{ baseUrl("/settings/users/{$user->id}") }}">
+                                        @endif
+                                        {{ $user->name }}
+                                        @if(userCan('users-manage') || $currentUser->id == $user->id)
+                                    </a>
                                 @endif
-                                {{ $user->email }}
+                            </td>
+                            <td>
                                 @if(userCan('users-manage') || $currentUser->id == $user->id)
-                            </a>
-                        @endif
-                    </td>
-                    <td>
-                        @foreach($user->roles as $index => $role)
-                            <small><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small>
-                        @endforeach
-                    </td>
-                </tr>
-            @endforeach
-        </table>
+                                    <a href="{{ baseUrl("/settings/users/{$user->id}") }}">
+                                        @endif
+                                        {{ $user->email }}
+                                        @if(userCan('users-manage') || $currentUser->id == $user->id)
+                                    </a>
+                                @endif
+                            </td>
+                            <td>
+                                @foreach($user->roles as $index => $role)
+                                    <small><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small>
+                                @endforeach
+                            </td>
+                        </tr>
+                    @endforeach
+                </table>
 
-        <div>
-            {{ $users->links() }}
+                <div>
+                    {{ $users->links() }}
+                </div>
+
+            </div>
         </div>
+
     </div>
 
 @stop
index 10bd0ec633a0c44cee7a3ac2c92c08446e23367a..be04c97a990a4b02488eb1b9592140cf71c566cd 100644 (file)
@@ -1,76 +1,81 @@
-@extends('base')
+@extends('sidebar-layout')
 
-@section('content')
+@section('toolbar')
+    <div class="col-sm-6 col-xs-1 faded">
+        <div class="breadcrumbs">
+            <a href="{{ $user->getProfileUrl() }}" class="text-button"><i class="zmdi zmdi-account"></i>{{ $user->name }}</a>
+        </div>
+    </div>
+@stop
 
-    <div class="container" ng-non-bindable>
-        <div class="row">
-            <div class="col-sm-7">
+@section('sidebar')
+    <div class="card" id="recent-activity">
+        <h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
+        @include('partials/activity-list', ['activity' => $activity])
+    </div>
+@stop
+
+@section('body')
 
-                <div class="padded-top large"></div>
+    <div class="container small" ng-non-bindable>
 
-                <div class="row">
-                    <div class="col-md-7">
-                        <div class="clearfix">
-                            <div class="padded-right float left">
-                                <img class="avatar square huge" src="{{ $user->getAvatar(120) }}" alt="{{ $user->name }}">
-                            </div>
-                            <div>
-                                <h3 style="margin-top: 0;">{{ $user->name }}</h3>
-                                <p class="text-muted">
-                                    {{ trans('entities.profile_user_for_x', ['time' => $user->created_at->diffForHumans(null, true)]) }}
-                                </p>
-                            </div>
-                        </div>
+        <div class="padded-top large"></div>
+
+        <div class="row">
+            <div class="col-md-7">
+                <div class="clearfix">
+                    <div class="padded-right float left">
+                        <img class="avatar square huge" src="{{ $user->getAvatar(120) }}" alt="{{ $user->name }}">
                     </div>
-                    <div class="col-md-5 text-bigger" id="content-counts">
-                        <div class="text-muted">{{ trans('entities.profile_created_content') }}</div>
-                        <div class="text-book">
-                            <i class="zmdi zmdi-book zmdi-hc-fw"></i> {{ $assetCounts['books'] }} {{ str_plural(trans('entities.book'), $assetCounts['books']) }}
-                        </div>
-                        <div class="text-chapter">
-                            <i class="zmdi zmdi-collection-bookmark zmdi-hc-fw"></i> {{ $assetCounts['chapters'] }} {{ str_plural(trans('entities.chapter'), $assetCounts['chapters']) }}
-                        </div>
-                        <div class="text-page">
-                            <i class="zmdi zmdi-file-text zmdi-hc-fw"></i> {{ $assetCounts['pages'] }} {{ str_plural(trans('entities.page'), $assetCounts['pages']) }}
-                        </div>
+                    <div>
+                        <h3 style="margin-top: 0;">{{ $user->name }}</h3>
+                        <p class="text-muted">
+                            {{ trans('entities.profile_user_for_x', ['time' => $user->created_at->diffForHumans(null, true)]) }}
+                        </p>
                     </div>
                 </div>
+            </div>
+            <div class="col-md-5 text-bigger" id="content-counts">
+                <div class="text-muted">{{ trans('entities.profile_created_content') }}</div>
+                <div class="text-book">
+                    <i class="zmdi zmdi-book zmdi-hc-fw"></i> {{ trans_choice('entities.x_books', $assetCounts['books']) }}
+                </div>
+                <div class="text-chapter">
+                    <i class="zmdi zmdi-collection-bookmark zmdi-hc-fw"></i> {{ trans_choice('entities.x_chapters', $assetCounts['chapters']) }}
+                </div>
+                <div class="text-page">
+                    <i class="zmdi zmdi-file-text zmdi-hc-fw"></i> {{ trans_choice('entities.x_pages', $assetCounts['pages']) }}
+                </div>
+            </div>
+        </div>
 
 
-                <hr class="even">
-
-                <h3>{{ trans('entities.recently_created_pages') }}</h3>
-                @if (count($recentlyCreated['pages']) > 0)
-                    @include('partials/entity-list', ['entities' => $recentlyCreated['pages']])
-                @else
-                    <p class="text-muted">{{ trans('entities.profile_not_created_pages', ['userName' => $user->name]) }}</p>
-                @endif
-
-                <hr class="even">
+        <hr class="even">
 
-                <h3>{{ trans('entities.recently_created_chapters') }}</h3>
-                @if (count($recentlyCreated['chapters']) > 0)
-                    @include('partials/entity-list', ['entities' => $recentlyCreated['chapters']])
-                @else
-                    <p class="text-muted">{{ trans('entities.profile_not_created_chapters', ['userName' => $user->name]) }}</p>
-                @endif
+        <h3>{{ trans('entities.recently_created_pages') }}</h3>
+        @if (count($recentlyCreated['pages']) > 0)
+            @include('partials/entity-list', ['entities' => $recentlyCreated['pages']])
+        @else
+            <p class="text-muted">{{ trans('entities.profile_not_created_pages', ['userName' => $user->name]) }}</p>
+        @endif
 
-                <hr class="even">
+        <hr class="even">
 
-                <h3>{{ trans('entities.recently_created_books') }}</h3>
-                @if (count($recentlyCreated['books']) > 0)
-                    @include('partials/entity-list', ['entities' => $recentlyCreated['books']])
-                @else
-                    <p class="text-muted">{{ trans('entities.profile_not_created_books', ['userName' => $user->name]) }}</p>
-                @endif
-            </div>
+        <h3>{{ trans('entities.recently_created_chapters') }}</h3>
+        @if (count($recentlyCreated['chapters']) > 0)
+            @include('partials/entity-list', ['entities' => $recentlyCreated['chapters']])
+        @else
+            <p class="text-muted">{{ trans('entities.profile_not_created_chapters', ['userName' => $user->name]) }}</p>
+        @endif
 
-            <div class="col-sm-4 col-sm-offset-1" id="recent-activity">
-                <h3>{{ trans('entities.recent_activity') }}</h3>
-                @include('partials/activity-list', ['activity' => $activity])
-            </div>
+        <hr class="even">
 
-        </div>
+        <h3>{{ trans('entities.recently_created_books') }}</h3>
+        @if (count($recentlyCreated['books']) > 0)
+            @include('partials/entity-list', ['entities' => $recentlyCreated['books']])
+        @else
+            <p class="text-muted">{{ trans('entities.profile_not_created_books', ['userName' => $user->name]) }}</p>
+        @endif
     </div>
 
 
index 8ecfd9465ec6c2962375248722031e4a2daa3270..8bff3b2ec0c2db0dee51e789b9e7b2cc70b206fa 100644 (file)
@@ -119,6 +119,11 @@ Route::group(['middleware' => 'auth'], function () {
 
     Route::get('/ajax/search/entities', 'SearchController@searchEntitiesAjax');
 
+    // Comments
+    Route::post('/ajax/page/{pageId}/comment', 'CommentController@savePageComment');
+    Route::put('/ajax/comment/{id}', 'CommentController@update');
+    Route::delete('/ajax/comment/{id}', 'CommentController@destroy');
+
     // Links
     Route::get('/link/{id}', 'PageController@redirectFromLink');
 
diff --git a/tests/Entity/CommentTest.php b/tests/Entity/CommentTest.php
new file mode 100644 (file)
index 0000000..04716c1
--- /dev/null
@@ -0,0 +1,74 @@
+<?php namespace Tests;
+
+use BookStack\Page;
+use BookStack\Comment;
+
+class CommentTest extends TestCase
+{
+
+    public function test_add_comment()
+    {
+        $this->asAdmin();
+        $page = Page::first();
+
+        $comment = factory(Comment::class)->make(['parent_id' => 2]);
+        $resp = $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+
+        $resp->assertStatus(200);
+        $resp->assertSee($comment->text);
+
+        $pageResp = $this->get($page->getUrl());
+        $pageResp->assertSee($comment->text);
+
+        $this->assertDatabaseHas('comments', [
+            'local_id' => 1,
+            'entity_id' => $page->id,
+            'entity_type' => 'BookStack\\Page',
+            'text' => $comment->text,
+            'parent_id' => 2
+        ]);
+    }
+
+    public function test_comment_edit()
+    {
+        $this->asAdmin();
+        $page = Page::first();
+
+        $comment = factory(Comment::class)->make();
+        $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+
+        $comment = $page->comments()->first();
+        $newText = 'updated text content';
+        $resp = $this->putJson("/ajax/comment/$comment->id", [
+            'text' => $newText,
+            'html' => '<p>'.$newText.'</p>',
+        ]);
+
+        $resp->assertStatus(200);
+        $resp->assertSee($newText);
+        $resp->assertDontSee($comment->text);
+
+        $this->assertDatabaseHas('comments', [
+            'text' => $newText,
+            'entity_id' => $page->id
+        ]);
+    }
+
+    public function test_comment_delete()
+    {
+        $this->asAdmin();
+        $page = Page::first();
+
+        $comment = factory(Comment::class)->make();
+        $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+
+        $comment = $page->comments()->first();
+
+        $resp = $this->delete("/ajax/comment/$comment->id");
+        $resp->assertStatus(200);
+
+        $this->assertDatabaseMissing('comments', [
+            'id' => $comment->id
+        ]);
+    }
+}
index ef5ea3aee1dbea99f9421b7fbb677688112556b4..07f1926c5b94f212e3f6064e41d5dccf834decd0 100644 (file)
@@ -257,11 +257,4 @@ class EntityTest extends BrowserKitTest
             ->seeInElement('#recently-updated-pages', $page->name);
     }
 
-    public function test_recently_created_pages_on_home()
-    {
-        $entityChain = $this->createEntityChainBelongingToUser($this->getEditor());
-        $this->asAdmin()->visit('/')
-            ->seeInElement('#recently-created-pages', $entityChain['page']->name);
-    }
-
 }
diff --git a/tests/HomepageTest.php b/tests/HomepageTest.php
new file mode 100644 (file)
index 0000000..7c77e94
--- /dev/null
@@ -0,0 +1,33 @@
+<?php namespace Tests;
+
+use BookStack\JointPermission;
+use BookStack\Page;
+use BookStack\Repos\EntityRepo;
+
+class HomepageTest extends TestCase
+{
+
+    public function test_default_homepage_visible()
+    {
+        $this->asEditor();
+        $homeVisit = $this->get('/');
+        $homeVisit->assertSee('My Recently Viewed');
+        $homeVisit->assertSee('Recently Updated Pages');
+        $homeVisit->assertSee('Recent Activity');
+    }
+
+    public function test_custom_homepage() {
+        $this->asEditor();
+        $name = 'My custom homepage';
+        $content = 'This is the body content of my custom homepage.';
+        $customPage = $this->newPage(['name' => $name, 'html' => $content]);
+        $this->setSettings(['app-homepage' => $customPage->id]);
+
+        $homeVisit = $this->get('/');
+        $homeVisit->assertSee($name);
+        $homeVisit->assertSee($content);
+        $homeVisit->assertSee('My Recently Viewed');
+        $homeVisit->assertSee('Recently Updated Pages');
+        $homeVisit->assertSee('Recent Activity');
+    }
+}
index eda5d092ab0c6a180454d047a3b7b92851852488..bd9e01d457b45f89a756b344622d52089fc73cf6 100644 (file)
@@ -627,7 +627,7 @@ class RolesTest extends BrowserKitTest
         $page = Page::first();
         $viewerRole = \BookStack\Role::getRole('viewer');
         $viewer = $this->getViewer();
-        $this->actingAs($viewer)->visit($page->getUrl())->assertResponseOk();
+        $this->actingAs($viewer)->visit($page->getUrl())->assertResponseStatus(200);
 
         $this->asAdmin()->put('/settings/roles/' . $viewerRole->id, [
             'display_name' => $viewerRole->display_name,
@@ -657,4 +657,109 @@ class RolesTest extends BrowserKitTest
             ->dontSee('Sort the current book');
     }
 
+    public function test_comment_create_permission () {
+        $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+
+        $this->actingAs($this->user)->addComment($ownPage);
+
+        $this->assertResponseStatus(403);
+
+        $this->giveUserPermissions($this->user, ['comment-create-all']);
+
+        $this->actingAs($this->user)->addComment($ownPage);
+        $this->assertResponseStatus(200);
+    }
+
+
+    public function test_comment_update_own_permission () {
+        $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+        $this->giveUserPermissions($this->user, ['comment-create-all']);
+        $commentId = $this->actingAs($this->user)->addComment($ownPage);
+
+        // no comment-update-own
+        $this->actingAs($this->user)->updateComment($commentId);
+        $this->assertResponseStatus(403);
+
+        $this->giveUserPermissions($this->user, ['comment-update-own']);
+
+        // now has comment-update-own
+        $this->actingAs($this->user)->updateComment($commentId);
+        $this->assertResponseStatus(200);
+    }
+
+    public function test_comment_update_all_permission () {
+        $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+        $commentId = $this->asAdmin()->addComment($ownPage);
+
+        // no comment-update-all
+        $this->actingAs($this->user)->updateComment($commentId);
+        $this->assertResponseStatus(403);
+
+        $this->giveUserPermissions($this->user, ['comment-update-all']);
+
+        // now has comment-update-all
+        $this->actingAs($this->user)->updateComment($commentId);
+        $this->assertResponseStatus(200);
+    }
+
+    public function test_comment_delete_own_permission () {
+        $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+        $this->giveUserPermissions($this->user, ['comment-create-all']);
+        $commentId = $this->actingAs($this->user)->addComment($ownPage);
+
+        // no comment-delete-own
+        $this->actingAs($this->user)->deleteComment($commentId);
+        $this->assertResponseStatus(403);
+
+        $this->giveUserPermissions($this->user, ['comment-delete-own']);
+
+        // now has comment-update-own
+        $this->actingAs($this->user)->deleteComment($commentId);
+        $this->assertResponseStatus(200);
+    }
+
+    public function test_comment_delete_all_permission () {
+        $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+        $commentId = $this->asAdmin()->addComment($ownPage);
+
+        // no comment-delete-all
+        $this->actingAs($this->user)->deleteComment($commentId);
+        $this->assertResponseStatus(403);
+
+        $this->giveUserPermissions($this->user, ['comment-delete-all']);
+
+        // now has comment-delete-all
+        $this->actingAs($this->user)->deleteComment($commentId);
+        $this->assertResponseStatus(200);
+    }
+
+    private function addComment($page) {
+        $comment = factory(\BookStack\Comment::class)->make();
+        $url = "/ajax/page/$page->id/comment";
+        $request = [
+            'text' => $comment->text,
+            'html' => $comment->html
+        ];
+
+        $this->postJson($url, $request);
+        $comment = $page->comments()->first();
+        return $comment === null ? null : $comment->id;
+    }
+
+    private function updateComment($commentId) {
+        $comment = factory(\BookStack\Comment::class)->make();
+        $url = "/ajax/comment/$commentId";
+        $request = [
+            'text' => $comment->text,
+            'html' => $comment->html
+        ];
+
+        return $this->putJson($url, $request);
+    }
+
+    private function deleteComment($commentId) {
+         $url = '/ajax/comment/' . $commentId;
+         return $this->json('DELETE', $url);
+    }
+
 }
index b008080d9c3ed92cad3976e6c554628facbb48a3..81bd93ec4de41fdea805824cf22980a5ea785531 100644 (file)
@@ -4,6 +4,7 @@ use BookStack\Book;
 use BookStack\Chapter;
 use BookStack\Repos\EntityRepo;
 use BookStack\Role;
+use BookStack\Services\SettingService;
 use Illuminate\Foundation\Testing\DatabaseTransactions;
 use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 
@@ -88,4 +89,16 @@ abstract class TestCase extends BaseTestCase
         $draftPage = $entityRepo->getDraftPage($book);
         return $entityRepo->publishPageDraft($draftPage, $input);
     }
+
+    /**
+     * Quickly sets an array of settings.
+     * @param $settingsArray
+     */
+    protected function setSettings($settingsArray)
+    {
+        $settings = app(SettingService::class);
+        foreach ($settingsArray as $key => $value) {
+            $settings->put($key, $value);
+        }
+    }
 }
\ No newline at end of file
diff --git a/version b/version
index 1c29999c8dc9d14ec52c9bfbe5f178f3ae25040d..2a8eccd9e299167bc71f7356c2065200bed903f2 100644 (file)
--- a/version
+++ b/version
@@ -1 +1 @@
-v0.17.4
\ No newline at end of file
+v0.18
\ No newline at end of file